@ariakit/react-core
Version:
Ariakit React core
341 lines (311 loc) • 11.4 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
var _T3QB4FR3cjs = require('./T3QB4FR3.cjs');
var _WBFXWJUHcjs = require('./WBFXWJUH.cjs');
var _MZ2HG624cjs = require('./MZ2HG624.cjs');
// src/focusable/focusable.tsx
var _dom = require('@ariakit/core/utils/dom');
var _events = require('@ariakit/core/utils/events');
var _focus = require('@ariakit/core/utils/focus');
var _misc = require('@ariakit/core/utils/misc');
var _platform = require('@ariakit/core/utils/platform');
var _react = require('react');
var TagName = "div";
var isSafariBrowser = _platform.isSafari.call(void 0, );
var alwaysFocusVisibleInputTypes = [
"text",
"search",
"url",
"tel",
"email",
"password",
"number",
"date",
"month",
"week",
"time",
"datetime",
"datetime-local"
];
var safariFocusAncestorSymbol = Symbol("safariFocusAncestor");
function isSafariFocusAncestor(element) {
if (!element) return false;
return !!element[safariFocusAncestorSymbol];
}
function markSafariFocusAncestor(element, value) {
if (!element) return;
element[safariFocusAncestorSymbol] = value;
}
function isAlwaysFocusVisible(element) {
const { tagName, readOnly, type } = element;
if (tagName === "TEXTAREA" && !readOnly) return true;
if (tagName === "SELECT" && !readOnly) return true;
if (tagName === "INPUT" && !readOnly) {
return alwaysFocusVisibleInputTypes.includes(type);
}
if (element.isContentEditable) return true;
const role = element.getAttribute("role");
if (role === "combobox" && element.dataset.name) {
return true;
}
return false;
}
function getLabels(element) {
if ("labels" in element) {
return element.labels;
}
return null;
}
function isNativeCheckboxOrRadio(element) {
const tagName = element.tagName.toLowerCase();
if (tagName === "input" && element.type) {
return element.type === "radio" || element.type === "checkbox";
}
return false;
}
function isNativeTabbable(tagName) {
if (!tagName) return true;
return tagName === "button" || tagName === "summary" || tagName === "input" || tagName === "select" || tagName === "textarea" || tagName === "a";
}
function supportsDisabledAttribute(tagName) {
if (!tagName) return true;
return tagName === "button" || tagName === "input" || tagName === "select" || tagName === "textarea";
}
function getTabIndex(focusable, trulyDisabled, nativeTabbable, supportsDisabled, tabIndexProp) {
if (!focusable) {
return tabIndexProp;
}
if (trulyDisabled) {
if (nativeTabbable && !supportsDisabled) {
return -1;
}
return;
}
if (nativeTabbable) {
return tabIndexProp;
}
return tabIndexProp || 0;
}
function useDisableEvent(onEvent, disabled) {
return _MZ2HG624cjs.useEvent.call(void 0, (event) => {
onEvent == null ? void 0 : onEvent(event);
if (event.defaultPrevented) return;
if (disabled) {
event.stopPropagation();
event.preventDefault();
}
});
}
var hasInstalledGlobalEventListeners = false;
var isKeyboardModality = true;
function onGlobalMouseDown(event) {
const target = event.target;
if (target && "hasAttribute" in target) {
if (!target.hasAttribute("data-focus-visible")) {
isKeyboardModality = false;
}
}
}
function onGlobalKeyDown(event) {
if (event.metaKey) return;
if (event.ctrlKey) return;
if (event.altKey) return;
isKeyboardModality = true;
}
var useFocusable = _WBFXWJUHcjs.createHook.call(void 0,
function useFocusable2({
focusable = true,
accessibleWhenDisabled,
autoFocus,
onFocusVisible,
...props
}) {
const ref = _react.useRef.call(void 0, null);
_react.useEffect.call(void 0, () => {
if (!focusable) return;
if (hasInstalledGlobalEventListeners) return;
_events.addGlobalEventListener.call(void 0, "mousedown", onGlobalMouseDown, true);
_events.addGlobalEventListener.call(void 0, "keydown", onGlobalKeyDown, true);
hasInstalledGlobalEventListeners = true;
}, [focusable]);
if (isSafariBrowser) {
_react.useEffect.call(void 0, () => {
if (!focusable) return;
const element = ref.current;
if (!element) return;
if (!isNativeCheckboxOrRadio(element)) return;
const labels = getLabels(element);
if (!labels) return;
const onMouseUp = () => queueMicrotask(() => element.focus());
for (const label of labels) {
label.addEventListener("mouseup", onMouseUp);
}
return () => {
for (const label of labels) {
label.removeEventListener("mouseup", onMouseUp);
}
};
}, [focusable]);
}
const disabled = focusable && _misc.disabledFromProps.call(void 0, props);
const trulyDisabled = !!disabled && !accessibleWhenDisabled;
const [focusVisible, setFocusVisible] = _react.useState.call(void 0, false);
_react.useEffect.call(void 0, () => {
if (!focusable) return;
if (trulyDisabled && focusVisible) {
setFocusVisible(false);
}
}, [focusable, trulyDisabled, focusVisible]);
_react.useEffect.call(void 0, () => {
if (!focusable) return;
if (!focusVisible) return;
const element = ref.current;
if (!element) return;
if (typeof IntersectionObserver === "undefined") return;
const observer = new IntersectionObserver(() => {
if (!_focus.isFocusable.call(void 0, element)) {
setFocusVisible(false);
}
});
observer.observe(element);
return () => observer.disconnect();
}, [focusable, focusVisible]);
const onKeyPressCapture = useDisableEvent(
props.onKeyPressCapture,
disabled
);
const onMouseDownCapture = useDisableEvent(
props.onMouseDownCapture,
disabled
);
const onClickCapture = useDisableEvent(props.onClickCapture, disabled);
const onMouseDownProp = props.onMouseDown;
const onMouseDown = _MZ2HG624cjs.useEvent.call(void 0, (event) => {
onMouseDownProp == null ? void 0 : onMouseDownProp(event);
if (event.defaultPrevented) return;
if (!focusable) return;
const element = event.currentTarget;
if (!isSafariBrowser) return;
if (_events.isPortalEvent.call(void 0, event)) return;
if (!_dom.isButton.call(void 0, element) && !isNativeCheckboxOrRadio(element)) return;
let receivedFocus = false;
const onFocus = () => {
receivedFocus = true;
};
const options = { capture: true, once: true };
element.addEventListener("focusin", onFocus, options);
const focusableContainer = _focus.getClosestFocusable.call(void 0, element.parentElement);
markSafariFocusAncestor(focusableContainer, true);
_events.queueBeforeEvent.call(void 0, element, "mouseup", () => {
element.removeEventListener("focusin", onFocus, true);
markSafariFocusAncestor(focusableContainer, false);
if (receivedFocus) return;
_focus.focusIfNeeded.call(void 0, element);
});
});
const handleFocusVisible = (event, currentTarget) => {
if (currentTarget) {
event.currentTarget = currentTarget;
}
if (!focusable) return;
const element = event.currentTarget;
if (!element) return;
if (!_focus.hasFocus.call(void 0, element)) return;
onFocusVisible == null ? void 0 : onFocusVisible(event);
if (event.defaultPrevented) return;
element.dataset.focusVisible = "true";
setFocusVisible(true);
};
const onKeyDownCaptureProp = props.onKeyDownCapture;
const onKeyDownCapture = _MZ2HG624cjs.useEvent.call(void 0, (event) => {
onKeyDownCaptureProp == null ? void 0 : onKeyDownCaptureProp(event);
if (event.defaultPrevented) return;
if (!focusable) return;
if (focusVisible) return;
if (event.metaKey) return;
if (event.altKey) return;
if (event.ctrlKey) return;
if (!_events.isSelfTarget.call(void 0, event)) return;
const element = event.currentTarget;
const applyFocusVisible = () => handleFocusVisible(event, element);
_events.queueBeforeEvent.call(void 0, element, "focusout", applyFocusVisible);
});
const onFocusCaptureProp = props.onFocusCapture;
const onFocusCapture = _MZ2HG624cjs.useEvent.call(void 0, (event) => {
onFocusCaptureProp == null ? void 0 : onFocusCaptureProp(event);
if (event.defaultPrevented) return;
if (!focusable) return;
if (!_events.isSelfTarget.call(void 0, event)) {
setFocusVisible(false);
return;
}
const element = event.currentTarget;
const applyFocusVisible = () => handleFocusVisible(event, element);
if (isKeyboardModality || isAlwaysFocusVisible(event.target)) {
_events.queueBeforeEvent.call(void 0, event.target, "focusout", applyFocusVisible);
} else {
setFocusVisible(false);
}
});
const onBlurProp = props.onBlur;
const onBlur = _MZ2HG624cjs.useEvent.call(void 0, (event) => {
onBlurProp == null ? void 0 : onBlurProp(event);
if (!focusable) return;
if (!_events.isFocusEventOutside.call(void 0, event)) return;
event.currentTarget.removeAttribute("data-focus-visible");
setFocusVisible(false);
});
const autoFocusOnShow = _react.useContext.call(void 0, _T3QB4FR3cjs.FocusableContext);
const autoFocusRef = _MZ2HG624cjs.useEvent.call(void 0, (element) => {
if (!focusable) return;
if (!autoFocus) return;
if (!element) return;
if (!autoFocusOnShow) return;
queueMicrotask(() => {
if (_focus.hasFocus.call(void 0, element)) return;
if (!_focus.isFocusable.call(void 0, element)) return;
element.focus();
});
});
const tagName = _MZ2HG624cjs.useTagName.call(void 0, ref);
const nativeTabbable = focusable && isNativeTabbable(tagName);
const supportsDisabled = focusable && supportsDisabledAttribute(tagName);
const styleProp = props.style;
const style = _react.useMemo.call(void 0, () => {
if (trulyDisabled) {
return { pointerEvents: "none", ...styleProp };
}
return styleProp;
}, [trulyDisabled, styleProp]);
props = {
"data-focus-visible": focusable && focusVisible || void 0,
"data-autofocus": autoFocus || void 0,
"aria-disabled": disabled || void 0,
...props,
ref: _MZ2HG624cjs.useMergeRefs.call(void 0, ref, autoFocusRef, props.ref),
style,
tabIndex: getTabIndex(
focusable,
trulyDisabled,
nativeTabbable,
supportsDisabled,
props.tabIndex
),
disabled: supportsDisabled && trulyDisabled ? true : void 0,
// TODO: Test Focusable contentEditable.
contentEditable: disabled ? void 0 : props.contentEditable,
onKeyPressCapture,
onClickCapture,
onMouseDownCapture,
onMouseDown,
onKeyDownCapture,
onFocusCapture,
onBlur
};
return _misc.removeUndefinedValues.call(void 0, props);
}
);
var Focusable = _WBFXWJUHcjs.forwardRef.call(void 0, function Focusable2(props) {
const htmlProps = useFocusable(props);
return _WBFXWJUHcjs.createElement.call(void 0, TagName, htmlProps);
});
exports.isSafariFocusAncestor = isSafariFocusAncestor; exports.useFocusable = useFocusable; exports.Focusable = Focusable;