@zag-js/dismissable 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -11,7 +11,15 @@ interface DismissableElementHandlers extends InteractOutsideHandlers {
11
11
  */
12
12
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
13
13
  }
14
- interface DismissableElementOptions extends DismissableElementHandlers {
14
+ interface PersistentElementOptions {
15
+ /**
16
+ * Returns the persistent elements that:
17
+ * - should not have pointer-events disabled
18
+ * - should not trigger the dismiss event
19
+ */
20
+ persistentElements?: Array<() => Element | null>;
21
+ }
22
+ interface DismissableElementOptions extends DismissableElementHandlers, PersistentElementOptions {
15
23
  /**
16
24
  * Whether to log debug information
17
25
  */
@@ -38,4 +46,4 @@ declare function trackDismissableBranch(nodeOrFn: NodeOrFn, options?: {
38
46
  defer?: boolean;
39
47
  }): () => void;
40
48
 
41
- export { type DismissableElementHandlers, type DismissableElementOptions, trackDismissableBranch, trackDismissableElement };
49
+ export { type DismissableElementHandlers, type DismissableElementOptions, type PersistentElementOptions, trackDismissableBranch, trackDismissableElement };
package/dist/index.d.ts CHANGED
@@ -11,7 +11,15 @@ interface DismissableElementHandlers extends InteractOutsideHandlers {
11
11
  */
12
12
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
13
13
  }
14
- interface DismissableElementOptions extends DismissableElementHandlers {
14
+ interface PersistentElementOptions {
15
+ /**
16
+ * Returns the persistent elements that:
17
+ * - should not have pointer-events disabled
18
+ * - should not trigger the dismiss event
19
+ */
20
+ persistentElements?: Array<() => Element | null>;
21
+ }
22
+ interface DismissableElementOptions extends DismissableElementHandlers, PersistentElementOptions {
15
23
  /**
16
24
  * Whether to log debug information
17
25
  */
@@ -38,4 +46,4 @@ declare function trackDismissableBranch(nodeOrFn: NodeOrFn, options?: {
38
46
  defer?: boolean;
39
47
  }): () => void;
40
48
 
41
- export { type DismissableElementHandlers, type DismissableElementOptions, trackDismissableBranch, trackDismissableElement };
49
+ export { type DismissableElementHandlers, type DismissableElementOptions, type PersistentElementOptions, trackDismissableBranch, trackDismissableElement };
package/dist/index.js CHANGED
@@ -35,8 +35,11 @@ var import_dom_event = require("@zag-js/dom-event");
35
35
  var import_dom_query = require("@zag-js/dom-query");
36
36
  function trackEscapeKeydown(node, fn) {
37
37
  const handleKeyDown = (event) => {
38
- if (event.key === "Escape")
39
- fn?.(event);
38
+ if (event.key !== "Escape")
39
+ return;
40
+ if (event.isComposing)
41
+ return;
42
+ fn?.(event);
40
43
  };
41
44
  return (0, import_dom_event.addDomEvent)((0, import_dom_query.getDocument)(node), "keydown", handleKeyDown, { capture: true });
42
45
  }
@@ -121,21 +124,32 @@ function assignPointerEventToLayers() {
121
124
  function clearPointerEvent(node) {
122
125
  node.style.pointerEvents = "";
123
126
  }
124
- var DATA_ATTR = "data-inert";
125
- function disablePointerEventsOutside(node) {
127
+ function disablePointerEventsOutside(node, peristentElements) {
126
128
  const doc = (0, import_dom_query3.getDocument)(node);
127
- if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
129
+ const cleanups = [];
130
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute("data-inert")) {
128
131
  originalBodyPointerEvents = document.body.style.pointerEvents;
129
- doc.body.style.pointerEvents = "none";
130
- doc.body.setAttribute(DATA_ATTR, "");
132
+ queueMicrotask(() => {
133
+ doc.body.style.pointerEvents = "none";
134
+ doc.body.setAttribute("data-inert", "");
135
+ });
136
+ }
137
+ if (peristentElements) {
138
+ const persistedCleanup = (0, import_dom_query3.waitForElements)(peristentElements, (el) => {
139
+ cleanups.push((0, import_dom_query3.setStyle)(el, { pointerEvents: "auto" }));
140
+ });
141
+ cleanups.push(persistedCleanup);
131
142
  }
132
143
  return () => {
133
144
  if (layerStack.hasPointerBlockingLayer())
134
145
  return;
135
- doc.body.style.pointerEvents = originalBodyPointerEvents;
136
- doc.body.removeAttribute(DATA_ATTR);
137
- if (doc.body.style.length === 0)
138
- doc.body.removeAttribute("style");
146
+ queueMicrotask(() => {
147
+ doc.body.style.pointerEvents = originalBodyPointerEvents;
148
+ doc.body.removeAttribute("data-inert");
149
+ if (doc.body.style.length === 0)
150
+ doc.body.removeAttribute("style");
151
+ });
152
+ cleanups.forEach((fn) => fn());
139
153
  };
140
154
  }
141
155
 
@@ -189,10 +203,13 @@ function trackDismissableElementImpl(node, options) {
189
203
  return false;
190
204
  const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers;
191
205
  const _containers = Array.isArray(containers) ? containers : [containers];
206
+ const persistentElements = options.persistentElements?.map((fn) => fn()).filter(import_dom_query4.isHTMLElement);
207
+ if (persistentElements)
208
+ _containers.push(...persistentElements);
192
209
  return _containers.some((node2) => (0, import_dom_query4.contains)(node2, target)) || layerStack.isInNestedLayer(node, target);
193
210
  }
194
211
  const cleanups = [
195
- pointerBlocking ? disablePointerEventsOutside(node) : void 0,
212
+ pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : void 0,
196
213
  trackEscapeKeydown(node, onEscapeKeyDown),
197
214
  (0, import_interact_outside.trackInteractOutside)(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer })
198
215
  ];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/dismissable-layer.ts","../src/escape-keydown.ts","../src/layer-stack.ts","../src/pointer-event-outside.ts"],"sourcesContent":["export * from \"./dismissable-layer\"\nexport type {\n InteractOutsideEvent,\n PointerDownOutsideEvent,\n FocusOutsideEvent,\n InteractOutsideHandlers,\n} from \"@zag-js/interact-outside\"\n","import { contains, getEventTarget, raf } from \"@zag-js/dom-query\"\nimport {\n trackInteractOutside,\n type FocusOutsideEvent,\n type InteractOutsideHandlers,\n type PointerDownOutsideEvent,\n} from \"@zag-js/interact-outside\"\nimport { isFunction, warn, type MaybeFunction } from \"@zag-js/utils\"\nimport { trackEscapeKeydown } from \"./escape-keydown\"\nimport { layerStack, type Layer } from \"./layer-stack\"\nimport { assignPointerEventToLayers, clearPointerEvent, disablePointerEventsOutside } from \"./pointer-event-outside\"\n\ntype MaybeElement = HTMLElement | null\ntype Container = MaybeElement | Array<MaybeElement>\ntype NodeOrFn = MaybeFunction<MaybeElement>\n\nexport interface DismissableElementHandlers extends InteractOutsideHandlers {\n /**\n * Function called when the escape key is pressed\n */\n onEscapeKeyDown?: (event: KeyboardEvent) => void\n}\n\nexport interface DismissableElementOptions extends DismissableElementHandlers {\n /**\n * Whether to log debug information\n */\n debug?: boolean\n /**\n * Whether to block pointer events outside the dismissable element\n */\n pointerBlocking?: boolean\n /**\n * Function called when the dismissable element is dismissed\n */\n onDismiss: VoidFunction\n /**\n * Exclude containers from the interact outside event\n */\n exclude?: MaybeFunction<Container>\n /**\n * Defer the interact outside event to the next frame\n */\n defer?: boolean\n}\n\nfunction trackDismissableElementImpl(node: MaybeElement, options: DismissableElementOptions) {\n if (!node) {\n warn(\"[@zag-js/dismissable] node is `null` or `undefined`\")\n return\n }\n\n const { onDismiss, pointerBlocking, exclude: excludeContainers, debug } = options\n\n const layer: Layer = { dismiss: onDismiss, node, pointerBlocking }\n\n layerStack.add(layer)\n assignPointerEventToLayers()\n\n function onPointerDownOutside(event: PointerDownOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isBelowPointerBlockingLayer(node!) || layerStack.isInBranch(target)) return\n options.onPointerDownOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onPointerDownOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onFocusOutside(event: FocusOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isInBranch(target)) return\n options.onFocusOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onFocusOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onEscapeKeyDown(event: KeyboardEvent) {\n if (!layerStack.isTopMost(node!)) return\n options.onEscapeKeyDown?.(event)\n if (!event.defaultPrevented && onDismiss) {\n event.preventDefault()\n onDismiss()\n }\n }\n\n function exclude(target: Element) {\n if (!node) return false\n const containers = typeof excludeContainers === \"function\" ? excludeContainers() : excludeContainers\n const _containers = Array.isArray(containers) ? containers : [containers]\n return _containers.some((node) => contains(node, target)) || layerStack.isInNestedLayer(node, target)\n }\n\n const cleanups = [\n pointerBlocking ? disablePointerEventsOutside(node) : undefined,\n trackEscapeKeydown(node, onEscapeKeyDown),\n trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer }),\n ]\n\n return () => {\n layerStack.remove(node!)\n // re-assign pointer event to remaining layers\n assignPointerEventToLayers()\n // remove pointer event from removed layer\n clearPointerEvent(node!)\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableElement(nodeOrFn: NodeOrFn, options: DismissableElementOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n cleanups.push(trackDismissableElementImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableBranch(nodeOrFn: NodeOrFn, options: { defer?: boolean } = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n if (!node) {\n warn(\"[@zag-js/dismissable] branch node is `null` or `undefined`\")\n return\n }\n layerStack.addBranch(node)\n cleanups.push(() => {\n layerStack.removeBranch(node)\n })\n }),\n )\n\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { addDomEvent } from \"@zag-js/dom-event\"\nimport { getDocument } from \"@zag-js/dom-query\"\n\nexport function trackEscapeKeydown(node: HTMLElement, fn?: (event: KeyboardEvent) => void) {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") fn?.(event)\n }\n return addDomEvent(getDocument(node), \"keydown\", handleKeyDown, { capture: true })\n}\n","import { contains } from \"@zag-js/dom-query\"\n\nexport interface Layer {\n dismiss: VoidFunction\n node: HTMLElement\n pointerBlocking?: boolean\n}\n\nexport const layerStack = {\n layers: [] as Layer[],\n branches: [] as HTMLElement[],\n count(): number {\n return this.layers.length\n },\n pointerBlockingLayers(): Layer[] {\n return this.layers.filter((layer) => layer.pointerBlocking)\n },\n topMostPointerBlockingLayer(): Layer | undefined {\n return [...this.pointerBlockingLayers()].slice(-1)[0]\n },\n hasPointerBlockingLayer(): boolean {\n return this.pointerBlockingLayers().length > 0\n },\n isBelowPointerBlockingLayer(node: HTMLElement) {\n const index = this.indexOf(node)\n const highestBlockingIndex = this.topMostPointerBlockingLayer()\n ? this.indexOf(this.topMostPointerBlockingLayer()?.node)\n : -1\n return index < highestBlockingIndex\n },\n isTopMost(node: HTMLElement | null) {\n const layer = this.layers[this.count() - 1]\n return layer?.node === node\n },\n getNestedLayers(node: HTMLElement) {\n return Array.from(this.layers).slice(this.indexOf(node) + 1)\n },\n isInNestedLayer(node: HTMLElement, target: HTMLElement | EventTarget | null) {\n return this.getNestedLayers(node).some((layer) => contains(layer.node, target))\n },\n isInBranch(target: HTMLElement | EventTarget | null) {\n return Array.from(this.branches).some((branch) => contains(branch, target))\n },\n add(layer: Layer) {\n const num = this.layers.push(layer)\n layer.node.style.setProperty(\"--layer-index\", `${num}`)\n },\n addBranch(node: HTMLElement) {\n this.branches.push(node)\n },\n remove(node: HTMLElement) {\n const index = this.indexOf(node)\n if (index < 0) return\n\n // dismiss nested layers\n if (index < this.count() - 1) {\n const _layers = this.getNestedLayers(node)\n _layers.forEach((layer) => layer.dismiss())\n }\n // remove this layer\n this.layers.splice(index, 1)\n node.style.removeProperty(\"--layer-index\")\n },\n removeBranch(node: HTMLElement) {\n const index = this.branches.indexOf(node)\n if (index >= 0) this.branches.splice(index, 1)\n },\n indexOf(node: HTMLElement | undefined) {\n return this.layers.findIndex((layer) => layer.node === node)\n },\n dismiss(node: HTMLElement) {\n this.layers[this.indexOf(node)]?.dismiss()\n },\n clear() {\n this.remove(this.layers[0].node)\n },\n}\n","import { getDocument } from \"@zag-js/dom-query\"\nimport { layerStack } from \"./layer-stack\"\n\nlet originalBodyPointerEvents: string\n\nexport function assignPointerEventToLayers() {\n layerStack.layers.forEach(({ node }) => {\n node.style.pointerEvents = layerStack.isBelowPointerBlockingLayer(node) ? \"none\" : \"auto\"\n })\n}\n\nexport function clearPointerEvent(node: HTMLElement) {\n node.style.pointerEvents = \"\"\n}\n\nconst DATA_ATTR = \"data-inert\"\n\nexport function disablePointerEventsOutside(node: HTMLElement) {\n const doc = getDocument(node)\n\n if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {\n originalBodyPointerEvents = document.body.style.pointerEvents\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(DATA_ATTR, \"\")\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n doc.body.style.pointerEvents = originalBodyPointerEvents\n doc.body.removeAttribute(DATA_ATTR)\n if (doc.body.style.length === 0) doc.body.removeAttribute(\"style\")\n }\n}\n"],"mappings":"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","names":["import_dom_query","import_dom_query","import_dom_query","node"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/dismissable-layer.ts","../src/escape-keydown.ts","../src/layer-stack.ts","../src/pointer-event-outside.ts"],"sourcesContent":["export type {\n FocusOutsideEvent,\n InteractOutsideEvent,\n InteractOutsideHandlers,\n PointerDownOutsideEvent,\n} from \"@zag-js/interact-outside\"\nexport * from \"./dismissable-layer\"\n","import { contains, getEventTarget, isHTMLElement, raf } from \"@zag-js/dom-query\"\nimport {\n trackInteractOutside,\n type FocusOutsideEvent,\n type InteractOutsideHandlers,\n type PointerDownOutsideEvent,\n} from \"@zag-js/interact-outside\"\nimport { isFunction, warn, type MaybeFunction } from \"@zag-js/utils\"\nimport { trackEscapeKeydown } from \"./escape-keydown\"\nimport { layerStack, type Layer } from \"./layer-stack\"\nimport { assignPointerEventToLayers, clearPointerEvent, disablePointerEventsOutside } from \"./pointer-event-outside\"\n\ntype MaybeElement = HTMLElement | null\ntype Container = MaybeElement | Array<MaybeElement>\ntype NodeOrFn = MaybeFunction<MaybeElement>\n\nexport interface DismissableElementHandlers extends InteractOutsideHandlers {\n /**\n * Function called when the escape key is pressed\n */\n onEscapeKeyDown?: (event: KeyboardEvent) => void\n}\n\nexport interface PersistentElementOptions {\n /**\n * Returns the persistent elements that:\n * - should not have pointer-events disabled\n * - should not trigger the dismiss event\n */\n persistentElements?: Array<() => Element | null>\n}\n\nexport interface DismissableElementOptions extends DismissableElementHandlers, PersistentElementOptions {\n /**\n * Whether to log debug information\n */\n debug?: boolean\n /**\n * Whether to block pointer events outside the dismissable element\n */\n pointerBlocking?: boolean\n /**\n * Function called when the dismissable element is dismissed\n */\n onDismiss: VoidFunction\n /**\n * Exclude containers from the interact outside event\n */\n exclude?: MaybeFunction<Container>\n /**\n * Defer the interact outside event to the next frame\n */\n defer?: boolean\n}\n\nfunction trackDismissableElementImpl(node: MaybeElement, options: DismissableElementOptions) {\n if (!node) {\n warn(\"[@zag-js/dismissable] node is `null` or `undefined`\")\n return\n }\n\n const { onDismiss, pointerBlocking, exclude: excludeContainers, debug } = options\n\n const layer: Layer = { dismiss: onDismiss, node, pointerBlocking }\n\n layerStack.add(layer)\n assignPointerEventToLayers()\n\n function onPointerDownOutside(event: PointerDownOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isBelowPointerBlockingLayer(node!) || layerStack.isInBranch(target)) return\n options.onPointerDownOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onPointerDownOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onFocusOutside(event: FocusOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isInBranch(target)) return\n options.onFocusOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onFocusOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onEscapeKeyDown(event: KeyboardEvent) {\n if (!layerStack.isTopMost(node!)) return\n options.onEscapeKeyDown?.(event)\n if (!event.defaultPrevented && onDismiss) {\n event.preventDefault()\n onDismiss()\n }\n }\n\n function exclude(target: Element) {\n if (!node) return false\n const containers = typeof excludeContainers === \"function\" ? excludeContainers() : excludeContainers\n const _containers = Array.isArray(containers) ? containers : [containers]\n const persistentElements = options.persistentElements?.map((fn) => fn()).filter(isHTMLElement)\n if (persistentElements) _containers.push(...persistentElements)\n return _containers.some((node) => contains(node, target)) || layerStack.isInNestedLayer(node, target)\n }\n\n const cleanups = [\n pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : undefined,\n trackEscapeKeydown(node, onEscapeKeyDown),\n trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer }),\n ]\n\n return () => {\n layerStack.remove(node!)\n // re-assign pointer event to remaining layers\n assignPointerEventToLayers()\n // remove pointer event from removed layer\n clearPointerEvent(node!)\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableElement(nodeOrFn: NodeOrFn, options: DismissableElementOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n cleanups.push(trackDismissableElementImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableBranch(nodeOrFn: NodeOrFn, options: { defer?: boolean } = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n if (!node) {\n warn(\"[@zag-js/dismissable] branch node is `null` or `undefined`\")\n return\n }\n layerStack.addBranch(node)\n cleanups.push(() => {\n layerStack.removeBranch(node)\n })\n }),\n )\n\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { addDomEvent } from \"@zag-js/dom-event\"\nimport { getDocument } from \"@zag-js/dom-query\"\n\nexport function trackEscapeKeydown(node: HTMLElement, fn?: (event: KeyboardEvent) => void) {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return\n if (event.isComposing) return\n fn?.(event)\n }\n\n return addDomEvent(getDocument(node), \"keydown\", handleKeyDown, { capture: true })\n}\n","import { contains } from \"@zag-js/dom-query\"\n\nexport interface Layer {\n dismiss: VoidFunction\n node: HTMLElement\n pointerBlocking?: boolean\n}\n\nexport const layerStack = {\n layers: [] as Layer[],\n branches: [] as HTMLElement[],\n count(): number {\n return this.layers.length\n },\n pointerBlockingLayers(): Layer[] {\n return this.layers.filter((layer) => layer.pointerBlocking)\n },\n topMostPointerBlockingLayer(): Layer | undefined {\n return [...this.pointerBlockingLayers()].slice(-1)[0]\n },\n hasPointerBlockingLayer(): boolean {\n return this.pointerBlockingLayers().length > 0\n },\n isBelowPointerBlockingLayer(node: HTMLElement) {\n const index = this.indexOf(node)\n const highestBlockingIndex = this.topMostPointerBlockingLayer()\n ? this.indexOf(this.topMostPointerBlockingLayer()?.node)\n : -1\n return index < highestBlockingIndex\n },\n isTopMost(node: HTMLElement | null) {\n const layer = this.layers[this.count() - 1]\n return layer?.node === node\n },\n getNestedLayers(node: HTMLElement) {\n return Array.from(this.layers).slice(this.indexOf(node) + 1)\n },\n isInNestedLayer(node: HTMLElement, target: HTMLElement | EventTarget | null) {\n return this.getNestedLayers(node).some((layer) => contains(layer.node, target))\n },\n isInBranch(target: HTMLElement | EventTarget | null) {\n return Array.from(this.branches).some((branch) => contains(branch, target))\n },\n add(layer: Layer) {\n const num = this.layers.push(layer)\n layer.node.style.setProperty(\"--layer-index\", `${num}`)\n },\n addBranch(node: HTMLElement) {\n this.branches.push(node)\n },\n remove(node: HTMLElement) {\n const index = this.indexOf(node)\n if (index < 0) return\n\n // dismiss nested layers\n if (index < this.count() - 1) {\n const _layers = this.getNestedLayers(node)\n _layers.forEach((layer) => layer.dismiss())\n }\n // remove this layer\n this.layers.splice(index, 1)\n node.style.removeProperty(\"--layer-index\")\n },\n removeBranch(node: HTMLElement) {\n const index = this.branches.indexOf(node)\n if (index >= 0) this.branches.splice(index, 1)\n },\n indexOf(node: HTMLElement | undefined) {\n return this.layers.findIndex((layer) => layer.node === node)\n },\n dismiss(node: HTMLElement) {\n this.layers[this.indexOf(node)]?.dismiss()\n },\n clear() {\n this.remove(this.layers[0].node)\n },\n}\n","import { getDocument, setStyle, waitForElements } from \"@zag-js/dom-query\"\nimport { layerStack } from \"./layer-stack\"\n\nlet originalBodyPointerEvents: string\n\nexport function assignPointerEventToLayers() {\n layerStack.layers.forEach(({ node }) => {\n node.style.pointerEvents = layerStack.isBelowPointerBlockingLayer(node) ? \"none\" : \"auto\"\n })\n}\n\nexport function clearPointerEvent(node: HTMLElement) {\n node.style.pointerEvents = \"\"\n}\n\nexport function disablePointerEventsOutside(node: HTMLElement, peristentElements?: Array<() => Element | null>) {\n const doc = getDocument(node)\n\n const cleanups: VoidFunction[] = []\n\n if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(\"data-inert\")) {\n originalBodyPointerEvents = document.body.style.pointerEvents\n queueMicrotask(() => {\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(\"data-inert\", \"\")\n })\n }\n\n if (peristentElements) {\n const persistedCleanup = waitForElements(peristentElements, (el) => {\n cleanups.push(setStyle(el, { pointerEvents: \"auto\" }))\n })\n cleanups.push(persistedCleanup)\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n queueMicrotask(() => {\n doc.body.style.pointerEvents = originalBodyPointerEvents\n doc.body.removeAttribute(\"data-inert\")\n if (doc.body.style.length === 0) doc.body.removeAttribute(\"style\")\n })\n cleanups.forEach((fn) => fn())\n }\n}\n"],"mappings":"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","names":["import_dom_query","import_dom_query","import_dom_query","node"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/dismissable-layer.ts
2
- import { contains as contains2, getEventTarget, raf } from "@zag-js/dom-query";
2
+ import { contains as contains2, getEventTarget, isHTMLElement, raf } from "@zag-js/dom-query";
3
3
  import {
4
4
  trackInteractOutside
5
5
  } from "@zag-js/interact-outside";
@@ -10,8 +10,11 @@ import { addDomEvent } from "@zag-js/dom-event";
10
10
  import { getDocument } from "@zag-js/dom-query";
11
11
  function trackEscapeKeydown(node, fn) {
12
12
  const handleKeyDown = (event) => {
13
- if (event.key === "Escape")
14
- fn?.(event);
13
+ if (event.key !== "Escape")
14
+ return;
15
+ if (event.isComposing)
16
+ return;
17
+ fn?.(event);
15
18
  };
16
19
  return addDomEvent(getDocument(node), "keydown", handleKeyDown, { capture: true });
17
20
  }
@@ -86,7 +89,7 @@ var layerStack = {
86
89
  };
87
90
 
88
91
  // src/pointer-event-outside.ts
89
- import { getDocument as getDocument2 } from "@zag-js/dom-query";
92
+ import { getDocument as getDocument2, setStyle, waitForElements } from "@zag-js/dom-query";
90
93
  var originalBodyPointerEvents;
91
94
  function assignPointerEventToLayers() {
92
95
  layerStack.layers.forEach(({ node }) => {
@@ -96,21 +99,32 @@ function assignPointerEventToLayers() {
96
99
  function clearPointerEvent(node) {
97
100
  node.style.pointerEvents = "";
98
101
  }
99
- var DATA_ATTR = "data-inert";
100
- function disablePointerEventsOutside(node) {
102
+ function disablePointerEventsOutside(node, peristentElements) {
101
103
  const doc = getDocument2(node);
102
- if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
104
+ const cleanups = [];
105
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute("data-inert")) {
103
106
  originalBodyPointerEvents = document.body.style.pointerEvents;
104
- doc.body.style.pointerEvents = "none";
105
- doc.body.setAttribute(DATA_ATTR, "");
107
+ queueMicrotask(() => {
108
+ doc.body.style.pointerEvents = "none";
109
+ doc.body.setAttribute("data-inert", "");
110
+ });
111
+ }
112
+ if (peristentElements) {
113
+ const persistedCleanup = waitForElements(peristentElements, (el) => {
114
+ cleanups.push(setStyle(el, { pointerEvents: "auto" }));
115
+ });
116
+ cleanups.push(persistedCleanup);
106
117
  }
107
118
  return () => {
108
119
  if (layerStack.hasPointerBlockingLayer())
109
120
  return;
110
- doc.body.style.pointerEvents = originalBodyPointerEvents;
111
- doc.body.removeAttribute(DATA_ATTR);
112
- if (doc.body.style.length === 0)
113
- doc.body.removeAttribute("style");
121
+ queueMicrotask(() => {
122
+ doc.body.style.pointerEvents = originalBodyPointerEvents;
123
+ doc.body.removeAttribute("data-inert");
124
+ if (doc.body.style.length === 0)
125
+ doc.body.removeAttribute("style");
126
+ });
127
+ cleanups.forEach((fn) => fn());
114
128
  };
115
129
  }
116
130
 
@@ -164,10 +178,13 @@ function trackDismissableElementImpl(node, options) {
164
178
  return false;
165
179
  const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers;
166
180
  const _containers = Array.isArray(containers) ? containers : [containers];
181
+ const persistentElements = options.persistentElements?.map((fn) => fn()).filter(isHTMLElement);
182
+ if (persistentElements)
183
+ _containers.push(...persistentElements);
167
184
  return _containers.some((node2) => contains2(node2, target)) || layerStack.isInNestedLayer(node, target);
168
185
  }
169
186
  const cleanups = [
170
- pointerBlocking ? disablePointerEventsOutside(node) : void 0,
187
+ pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : void 0,
171
188
  trackEscapeKeydown(node, onEscapeKeyDown),
172
189
  trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer })
173
190
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dismissable-layer.ts","../src/escape-keydown.ts","../src/layer-stack.ts","../src/pointer-event-outside.ts"],"sourcesContent":["import { contains, getEventTarget, raf } from \"@zag-js/dom-query\"\nimport {\n trackInteractOutside,\n type FocusOutsideEvent,\n type InteractOutsideHandlers,\n type PointerDownOutsideEvent,\n} from \"@zag-js/interact-outside\"\nimport { isFunction, warn, type MaybeFunction } from \"@zag-js/utils\"\nimport { trackEscapeKeydown } from \"./escape-keydown\"\nimport { layerStack, type Layer } from \"./layer-stack\"\nimport { assignPointerEventToLayers, clearPointerEvent, disablePointerEventsOutside } from \"./pointer-event-outside\"\n\ntype MaybeElement = HTMLElement | null\ntype Container = MaybeElement | Array<MaybeElement>\ntype NodeOrFn = MaybeFunction<MaybeElement>\n\nexport interface DismissableElementHandlers extends InteractOutsideHandlers {\n /**\n * Function called when the escape key is pressed\n */\n onEscapeKeyDown?: (event: KeyboardEvent) => void\n}\n\nexport interface DismissableElementOptions extends DismissableElementHandlers {\n /**\n * Whether to log debug information\n */\n debug?: boolean\n /**\n * Whether to block pointer events outside the dismissable element\n */\n pointerBlocking?: boolean\n /**\n * Function called when the dismissable element is dismissed\n */\n onDismiss: VoidFunction\n /**\n * Exclude containers from the interact outside event\n */\n exclude?: MaybeFunction<Container>\n /**\n * Defer the interact outside event to the next frame\n */\n defer?: boolean\n}\n\nfunction trackDismissableElementImpl(node: MaybeElement, options: DismissableElementOptions) {\n if (!node) {\n warn(\"[@zag-js/dismissable] node is `null` or `undefined`\")\n return\n }\n\n const { onDismiss, pointerBlocking, exclude: excludeContainers, debug } = options\n\n const layer: Layer = { dismiss: onDismiss, node, pointerBlocking }\n\n layerStack.add(layer)\n assignPointerEventToLayers()\n\n function onPointerDownOutside(event: PointerDownOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isBelowPointerBlockingLayer(node!) || layerStack.isInBranch(target)) return\n options.onPointerDownOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onPointerDownOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onFocusOutside(event: FocusOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isInBranch(target)) return\n options.onFocusOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onFocusOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onEscapeKeyDown(event: KeyboardEvent) {\n if (!layerStack.isTopMost(node!)) return\n options.onEscapeKeyDown?.(event)\n if (!event.defaultPrevented && onDismiss) {\n event.preventDefault()\n onDismiss()\n }\n }\n\n function exclude(target: Element) {\n if (!node) return false\n const containers = typeof excludeContainers === \"function\" ? excludeContainers() : excludeContainers\n const _containers = Array.isArray(containers) ? containers : [containers]\n return _containers.some((node) => contains(node, target)) || layerStack.isInNestedLayer(node, target)\n }\n\n const cleanups = [\n pointerBlocking ? disablePointerEventsOutside(node) : undefined,\n trackEscapeKeydown(node, onEscapeKeyDown),\n trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer }),\n ]\n\n return () => {\n layerStack.remove(node!)\n // re-assign pointer event to remaining layers\n assignPointerEventToLayers()\n // remove pointer event from removed layer\n clearPointerEvent(node!)\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableElement(nodeOrFn: NodeOrFn, options: DismissableElementOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n cleanups.push(trackDismissableElementImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableBranch(nodeOrFn: NodeOrFn, options: { defer?: boolean } = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n if (!node) {\n warn(\"[@zag-js/dismissable] branch node is `null` or `undefined`\")\n return\n }\n layerStack.addBranch(node)\n cleanups.push(() => {\n layerStack.removeBranch(node)\n })\n }),\n )\n\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { addDomEvent } from \"@zag-js/dom-event\"\nimport { getDocument } from \"@zag-js/dom-query\"\n\nexport function trackEscapeKeydown(node: HTMLElement, fn?: (event: KeyboardEvent) => void) {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") fn?.(event)\n }\n return addDomEvent(getDocument(node), \"keydown\", handleKeyDown, { capture: true })\n}\n","import { contains } from \"@zag-js/dom-query\"\n\nexport interface Layer {\n dismiss: VoidFunction\n node: HTMLElement\n pointerBlocking?: boolean\n}\n\nexport const layerStack = {\n layers: [] as Layer[],\n branches: [] as HTMLElement[],\n count(): number {\n return this.layers.length\n },\n pointerBlockingLayers(): Layer[] {\n return this.layers.filter((layer) => layer.pointerBlocking)\n },\n topMostPointerBlockingLayer(): Layer | undefined {\n return [...this.pointerBlockingLayers()].slice(-1)[0]\n },\n hasPointerBlockingLayer(): boolean {\n return this.pointerBlockingLayers().length > 0\n },\n isBelowPointerBlockingLayer(node: HTMLElement) {\n const index = this.indexOf(node)\n const highestBlockingIndex = this.topMostPointerBlockingLayer()\n ? this.indexOf(this.topMostPointerBlockingLayer()?.node)\n : -1\n return index < highestBlockingIndex\n },\n isTopMost(node: HTMLElement | null) {\n const layer = this.layers[this.count() - 1]\n return layer?.node === node\n },\n getNestedLayers(node: HTMLElement) {\n return Array.from(this.layers).slice(this.indexOf(node) + 1)\n },\n isInNestedLayer(node: HTMLElement, target: HTMLElement | EventTarget | null) {\n return this.getNestedLayers(node).some((layer) => contains(layer.node, target))\n },\n isInBranch(target: HTMLElement | EventTarget | null) {\n return Array.from(this.branches).some((branch) => contains(branch, target))\n },\n add(layer: Layer) {\n const num = this.layers.push(layer)\n layer.node.style.setProperty(\"--layer-index\", `${num}`)\n },\n addBranch(node: HTMLElement) {\n this.branches.push(node)\n },\n remove(node: HTMLElement) {\n const index = this.indexOf(node)\n if (index < 0) return\n\n // dismiss nested layers\n if (index < this.count() - 1) {\n const _layers = this.getNestedLayers(node)\n _layers.forEach((layer) => layer.dismiss())\n }\n // remove this layer\n this.layers.splice(index, 1)\n node.style.removeProperty(\"--layer-index\")\n },\n removeBranch(node: HTMLElement) {\n const index = this.branches.indexOf(node)\n if (index >= 0) this.branches.splice(index, 1)\n },\n indexOf(node: HTMLElement | undefined) {\n return this.layers.findIndex((layer) => layer.node === node)\n },\n dismiss(node: HTMLElement) {\n this.layers[this.indexOf(node)]?.dismiss()\n },\n clear() {\n this.remove(this.layers[0].node)\n },\n}\n","import { getDocument } from \"@zag-js/dom-query\"\nimport { layerStack } from \"./layer-stack\"\n\nlet originalBodyPointerEvents: string\n\nexport function assignPointerEventToLayers() {\n layerStack.layers.forEach(({ node }) => {\n node.style.pointerEvents = layerStack.isBelowPointerBlockingLayer(node) ? \"none\" : \"auto\"\n })\n}\n\nexport function clearPointerEvent(node: HTMLElement) {\n node.style.pointerEvents = \"\"\n}\n\nconst DATA_ATTR = \"data-inert\"\n\nexport function disablePointerEventsOutside(node: HTMLElement) {\n const doc = getDocument(node)\n\n if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {\n originalBodyPointerEvents = document.body.style.pointerEvents\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(DATA_ATTR, \"\")\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n doc.body.style.pointerEvents = originalBodyPointerEvents\n doc.body.removeAttribute(DATA_ATTR)\n if (doc.body.style.length === 0) doc.body.removeAttribute(\"style\")\n }\n}\n"],"mappings":"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","names":["contains","getDocument","getDocument","node","contains"]}
1
+ {"version":3,"sources":["../src/dismissable-layer.ts","../src/escape-keydown.ts","../src/layer-stack.ts","../src/pointer-event-outside.ts"],"sourcesContent":["import { contains, getEventTarget, isHTMLElement, raf } from \"@zag-js/dom-query\"\nimport {\n trackInteractOutside,\n type FocusOutsideEvent,\n type InteractOutsideHandlers,\n type PointerDownOutsideEvent,\n} from \"@zag-js/interact-outside\"\nimport { isFunction, warn, type MaybeFunction } from \"@zag-js/utils\"\nimport { trackEscapeKeydown } from \"./escape-keydown\"\nimport { layerStack, type Layer } from \"./layer-stack\"\nimport { assignPointerEventToLayers, clearPointerEvent, disablePointerEventsOutside } from \"./pointer-event-outside\"\n\ntype MaybeElement = HTMLElement | null\ntype Container = MaybeElement | Array<MaybeElement>\ntype NodeOrFn = MaybeFunction<MaybeElement>\n\nexport interface DismissableElementHandlers extends InteractOutsideHandlers {\n /**\n * Function called when the escape key is pressed\n */\n onEscapeKeyDown?: (event: KeyboardEvent) => void\n}\n\nexport interface PersistentElementOptions {\n /**\n * Returns the persistent elements that:\n * - should not have pointer-events disabled\n * - should not trigger the dismiss event\n */\n persistentElements?: Array<() => Element | null>\n}\n\nexport interface DismissableElementOptions extends DismissableElementHandlers, PersistentElementOptions {\n /**\n * Whether to log debug information\n */\n debug?: boolean\n /**\n * Whether to block pointer events outside the dismissable element\n */\n pointerBlocking?: boolean\n /**\n * Function called when the dismissable element is dismissed\n */\n onDismiss: VoidFunction\n /**\n * Exclude containers from the interact outside event\n */\n exclude?: MaybeFunction<Container>\n /**\n * Defer the interact outside event to the next frame\n */\n defer?: boolean\n}\n\nfunction trackDismissableElementImpl(node: MaybeElement, options: DismissableElementOptions) {\n if (!node) {\n warn(\"[@zag-js/dismissable] node is `null` or `undefined`\")\n return\n }\n\n const { onDismiss, pointerBlocking, exclude: excludeContainers, debug } = options\n\n const layer: Layer = { dismiss: onDismiss, node, pointerBlocking }\n\n layerStack.add(layer)\n assignPointerEventToLayers()\n\n function onPointerDownOutside(event: PointerDownOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isBelowPointerBlockingLayer(node!) || layerStack.isInBranch(target)) return\n options.onPointerDownOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onPointerDownOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onFocusOutside(event: FocusOutsideEvent) {\n const target = getEventTarget(event.detail.originalEvent)\n if (layerStack.isInBranch(target)) return\n options.onFocusOutside?.(event)\n options.onInteractOutside?.(event)\n if (event.defaultPrevented) return\n if (debug) {\n console.log(\"onFocusOutside:\", event.detail.originalEvent)\n }\n onDismiss?.()\n }\n\n function onEscapeKeyDown(event: KeyboardEvent) {\n if (!layerStack.isTopMost(node!)) return\n options.onEscapeKeyDown?.(event)\n if (!event.defaultPrevented && onDismiss) {\n event.preventDefault()\n onDismiss()\n }\n }\n\n function exclude(target: Element) {\n if (!node) return false\n const containers = typeof excludeContainers === \"function\" ? excludeContainers() : excludeContainers\n const _containers = Array.isArray(containers) ? containers : [containers]\n const persistentElements = options.persistentElements?.map((fn) => fn()).filter(isHTMLElement)\n if (persistentElements) _containers.push(...persistentElements)\n return _containers.some((node) => contains(node, target)) || layerStack.isInNestedLayer(node, target)\n }\n\n const cleanups = [\n pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : undefined,\n trackEscapeKeydown(node, onEscapeKeyDown),\n trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer }),\n ]\n\n return () => {\n layerStack.remove(node!)\n // re-assign pointer event to remaining layers\n assignPointerEventToLayers()\n // remove pointer event from removed layer\n clearPointerEvent(node!)\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableElement(nodeOrFn: NodeOrFn, options: DismissableElementOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n cleanups.push(trackDismissableElementImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n\nexport function trackDismissableBranch(nodeOrFn: NodeOrFn, options: { defer?: boolean } = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n\n cleanups.push(\n func(() => {\n const node = isFunction(nodeOrFn) ? nodeOrFn() : nodeOrFn\n if (!node) {\n warn(\"[@zag-js/dismissable] branch node is `null` or `undefined`\")\n return\n }\n layerStack.addBranch(node)\n cleanups.push(() => {\n layerStack.removeBranch(node)\n })\n }),\n )\n\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { addDomEvent } from \"@zag-js/dom-event\"\nimport { getDocument } from \"@zag-js/dom-query\"\n\nexport function trackEscapeKeydown(node: HTMLElement, fn?: (event: KeyboardEvent) => void) {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return\n if (event.isComposing) return\n fn?.(event)\n }\n\n return addDomEvent(getDocument(node), \"keydown\", handleKeyDown, { capture: true })\n}\n","import { contains } from \"@zag-js/dom-query\"\n\nexport interface Layer {\n dismiss: VoidFunction\n node: HTMLElement\n pointerBlocking?: boolean\n}\n\nexport const layerStack = {\n layers: [] as Layer[],\n branches: [] as HTMLElement[],\n count(): number {\n return this.layers.length\n },\n pointerBlockingLayers(): Layer[] {\n return this.layers.filter((layer) => layer.pointerBlocking)\n },\n topMostPointerBlockingLayer(): Layer | undefined {\n return [...this.pointerBlockingLayers()].slice(-1)[0]\n },\n hasPointerBlockingLayer(): boolean {\n return this.pointerBlockingLayers().length > 0\n },\n isBelowPointerBlockingLayer(node: HTMLElement) {\n const index = this.indexOf(node)\n const highestBlockingIndex = this.topMostPointerBlockingLayer()\n ? this.indexOf(this.topMostPointerBlockingLayer()?.node)\n : -1\n return index < highestBlockingIndex\n },\n isTopMost(node: HTMLElement | null) {\n const layer = this.layers[this.count() - 1]\n return layer?.node === node\n },\n getNestedLayers(node: HTMLElement) {\n return Array.from(this.layers).slice(this.indexOf(node) + 1)\n },\n isInNestedLayer(node: HTMLElement, target: HTMLElement | EventTarget | null) {\n return this.getNestedLayers(node).some((layer) => contains(layer.node, target))\n },\n isInBranch(target: HTMLElement | EventTarget | null) {\n return Array.from(this.branches).some((branch) => contains(branch, target))\n },\n add(layer: Layer) {\n const num = this.layers.push(layer)\n layer.node.style.setProperty(\"--layer-index\", `${num}`)\n },\n addBranch(node: HTMLElement) {\n this.branches.push(node)\n },\n remove(node: HTMLElement) {\n const index = this.indexOf(node)\n if (index < 0) return\n\n // dismiss nested layers\n if (index < this.count() - 1) {\n const _layers = this.getNestedLayers(node)\n _layers.forEach((layer) => layer.dismiss())\n }\n // remove this layer\n this.layers.splice(index, 1)\n node.style.removeProperty(\"--layer-index\")\n },\n removeBranch(node: HTMLElement) {\n const index = this.branches.indexOf(node)\n if (index >= 0) this.branches.splice(index, 1)\n },\n indexOf(node: HTMLElement | undefined) {\n return this.layers.findIndex((layer) => layer.node === node)\n },\n dismiss(node: HTMLElement) {\n this.layers[this.indexOf(node)]?.dismiss()\n },\n clear() {\n this.remove(this.layers[0].node)\n },\n}\n","import { getDocument, setStyle, waitForElements } from \"@zag-js/dom-query\"\nimport { layerStack } from \"./layer-stack\"\n\nlet originalBodyPointerEvents: string\n\nexport function assignPointerEventToLayers() {\n layerStack.layers.forEach(({ node }) => {\n node.style.pointerEvents = layerStack.isBelowPointerBlockingLayer(node) ? \"none\" : \"auto\"\n })\n}\n\nexport function clearPointerEvent(node: HTMLElement) {\n node.style.pointerEvents = \"\"\n}\n\nexport function disablePointerEventsOutside(node: HTMLElement, peristentElements?: Array<() => Element | null>) {\n const doc = getDocument(node)\n\n const cleanups: VoidFunction[] = []\n\n if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(\"data-inert\")) {\n originalBodyPointerEvents = document.body.style.pointerEvents\n queueMicrotask(() => {\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(\"data-inert\", \"\")\n })\n }\n\n if (peristentElements) {\n const persistedCleanup = waitForElements(peristentElements, (el) => {\n cleanups.push(setStyle(el, { pointerEvents: \"auto\" }))\n })\n cleanups.push(persistedCleanup)\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n queueMicrotask(() => {\n doc.body.style.pointerEvents = originalBodyPointerEvents\n doc.body.removeAttribute(\"data-inert\")\n if (doc.body.style.length === 0) doc.body.removeAttribute(\"style\")\n })\n cleanups.forEach((fn) => fn())\n }\n}\n"],"mappings":";AAAA,SAAS,YAAAA,WAAU,gBAAgB,eAAe,WAAW;AAC7D;AAAA,EACE;AAAA,OAIK;AACP,SAAS,YAAY,YAAgC;;;ACPrD,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAErB,SAAS,mBAAmB,MAAmB,IAAqC;AACzF,QAAM,gBAAgB,CAAC,UAAyB;AAC9C,QAAI,MAAM,QAAQ;AAAU;AAC5B,QAAI,MAAM;AAAa;AACvB,SAAK,KAAK;AAAA,EACZ;AAEA,SAAO,YAAY,YAAY,IAAI,GAAG,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACnF;;;ACXA,SAAS,gBAAgB;AAQlB,IAAM,aAAa;AAAA,EACxB,QAAQ,CAAC;AAAA,EACT,UAAU,CAAC;AAAA,EACX,QAAgB;AACd,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EACA,wBAAiC;AAC/B,WAAO,KAAK,OAAO,OAAO,CAAC,UAAU,MAAM,eAAe;AAAA,EAC5D;AAAA,EACA,8BAAiD;AAC/C,WAAO,CAAC,GAAG,KAAK,sBAAsB,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;AAAA,EACtD;AAAA,EACA,0BAAmC;AACjC,WAAO,KAAK,sBAAsB,EAAE,SAAS;AAAA,EAC/C;AAAA,EACA,4BAA4B,MAAmB;AAC7C,UAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,UAAM,uBAAuB,KAAK,4BAA4B,IAC1D,KAAK,QAAQ,KAAK,4BAA4B,GAAG,IAAI,IACrD;AACJ,WAAO,QAAQ;AAAA,EACjB;AAAA,EACA,UAAU,MAA0B;AAClC,UAAM,QAAQ,KAAK,OAAO,KAAK,MAAM,IAAI,CAAC;AAC1C,WAAO,OAAO,SAAS;AAAA,EACzB;AAAA,EACA,gBAAgB,MAAmB;AACjC,WAAO,MAAM,KAAK,KAAK,MAAM,EAAE,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,EAC7D;AAAA,EACA,gBAAgB,MAAmB,QAA0C;AAC3E,WAAO,KAAK,gBAAgB,IAAI,EAAE,KAAK,CAAC,UAAU,SAAS,MAAM,MAAM,MAAM,CAAC;AAAA,EAChF;AAAA,EACA,WAAW,QAA0C;AACnD,WAAO,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,CAAC,WAAW,SAAS,QAAQ,MAAM,CAAC;AAAA,EAC5E;AAAA,EACA,IAAI,OAAc;AAChB,UAAM,MAAM,KAAK,OAAO,KAAK,KAAK;AAClC,UAAM,KAAK,MAAM,YAAY,iBAAiB,GAAG,GAAG,EAAE;AAAA,EACxD;AAAA,EACA,UAAU,MAAmB;AAC3B,SAAK,SAAS,KAAK,IAAI;AAAA,EACzB;AAAA,EACA,OAAO,MAAmB;AACxB,UAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,QAAI,QAAQ;AAAG;AAGf,QAAI,QAAQ,KAAK,MAAM,IAAI,GAAG;AAC5B,YAAM,UAAU,KAAK,gBAAgB,IAAI;AACzC,cAAQ,QAAQ,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,IAC5C;AAEA,SAAK,OAAO,OAAO,OAAO,CAAC;AAC3B,SAAK,MAAM,eAAe,eAAe;AAAA,EAC3C;AAAA,EACA,aAAa,MAAmB;AAC9B,UAAM,QAAQ,KAAK,SAAS,QAAQ,IAAI;AACxC,QAAI,SAAS;AAAG,WAAK,SAAS,OAAO,OAAO,CAAC;AAAA,EAC/C;AAAA,EACA,QAAQ,MAA+B;AACrC,WAAO,KAAK,OAAO,UAAU,CAAC,UAAU,MAAM,SAAS,IAAI;AAAA,EAC7D;AAAA,EACA,QAAQ,MAAmB;AACzB,SAAK,OAAO,KAAK,QAAQ,IAAI,CAAC,GAAG,QAAQ;AAAA,EAC3C;AAAA,EACA,QAAQ;AACN,SAAK,OAAO,KAAK,OAAO,CAAC,EAAE,IAAI;AAAA,EACjC;AACF;;;AC5EA,SAAS,eAAAC,cAAa,UAAU,uBAAuB;AAGvD,IAAI;AAEG,SAAS,6BAA6B;AAC3C,aAAW,OAAO,QAAQ,CAAC,EAAE,KAAK,MAAM;AACtC,SAAK,MAAM,gBAAgB,WAAW,4BAA4B,IAAI,IAAI,SAAS;AAAA,EACrF,CAAC;AACH;AAEO,SAAS,kBAAkB,MAAmB;AACnD,OAAK,MAAM,gBAAgB;AAC7B;AAEO,SAAS,4BAA4B,MAAmB,mBAAiD;AAC9G,QAAM,MAAMC,aAAY,IAAI;AAE5B,QAAM,WAA2B,CAAC;AAElC,MAAI,WAAW,wBAAwB,KAAK,CAAC,IAAI,KAAK,aAAa,YAAY,GAAG;AAChF,gCAA4B,SAAS,KAAK,MAAM;AAChD,mBAAe,MAAM;AACnB,UAAI,KAAK,MAAM,gBAAgB;AAC/B,UAAI,KAAK,aAAa,cAAc,EAAE;AAAA,IACxC,CAAC;AAAA,EACH;AAEA,MAAI,mBAAmB;AACrB,UAAM,mBAAmB,gBAAgB,mBAAmB,CAAC,OAAO;AAClE,eAAS,KAAK,SAAS,IAAI,EAAE,eAAe,OAAO,CAAC,CAAC;AAAA,IACvD,CAAC;AACD,aAAS,KAAK,gBAAgB;AAAA,EAChC;AAEA,SAAO,MAAM;AACX,QAAI,WAAW,wBAAwB;AAAG;AAC1C,mBAAe,MAAM;AACnB,UAAI,KAAK,MAAM,gBAAgB;AAC/B,UAAI,KAAK,gBAAgB,YAAY;AACrC,UAAI,IAAI,KAAK,MAAM,WAAW;AAAG,YAAI,KAAK,gBAAgB,OAAO;AAAA,IACnE,CAAC;AACD,aAAS,QAAQ,CAAC,OAAO,GAAG,CAAC;AAAA,EAC/B;AACF;;;AHWA,SAAS,4BAA4B,MAAoB,SAAoC;AAC3F,MAAI,CAAC,MAAM;AACT,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAEA,QAAM,EAAE,WAAW,iBAAiB,SAAS,mBAAmB,MAAM,IAAI;AAE1E,QAAM,QAAe,EAAE,SAAS,WAAW,MAAM,gBAAgB;AAEjE,aAAW,IAAI,KAAK;AACpB,6BAA2B;AAE3B,WAAS,qBAAqB,OAAgC;AAC5D,UAAM,SAAS,eAAe,MAAM,OAAO,aAAa;AACxD,QAAI,WAAW,4BAA4B,IAAK,KAAK,WAAW,WAAW,MAAM;AAAG;AACpF,YAAQ,uBAAuB,KAAK;AACpC,YAAQ,oBAAoB,KAAK;AACjC,QAAI,MAAM;AAAkB;AAC5B,QAAI,OAAO;AACT,cAAQ,IAAI,yBAAyB,MAAM,OAAO,aAAa;AAAA,IACjE;AACA,gBAAY;AAAA,EACd;AAEA,WAAS,eAAe,OAA0B;AAChD,UAAM,SAAS,eAAe,MAAM,OAAO,aAAa;AACxD,QAAI,WAAW,WAAW,MAAM;AAAG;AACnC,YAAQ,iBAAiB,KAAK;AAC9B,YAAQ,oBAAoB,KAAK;AACjC,QAAI,MAAM;AAAkB;AAC5B,QAAI,OAAO;AACT,cAAQ,IAAI,mBAAmB,MAAM,OAAO,aAAa;AAAA,IAC3D;AACA,gBAAY;AAAA,EACd;AAEA,WAAS,gBAAgB,OAAsB;AAC7C,QAAI,CAAC,WAAW,UAAU,IAAK;AAAG;AAClC,YAAQ,kBAAkB,KAAK;AAC/B,QAAI,CAAC,MAAM,oBAAoB,WAAW;AACxC,YAAM,eAAe;AACrB,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,WAAS,QAAQ,QAAiB;AAChC,QAAI,CAAC;AAAM,aAAO;AAClB,UAAM,aAAa,OAAO,sBAAsB,aAAa,kBAAkB,IAAI;AACnF,UAAM,cAAc,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;AACxE,UAAM,qBAAqB,QAAQ,oBAAoB,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,OAAO,aAAa;AAC7F,QAAI;AAAoB,kBAAY,KAAK,GAAG,kBAAkB;AAC9D,WAAO,YAAY,KAAK,CAACC,UAASC,UAASD,OAAM,MAAM,CAAC,KAAK,WAAW,gBAAgB,MAAM,MAAM;AAAA,EACtG;AAEA,QAAM,WAAW;AAAA,IACf,kBAAkB,4BAA4B,MAAM,QAAQ,kBAAkB,IAAI;AAAA,IAClF,mBAAmB,MAAM,eAAe;AAAA,IACxC,qBAAqB,MAAM,EAAE,SAAS,gBAAgB,sBAAsB,OAAO,QAAQ,MAAM,CAAC;AAAA,EACpG;AAEA,SAAO,MAAM;AACX,eAAW,OAAO,IAAK;AAEvB,+BAA2B;AAE3B,sBAAkB,IAAK;AACvB,aAAS,QAAQ,CAAC,OAAO,KAAK,CAAC;AAAA,EACjC;AACF;AAEO,SAAS,wBAAwB,UAAoB,SAAoC;AAC9F,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,OAAO,QAAQ,MAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAChD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,OAAO,WAAW,QAAQ,IAAI,SAAS,IAAI;AACjD,eAAS,KAAK,4BAA4B,MAAM,OAAO,CAAC;AAAA,IAC1D,CAAC;AAAA,EACH;AACA,SAAO,MAAM;AACX,aAAS,QAAQ,CAAC,OAAO,KAAK,CAAC;AAAA,EACjC;AACF;AAEO,SAAS,uBAAuB,UAAoB,UAA+B,CAAC,GAAG;AAC5F,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,OAAO,QAAQ,MAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAEhD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,OAAO,WAAW,QAAQ,IAAI,SAAS,IAAI;AACjD,UAAI,CAAC,MAAM;AACT,aAAK,4DAA4D;AACjE;AAAA,MACF;AACA,iBAAW,UAAU,IAAI;AACzB,eAAS,KAAK,MAAM;AAClB,mBAAW,aAAa,IAAI;AAAA,MAC9B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,SAAO,MAAM;AACX,aAAS,QAAQ,CAAC,OAAO,KAAK,CAAC;AAAA,EACjC;AACF;","names":["contains","getDocument","getDocument","node","contains"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/dismissable",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "Dismissable layer utilities for the DOM",
5
5
  "keywords": [
6
6
  "js",
@@ -24,10 +24,10 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@zag-js/interact-outside": "0.49.0",
28
- "@zag-js/dom-query": "0.49.0",
29
- "@zag-js/dom-event": "0.49.0",
30
- "@zag-js/utils": "0.49.0"
27
+ "@zag-js/interact-outside": "0.50.0",
28
+ "@zag-js/dom-query": "0.50.0",
29
+ "@zag-js/dom-event": "0.50.0",
30
+ "@zag-js/utils": "0.50.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "clean-package": "2.2.0"
@@ -1,4 +1,4 @@
1
- import { contains, getEventTarget, raf } from "@zag-js/dom-query"
1
+ import { contains, getEventTarget, isHTMLElement, raf } from "@zag-js/dom-query"
2
2
  import {
3
3
  trackInteractOutside,
4
4
  type FocusOutsideEvent,
@@ -21,7 +21,16 @@ export interface DismissableElementHandlers extends InteractOutsideHandlers {
21
21
  onEscapeKeyDown?: (event: KeyboardEvent) => void
22
22
  }
23
23
 
24
- export interface DismissableElementOptions extends DismissableElementHandlers {
24
+ export interface PersistentElementOptions {
25
+ /**
26
+ * Returns the persistent elements that:
27
+ * - should not have pointer-events disabled
28
+ * - should not trigger the dismiss event
29
+ */
30
+ persistentElements?: Array<() => Element | null>
31
+ }
32
+
33
+ export interface DismissableElementOptions extends DismissableElementHandlers, PersistentElementOptions {
25
34
  /**
26
35
  * Whether to log debug information
27
36
  */
@@ -94,11 +103,13 @@ function trackDismissableElementImpl(node: MaybeElement, options: DismissableEle
94
103
  if (!node) return false
95
104
  const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers
96
105
  const _containers = Array.isArray(containers) ? containers : [containers]
106
+ const persistentElements = options.persistentElements?.map((fn) => fn()).filter(isHTMLElement)
107
+ if (persistentElements) _containers.push(...persistentElements)
97
108
  return _containers.some((node) => contains(node, target)) || layerStack.isInNestedLayer(node, target)
98
109
  }
99
110
 
100
111
  const cleanups = [
101
- pointerBlocking ? disablePointerEventsOutside(node) : undefined,
112
+ pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : undefined,
102
113
  trackEscapeKeydown(node, onEscapeKeyDown),
103
114
  trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer }),
104
115
  ]
@@ -3,7 +3,10 @@ import { getDocument } from "@zag-js/dom-query"
3
3
 
4
4
  export function trackEscapeKeydown(node: HTMLElement, fn?: (event: KeyboardEvent) => void) {
5
5
  const handleKeyDown = (event: KeyboardEvent) => {
6
- if (event.key === "Escape") fn?.(event)
6
+ if (event.key !== "Escape") return
7
+ if (event.isComposing) return
8
+ fn?.(event)
7
9
  }
10
+
8
11
  return addDomEvent(getDocument(node), "keydown", handleKeyDown, { capture: true })
9
12
  }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
- export * from "./dismissable-layer"
2
1
  export type {
3
- InteractOutsideEvent,
4
- PointerDownOutsideEvent,
5
2
  FocusOutsideEvent,
3
+ InteractOutsideEvent,
6
4
  InteractOutsideHandlers,
5
+ PointerDownOutsideEvent,
7
6
  } from "@zag-js/interact-outside"
7
+ export * from "./dismissable-layer"
@@ -1,4 +1,4 @@
1
- import { getDocument } from "@zag-js/dom-query"
1
+ import { getDocument, setStyle, waitForElements } from "@zag-js/dom-query"
2
2
  import { layerStack } from "./layer-stack"
3
3
 
4
4
  let originalBodyPointerEvents: string
@@ -13,21 +13,33 @@ export function clearPointerEvent(node: HTMLElement) {
13
13
  node.style.pointerEvents = ""
14
14
  }
15
15
 
16
- const DATA_ATTR = "data-inert"
17
-
18
- export function disablePointerEventsOutside(node: HTMLElement) {
16
+ export function disablePointerEventsOutside(node: HTMLElement, peristentElements?: Array<() => Element | null>) {
19
17
  const doc = getDocument(node)
20
18
 
21
- if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
19
+ const cleanups: VoidFunction[] = []
20
+
21
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute("data-inert")) {
22
22
  originalBodyPointerEvents = document.body.style.pointerEvents
23
- doc.body.style.pointerEvents = "none"
24
- doc.body.setAttribute(DATA_ATTR, "")
23
+ queueMicrotask(() => {
24
+ doc.body.style.pointerEvents = "none"
25
+ doc.body.setAttribute("data-inert", "")
26
+ })
27
+ }
28
+
29
+ if (peristentElements) {
30
+ const persistedCleanup = waitForElements(peristentElements, (el) => {
31
+ cleanups.push(setStyle(el, { pointerEvents: "auto" }))
32
+ })
33
+ cleanups.push(persistedCleanup)
25
34
  }
26
35
 
27
36
  return () => {
28
37
  if (layerStack.hasPointerBlockingLayer()) return
29
- doc.body.style.pointerEvents = originalBodyPointerEvents
30
- doc.body.removeAttribute(DATA_ATTR)
31
- if (doc.body.style.length === 0) doc.body.removeAttribute("style")
38
+ queueMicrotask(() => {
39
+ doc.body.style.pointerEvents = originalBodyPointerEvents
40
+ doc.body.removeAttribute("data-inert")
41
+ if (doc.body.style.length === 0) doc.body.removeAttribute("style")
42
+ })
43
+ cleanups.forEach((fn) => fn())
32
44
  }
33
45
  }