@base-ui/react
Version:
Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.
78 lines (75 loc) • 2.54 kB
JavaScript
;
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useCompositeListItem = useCompositeListItem;
var React = _interopRequireWildcard(require("react"));
var _useIsoLayoutEffect = require("@base-ui/utils/useIsoLayoutEffect");
var _CompositeListContext = require("./CompositeListContext");
/**
* Used to register a list item and its index (DOM position) in the `CompositeList`.
*/
function useCompositeListItem(params = {}) {
const {
guess,
label,
metadata,
textRef,
index: externalIndex
} = params;
const {
register,
unregister,
subscribeMapChange,
nextIndexRef
} = (0, _CompositeListContext.useCompositeListContext)();
// Guess the index from the render order. This avoids a re-render after mount for
// flat lists rendered in DOM order; when the guess is wrong (grouped or out-of-order
// rendering), the commit flush corrects it before paint.
const indexRef = React.useRef(-1);
const [internalIndex, setInternalIndex] = React.useState(externalIndex == null && guess ? () => {
if (indexRef.current === -1) {
const newIndex = nextIndexRef.current;
nextIndexRef.current += 1;
indexRef.current = newIndex;
}
return indexRef.current;
} : -1);
const index = externalIndex ?? internalIndex;
const componentRef = React.useRef(null);
// Deliberately identity-sensitive: nested items sharing one DOM node rely on ref attachment
// order to decide which registration wins, and republishing from an effect instead would let
// an inner item's later update silently take ownership from the outer one.
const ref = React.useCallback(node => {
const previousNode = componentRef.current;
if (previousNode) {
unregister(previousNode);
}
componentRef.current = node;
if (node) {
register(node, {
metadata: metadata ?? null,
index: externalIndex ?? null,
label,
textRef
});
}
}, [externalIndex, register, unregister, metadata, label, textRef]);
(0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => {
if (externalIndex != null) {
return undefined;
}
return subscribeMapChange(map => {
const i = componentRef.current ? map.get(componentRef.current)?.index : null;
if (i != null) {
setInternalIndex(i);
}
});
}, [externalIndex, subscribeMapChange]);
return {
ref,
index
};
}