@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
105 lines (104 loc) • 3.77 kB
JavaScript
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details.
v3.2.1 */
import { makeGenericController } from "@arcgis/lumina/controllers";
import { createFocusTrap } from "focus-trap";
import { p as tabbableOptions, b as focusElement } from "./dom.js";
import { f as focusTrapStack } from "./runtime.js";
const outsideClickDeactivated = /* @__PURE__ */ new WeakSet();
function createFocusTrapOptions(hostEl, options) {
const fallbackFocus = options?.fallbackFocus || hostEl;
const clickOutsideDeactivates = options?.clickOutsideDeactivates ?? true;
return {
fallbackFocus,
setReturnFocus: (el) => {
if (!outsideClickDeactivated.has(hostEl)) {
focusElement(el);
}
return false;
},
...options,
// the following options are not overridable
document: hostEl.ownerDocument,
tabbableOptions,
trapStack: focusTrapStack,
clickOutsideDeactivates: (event) => {
if (!outsideClickDeactivated.has(hostEl)) {
outsideClickDeactivated.add(hostEl);
}
return typeof clickOutsideDeactivates === "function" ? clickOutsideDeactivates(event) : clickOutsideDeactivates;
},
onPostDeactivate: () => {
outsideClickDeactivated.delete(hostEl);
}
};
}
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 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 = {
activate: () => {
const targetEl = focusTrapEl || component.el;
if (!targetEl.isConnected) {
return;
}
if (!focusTrap) {
const effectiveFocusTrapOptions = {
...internalFocusTrapOptions,
...component.focusTrapOptions
};
effectiveContainers ||= getEffectiveContainerElements(targetEl, component);
focusTrap = createFocusTrap(effectiveContainers, createFocusTrapOptions(targetEl, effectiveFocusTrapOptions));
}
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
};