UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

206 lines (202 loc) • 6.77 kB
"use client"; import { createContext as createContext$1 } from "../../utils/context.js"; import { useSafeLayoutEffect } from "../../utils/effect.js"; import { mergeRefs } from "../../utils/ref.js"; import { utils_exports } from "../../utils/index.js"; import { useRef } from "react"; //#region src/hooks/use-descendants/index.ts const sortNodes = (nodes) => nodes.sort((a, b) => { const compare = a.compareDocumentPosition(b); if (compare & Node.DOCUMENT_POSITION_FOLLOWING || compare & Node.DOCUMENT_POSITION_CONTAINED_BY) return -1; if (compare & Node.DOCUMENT_POSITION_PRECEDING || compare & Node.DOCUMENT_POSITION_CONTAINS) return 1; if (compare & Node.DOCUMENT_POSITION_DISCONNECTED || compare & Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC) { console.warn("Cannot sort the given nodes."); return 0; } else return 0; }); const getNextIndex = (current, max, loop) => { let next = current + 1; if (loop && next >= max) next = 0; return next; }; const getPrevIndex = (current, max, loop) => { let next = current - 1; if (loop && next < 0) next = max; return next; }; const descendantManager = () => { const descendants = /* @__PURE__ */ new Map(); const setIndexes = (next) => { descendants.forEach((descendant) => { descendant.index = next.indexOf(descendant.node); descendant.node.dataset.index = descendant.index.toString(); }); }; const set = (node, props) => { if (!node || descendants.has(node)) return; const sorted = sortNodes(Array.from(descendants.keys()).concat(node)); const descendant = { ...props, index: -1, node }; descendants.set(node, descendant); setIndexes(sorted); }; const destroy = () => descendants.clear(); const register = (props) => { return (node) => set(node, props); }; const unregister = (node) => { if (node == null) return; descendants.delete(node); setIndexes(sortNodes(Array.from(descendants.keys()))); }; const count = () => values().length; const enabledCount = () => enabledValues().length; const active = (target, options) => { if (!target) return; if (!(target instanceof Node)) target = target.node; if ((0, utils_exports.isTruthyDataAttr)(target.dataset.activedescendant)) return; values().forEach(({ node }) => { delete node.dataset.activedescendant; }); target.dataset.activedescendant = ""; if (options) target.focus(options); }; const indexOf = (target) => { if (!target) return -1; if (target instanceof Node) return descendants.get(target)?.index ?? -1; else return descendants.get(target.node)?.index ?? -1; }; const enabledIndexOf = (target) => { if (!target) return -1; if (target instanceof Node) return enabledValues().findIndex(({ node }) => node.isSameNode(target)); else return enabledValues().findIndex(({ node }) => node.isSameNode(target.node)); }; const values = () => Array.from(descendants.values()).sort((a, b) => a.index - b.index); const enabledValues = () => values().filter(({ disabled, node }) => !(0, utils_exports.runIfFn)(disabled, node)); const value = (indexOrNode) => { if (!count() || indexOrNode == null) return void 0; return (0, utils_exports.isNumber)(indexOrNode) ? values()[indexOrNode] : descendants.get(indexOrNode); }; const enabledValue = (index) => { if (!enabledCount()) return void 0; return enabledValues()[index]; }; const firstValue = () => value(0); const enabledFirstValue = () => enabledValue(0); const lastValue = () => value(count() - 1); const enabledLastValue = () => enabledValue(enabledCount() - 1); const prevValue = (indexOrNode, loop = true) => { if (!count()) return void 0; const currentIndex = (0, utils_exports.isNumber)(indexOrNode) ? indexOrNode : indexOf(indexOrNode); if (currentIndex === -1) return void 0; return value(getPrevIndex(currentIndex, count() - 1, loop)); }; const enabledPrevValue = (indexOrNode, loop = true) => { if (!enabledCount()) return void 0; let index = (0, utils_exports.isNumber)(indexOrNode) ? indexOrNode : indexOf(indexOrNode); let enabledValue$1 = null; let recurred = false; while (enabledValue$1 == null) { index--; if (index < 0) { if (!loop) return; index = count() - 1; recurred = true; } const descendant = value(index); enabledValue$1 = descendant && !(0, utils_exports.runIfFn)(descendant.disabled, descendant.node) ? descendant : null; } if (recurred) enabledValue$1.recurred = recurred; return enabledValue$1; }; const nextValue = (indexOrNode, loop = true) => { if (!count()) return void 0; const currentIndex = (0, utils_exports.isNumber)(indexOrNode) ? indexOrNode : indexOf(indexOrNode); if (currentIndex === -1) return void 0; return value(getNextIndex(currentIndex, count(), loop)); }; const enabledNextValue = (indexOrNode, loop = true) => { if (!enabledCount()) return void 0; let index = (0, utils_exports.isNumber)(indexOrNode) ? indexOrNode : indexOf(indexOrNode); let enabledValue$1 = null; let recurred = false; while (enabledValue$1 == null) { index++; if (index >= count()) { if (!loop) return; index = 0; recurred = true; } const descendant = value(index); enabledValue$1 = descendant && !(0, utils_exports.runIfFn)(descendant.disabled, descendant.node) ? descendant : null; } if (recurred) enabledValue$1.recurred = recurred; return enabledValue$1; }; return { active, count, destroy, enabledCount, enabledFirstValue, enabledIndexOf, enabledLastValue, enabledNextValue, enabledPrevValue, enabledValue, enabledValues, firstValue, indexOf, lastValue, nextValue, prevValue, register, unregister, value, values }; }; /** * `useDescendants` is a custom hook that manages descendants. * * @see https://yamada-ui.com/docs/hooks/use-descendants */ const createDescendants = () => { const [DescendantsContext, useDescendantsContext] = createContext$1({ name: "DescendantsContext" }); const useDescendantRegister = (descendants) => { const ref = useRef(null); useSafeLayoutEffect(() => { return () => { if (ref.current) descendants?.unregister(ref.current); }; }, []); return (props) => mergeRefs(ref, descendants?.register(props)); }; const useDescendants = () => { const descendants = useRef(descendantManager()); useSafeLayoutEffect(() => { return () => descendants.current.destroy(); }); return descendants.current; }; const useDescendant = (props) => { const descendants = useDescendantsContext(); return { descendants, register: useDescendantRegister(descendants)(props) }; }; return { DescendantsContext, useDescendant, useDescendantRegister, useDescendants, useDescendantsContext }; }; //#endregion export { createDescendants }; //# sourceMappingURL=index.js.map