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