UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

187 lines (186 loc) • 6 kB
import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect.js"; import { c } from "react-compiler-runtime"; import { jsx } from "react/jsx-runtime"; import { createContext, useContext, useEffect, useId, useReducer, useRef, useState } from "react"; //#region src/utils/descendant-registry.tsx /** * 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 = /* @__PURE__ */ 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 }); $[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 }; } //#endregion export { createDescendantRegistry };