UNPKG

@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
'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; }