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.

61 lines (58 loc) 2.26 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.useAriaLabelledBy = useAriaLabelledBy; var React = _interopRequireWildcard(require("react")); var _useIsoLayoutEffect = require("@base-ui/utils/useIsoLayoutEffect"); var _useBaseUiId = require("../useBaseUiId"); /** * @internal */ function useAriaLabelledBy(explicitAriaLabelledBy, labelId, labelSourceRef, enableFallback = true, labelSourceId) { const [fallbackAriaLabelledBy, setFallbackAriaLabelledBy] = React.useState(); const generatedLabelId = (0, _useBaseUiId.useBaseUiId)(labelSourceId ? `${labelSourceId}-label` : undefined); const ariaLabelledBy = explicitAriaLabelledBy ?? labelId ?? fallbackAriaLabelledBy; // Fallback for <span> controls labelled by wrapping/sibling native <label>. // Run after every commit so DOM association changes (e.g. label mount/unmount) // are reflected even when props/state deps are unchanged. // eslint-disable-next-line react-hooks/exhaustive-deps (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { const nextAriaLabelledBy = explicitAriaLabelledBy || labelId || !enableFallback ? undefined : getAriaLabelledBy(labelSourceRef.current, generatedLabelId); if (fallbackAriaLabelledBy !== nextAriaLabelledBy) { setFallbackAriaLabelledBy(nextAriaLabelledBy); } }); return ariaLabelledBy; } function getAriaLabelledBy(labelSource, generatedLabelId) { const label = findAssociatedLabel(labelSource); if (!label) { return undefined; } if (!label.id && generatedLabelId) { label.id = generatedLabelId; } return label.id || undefined; } function findAssociatedLabel(labelSource) { if (!labelSource) { return undefined; } // Fast path before the expensive `.labels` read. const parent = labelSource.parentElement; if (parent && parent.tagName === 'LABEL') { return parent; } const controlId = labelSource.id; if (controlId) { const nextSibling = labelSource.nextElementSibling; if (nextSibling && nextSibling.htmlFor === controlId) { return nextSibling; } } const labels = labelSource.labels; return labels && labels[0]; }