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