@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.
66 lines (65 loc) • 2.19 kB
JavaScript
'use client';
import * as React from 'react';
import { useIsoLayoutEffect } from '@base-ui/utils/useIsoLayoutEffect';
import { useStableCallback } from '@base-ui/utils/useStableCallback';
import { useRefWithInit } from '@base-ui/utils/useRefWithInit';
import { isElement } from '@floating-ui/utils/dom';
import { NOOP } from "../utils/noop.js";
import { useBaseUiId } from "../utils/useBaseUiId.js";
import { useLabelableContext } from "./LabelableContext.js";
export function useLabelableId(params = {}) {
const {
id,
implicit = false,
controlRef
} = params;
const {
controlId,
registerControlId
} = useLabelableContext();
const defaultId = useBaseUiId(id);
const controlIdForEffect = implicit ? controlId : undefined;
const controlSourceRef = useRefWithInit(() => Symbol('labelable-control'));
const hasRegisteredRef = React.useRef(false);
const hadExplicitIdRef = React.useRef(id != null);
const unregisterControlId = useStableCallback(() => {
if (!hasRegisteredRef.current || registerControlId === NOOP) {
return;
}
hasRegisteredRef.current = false;
registerControlId(controlSourceRef.current, undefined);
});
useIsoLayoutEffect(() => {
if (registerControlId === NOOP) {
return undefined;
}
let nextId;
if (implicit) {
const elem = controlRef?.current;
if (isElement(elem) && elem.closest('label') != null) {
nextId = id ?? null;
} else {
nextId = controlIdForEffect ?? defaultId;
}
} else if (id != null) {
hadExplicitIdRef.current = true;
nextId = id;
} else if (hadExplicitIdRef.current) {
nextId = defaultId;
} else {
unregisterControlId();
return undefined;
}
if (nextId === undefined) {
unregisterControlId();
return undefined;
}
hasRegisteredRef.current = true;
registerControlId(controlSourceRef.current, nextId);
return undefined;
}, [id, controlRef, controlIdForEffect, registerControlId, implicit, defaultId, controlSourceRef, unregisterControlId]);
React.useEffect(() => {
return unregisterControlId;
}, [unregisterControlId]);
return controlId ?? defaultId;
}