UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

105 lines (104 loc) 3.77 kB
/*! 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 };