@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
116 lines (115 loc) • 4.17 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.0/LICENSE.txt */
import { makeGenericController } from "@arcgis/lumina/controllers";
import { createFocusTrap } from "focus-trap";
import { u as tabbableOptions, j as focusElement } from "./dom.js";
import { a as getConfig } from "./runtime.js";
function getEffectiveContainerElements(targetEl, { focusTrapOptions }, extraContainers) {
if (!focusTrapOptions?.extraContainers && !extraContainers) {
return targetEl;
}
return [targetEl, ...toContainerArray(focusTrapOptions?.extraContainers), ...toContainerArray(extraContainers)];
}
function toContainerArray(containers = []) {
return Array.isArray(containers) ? containers : [containers];
}
const outsideClickDeactivated = /* @__PURE__ */ new WeakSet();
function defaultSetReturnFocus(hostEl, el) {
const hasPreviousRelatedFocusedEl = el && el !== document.body && el !== document.documentElement;
if (!outsideClickDeactivated.has(hostEl) && hasPreviousRelatedFocusedEl) {
focusElement(el);
}
return false;
}
function createFocusTrapOptions(hostEl, options) {
const fallbackFocus = options?.fallbackFocus || hostEl;
const clickOutsideDeactivates = options?.clickOutsideDeactivates ?? true;
return {
fallbackFocus,
...options,
// the following options are not overridable
document: hostEl.ownerDocument,
tabbableOptions,
trapStack: getConfig().focusTrapStack,
clickOutsideDeactivates: (event) => {
if (!outsideClickDeactivated.has(hostEl)) {
outsideClickDeactivated.add(hostEl);
}
return typeof clickOutsideDeactivates === "function" ? clickOutsideDeactivates(event) : clickOutsideDeactivates;
},
onPostDeactivate: () => {
outsideClickDeactivated.delete(hostEl);
},
setReturnFocus: (el) => {
const returnFocusTarget = typeof options?.setReturnFocus === "function" ? options.setReturnFocus(el) : options?.setReturnFocus;
return returnFocusTarget === void 0 ? defaultSetReturnFocus(hostEl, el) : returnFocusTarget;
}
};
}
const useFocusTrap = (options) => {
return makeGenericController((component, controller) => {
let focusTrap;
let focusTrapEl;
let effectiveContainers;
const internalFocusTrapOptions = options.focusTrapOptions;
controller.onConnected(() => {
if (component[options.triggerProp] && focusTrap) {
utils.activate();
}
});
controller.onUpdate((changes) => {
if (component.hasUpdated && changes.has("focusTrapDisabled")) {
if (component.focusTrapDisabled) {
utils.deactivate();
} else {
utils.activate();
}
}
});
controller.onDisconnected(() => utils.deactivate());
const utils = {
get _instance() {
if (process.env.NODE_ENV === "test") {
return focusTrap;
}
return void 0;
},
activate: () => {
const targetEl = focusTrapEl || component.el;
if (!targetEl.isConnected) {
return;
}
if (!focusTrap) {
effectiveContainers ||= getEffectiveContainerElements(targetEl, component);
focusTrap = createFocusTrap(
effectiveContainers,
createFocusTrapOptions(targetEl, {
...internalFocusTrapOptions,
...component.focusTrapOptions
})
);
}
if (typeof component.focusTrapDisabledOverride === "function" ? !component.focusTrapDisabledOverride() : !component.focusTrapDisabled) {
focusTrap.activate();
}
},
deactivate: () => focusTrap?.deactivate(),
overrideFocusTrapEl: (el) => {
if (focusTrap) {
throw new Error("Focus trap already created");
}
focusTrapEl = el;
},
setExtraContainers: (extraContainers) => {
const targetEl = focusTrapEl || component.el;
effectiveContainers = getEffectiveContainerElements(targetEl, component, extraContainers);
},
updateContainerElements: () => {
return focusTrap?.updateContainerElements(effectiveContainers);
}
};
return utils;
});
};
export {
useFocusTrap as u
};