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