UNPKG

2.78 kBJavaScriptView Raw
1import * as React from 'react';
2export const CompoundComponentContext = /*#__PURE__*/React.createContext(null);
3CompoundComponentContext.displayName = 'CompoundComponentContext';
4/**
5 * Sorts the subitems by their position in the DOM.
6 */
7function sortSubitems(subitems) {
8 const subitemsArray = Array.from(subitems.keys()).map(key => {
9 const subitem = subitems.get(key);
10 return {
11 key,
12 subitem
13 };
14 });
15 subitemsArray.sort((a, b) => {
16 const aNode = a.subitem.ref.current;
17 const bNode = b.subitem.ref.current;
18 if (aNode === null || bNode === null || aNode === bNode) {
19 return 0;
20 }
21
22 // eslint-disable-next-line no-bitwise
23 return aNode.compareDocumentPosition(bNode) & Node.DOCUMENT_POSITION_PRECEDING ? 1 : -1;
24 });
25 return new Map(subitemsArray.map(item => [item.key, item.subitem]));
26}
27
28/**
29 * Provides a way for a component to know about its children.
30 *
31 * Child components register themselves with the `useCompoundItem` hook, passing in arbitrary metadata to the parent.
32 *
33 * This is a more powerful altervantive to `children` traversal, as child components don't have to be placed
34 * directly inside the parent component. They can be anywhere in the tree (and even rendered by other components).
35 *
36 * The downside is that this doesn't work with SSR as it relies on the useEffect hook.
37 *
38 * @ignore - internal hook.
39 */
40export function useCompoundParent() {
41 const [subitems, setSubitems] = React.useState(new Map());
42 const subitemKeys = React.useRef(new Set());
43 const deregisterItem = React.useCallback(function deregisterItem(id) {
44 subitemKeys.current.delete(id);
45 setSubitems(previousState => {
46 const newState = new Map(previousState);
47 newState.delete(id);
48 return newState;
49 });
50 }, []);
51 const registerItem = React.useCallback(function registerItem(id, item) {
52 let providedOrGeneratedId;
53 if (typeof id === 'function') {
54 providedOrGeneratedId = id(subitemKeys.current);
55 } else {
56 providedOrGeneratedId = id;
57 }
58 subitemKeys.current.add(providedOrGeneratedId);
59 setSubitems(previousState => {
60 const newState = new Map(previousState);
61 newState.set(providedOrGeneratedId, item);
62 return newState;
63 });
64 return {
65 id: providedOrGeneratedId,
66 deregister: () => deregisterItem(providedOrGeneratedId)
67 };
68 }, [deregisterItem]);
69 const sortedSubitems = React.useMemo(() => sortSubitems(subitems), [subitems]);
70 const getItemIndex = React.useCallback(function getItemIndex(id) {
71 return Array.from(sortedSubitems.keys()).indexOf(id);
72 }, [sortedSubitems]);
73 return {
74 contextValue: {
75 getItemIndex,
76 registerItem,
77 totalSubitemCount: subitems.size
78 },
79 subitems: sortedSubitems
80 };
81}
\No newline at end of file