@zag-js/popover 0.1.8 → 0.1.9

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.mjs CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __defProp = Object.defineProperty;
3
2
  var __defProps = Object.defineProperties;
4
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -23,13 +22,99 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
22
  var dataAttr = (guard) => {
24
23
  return guard ? "" : void 0;
25
24
  };
26
- var hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
27
- var isLeftClick = (v) => v.button === 0;
28
25
  var runIfFn = (v, ...a) => {
29
26
  const res = typeof v === "function" ? v(...a) : v;
30
27
  return res != null ? res : void 0;
31
28
  };
32
- var pipe = (...fns) => (v) => fns.reduce((a, b) => b(a), v);
29
+ var hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
30
+ function isWindow(value) {
31
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
32
+ }
33
+ function isFrame(element) {
34
+ return element.localName === "iframe";
35
+ }
36
+ function getDocument(el) {
37
+ var _a;
38
+ if (isWindow(el))
39
+ return el.document;
40
+ return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
41
+ }
42
+ function getEventTarget(event) {
43
+ var _a, _b;
44
+ return (_b = (_a = event.composedPath) == null ? void 0 : _a.call(event)[0]) != null ? _b : event.target;
45
+ }
46
+ function contains(parent, child) {
47
+ if (!parent)
48
+ return false;
49
+ return parent === child || isHTMLElement(parent) && isHTMLElement(child) && parent.contains(child);
50
+ }
51
+ function isHTMLElement(v) {
52
+ return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
53
+ }
54
+ function isVisible(el) {
55
+ if (!isHTMLElement(el))
56
+ return false;
57
+ return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
58
+ }
59
+ var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
60
+ function hasNegativeTabIndex(element) {
61
+ const tabIndex = parseInt(element.getAttribute("tabindex") || "0", 10);
62
+ return tabIndex < 0;
63
+ }
64
+ var focusableSelector = "input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], button:not([disabled]), [tabindex], iframe, object, embed, area[href], audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), details > summary:first-of-type";
65
+ var getFocusables = (container, includeContainer = false) => {
66
+ if (!container)
67
+ return [];
68
+ const elements = Array.from(container.querySelectorAll(focusableSelector));
69
+ const include = includeContainer == true || includeContainer == "if-empty" && elements.length === 0;
70
+ if (include && isHTMLElement(container) && isFocusable(container)) {
71
+ elements.unshift(container);
72
+ }
73
+ const focusableElements = elements.filter(isFocusable);
74
+ focusableElements.forEach((element, i) => {
75
+ if (isFrame(element) && element.contentDocument) {
76
+ const frameBody = element.contentDocument.body;
77
+ focusableElements.splice(i, 1, ...getFocusables(frameBody));
78
+ }
79
+ });
80
+ return focusableElements;
81
+ };
82
+ function isFocusable(element) {
83
+ if (!element)
84
+ return false;
85
+ return element.matches(focusableSelector) && isVisible(element);
86
+ }
87
+ function getTabbables(container, includeContainer) {
88
+ if (!container)
89
+ return [];
90
+ const elements = Array.from(container.querySelectorAll(focusableSelector));
91
+ const tabbableElements = elements.filter(isTabbable);
92
+ if (includeContainer && isTabbable(container)) {
93
+ tabbableElements.unshift(container);
94
+ }
95
+ tabbableElements.forEach((element, i) => {
96
+ if (isFrame(element) && element.contentDocument) {
97
+ const frameBody = element.contentDocument.body;
98
+ const allFrameTabbable = getTabbables(frameBody);
99
+ tabbableElements.splice(i, 1, ...allFrameTabbable);
100
+ }
101
+ });
102
+ if (!tabbableElements.length && includeContainer) {
103
+ return elements;
104
+ }
105
+ return tabbableElements;
106
+ }
107
+ function isTabbable(el) {
108
+ return isFocusable(el) && !hasNegativeTabIndex(el);
109
+ }
110
+ function getFirstTabbable(container, includeContainer) {
111
+ const [first] = getTabbables(container, includeContainer);
112
+ return first || null;
113
+ }
114
+ function getLastTabbable(container, includeContainer) {
115
+ const elements = getTabbables(container, includeContainer);
116
+ return elements[elements.length - 1] || null;
117
+ }
33
118
  var isRef = (v) => hasProp(v, "current");
34
119
  function addDomEvent(target, eventName, handler, options) {
35
120
  const node = isRef(target) ? target.current : runIfFn(target);
@@ -57,179 +142,11 @@ function raf(fn) {
57
142
  globalThis.cancelAnimationFrame(id);
58
143
  };
59
144
  }
60
- var changeCount = 0;
61
- var originalBodyPointerEvents;
62
- function preventBodyPointerEvents(el, opts = {}) {
63
- const { disabled = false, document: docProp } = opts;
64
- const doc = docProp || document;
65
- let isTouchOrPenPressed = false;
66
- let isLeftClickPressed = false;
67
- function listen() {
68
- const onPointerDown = (event) => {
69
- const isMouse = event.pointerType === "mouse";
70
- isTouchOrPenPressed = !isMouse;
71
- isLeftClickPressed = isMouse && isLeftClick(event);
72
- };
73
- const onPointerUp = () => {
74
- isTouchOrPenPressed = false;
75
- isLeftClickPressed = false;
76
- };
77
- return pipe(addDomEvent(doc, "pointerdown", onPointerDown), addDomEvent(doc, "pointerup", onPointerUp));
78
- }
79
- function reset() {
80
- changeCount--;
81
- if (changeCount === 0) {
82
- doc.body.style.pointerEvents = originalBodyPointerEvents;
83
- }
84
- if (el) {
85
- el.style.pointerEvents = "";
86
- }
87
- }
88
- function apply() {
89
- if (disabled)
90
- return;
91
- if (changeCount === 0) {
92
- originalBodyPointerEvents = doc.body.style.pointerEvents;
93
- }
94
- doc.body.style.pointerEvents = "none";
95
- if (el) {
96
- el.style.pointerEvents = "auto";
97
- }
98
- changeCount++;
99
- return function() {
100
- if (isTouchOrPenPressed) {
101
- addDomEvent(doc, "click", reset, { once: true });
102
- } else if (isLeftClickPressed) {
103
- addDomEvent(doc, "pointerup", reset, { once: true });
104
- } else {
105
- reset();
106
- }
107
- };
108
- }
109
- const cleanups = [];
110
- cleanups.push(apply());
111
- nextTick(() => {
112
- cleanups.push(listen());
113
- });
114
- return function() {
115
- cleanups.forEach((cleanup) => cleanup == null ? void 0 : cleanup());
116
- };
117
- }
118
- function getStyleCache() {
119
- ;
120
- globalThis.__styleCache__ = globalThis.__styleCache__ || /* @__PURE__ */ new WeakMap();
121
- return globalThis.__styleCache__;
122
- }
123
- function getComputedStyle(el) {
124
- var _a;
125
- if (!el)
126
- return {};
127
- const cache = getStyleCache();
128
- let style = cache.get(el);
129
- if (!style) {
130
- const win = (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
131
- style = win.getComputedStyle(el);
132
- cache.set(el, style);
133
- }
134
- return style;
135
- }
136
- function contains(parent, child) {
137
- if (!parent)
138
- return false;
139
- return parent === child || isHTMLElement(parent) && isHTMLElement(child) && parent.contains(child);
140
- }
141
- function isHTMLElement(v) {
142
- return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
143
- }
144
- var isDisabled = (el) => {
145
- return (el == null ? void 0 : el.getAttribute("disabled")) != null || !!(el == null ? void 0 : el.getAttribute("aria-disabled")) === true;
146
- };
147
- function validateBlur(event, opts) {
148
- var _a;
149
- const exclude = Array.isArray(opts.exclude) ? opts.exclude : [opts.exclude];
150
- const relatedTarget = (_a = event.relatedTarget) != null ? _a : opts.fallback;
151
- return exclude.every((el) => !(el == null ? void 0 : el.contains(relatedTarget)));
152
- }
153
- var focusableSelector = /* @__PURE__ */ [
154
- "input:not([disabled]):not([type=hidden])",
155
- "select:not([disabled])",
156
- "textarea:not([disabled])",
157
- "button:not([disabled])",
158
- "embed",
159
- "iframe",
160
- "object",
161
- "a[href]",
162
- "area[href]",
163
- "[tabindex]",
164
- "audio[controls]",
165
- "video[controls]",
166
- "*[tabindex]:not([aria-disabled])",
167
- "[contenteditable]:not([contenteditable=false])",
168
- "details > summary:first-of-type"
169
- ].join(",");
170
- function isHidden(el, until) {
171
- const style = getComputedStyle(el);
172
- if (!el || style.getPropertyValue("visibility") === "hidden")
173
- return true;
174
- while (el) {
175
- if (until != null && el === until)
176
- return false;
177
- if (style.getPropertyValue("display") === "none")
178
- return true;
179
- el = el.parentElement;
180
- }
181
- return false;
182
- }
183
- var getFocusables = (el, includeContainer = false) => {
184
- if (!el)
185
- return [];
186
- let els = Array.from(el.querySelectorAll(focusableSelector));
187
- const shouldAddContainer = includeContainer == true || includeContainer == "if-empty" && els.length === 0;
188
- if (shouldAddContainer && isHTMLElement(el)) {
189
- els.unshift(el);
190
- }
191
- return els.filter((el2) => isFocusable(el2) && !isHidden(el2));
192
- };
193
- var isFocusable = (el) => {
194
- if (!isHTMLElement(el) || isHidden(el) || isDisabled(el))
195
- return false;
196
- return el == null ? void 0 : el.matches(focusableSelector);
197
- };
198
- var getTabbables = (el, includeContainer = false) => {
199
- if (!el)
200
- return [];
201
- return getFocusables(el, includeContainer).filter(isTabbable);
202
- };
203
- var isTabbable = (el) => {
204
- return isFocusable(el) && !isDisabled(el) && !isHidden(el);
205
- };
206
- function trackPointerDown(doc, onPointerDown) {
207
- var _a;
208
- const win = (_a = doc.defaultView) != null ? _a : window;
209
- const fn = (event) => {
210
- if (event.target instanceof win.HTMLElement) {
211
- onPointerDown(event.target);
212
- }
213
- };
214
- return addDomEvent(doc, "pointerdown", fn);
215
- }
216
145
 
217
146
  // src/popover.connect.ts
218
147
  import { getPlacementStyles } from "@zag-js/popper";
219
148
 
220
- // ../../types/dist/index.mjs
221
- function createNormalizer(fn) {
222
- return new Proxy({}, {
223
- get() {
224
- return fn;
225
- }
226
- });
227
- }
228
- var normalizeProp = createNormalizer((v) => v);
229
-
230
149
  // ../../utilities/core/dist/index.mjs
231
- var first = (v) => v[0];
232
- var last = (v) => v[v.length - 1];
233
150
  function nextIndex(v, idx, opts = {}) {
234
151
  const { step = 1, loop = true } = opts;
235
152
  const next2 = idx + step;
@@ -250,7 +167,6 @@ var runIfFn2 = (v, ...a) => {
250
167
  const res = typeof v === "function" ? v(...a) : v;
251
168
  return res != null ? res : void 0;
252
169
  };
253
- var cast = (v) => v;
254
170
 
255
171
  // src/popover.dom.ts
256
172
  var dom = {
@@ -258,6 +174,10 @@ var dom = {
258
174
  var _a;
259
175
  return (_a = ctx.doc) != null ? _a : document;
260
176
  },
177
+ getWin: (ctx) => {
178
+ var _a;
179
+ return (_a = dom.getDoc(ctx).defaultView) != null ? _a : window;
180
+ },
261
181
  getActiveEl: (ctx) => dom.getDoc(ctx).activeElement,
262
182
  getRootNode: (ctx) => {
263
183
  var _a;
@@ -297,10 +217,10 @@ var dom = {
297
217
  getDescriptionEl: (ctx) => dom.getRootNode(ctx).getElementById(dom.getDescriptionId(ctx)),
298
218
  getFocusableEls: (ctx) => getFocusables(dom.getContentEl(ctx)),
299
219
  getFirstFocusableEl: (ctx) => dom.getFocusableEls(ctx)[0],
300
- getDocTabbableEls: (ctx) => getTabbables(cast(dom.getDoc(ctx))),
220
+ getDocTabbableEls: (ctx) => getTabbables(dom.getDoc(ctx).body),
301
221
  getTabbableEls: (ctx) => getTabbables(dom.getContentEl(ctx), "if-empty"),
302
- getFirstTabbableEl: (ctx) => first(dom.getTabbableEls(ctx)),
303
- getLastTabbableEl: (ctx) => last(dom.getTabbableEls(ctx)),
222
+ getFirstTabbableEl: (ctx) => getFirstTabbable(dom.getContentEl(ctx), "if-empty"),
223
+ getLastTabbableEl: (ctx) => getLastTabbable(dom.getContentEl(ctx), "if-empty"),
304
224
  getInitialFocusEl: (ctx) => {
305
225
  let el = runIfFn2(ctx.initialFocusEl);
306
226
  if (!el && ctx.autoFocus)
@@ -312,16 +232,17 @@ var dom = {
312
232
  };
313
233
 
314
234
  // src/popover.connect.ts
315
- function connect(state, send, normalize = normalizeProp) {
235
+ function connect(state, send, normalize) {
316
236
  const isOpen = state.matches("open");
317
- const pointerdownNode = state.context.pointerdownNode;
318
237
  const currentPlacement = state.context.currentPlacement;
238
+ const portalled = state.context.currentPortalled;
239
+ const rendered = state.context.renderedElements;
319
240
  const popperStyles = getPlacementStyles({
320
241
  measured: !!state.context.isPlacementComplete,
321
242
  placement: currentPlacement
322
243
  });
323
244
  return {
324
- portalled: state.context.currentPortalled,
245
+ portalled,
325
246
  isOpen,
326
247
  open() {
327
248
  send("OPEN");
@@ -352,12 +273,10 @@ function connect(state, send, normalize = normalizeProp) {
352
273
  "data-expanded": dataAttr(isOpen),
353
274
  "aria-controls": dom.getContentId(state.context),
354
275
  onClick() {
355
- send("TRIGGER_CLICK");
276
+ send("TOGGLE");
356
277
  },
357
- onKeyDown(event) {
358
- if (event.key === "Escape") {
359
- send("ESCAPE");
360
- }
278
+ onBlur(event) {
279
+ send({ type: "TRIGGER_BLUR", target: event.relatedTarget });
361
280
  }
362
281
  }),
363
282
  positionerProps: normalize.element({
@@ -372,34 +291,9 @@ function connect(state, send, normalize = normalizeProp) {
372
291
  role: "dialog",
373
292
  hidden: !isOpen,
374
293
  "data-expanded": dataAttr(isOpen),
375
- "aria-labelledby": state.context.renderedElements.title ? dom.getTitleId(state.context) : void 0,
376
- "aria-describedby": state.context.renderedElements.description ? dom.getDescriptionId(state.context) : void 0,
377
- "data-placement": currentPlacement,
378
- onKeyDown(event) {
379
- const keyMap = {
380
- Escape(event2) {
381
- send("ESCAPE");
382
- event2.stopPropagation();
383
- },
384
- Tab(event2) {
385
- const type = event2.shiftKey ? "SHIFT_TAB" : "TAB";
386
- send({ type, preventDefault: () => event2.preventDefault() });
387
- }
388
- };
389
- const exec = keyMap[event.key];
390
- exec == null ? void 0 : exec(event);
391
- },
392
- onBlur(event) {
393
- var _a;
394
- const isValidBlur = validateBlur(event, {
395
- exclude: [dom.getTriggerEl(state.context), dom.getContentEl(state.context)],
396
- fallback: pointerdownNode
397
- });
398
- if (isValidBlur) {
399
- const el = (_a = event.relatedTarget) != null ? _a : pointerdownNode;
400
- send({ type: "INTERACT_OUTSIDE", focusable: isFocusable(el) });
401
- }
402
- }
294
+ "aria-labelledby": rendered.title ? dom.getTitleId(state.context) : void 0,
295
+ "aria-describedby": rendered.description ? dom.getDescriptionId(state.context) : void 0,
296
+ "data-placement": currentPlacement
403
297
  }),
404
298
  titleProps: normalize.element({
405
299
  "data-part": "title",
@@ -415,7 +309,7 @@ function connect(state, send, normalize = normalizeProp) {
415
309
  type: "button",
416
310
  "aria-label": "close",
417
311
  onClick() {
418
- send("CLOSE");
312
+ send("REQUEST_CLOSE");
419
313
  }
420
314
  })
421
315
  };
@@ -424,17 +318,336 @@ function connect(state, send, normalize = normalizeProp) {
424
318
  // src/popover.machine.ts
425
319
  import { ariaHidden } from "@zag-js/aria-hidden";
426
320
  import { createMachine, guards, ref } from "@zag-js/core";
321
+
322
+ // ../../utilities/interact-outside/dist/index.mjs
323
+ var runIfFn3 = (v, ...a) => {
324
+ const res = typeof v === "function" ? v(...a) : v;
325
+ return res != null ? res : void 0;
326
+ };
327
+ var hasProp2 = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
328
+ var isDom = () => typeof window !== "undefined";
329
+ function getPlatform() {
330
+ var _a;
331
+ const agent = navigator.userAgentData;
332
+ return (_a = agent == null ? void 0 : agent.platform) != null ? _a : navigator.platform;
333
+ }
334
+ var pt = (v) => isDom() && v.test(getPlatform());
335
+ var isTouchDevice = () => isDom() && !!navigator.maxTouchPoints;
336
+ var isMac = () => pt(/^Mac/) && !isTouchDevice;
337
+ function isWindow2(value) {
338
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
339
+ }
340
+ function getDocument2(el) {
341
+ var _a;
342
+ if (isWindow2(el))
343
+ return el.document;
344
+ return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
345
+ }
346
+ function getWindow(el) {
347
+ var _a;
348
+ return (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
349
+ }
350
+ function getEventTarget2(event) {
351
+ var _a, _b;
352
+ return (_b = (_a = event.composedPath) == null ? void 0 : _a.call(event)[0]) != null ? _b : event.target;
353
+ }
354
+ function contains2(parent, child) {
355
+ if (!parent)
356
+ return false;
357
+ return parent === child || isHTMLElement2(parent) && isHTMLElement2(child) && parent.contains(child);
358
+ }
359
+ function isHTMLElement2(v) {
360
+ return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
361
+ }
362
+ function isVisible2(el) {
363
+ if (!isHTMLElement2(el))
364
+ return false;
365
+ return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
366
+ }
367
+ var isContextMenuEvent = (e) => {
368
+ return e.button === 2 || isCtrlKey(e) && e.button === 0;
369
+ };
370
+ var isCtrlKey = (v) => isMac() ? v.metaKey && !v.ctrlKey : v.ctrlKey && !v.metaKey;
371
+ function fireCustomEvent(el, type, init) {
372
+ if (!el)
373
+ return;
374
+ const win = getWindow(el);
375
+ const event = new win.CustomEvent(type, init);
376
+ return el.dispatchEvent(event);
377
+ }
378
+ var focusableSelector2 = "input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], button:not([disabled]), [tabindex], iframe, object, embed, area[href], audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), details > summary:first-of-type";
379
+ function isFocusable2(element) {
380
+ if (!element)
381
+ return false;
382
+ return element.matches(focusableSelector2) && isVisible2(element);
383
+ }
384
+ var isRef2 = (v) => hasProp2(v, "current");
385
+ function addDomEvent2(target, eventName, handler, options) {
386
+ const node = isRef2(target) ? target.current : runIfFn3(target);
387
+ node == null ? void 0 : node.addEventListener(eventName, handler, options);
388
+ return () => {
389
+ node == null ? void 0 : node.removeEventListener(eventName, handler, options);
390
+ };
391
+ }
392
+ var POINTER_OUTSIDE_EVENT = "pointerdown.outside";
393
+ var FOCUS_OUTSIDE_EVENT = "focus.outside";
394
+ function trackInteractOutside(node, options) {
395
+ const { exclude, onFocusOutside, onPointerDownOutside } = options;
396
+ if (!node)
397
+ return;
398
+ const doc = getDocument2(node);
399
+ const win = getWindow(node);
400
+ function isEventOutside(event) {
401
+ const target = getEventTarget2(event);
402
+ if (!(target instanceof win.HTMLElement)) {
403
+ return false;
404
+ }
405
+ if (!contains2(doc.documentElement, target)) {
406
+ return false;
407
+ }
408
+ if (contains2(node, target)) {
409
+ return false;
410
+ }
411
+ return !(exclude == null ? void 0 : exclude(target));
412
+ }
413
+ let clickHandler;
414
+ function onPointerDown(event) {
415
+ function handler() {
416
+ if (!node || !isEventOutside(event))
417
+ return;
418
+ if (onPointerDownOutside) {
419
+ node.addEventListener(POINTER_OUTSIDE_EVENT, onPointerDownOutside, { once: true });
420
+ }
421
+ fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {
422
+ bubbles: false,
423
+ cancelable: true,
424
+ detail: {
425
+ originalEvent: event,
426
+ contextmenu: isContextMenuEvent(event),
427
+ focusable: isFocusable2(getEventTarget2(event))
428
+ }
429
+ });
430
+ }
431
+ if (event.pointerType === "touch") {
432
+ doc.removeEventListener("click", handler);
433
+ clickHandler = handler;
434
+ doc.addEventListener("click", handler, { once: true });
435
+ } else {
436
+ handler();
437
+ }
438
+ }
439
+ const cleanups = /* @__PURE__ */ new Set();
440
+ const timer = setTimeout(() => {
441
+ cleanups.add(addDomEvent2(doc, "pointerdown", onPointerDown, true));
442
+ }, 0);
443
+ function onFocusin(event) {
444
+ if (!node || !isEventOutside(event))
445
+ return;
446
+ if (onFocusOutside) {
447
+ node.addEventListener(FOCUS_OUTSIDE_EVENT, onFocusOutside, { once: true });
448
+ }
449
+ fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {
450
+ bubbles: false,
451
+ cancelable: true,
452
+ detail: {
453
+ originalEvent: event,
454
+ contextmenu: false,
455
+ focusable: isFocusable2(getEventTarget2(event))
456
+ }
457
+ });
458
+ }
459
+ cleanups.add(addDomEvent2(doc, "focusin", onFocusin, true));
460
+ return () => {
461
+ clearTimeout(timer);
462
+ if (clickHandler)
463
+ doc.removeEventListener("click", clickHandler);
464
+ cleanups.forEach((fn) => fn());
465
+ };
466
+ }
467
+
468
+ // ../../utilities/dismissable/src/escape-keydown.ts
469
+ function trackEscapeKeydown(fn) {
470
+ const handleKeyDown = (event) => {
471
+ if (event.key === "Escape")
472
+ fn == null ? void 0 : fn(event);
473
+ };
474
+ return addDomEvent(document, "keydown", handleKeyDown);
475
+ }
476
+
477
+ // ../../utilities/dismissable/src/layer-stack.ts
478
+ var layerStack = {
479
+ layers: [],
480
+ branches: [],
481
+ count() {
482
+ return this.layers.length;
483
+ },
484
+ pointerBlockingLayers() {
485
+ return this.layers.filter((layer) => layer.pointerBlocking);
486
+ },
487
+ topMostPointerBlockingLayer() {
488
+ return [...this.pointerBlockingLayers()].slice(-1)[0];
489
+ },
490
+ hasPointerBlockingLayer() {
491
+ return this.pointerBlockingLayers().length > 0;
492
+ },
493
+ isBelowPointerBlockingLayer(node) {
494
+ var _a;
495
+ const index = this.indexOf(node);
496
+ const highestBlockingIndex = this.topMostPointerBlockingLayer() ? this.indexOf((_a = this.topMostPointerBlockingLayer()) == null ? void 0 : _a.node) : -1;
497
+ return index < highestBlockingIndex;
498
+ },
499
+ isTopMost(node) {
500
+ const layer = this.layers[this.count() - 1];
501
+ return (layer == null ? void 0 : layer.node) === node;
502
+ },
503
+ getNestedLayers(node) {
504
+ return Array.from(this.layers).slice(this.indexOf(node) + 1);
505
+ },
506
+ isInNestedLayer(node, target) {
507
+ return this.getNestedLayers(node).some((layer) => contains(layer.node, target));
508
+ },
509
+ isInBranch(target) {
510
+ return Array.from(this.branches).some((branch) => contains(branch, target));
511
+ },
512
+ add(layer) {
513
+ this.layers.push(layer);
514
+ },
515
+ addBranch(node) {
516
+ this.branches.push(node);
517
+ },
518
+ remove(node) {
519
+ const index = this.indexOf(node);
520
+ if (index < 0)
521
+ return;
522
+ if (index < this.count() - 1) {
523
+ const _layers = this.getNestedLayers(node);
524
+ _layers.forEach((layer) => layer.dismiss());
525
+ }
526
+ this.layers.splice(index, 1);
527
+ },
528
+ removeBranch(node) {
529
+ const index = this.branches.indexOf(node);
530
+ if (index >= 0)
531
+ this.branches.splice(index, 1);
532
+ },
533
+ indexOf(node) {
534
+ return this.layers.findIndex((layer) => layer.node === node);
535
+ },
536
+ dismiss(node) {
537
+ var _a;
538
+ (_a = this.layers[this.indexOf(node)]) == null ? void 0 : _a.dismiss();
539
+ },
540
+ clear() {
541
+ this.remove(this.layers[0].node);
542
+ }
543
+ };
544
+
545
+ // ../../utilities/dismissable/src/pointer-event-outside.ts
546
+ var originalBodyPointerEvents;
547
+ function assignPointerEventToLayers() {
548
+ layerStack.layers.forEach(({ node }) => {
549
+ node.style.pointerEvents = layerStack.isBelowPointerBlockingLayer(node) ? "none" : "auto";
550
+ });
551
+ }
552
+ function clearPointerEvent(node) {
553
+ node.style.pointerEvents = "";
554
+ }
555
+ var DATA_ATTR = "data-inert";
556
+ function disablePointerEventsOutside(node) {
557
+ const doc = getDocument(node);
558
+ if (layerStack.hasPointerBlockingLayer() && !doc.body.hasAttribute(DATA_ATTR)) {
559
+ originalBodyPointerEvents = document.body.style.pointerEvents;
560
+ doc.body.style.pointerEvents = "none";
561
+ doc.body.setAttribute(DATA_ATTR, "");
562
+ }
563
+ return () => {
564
+ if (layerStack.hasPointerBlockingLayer())
565
+ return;
566
+ doc.body.style.pointerEvents = originalBodyPointerEvents;
567
+ doc.body.removeAttribute(DATA_ATTR);
568
+ if (doc.body.style.length === 0)
569
+ doc.body.removeAttribute("style");
570
+ };
571
+ }
572
+
573
+ // ../../utilities/dismissable/src/dismissable-layer.ts
574
+ function trackDismissableElement(node, options) {
575
+ if (!node)
576
+ return;
577
+ const { onDismiss, pointerBlocking, exclude: excludeContainers, debug } = options;
578
+ const layer = { dismiss: onDismiss, node, pointerBlocking };
579
+ layerStack.add(layer);
580
+ assignPointerEventToLayers();
581
+ function onPointerDownOutside(event) {
582
+ var _a, _b;
583
+ const target = getEventTarget(event.detail.originalEvent);
584
+ if (layerStack.isBelowPointerBlockingLayer(node) || layerStack.isInBranch(target))
585
+ return;
586
+ (_a = options.onPointerDownOutside) == null ? void 0 : _a.call(options, event);
587
+ (_b = options.onInteractOutside) == null ? void 0 : _b.call(options, event);
588
+ if (event.defaultPrevented)
589
+ return;
590
+ if (debug) {
591
+ console.log("onPointerDownOutside:", event.detail.originalEvent);
592
+ }
593
+ onDismiss == null ? void 0 : onDismiss();
594
+ }
595
+ function onFocusOutside(event) {
596
+ var _a, _b;
597
+ const target = getEventTarget(event.detail.originalEvent);
598
+ if (layerStack.isInBranch(target))
599
+ return;
600
+ (_a = options.onFocusOutside) == null ? void 0 : _a.call(options, event);
601
+ (_b = options.onInteractOutside) == null ? void 0 : _b.call(options, event);
602
+ if (event.defaultPrevented)
603
+ return;
604
+ if (debug) {
605
+ console.log("onFocusOutside:", event.detail.originalEvent);
606
+ }
607
+ onDismiss == null ? void 0 : onDismiss();
608
+ }
609
+ function onEscapeKeyDown(event) {
610
+ var _a;
611
+ if (!layerStack.isTopMost(node))
612
+ return;
613
+ (_a = options.onEscapeKeyDown) == null ? void 0 : _a.call(options, event);
614
+ if (!event.defaultPrevented && onDismiss) {
615
+ event.preventDefault();
616
+ onDismiss();
617
+ }
618
+ }
619
+ function exclude(target) {
620
+ if (!node)
621
+ return false;
622
+ const containers = typeof excludeContainers === "function" ? excludeContainers() : excludeContainers;
623
+ const _containers = Array.isArray(containers) ? containers : [containers];
624
+ return _containers.some((node2) => contains(node2, target)) || layerStack.isInNestedLayer(node, target);
625
+ }
626
+ const cleanups = [
627
+ pointerBlocking ? disablePointerEventsOutside(node) : void 0,
628
+ trackEscapeKeydown(onEscapeKeyDown),
629
+ trackInteractOutside(node, { exclude, onFocusOutside, onPointerDownOutside })
630
+ ];
631
+ return () => {
632
+ layerStack.remove(node);
633
+ assignPointerEventToLayers();
634
+ clearPointerEvent(node);
635
+ cleanups.forEach((fn) => fn == null ? void 0 : fn());
636
+ };
637
+ }
638
+
639
+ // src/popover.machine.ts
427
640
  import { getPlacement } from "@zag-js/popper";
428
641
  import { preventBodyScroll } from "@zag-js/remove-scroll";
429
642
  import { createFocusTrap } from "focus-trap";
430
- var { and, or } = guards;
643
+ var { and, or, not } = guards;
431
644
  function machine(ctx = {}) {
432
645
  return createMachine({
433
646
  id: "popover",
434
647
  initial: "unknown",
435
648
  context: __spreadProps(__spreadValues({
436
649
  uid: "",
437
- closeOnBlur: true,
650
+ closeOnInteractOutside: true,
438
651
  closeOnEsc: true,
439
652
  autoFocus: true,
440
653
  modal: false,
@@ -443,6 +656,7 @@ function machine(ctx = {}) {
443
656
  }, ctx.positioning),
444
657
  currentPlacement: void 0
445
658
  }, ctx), {
659
+ focusTriggerOnClose: true,
446
660
  renderedElements: {
447
661
  title: true,
448
662
  description: true,
@@ -456,7 +670,7 @@ function machine(ctx = {}) {
456
670
  unknown: {
457
671
  on: {
458
672
  SETUP: {
459
- target: ctx.open ? "open" : "closed",
673
+ target: ctx.defaultOpen ? "open" : "closed",
460
674
  actions: ["setupDocument", "checkRenderedElements"]
461
675
  }
462
676
  }
@@ -464,55 +678,46 @@ function machine(ctx = {}) {
464
678
  closed: {
465
679
  entry: ["clearPointerDown", "invokeOnClose"],
466
680
  on: {
467
- TRIGGER_CLICK: "open",
681
+ TOGGLE: "open",
468
682
  OPEN: "open"
469
683
  }
470
684
  },
471
685
  open: {
472
686
  activities: [
473
- "trackPointerDown",
474
687
  "trapFocus",
475
688
  "preventScroll",
476
689
  "hideContentBelow",
477
- "disableOutsidePointerEvents",
478
- "computePlacement"
690
+ "computePlacement",
691
+ "trackInteractionOutside",
692
+ "trackTabKeyDown"
479
693
  ],
480
694
  entry: ["setInitialFocus", "invokeOnOpen"],
481
695
  on: {
482
- CLOSE: {
483
- target: "closed",
484
- actions: "focusTrigger"
485
- },
486
- TRIGGER_CLICK: {
487
- target: "closed",
488
- actions: "focusTrigger"
489
- },
490
- ESCAPE: {
491
- guard: "closeOnEsc",
696
+ CLOSE: "closed",
697
+ REQUEST_CLOSE: {
492
698
  target: "closed",
493
- actions: "focusTrigger"
699
+ actions: "focusTriggerIfNeeded"
494
700
  },
495
- TAB: {
496
- guard: and("isLastTabbableElement", "closeOnBlur", "portalled"),
497
- target: "closed",
498
- actions: "focusNextTabbableElementAfterTrigger"
701
+ TOGGLE: "closed",
702
+ TRIGGER_BLUR: {
703
+ guard: not("isRelatedTargetWithinContent"),
704
+ target: "closed"
499
705
  },
500
- SHIFT_TAB: {
501
- guard: and(or("isFirstTabbableElement", "isContentFocused"), "closeOnBlur", "portalled"),
502
- target: "closed",
503
- actions: "focusTrigger"
504
- },
505
- INTERACT_OUTSIDE: [
706
+ TAB: [
506
707
  {
507
- guard: and("closeOnBlur", "isRelatedTargetFocusable"),
508
- target: "closed"
708
+ guard: and("isTriggerFocused", "portalled"),
709
+ actions: "focusFirstTabbableElement"
509
710
  },
510
711
  {
511
- guard: "closeOnBlur",
712
+ guard: and("isLastTabbableElement", "closeOnInteractOutside", "portalled"),
512
713
  target: "closed",
513
- actions: "focusTrigger"
714
+ actions: "focusNextTabbableElementAfterTrigger"
514
715
  }
515
- ]
716
+ ],
717
+ SHIFT_TAB: {
718
+ guard: and(or("isFirstTabbableElement", "isContentFocused"), "portalled"),
719
+ actions: "focusTriggerIfNeeded"
720
+ }
516
721
  }
517
722
  }
518
723
  }
@@ -532,32 +737,70 @@ function machine(ctx = {}) {
532
737
  }
533
738
  }));
534
739
  },
535
- trackPointerDown(ctx2) {
536
- return trackPointerDown(dom.getDoc(ctx2), (el) => {
537
- ctx2.pointerdownNode = ref(el);
740
+ trackInteractionOutside(ctx2, _evt, { send }) {
741
+ return trackDismissableElement(dom.getContentEl(ctx2), {
742
+ pointerBlocking: ctx2.modal,
743
+ exclude: dom.getTriggerEl(ctx2),
744
+ onEscapeKeyDown(event) {
745
+ var _a;
746
+ (_a = ctx2.onEscapeKeyDown) == null ? void 0 : _a.call(ctx2, event);
747
+ if (ctx2.closeOnEsc)
748
+ return;
749
+ ctx2.focusTriggerOnClose = true;
750
+ event.preventDefault();
751
+ },
752
+ onInteractOutside(event) {
753
+ var _a;
754
+ (_a = ctx2.onInteractOutside) == null ? void 0 : _a.call(ctx2, event);
755
+ if (event.defaultPrevented)
756
+ return;
757
+ ctx2.focusTriggerOnClose = !(event.detail.focusable || event.detail.contextmenu);
758
+ if (!ctx2.closeOnInteractOutside) {
759
+ event.preventDefault();
760
+ }
761
+ },
762
+ onPointerDownOutside(event) {
763
+ var _a;
764
+ (_a = ctx2.onPointerDownOutside) == null ? void 0 : _a.call(ctx2, event);
765
+ },
766
+ onFocusOutside(event) {
767
+ var _a;
768
+ (_a = ctx2.onFocusOutside) == null ? void 0 : _a.call(ctx2, event);
769
+ if (ctx2.currentPortalled) {
770
+ event.preventDefault();
771
+ }
772
+ },
773
+ onDismiss() {
774
+ send({ type: "REQUEST_CLOSE", src: "#interact-outside" });
775
+ }
538
776
  });
539
777
  },
540
- disableOutsidePointerEvents(ctx2) {
541
- const el = dom.getContentEl(ctx2);
542
- return preventBodyPointerEvents(el, {
543
- document: dom.getDoc(ctx2),
544
- disabled: !ctx2.modal
545
- });
778
+ trackTabKeyDown(ctx2, _evt, { send }) {
779
+ if (ctx2.modal)
780
+ return;
781
+ return addDomEvent(dom.getWin(ctx2), "keydown", (event) => {
782
+ const isTabKey = event.key === "Tab" && !isModifiedEvent(event);
783
+ if (!isTabKey)
784
+ return;
785
+ send({
786
+ type: event.shiftKey ? "SHIFT_TAB" : "TAB",
787
+ preventDefault: () => event.preventDefault()
788
+ });
789
+ }, true);
546
790
  },
547
791
  hideContentBelow(ctx2) {
548
792
  if (!ctx2.modal)
549
793
  return;
550
- let unhide;
794
+ let cleanup;
551
795
  nextTick(() => {
552
- unhide = ariaHidden([dom.getContentEl(ctx2), dom.getTriggerEl(ctx2)]);
796
+ cleanup = ariaHidden([dom.getContentEl(ctx2), dom.getTriggerEl(ctx2)]);
553
797
  });
554
- return () => unhide == null ? void 0 : unhide();
798
+ return () => cleanup == null ? void 0 : cleanup();
555
799
  },
556
800
  preventScroll(ctx2) {
557
- return preventBodyScroll({
558
- disabled: !ctx2.modal,
559
- document: dom.getDoc(ctx2)
560
- });
801
+ if (!ctx2.modal)
802
+ return;
803
+ return preventBodyScroll(dom.getDoc(ctx2));
561
804
  },
562
805
  trapFocus(ctx2) {
563
806
  if (!ctx2.modal)
@@ -584,13 +827,11 @@ function machine(ctx = {}) {
584
827
  }
585
828
  },
586
829
  guards: {
587
- closeOnEsc: (ctx2) => !!ctx2.closeOnEsc,
588
- autoFocus: (ctx2) => !!ctx2.autoFocus,
589
- modal: (ctx2) => !!ctx2.modal,
590
- portalled: (ctx2) => !!ctx2.portalled,
591
- isRelatedTargetFocusable: (_ctx, evt) => evt.focusable,
592
- closeOnBlur: (ctx2) => !!ctx2.closeOnBlur,
830
+ portalled: (ctx2) => ctx2.currentPortalled,
831
+ isRelatedTargetWithinContent: (ctx2, evt) => contains(dom.getContentEl(ctx2), evt.target),
832
+ closeOnInteractOutside: (ctx2) => !!ctx2.closeOnInteractOutside,
593
833
  isContentFocused: (ctx2) => dom.getContentEl(ctx2) === dom.getActiveEl(ctx2),
834
+ isTriggerFocused: (ctx2) => dom.getTriggerEl(ctx2) === dom.getActiveEl(ctx2),
594
835
  isFirstTabbableElement: (ctx2) => dom.getFirstTabbableEl(ctx2) === dom.getActiveEl(ctx2),
595
836
  isLastTabbableElement: (ctx2) => dom.getLastTabbableEl(ctx2) === dom.getActiveEl(ctx2)
596
837
  },
@@ -620,32 +861,38 @@ function machine(ctx = {}) {
620
861
  (_a = dom.getInitialFocusEl(ctx2)) == null ? void 0 : _a.focus();
621
862
  });
622
863
  },
623
- focusTrigger(ctx2) {
864
+ focusTriggerIfNeeded(ctx2) {
865
+ if (!ctx2.focusTriggerOnClose)
866
+ return;
624
867
  raf(() => {
625
868
  var _a;
626
- (_a = dom.getTriggerEl(ctx2)) == null ? void 0 : _a.focus();
869
+ return (_a = dom.getTriggerEl(ctx2)) == null ? void 0 : _a.focus();
627
870
  });
628
871
  },
872
+ focusFirstTabbableElement(ctx2, evt) {
873
+ var _a;
874
+ evt.preventDefault();
875
+ (_a = dom.getFirstTabbableEl(ctx2)) == null ? void 0 : _a.focus();
876
+ },
629
877
  invokeOnOpen(ctx2, evt) {
630
878
  var _a;
631
879
  if (evt.type !== "SETUP") {
632
- (_a = ctx2.onOpen) == null ? void 0 : _a.call(ctx2);
880
+ (_a = ctx2.onOpenChange) == null ? void 0 : _a.call(ctx2, true);
633
881
  }
634
882
  },
635
883
  invokeOnClose(ctx2, evt) {
636
884
  var _a;
637
885
  if (evt.type !== "SETUP") {
638
- (_a = ctx2.onClose) == null ? void 0 : _a.call(ctx2);
886
+ (_a = ctx2.onOpenChange) == null ? void 0 : _a.call(ctx2, false);
639
887
  }
640
888
  },
641
889
  focusNextTabbableElementAfterTrigger(ctx2, evt) {
642
890
  const content = dom.getContentEl(ctx2);
643
- const doc = dom.getDoc(ctx2);
644
891
  const button = dom.getTriggerEl(ctx2);
645
892
  if (!content || !button)
646
893
  return;
647
894
  const lastTabbable = dom.getLastTabbableEl(ctx2);
648
- if (lastTabbable !== doc.activeElement)
895
+ if (lastTabbable !== dom.getActiveEl(ctx2))
649
896
  return;
650
897
  let tabbables = dom.getDocTabbableEls(ctx2);
651
898
  let elementAfterTrigger = next(tabbables, tabbables.indexOf(button), { loop: false });