UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

223 lines (217 loc) • 6.7 kB
import { c } from 'react-compiler-runtime'; import { useContext, useId, useEffect, useState, useRef, useReducer, createContext } from 'react'; import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect.js'; import { jsx } from 'react/jsx-runtime'; /** * Create a "descendant registry" for a component. This allows a parent to store and track an ordered registry of * child components, even if they are deeply nested in the tree. For example, a menu component can use this to track * every menu item inside of it, including menu items inside of fragments and subcomponents. The order of the resulting * `Map` will match the render order in the React tree. * * Usage: * 1. Create a registry at the file level in a similar manner to creating a new React context. Set `T` to the data type * of the registry - this can be anything **except** a function. * 2. In the parent component, instantiate the registry state via `useRegistryState`. Then wrap `children` in the * registry context `Provider` with `setRegistry` set to the state setter function. * 3. Register child components via `useRegisterDescendant` or `useRegisterDescendantCallback`. * 4. Access the registered data using the value from `useRegistryState`. This will be a map of `string` to `T`, where * the string key is a unique and stable identifier for each component which can be used as a `key` if necessary. * * @note Note that this pattern is not SSR compatible. It won't raise errors or hydration mismatches, but the * registry will not be available during SSR. The registry is built during the effect phase, so it will be populated * after hydration on the client. The initial `undefined` value can be used to safely show loading UI during SSR/initial * render if necessary. */ function createDescendantRegistry() { const Context = /*#__PURE__*/createContext({ register: () => () => {}, updateValue: () => {}, key: -1 }); /** * Instantiate descendant registry state. The initial value will be `undefined`, indicating that the registry hasn't * been built yet. */ function useRegistryState() { return useState(); } /** Register a descendant component with the registry. */ function useRegisterDescendant(value) { const $ = c(16); const { register, updateValue, key } = useContext(Context); const id = useId(); let t0; if ($[0] !== id || $[1] !== register) { t0 = () => register(id); $[0] = id; $[1] = register; $[2] = t0; } else { t0 = $[2]; } let t1; if ($[3] !== id || $[4] !== key || $[5] !== register) { t1 = [register, id, key]; $[3] = id; $[4] = key; $[5] = register; $[6] = t1; } else { t1 = $[6]; } useEffect(t0, t1); let t2; if ($[7] !== id || $[8] !== updateValue || $[9] !== value) { t2 = () => updateValue(id, value); $[7] = id; $[8] = updateValue; $[9] = value; $[10] = t2; } else { t2 = $[10]; } let t3; if ($[11] !== id || $[12] !== key || $[13] !== updateValue || $[14] !== value) { t3 = [updateValue, id, value, key]; $[11] = id; $[12] = key; $[13] = updateValue; $[14] = value; $[15] = t3; } else { t3 = $[15]; } useEffect(t2, t3); return id; } const unsetValue = Symbol('unset'); /** Provide context for registering descendant components. This only needs to wrap `children`. */ function Provider(t0) { const $ = c(14); const { children, setRegistry } = t0; const workingRegistryRef = useRef("queued"); const [key, rebuildRegistry] = useReducer(_temp, 0); let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = function instantiateNewRegistry() { if (workingRegistryRef.current === "queued") { workingRegistryRef.current = new Map(); } }; $[0] = t1; } else { t1 = $[0]; } useIsomorphicLayoutEffect(t1); let t2; if ($[1] !== setRegistry) { t2 = id => { if (workingRegistryRef.current instanceof Map) { workingRegistryRef.current.set(id, unsetValue); } else { if (workingRegistryRef.current === "idle") { workingRegistryRef.current = "queued"; rebuildRegistry(); } } return function unregister() { if (workingRegistryRef.current instanceof Map) { workingRegistryRef.current.delete(id); } else { if (workingRegistryRef.current === "idle") { setRegistry(prev_0 => { const copy = new Map(prev_0); copy.delete(id); return copy; }); } } }; }; $[1] = setRegistry; $[2] = t2; } else { t2 = $[2]; } const register = t2; let t3; if ($[3] !== setRegistry) { t3 = (id_0, value) => { if (workingRegistryRef.current instanceof Map) { workingRegistryRef.current.set(id_0, value); } else { if (workingRegistryRef.current === "idle") { setRegistry(prev_1 => new Map(prev_1).set(id_0, value)); } } }; $[3] = setRegistry; $[4] = t3; } else { t3 = $[4]; } const updateValue = t3; let t4; if ($[5] !== setRegistry) { t4 = function commitWorkingRegistry() { if (workingRegistryRef.current instanceof Map) { const setEntries = Array.from(workingRegistryRef.current.entries()).filter(_temp2); setRegistry(new Map(setEntries)); workingRegistryRef.current = "idle"; } }; $[5] = setRegistry; $[6] = t4; } else { t4 = $[6]; } useEffect(t4); let t5; if ($[7] !== key || $[8] !== register || $[9] !== updateValue) { t5 = { register, updateValue, key }; $[7] = key; $[8] = register; $[9] = updateValue; $[10] = t5; } else { t5 = $[10]; } const contextValue = t5; let t6; if ($[11] !== children || $[12] !== contextValue) { t6 = /*#__PURE__*/jsx(Context.Provider, { value: contextValue, children: children }); $[11] = children; $[12] = contextValue; $[13] = t6; } else { t6 = $[13]; } return t6; } function _temp2(entry) { return entry[1] !== unsetValue; } function _temp(prev) { return prev + 1; } return { Provider, useRegistryState, useRegisterDescendant }; } export { createDescendantRegistry };