@zag-js/dismissable 0.0.0-dev-20240514231431 → 0.0.0-dev-20240515160243

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
@@ -124,25 +124,32 @@ function assignPointerEventToLayers() {
124
124
  function clearPointerEvent(node) {
125
125
  node.style.pointerEvents = "";
126
126
  }
127
- var DATA_ATTR = "data-inert";
128
- function disablePointerEventsOutside(node) {
127
+ function disablePointerEventsOutside(node, peristentElements) {
129
128
  const doc = (0, import_dom_query3.getDocument)(node);
130
- if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
129
+ const cleanups = [];
130
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute("data-inert")) {
131
131
  originalBodyPointerEvents = document.body.style.pointerEvents;
132
132
  queueMicrotask(() => {
133
133
  doc.body.style.pointerEvents = "none";
134
- doc.body.setAttribute(DATA_ATTR, "");
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" }));
135
140
  });
141
+ cleanups.push(persistedCleanup);
136
142
  }
137
143
  return () => {
138
144
  if (layerStack.hasPointerBlockingLayer())
139
145
  return;
140
146
  queueMicrotask(() => {
141
147
  doc.body.style.pointerEvents = originalBodyPointerEvents;
142
- doc.body.removeAttribute(DATA_ATTR);
148
+ doc.body.removeAttribute("data-inert");
143
149
  if (doc.body.style.length === 0)
144
150
  doc.body.removeAttribute("style");
145
151
  });
152
+ cleanups.forEach((fn) => fn());
146
153
  };
147
154
  }
148
155
 
@@ -196,10 +203,13 @@ function trackDismissableElementImpl(node, options) {
196
203
  return false;
197
204
  const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers;
198
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);
199
209
  return _containers.some((node2) => (0, import_dom_query4.contains)(node2, target)) || layerStack.isInNestedLayer(node, target);
200
210
  }
201
211
  const cleanups = [
202
- pointerBlocking ? disablePointerEventsOutside(node) : void 0,
212
+ pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : void 0,
203
213
  trackEscapeKeydown(node, onEscapeKeyDown),
204
214
  (0, import_interact_outside.trackInteractOutside)(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer })
205
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\") 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 } 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 queueMicrotask(() => {\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(DATA_ATTR, \"\")\n })\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n queueMicrotask(() => {\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}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAA8C;AAC9C,8BAKO;AACP,mBAAqD;;;ACPrD,uBAA4B;AAC5B,uBAA4B;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,aAAO,kCAAY,8BAAY,IAAI,GAAG,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACnF;;;ACXA,IAAAC,oBAAyB;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,cAAU,4BAAS,MAAM,MAAM,MAAM,CAAC;AAAA,EAChF;AAAA,EACA,WAAW,QAA0C;AACnD,WAAO,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,CAAC,eAAW,4BAAS,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,IAAAC,oBAA4B;AAG5B,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;AAEA,IAAM,YAAY;AAEX,SAAS,4BAA4B,MAAmB;AAC7D,QAAM,UAAM,+BAAY,IAAI;AAE5B,MAAI,WAAW,wBAAwB,KAAK,CAAC,IAAI,KAAK,aAAa,SAAS,GAAG;AAC7E,gCAA4B,SAAS,KAAK,MAAM;AAChD,mBAAe,MAAM;AACnB,UAAI,KAAK,MAAM,gBAAgB;AAC/B,UAAI,KAAK,aAAa,WAAW,EAAE;AAAA,IACrC,CAAC;AAAA,EACH;AAEA,SAAO,MAAM;AACX,QAAI,WAAW,wBAAwB;AAAG;AAC1C,mBAAe,MAAM;AACnB,UAAI,KAAK,MAAM,gBAAgB;AAC/B,UAAI,KAAK,gBAAgB,SAAS;AAClC,UAAI,IAAI,KAAK,MAAM,WAAW;AAAG,YAAI,KAAK,gBAAgB,OAAO;AAAA,IACnE,CAAC;AAAA,EACH;AACF;;;AHUA,SAAS,4BAA4B,MAAoB,SAAoC;AAC3F,MAAI,CAAC,MAAM;AACT,2BAAK,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,aAAS,kCAAe,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,aAAS,kCAAe,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,WAAO,YAAY,KAAK,CAACC,cAAS,4BAASA,OAAM,MAAM,CAAC,KAAK,WAAW,gBAAgB,MAAM,MAAM;AAAA,EACtG;AAEA,QAAM,WAAW;AAAA,IACf,kBAAkB,4BAA4B,IAAI,IAAI;AAAA,IACtD,mBAAmB,MAAM,eAAe;AAAA,QACxC,8CAAqB,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,wBAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAChD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,WAAO,yBAAW,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,wBAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAEhD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,WAAO,yBAAW,QAAQ,IAAI,SAAS,IAAI;AACjD,UAAI,CAAC,MAAM;AACT,+BAAK,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":["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";
@@ -89,7 +89,7 @@ var layerStack = {
89
89
  };
90
90
 
91
91
  // src/pointer-event-outside.ts
92
- import { getDocument as getDocument2 } from "@zag-js/dom-query";
92
+ import { getDocument as getDocument2, setStyle, waitForElements } from "@zag-js/dom-query";
93
93
  var originalBodyPointerEvents;
94
94
  function assignPointerEventToLayers() {
95
95
  layerStack.layers.forEach(({ node }) => {
@@ -99,25 +99,32 @@ function assignPointerEventToLayers() {
99
99
  function clearPointerEvent(node) {
100
100
  node.style.pointerEvents = "";
101
101
  }
102
- var DATA_ATTR = "data-inert";
103
- function disablePointerEventsOutside(node) {
102
+ function disablePointerEventsOutside(node, peristentElements) {
104
103
  const doc = getDocument2(node);
105
- if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
104
+ const cleanups = [];
105
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute("data-inert")) {
106
106
  originalBodyPointerEvents = document.body.style.pointerEvents;
107
107
  queueMicrotask(() => {
108
108
  doc.body.style.pointerEvents = "none";
109
- doc.body.setAttribute(DATA_ATTR, "");
109
+ doc.body.setAttribute("data-inert", "");
110
+ });
111
+ }
112
+ if (peristentElements) {
113
+ const persistedCleanup = waitForElements(peristentElements, (el) => {
114
+ cleanups.push(setStyle(el, { pointerEvents: "auto" }));
110
115
  });
116
+ cleanups.push(persistedCleanup);
111
117
  }
112
118
  return () => {
113
119
  if (layerStack.hasPointerBlockingLayer())
114
120
  return;
115
121
  queueMicrotask(() => {
116
122
  doc.body.style.pointerEvents = originalBodyPointerEvents;
117
- doc.body.removeAttribute(DATA_ATTR);
123
+ doc.body.removeAttribute("data-inert");
118
124
  if (doc.body.style.length === 0)
119
125
  doc.body.removeAttribute("style");
120
126
  });
127
+ cleanups.forEach((fn) => fn());
121
128
  };
122
129
  }
123
130
 
@@ -171,10 +178,13 @@ function trackDismissableElementImpl(node, options) {
171
178
  return false;
172
179
  const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers;
173
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);
174
184
  return _containers.some((node2) => contains2(node2, target)) || layerStack.isInNestedLayer(node, target);
175
185
  }
176
186
  const cleanups = [
177
- pointerBlocking ? disablePointerEventsOutside(node) : void 0,
187
+ pointerBlocking ? disablePointerEventsOutside(node, options.persistentElements) : void 0,
178
188
  trackEscapeKeydown(node, onEscapeKeyDown),
179
189
  trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside, defer: options.defer })
180
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\") 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 } 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 queueMicrotask(() => {\n doc.body.style.pointerEvents = \"none\"\n doc.body.setAttribute(DATA_ATTR, \"\")\n })\n }\n\n return () => {\n if (layerStack.hasPointerBlockingLayer()) return\n queueMicrotask(() => {\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}\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":"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","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.0.0-dev-20240514231431",
3
+ "version": "0.0.0-dev-20240515160243",
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.0.0-dev-20240514231431",
28
- "@zag-js/dom-query": "0.0.0-dev-20240514231431",
29
- "@zag-js/dom-event": "0.0.0-dev-20240514231431",
30
- "@zag-js/utils": "0.0.0-dev-20240514231431"
27
+ "@zag-js/interact-outside": "0.0.0-dev-20240515160243",
28
+ "@zag-js/dom-query": "0.0.0-dev-20240515160243",
29
+ "@zag-js/dom-event": "0.0.0-dev-20240515160243",
30
+ "@zag-js/utils": "0.0.0-dev-20240515160243"
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
  ]
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,25 +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
23
  queueMicrotask(() => {
24
24
  doc.body.style.pointerEvents = "none"
25
- doc.body.setAttribute(DATA_ATTR, "")
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" }))
26
32
  })
33
+ cleanups.push(persistedCleanup)
27
34
  }
28
35
 
29
36
  return () => {
30
37
  if (layerStack.hasPointerBlockingLayer()) return
31
38
  queueMicrotask(() => {
32
39
  doc.body.style.pointerEvents = originalBodyPointerEvents
33
- doc.body.removeAttribute(DATA_ATTR)
40
+ doc.body.removeAttribute("data-inert")
34
41
  if (doc.body.style.length === 0) doc.body.removeAttribute("style")
35
42
  })
43
+ cleanups.forEach((fn) => fn())
36
44
  }
37
45
  }