@primer/react
Version:
An implementation of GitHub's Primer Design System using React
187 lines (186 loc) • 6 kB
JavaScript
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 };