diff --git a/packages/descendants/examples/add-remove.example.tsx b/packages/descendants/examples/add-remove.example.tsx new file mode 100644 index 000000000..7b6d55d5a --- /dev/null +++ b/packages/descendants/examples/add-remove.example.tsx @@ -0,0 +1,162 @@ +import * as React from "react"; +import { useStatefulRefValue } from "@reach/utils/use-stateful-ref-value"; +import { + createDescendantContext, + DescendantProvider, + useDescendant, + useDescendantsInit, +} from "@reach/descendants"; +import type { Descendant } from "@reach/descendants"; +import { isFunction } from "@reach/utils/type-check"; + +/* +For dynamic lists where items may be rearranged, you should explicitly pass an +index prop. Of course you don't *need* the descendants package at all in this +case, this is to illustrate that the explicit index takes precedence over the +calculated index. + */ + +const name = "Adding/Removing"; + +function Example() { + let addBtn = React.useRef(null); + let [{ items, inputValue }, set] = React.useState(() => ({ + items: [] as string[], + inputValue: "", + })); + + return ( +
+
{ + event.preventDefault(); + let value = inputValue.trim(); + if (!value) { + console.log("Please enter a value!"); + return; + } else if (items.includes(value)) { + console.log("Items in the list must be unique"); + return; + } + + set(({ items }) => ({ + items: [...items, value], + inputValue: "", + })); + }} + > +
+ +
+ + +
+ +
+ + {items.map((item, i) => ( + + {({ index }) => ( + +
Item: {item}
+
Index: {index}
+
+ )} +
+ ))} +
+
+ ); +} + +Example.storyName = name; +export { Example }; + +const DescendantContext = + createDescendantContext("DescendantContext"); + +const ListProvider: React.FC = ({ children }) => { + let [descendants, setDescendants] = useDescendantsInit(); + return ( + + {children} + + ); +}; + +const ListItem: React.FC<{ + children: React.ReactNode | ((props: { index: number }) => React.ReactNode); + index?: number; + style?: React.CSSProperties; +}> = ({ children, index: indexProp, ...rest }) => { + let ref = React.useRef(null); + let [element, handleRefSet] = useStatefulRefValue(ref, null); + let descendant: Omit = React.useMemo(() => { + return { element }; + }, [element]); + let index = useDescendant(descendant, DescendantContext, indexProp); + + return ( +
+ {isFunction(children) ? children({ index }) : children} +
+ ); +}; + +type DescendantType = Descendant; diff --git a/packages/descendants/examples/index.story.js b/packages/descendants/examples/index.story.js new file mode 100644 index 000000000..3ff13f3f0 --- /dev/null +++ b/packages/descendants/examples/index.story.js @@ -0,0 +1,6 @@ +export { Example as AddRemove } from "./add-remove.example"; +export { Example as Sorting } from "./sorting.example"; + +export default { + title: "Descendants", +}; diff --git a/packages/descendants/examples/sorting.example.tsx b/packages/descendants/examples/sorting.example.tsx new file mode 100644 index 000000000..0e82a5913 --- /dev/null +++ b/packages/descendants/examples/sorting.example.tsx @@ -0,0 +1,101 @@ +import * as React from "react"; +import { action } from "@storybook/addon-actions"; +import { useStatefulRefValue } from "@reach/utils/use-stateful-ref-value"; +import { + createDescendantContext, + DescendantProvider, + useDescendant, + useDescendantsInit, +} from "@reach/descendants"; +import type { Descendant } from "@reach/descendants"; +import { isFunction } from "@reach/utils/type-check"; + +/* +For dynamic lists where items may be rearranged, you should explicitly pass an +index prop. Of course you don't *need* the descendants package at all in this +case, this is to illustrate that the explicit index takes precedence over the +calculated index. + */ + +const name = "Sorting"; + +function Example() { + let [items, setItems] = React.useState([0, 1, 2, 3, 4, 5]); + return ( +
+ +
+ + {items.map((item, i) => ( + + {({ index }) => ( + +
Item: {item}
+
Index: {index}
+
+ )} +
+ ))} +
+
+ ); +} + +Example.storyName = name; +export { Example }; + +const DescendantContext = + createDescendantContext("DescendantContext"); + +const ListProvider: React.FC = ({ children }) => { + let [descendants, setDescendants] = useDescendantsInit(); + return ( + + {children} + + ); +}; + +const ListItem: React.FC<{ + children: React.ReactNode | ((props: { index: number }) => React.ReactNode); + index?: number; + style?: React.CSSProperties; +}> = ({ children, index: indexProp, ...rest }) => { + let ref = React.useRef(null); + let [element, handleRefSet] = useStatefulRefValue(ref, null); + let descendant: Omit = React.useMemo(() => { + return { element }; + }, [element]); + let index = useDescendant(descendant, DescendantContext, indexProp); + + return ( +
+ {isFunction(children) ? children({ index }) : children} +
+ ); +}; + +type DescendantType = Descendant;