@zag-js/popover 0.2.13 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,21 +4,20 @@ import {
4
4
 
5
5
  // src/popover.machine.ts
6
6
  import { ariaHidden } from "@zag-js/aria-hidden";
7
- import { createMachine, guards } from "@zag-js/core";
7
+ import { createMachine } from "@zag-js/core";
8
8
  import { trackDismissableElement } from "@zag-js/dismissable";
9
- import { contains, nextTick, raf } from "@zag-js/dom-query";
10
- import { addDomEvent, isModifiedEvent } from "@zag-js/dom-event";
9
+ import { nextTick, raf } from "@zag-js/dom-query";
11
10
  import { getPlacement } from "@zag-js/popper";
12
11
  import { preventBodyScroll } from "@zag-js/remove-scroll";
13
- import { compact, next, runIfFn } from "@zag-js/utils";
12
+ import { proxyTabFocus } from "@zag-js/tabbable";
13
+ import { compact, runIfFn } from "@zag-js/utils";
14
14
  import { createFocusTrap } from "focus-trap";
15
- var { and, or, not } = guards;
16
15
  function machine(userContext) {
17
16
  const ctx = compact(userContext);
18
17
  return createMachine(
19
18
  {
20
19
  id: "popover",
21
- initial: ctx.defaultOpen ? "open" : "closed",
20
+ initial: ctx.open ? "open" : "closed",
22
21
  context: {
23
22
  closeOnInteractOutside: true,
24
23
  closeOnEsc: true,
@@ -33,13 +32,15 @@ function machine(userContext) {
33
32
  focusTriggerOnClose: true,
34
33
  renderedElements: {
35
34
  title: true,
36
- description: true,
37
- anchor: false
35
+ description: true
38
36
  }
39
37
  },
40
38
  computed: {
41
39
  currentPortalled: (ctx2) => !!ctx2.modal || !!ctx2.portalled
42
40
  },
41
+ watch: {
42
+ open: ["toggleVisibility"]
43
+ },
43
44
  entry: ["checkRenderedElements"],
44
45
  states: {
45
46
  closed: {
@@ -54,9 +55,9 @@ function machine(userContext) {
54
55
  "trapFocus",
55
56
  "preventScroll",
56
57
  "hideContentBelow",
57
- "computePlacement",
58
- "trackInteractionOutside",
59
- "trackTabKeyDown"
58
+ "trackPositioning",
59
+ "trackDismissableElement",
60
+ "proxyTabFocus"
60
61
  ],
61
62
  entry: ["setInitialFocus", "invokeOnOpen"],
62
63
  on: {
@@ -66,24 +67,8 @@ function machine(userContext) {
66
67
  actions: "focusTriggerIfNeeded"
67
68
  },
68
69
  TOGGLE: "closed",
69
- TRIGGER_BLUR: {
70
- guard: not("isRelatedTargetWithinContent"),
71
- target: "closed"
72
- },
73
- TAB: [
74
- {
75
- guard: and("isTriggerFocused", "portalled"),
76
- actions: "focusFirstTabbableElement"
77
- },
78
- {
79
- guard: and("isLastTabbableElement", "closeOnInteractOutside", "portalled"),
80
- target: "closed",
81
- actions: "focusNextTabbableElementAfterTrigger"
82
- }
83
- ],
84
- SHIFT_TAB: {
85
- guard: and(or("isFirstTabbableElement", "isContentFocused"), "portalled"),
86
- actions: "focusTriggerIfNeeded"
70
+ SET_POSITIONING: {
71
+ actions: "setPositioning"
87
72
  }
88
73
  }
89
74
  }
@@ -91,22 +76,20 @@ function machine(userContext) {
91
76
  },
92
77
  {
93
78
  activities: {
94
- computePlacement(ctx2) {
79
+ trackPositioning(ctx2) {
95
80
  ctx2.currentPlacement = ctx2.positioning.placement;
96
- const anchorEl = ctx2.renderedElements.anchor ? dom.getAnchorEl(ctx2) : dom.getTriggerEl(ctx2);
81
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
97
82
  return getPlacement(anchorEl, dom.getPositionerEl(ctx2), {
98
83
  ...ctx2.positioning,
99
84
  onComplete(data) {
100
85
  ctx2.currentPlacement = data.placement;
101
- ctx2.isPlacementComplete = true;
102
86
  },
103
87
  onCleanup() {
104
88
  ctx2.currentPlacement = void 0;
105
- ctx2.isPlacementComplete = false;
106
89
  }
107
90
  });
108
91
  },
109
- trackInteractionOutside(ctx2, _evt, { send }) {
92
+ trackDismissableElement(ctx2, _evt, { send }) {
110
93
  return trackDismissableElement(dom.getContentEl(ctx2), {
111
94
  pointerBlocking: ctx2.modal,
112
95
  exclude: dom.getTriggerEl(ctx2),
@@ -131,32 +114,18 @@ function machine(userContext) {
131
114
  },
132
115
  onFocusOutside(event) {
133
116
  ctx2.onFocusOutside?.(event);
134
- if (ctx2.currentPortalled) {
135
- event.preventDefault();
136
- }
137
117
  },
138
118
  onDismiss() {
139
119
  send({ type: "REQUEST_CLOSE", src: "#interact-outside" });
140
120
  }
141
121
  });
142
122
  },
143
- trackTabKeyDown(ctx2, _evt, { send }) {
144
- if (ctx2.modal)
123
+ proxyTabFocus(ctx2) {
124
+ if (ctx2.modal || !ctx2.portalled)
145
125
  return;
146
- return addDomEvent(
147
- dom.getWin(ctx2),
148
- "keydown",
149
- (event) => {
150
- const isTabKey = event.key === "Tab" && !isModifiedEvent(event);
151
- if (!isTabKey)
152
- return;
153
- send({
154
- type: event.shiftKey ? "SHIFT_TAB" : "TAB",
155
- preventDefault: () => event.preventDefault()
156
- });
157
- },
158
- true
159
- );
126
+ return proxyTabFocus(dom.getContentEl(ctx2), dom.getTriggerEl(ctx2), (el) => {
127
+ el.focus({ preventScroll: true });
128
+ });
160
129
  },
161
130
  hideContentBelow(ctx2) {
162
131
  if (!ctx2.modal)
@@ -196,22 +165,22 @@ function machine(userContext) {
196
165
  return () => trap?.deactivate();
197
166
  }
198
167
  },
199
- guards: {
200
- portalled: (ctx2) => ctx2.currentPortalled,
201
- isRelatedTargetWithinContent: (ctx2, evt) => contains(dom.getContentEl(ctx2), evt.target),
202
- closeOnInteractOutside: (ctx2) => !!ctx2.closeOnInteractOutside,
203
- isContentFocused: (ctx2) => dom.getContentEl(ctx2) === dom.getActiveEl(ctx2),
204
- isTriggerFocused: (ctx2) => dom.getTriggerEl(ctx2) === dom.getActiveEl(ctx2),
205
- isFirstTabbableElement: (ctx2) => dom.getFirstTabbableEl(ctx2) === dom.getActiveEl(ctx2),
206
- isLastTabbableElement: (ctx2) => dom.getLastTabbableEl(ctx2) === dom.getActiveEl(ctx2)
207
- },
208
168
  actions: {
169
+ setPositioning(ctx2, evt) {
170
+ raf(() => {
171
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
172
+ getPlacement(anchorEl, dom.getPositionerEl(ctx2), {
173
+ ...ctx2.positioning,
174
+ ...evt.options,
175
+ listeners: false
176
+ });
177
+ });
178
+ },
209
179
  checkRenderedElements(ctx2) {
210
180
  raf(() => {
211
181
  Object.assign(ctx2.renderedElements, {
212
182
  title: !!dom.getTitleEl(ctx2),
213
- description: !!dom.getDescriptionEl(ctx2),
214
- anchor: !!dom.getAnchorEl(ctx2)
183
+ description: !!dom.getDescriptionEl(ctx2)
215
184
  });
216
185
  });
217
186
  },
@@ -225,38 +194,14 @@ function machine(userContext) {
225
194
  return;
226
195
  raf(() => dom.getTriggerEl(ctx2)?.focus());
227
196
  },
228
- focusFirstTabbableElement(ctx2, evt) {
229
- evt.preventDefault();
230
- dom.getFirstTabbableEl(ctx2)?.focus();
231
- },
232
- invokeOnOpen(ctx2, evt) {
233
- if (evt.type !== "SETUP") {
234
- ctx2.onOpenChange?.(true);
235
- }
197
+ invokeOnOpen(ctx2) {
198
+ ctx2.onOpenChange?.(true);
236
199
  },
237
- invokeOnClose(ctx2, evt) {
238
- if (evt.type !== "SETUP") {
239
- ctx2.onOpenChange?.(false);
240
- }
200
+ invokeOnClose(ctx2) {
201
+ ctx2.onOpenChange?.(false);
241
202
  },
242
- focusNextTabbableElementAfterTrigger(ctx2, evt) {
243
- const content = dom.getContentEl(ctx2);
244
- const button = dom.getTriggerEl(ctx2);
245
- if (!content || !button)
246
- return;
247
- const lastTabbable = dom.getLastTabbableEl(ctx2);
248
- if (lastTabbable !== dom.getActiveEl(ctx2))
249
- return;
250
- let tabbables = dom.getDocTabbableEls(ctx2);
251
- let elementAfterTrigger = next(tabbables, tabbables.indexOf(button), { loop: false });
252
- if (elementAfterTrigger === content) {
253
- tabbables = tabbables.filter((el) => !contains(content, el));
254
- elementAfterTrigger = next(tabbables, tabbables.indexOf(button), { loop: false });
255
- }
256
- if (!elementAfterTrigger || elementAfterTrigger === button)
257
- return;
258
- evt.preventDefault();
259
- raf(() => elementAfterTrigger?.focus());
203
+ toggleVisibility(ctx2, _evt, { send }) {
204
+ send({ type: ctx2.open ? "OPEN" : "CLOSE", src: "controlled" });
260
205
  }
261
206
  }
262
207
  }
@@ -14,7 +14,6 @@ function connect(state, send, normalize) {
14
14
  const portalled = state.context.currentPortalled;
15
15
  const rendered = state.context.renderedElements;
16
16
  const popperStyles = getPlacementStyles({
17
- measured: !!state.context.isPlacementComplete,
18
17
  placement: currentPlacement
19
18
  });
20
19
  return {
@@ -38,6 +37,12 @@ function connect(state, send, normalize) {
38
37
  close() {
39
38
  send("CLOSE");
40
39
  },
40
+ /**
41
+ * Function to reposition the popover
42
+ */
43
+ setPositioning(options) {
44
+ send({ type: "SET_POSITIONING", options });
45
+ },
41
46
  arrowProps: normalize.element({
42
47
  id: dom.getArrowId(state.context),
43
48
  ...parts.arrow.attrs,
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { connect } from './popover.connect.js';
3
3
  export { machine } from './popover.machine.js';
4
4
  export { UserDefinedContext as Context } from './popover.types.js';
5
5
  import '@zag-js/anatomy';
6
+ import '@zag-js/popper';
6
7
  import '@zag-js/types';
7
8
  import '@zag-js/core';
8
9
  import '@zag-js/dismissable';
9
- import '@zag-js/popper';
package/dist/index.js CHANGED
@@ -88,7 +88,6 @@ function connect(state, send, normalize) {
88
88
  const portalled = state.context.currentPortalled;
89
89
  const rendered = state.context.renderedElements;
90
90
  const popperStyles = (0, import_popper.getPlacementStyles)({
91
- measured: !!state.context.isPlacementComplete,
92
91
  placement: currentPlacement
93
92
  });
94
93
  return {
@@ -112,6 +111,12 @@ function connect(state, send, normalize) {
112
111
  close() {
113
112
  send("CLOSE");
114
113
  },
114
+ /**
115
+ * Function to reposition the popover
116
+ */
117
+ setPositioning(options) {
118
+ send({ type: "SET_POSITIONING", options });
119
+ },
115
120
  arrowProps: normalize.element({
116
121
  id: dom.getArrowId(state.context),
117
122
  ...parts.arrow.attrs,
@@ -182,18 +187,17 @@ var import_aria_hidden = require("@zag-js/aria-hidden");
182
187
  var import_core = require("@zag-js/core");
183
188
  var import_dismissable = require("@zag-js/dismissable");
184
189
  var import_dom_query3 = require("@zag-js/dom-query");
185
- var import_dom_event = require("@zag-js/dom-event");
186
190
  var import_popper2 = require("@zag-js/popper");
187
191
  var import_remove_scroll = require("@zag-js/remove-scroll");
192
+ var import_tabbable2 = require("@zag-js/tabbable");
188
193
  var import_utils2 = require("@zag-js/utils");
189
194
  var import_focus_trap = require("focus-trap");
190
- var { and, or, not } = import_core.guards;
191
195
  function machine(userContext) {
192
196
  const ctx = (0, import_utils2.compact)(userContext);
193
197
  return (0, import_core.createMachine)(
194
198
  {
195
199
  id: "popover",
196
- initial: ctx.defaultOpen ? "open" : "closed",
200
+ initial: ctx.open ? "open" : "closed",
197
201
  context: {
198
202
  closeOnInteractOutside: true,
199
203
  closeOnEsc: true,
@@ -208,13 +212,15 @@ function machine(userContext) {
208
212
  focusTriggerOnClose: true,
209
213
  renderedElements: {
210
214
  title: true,
211
- description: true,
212
- anchor: false
215
+ description: true
213
216
  }
214
217
  },
215
218
  computed: {
216
219
  currentPortalled: (ctx2) => !!ctx2.modal || !!ctx2.portalled
217
220
  },
221
+ watch: {
222
+ open: ["toggleVisibility"]
223
+ },
218
224
  entry: ["checkRenderedElements"],
219
225
  states: {
220
226
  closed: {
@@ -229,9 +235,9 @@ function machine(userContext) {
229
235
  "trapFocus",
230
236
  "preventScroll",
231
237
  "hideContentBelow",
232
- "computePlacement",
233
- "trackInteractionOutside",
234
- "trackTabKeyDown"
238
+ "trackPositioning",
239
+ "trackDismissableElement",
240
+ "proxyTabFocus"
235
241
  ],
236
242
  entry: ["setInitialFocus", "invokeOnOpen"],
237
243
  on: {
@@ -241,24 +247,8 @@ function machine(userContext) {
241
247
  actions: "focusTriggerIfNeeded"
242
248
  },
243
249
  TOGGLE: "closed",
244
- TRIGGER_BLUR: {
245
- guard: not("isRelatedTargetWithinContent"),
246
- target: "closed"
247
- },
248
- TAB: [
249
- {
250
- guard: and("isTriggerFocused", "portalled"),
251
- actions: "focusFirstTabbableElement"
252
- },
253
- {
254
- guard: and("isLastTabbableElement", "closeOnInteractOutside", "portalled"),
255
- target: "closed",
256
- actions: "focusNextTabbableElementAfterTrigger"
257
- }
258
- ],
259
- SHIFT_TAB: {
260
- guard: and(or("isFirstTabbableElement", "isContentFocused"), "portalled"),
261
- actions: "focusTriggerIfNeeded"
250
+ SET_POSITIONING: {
251
+ actions: "setPositioning"
262
252
  }
263
253
  }
264
254
  }
@@ -266,22 +256,20 @@ function machine(userContext) {
266
256
  },
267
257
  {
268
258
  activities: {
269
- computePlacement(ctx2) {
259
+ trackPositioning(ctx2) {
270
260
  ctx2.currentPlacement = ctx2.positioning.placement;
271
- const anchorEl = ctx2.renderedElements.anchor ? dom.getAnchorEl(ctx2) : dom.getTriggerEl(ctx2);
261
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
272
262
  return (0, import_popper2.getPlacement)(anchorEl, dom.getPositionerEl(ctx2), {
273
263
  ...ctx2.positioning,
274
264
  onComplete(data) {
275
265
  ctx2.currentPlacement = data.placement;
276
- ctx2.isPlacementComplete = true;
277
266
  },
278
267
  onCleanup() {
279
268
  ctx2.currentPlacement = void 0;
280
- ctx2.isPlacementComplete = false;
281
269
  }
282
270
  });
283
271
  },
284
- trackInteractionOutside(ctx2, _evt, { send }) {
272
+ trackDismissableElement(ctx2, _evt, { send }) {
285
273
  return (0, import_dismissable.trackDismissableElement)(dom.getContentEl(ctx2), {
286
274
  pointerBlocking: ctx2.modal,
287
275
  exclude: dom.getTriggerEl(ctx2),
@@ -306,32 +294,18 @@ function machine(userContext) {
306
294
  },
307
295
  onFocusOutside(event) {
308
296
  ctx2.onFocusOutside?.(event);
309
- if (ctx2.currentPortalled) {
310
- event.preventDefault();
311
- }
312
297
  },
313
298
  onDismiss() {
314
299
  send({ type: "REQUEST_CLOSE", src: "#interact-outside" });
315
300
  }
316
301
  });
317
302
  },
318
- trackTabKeyDown(ctx2, _evt, { send }) {
319
- if (ctx2.modal)
303
+ proxyTabFocus(ctx2) {
304
+ if (ctx2.modal || !ctx2.portalled)
320
305
  return;
321
- return (0, import_dom_event.addDomEvent)(
322
- dom.getWin(ctx2),
323
- "keydown",
324
- (event) => {
325
- const isTabKey = event.key === "Tab" && !(0, import_dom_event.isModifiedEvent)(event);
326
- if (!isTabKey)
327
- return;
328
- send({
329
- type: event.shiftKey ? "SHIFT_TAB" : "TAB",
330
- preventDefault: () => event.preventDefault()
331
- });
332
- },
333
- true
334
- );
306
+ return (0, import_tabbable2.proxyTabFocus)(dom.getContentEl(ctx2), dom.getTriggerEl(ctx2), (el) => {
307
+ el.focus({ preventScroll: true });
308
+ });
335
309
  },
336
310
  hideContentBelow(ctx2) {
337
311
  if (!ctx2.modal)
@@ -371,22 +345,22 @@ function machine(userContext) {
371
345
  return () => trap?.deactivate();
372
346
  }
373
347
  },
374
- guards: {
375
- portalled: (ctx2) => ctx2.currentPortalled,
376
- isRelatedTargetWithinContent: (ctx2, evt) => (0, import_dom_query3.contains)(dom.getContentEl(ctx2), evt.target),
377
- closeOnInteractOutside: (ctx2) => !!ctx2.closeOnInteractOutside,
378
- isContentFocused: (ctx2) => dom.getContentEl(ctx2) === dom.getActiveEl(ctx2),
379
- isTriggerFocused: (ctx2) => dom.getTriggerEl(ctx2) === dom.getActiveEl(ctx2),
380
- isFirstTabbableElement: (ctx2) => dom.getFirstTabbableEl(ctx2) === dom.getActiveEl(ctx2),
381
- isLastTabbableElement: (ctx2) => dom.getLastTabbableEl(ctx2) === dom.getActiveEl(ctx2)
382
- },
383
348
  actions: {
349
+ setPositioning(ctx2, evt) {
350
+ (0, import_dom_query3.raf)(() => {
351
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
352
+ (0, import_popper2.getPlacement)(anchorEl, dom.getPositionerEl(ctx2), {
353
+ ...ctx2.positioning,
354
+ ...evt.options,
355
+ listeners: false
356
+ });
357
+ });
358
+ },
384
359
  checkRenderedElements(ctx2) {
385
360
  (0, import_dom_query3.raf)(() => {
386
361
  Object.assign(ctx2.renderedElements, {
387
362
  title: !!dom.getTitleEl(ctx2),
388
- description: !!dom.getDescriptionEl(ctx2),
389
- anchor: !!dom.getAnchorEl(ctx2)
363
+ description: !!dom.getDescriptionEl(ctx2)
390
364
  });
391
365
  });
392
366
  },
@@ -400,38 +374,14 @@ function machine(userContext) {
400
374
  return;
401
375
  (0, import_dom_query3.raf)(() => dom.getTriggerEl(ctx2)?.focus());
402
376
  },
403
- focusFirstTabbableElement(ctx2, evt) {
404
- evt.preventDefault();
405
- dom.getFirstTabbableEl(ctx2)?.focus();
406
- },
407
- invokeOnOpen(ctx2, evt) {
408
- if (evt.type !== "SETUP") {
409
- ctx2.onOpenChange?.(true);
410
- }
377
+ invokeOnOpen(ctx2) {
378
+ ctx2.onOpenChange?.(true);
411
379
  },
412
- invokeOnClose(ctx2, evt) {
413
- if (evt.type !== "SETUP") {
414
- ctx2.onOpenChange?.(false);
415
- }
380
+ invokeOnClose(ctx2) {
381
+ ctx2.onOpenChange?.(false);
416
382
  },
417
- focusNextTabbableElementAfterTrigger(ctx2, evt) {
418
- const content = dom.getContentEl(ctx2);
419
- const button = dom.getTriggerEl(ctx2);
420
- if (!content || !button)
421
- return;
422
- const lastTabbable = dom.getLastTabbableEl(ctx2);
423
- if (lastTabbable !== dom.getActiveEl(ctx2))
424
- return;
425
- let tabbables = dom.getDocTabbableEls(ctx2);
426
- let elementAfterTrigger = (0, import_utils2.next)(tabbables, tabbables.indexOf(button), { loop: false });
427
- if (elementAfterTrigger === content) {
428
- tabbables = tabbables.filter((el) => !(0, import_dom_query3.contains)(content, el));
429
- elementAfterTrigger = (0, import_utils2.next)(tabbables, tabbables.indexOf(button), { loop: false });
430
- }
431
- if (!elementAfterTrigger || elementAfterTrigger === button)
432
- return;
433
- evt.preventDefault();
434
- (0, import_dom_query3.raf)(() => elementAfterTrigger?.focus());
383
+ toggleVisibility(ctx2, _evt, { send }) {
384
+ send({ type: ctx2.open ? "OPEN" : "CLOSE", src: "controlled" });
435
385
  }
436
386
  }
437
387
  }
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  connect
3
- } from "./chunk-GKHXQZZB.mjs";
3
+ } from "./chunk-QLF6NDJW.mjs";
4
4
  import {
5
5
  anatomy
6
6
  } from "./chunk-KTOPDXGV.mjs";
7
7
  import {
8
8
  machine
9
- } from "./chunk-JT2RXXW4.mjs";
9
+ } from "./chunk-I6GHRN6K.mjs";
10
10
  import "./chunk-4IGGT6KB.mjs";
11
11
  export {
12
12
  anatomy,
@@ -1,8 +1,8 @@
1
+ import { PositioningOptions } from '@zag-js/popper';
1
2
  import { PropTypes, NormalizeProps } from '@zag-js/types';
2
3
  import { State, Send } from './popover.types.js';
3
4
  import '@zag-js/core';
4
5
  import '@zag-js/dismissable';
5
- import '@zag-js/popper';
6
6
 
7
7
  declare function connect<T extends PropTypes>(state: State, send: Send, normalize: NormalizeProps<T>): {
8
8
  /**
@@ -21,6 +21,10 @@ declare function connect<T extends PropTypes>(state: State, send: Send, normaliz
21
21
  * Function to close the popover
22
22
  */
23
23
  close(): void;
24
+ /**
25
+ * Function to reposition the popover
26
+ */
27
+ setPositioning(options: Partial<PositioningOptions>): void;
24
28
  arrowProps: T["element"];
25
29
  arrowTipProps: T["element"];
26
30
  anchorProps: T["element"];
@@ -84,7 +84,6 @@ function connect(state, send, normalize) {
84
84
  const portalled = state.context.currentPortalled;
85
85
  const rendered = state.context.renderedElements;
86
86
  const popperStyles = (0, import_popper.getPlacementStyles)({
87
- measured: !!state.context.isPlacementComplete,
88
87
  placement: currentPlacement
89
88
  });
90
89
  return {
@@ -108,6 +107,12 @@ function connect(state, send, normalize) {
108
107
  close() {
109
108
  send("CLOSE");
110
109
  },
110
+ /**
111
+ * Function to reposition the popover
112
+ */
113
+ setPositioning(options) {
114
+ send({ type: "SET_POSITIONING", options });
115
+ },
111
116
  arrowProps: normalize.element({
112
117
  id: dom.getArrowId(state.context),
113
118
  ...parts.arrow.attrs,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  connect
3
- } from "./chunk-GKHXQZZB.mjs";
3
+ } from "./chunk-QLF6NDJW.mjs";
4
4
  import "./chunk-KTOPDXGV.mjs";
5
5
  import "./chunk-4IGGT6KB.mjs";
6
6
  export {
@@ -27,9 +27,9 @@ var import_aria_hidden = require("@zag-js/aria-hidden");
27
27
  var import_core = require("@zag-js/core");
28
28
  var import_dismissable = require("@zag-js/dismissable");
29
29
  var import_dom_query2 = require("@zag-js/dom-query");
30
- var import_dom_event = require("@zag-js/dom-event");
31
30
  var import_popper = require("@zag-js/popper");
32
31
  var import_remove_scroll = require("@zag-js/remove-scroll");
32
+ var import_tabbable2 = require("@zag-js/tabbable");
33
33
  var import_utils2 = require("@zag-js/utils");
34
34
  var import_focus_trap = require("focus-trap");
35
35
 
@@ -70,13 +70,12 @@ var dom = (0, import_dom_query.createScope)({
70
70
  });
71
71
 
72
72
  // src/popover.machine.ts
73
- var { and, or, not } = import_core.guards;
74
73
  function machine(userContext) {
75
74
  const ctx = (0, import_utils2.compact)(userContext);
76
75
  return (0, import_core.createMachine)(
77
76
  {
78
77
  id: "popover",
79
- initial: ctx.defaultOpen ? "open" : "closed",
78
+ initial: ctx.open ? "open" : "closed",
80
79
  context: {
81
80
  closeOnInteractOutside: true,
82
81
  closeOnEsc: true,
@@ -91,13 +90,15 @@ function machine(userContext) {
91
90
  focusTriggerOnClose: true,
92
91
  renderedElements: {
93
92
  title: true,
94
- description: true,
95
- anchor: false
93
+ description: true
96
94
  }
97
95
  },
98
96
  computed: {
99
97
  currentPortalled: (ctx2) => !!ctx2.modal || !!ctx2.portalled
100
98
  },
99
+ watch: {
100
+ open: ["toggleVisibility"]
101
+ },
101
102
  entry: ["checkRenderedElements"],
102
103
  states: {
103
104
  closed: {
@@ -112,9 +113,9 @@ function machine(userContext) {
112
113
  "trapFocus",
113
114
  "preventScroll",
114
115
  "hideContentBelow",
115
- "computePlacement",
116
- "trackInteractionOutside",
117
- "trackTabKeyDown"
116
+ "trackPositioning",
117
+ "trackDismissableElement",
118
+ "proxyTabFocus"
118
119
  ],
119
120
  entry: ["setInitialFocus", "invokeOnOpen"],
120
121
  on: {
@@ -124,24 +125,8 @@ function machine(userContext) {
124
125
  actions: "focusTriggerIfNeeded"
125
126
  },
126
127
  TOGGLE: "closed",
127
- TRIGGER_BLUR: {
128
- guard: not("isRelatedTargetWithinContent"),
129
- target: "closed"
130
- },
131
- TAB: [
132
- {
133
- guard: and("isTriggerFocused", "portalled"),
134
- actions: "focusFirstTabbableElement"
135
- },
136
- {
137
- guard: and("isLastTabbableElement", "closeOnInteractOutside", "portalled"),
138
- target: "closed",
139
- actions: "focusNextTabbableElementAfterTrigger"
140
- }
141
- ],
142
- SHIFT_TAB: {
143
- guard: and(or("isFirstTabbableElement", "isContentFocused"), "portalled"),
144
- actions: "focusTriggerIfNeeded"
128
+ SET_POSITIONING: {
129
+ actions: "setPositioning"
145
130
  }
146
131
  }
147
132
  }
@@ -149,22 +134,20 @@ function machine(userContext) {
149
134
  },
150
135
  {
151
136
  activities: {
152
- computePlacement(ctx2) {
137
+ trackPositioning(ctx2) {
153
138
  ctx2.currentPlacement = ctx2.positioning.placement;
154
- const anchorEl = ctx2.renderedElements.anchor ? dom.getAnchorEl(ctx2) : dom.getTriggerEl(ctx2);
139
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
155
140
  return (0, import_popper.getPlacement)(anchorEl, dom.getPositionerEl(ctx2), {
156
141
  ...ctx2.positioning,
157
142
  onComplete(data) {
158
143
  ctx2.currentPlacement = data.placement;
159
- ctx2.isPlacementComplete = true;
160
144
  },
161
145
  onCleanup() {
162
146
  ctx2.currentPlacement = void 0;
163
- ctx2.isPlacementComplete = false;
164
147
  }
165
148
  });
166
149
  },
167
- trackInteractionOutside(ctx2, _evt, { send }) {
150
+ trackDismissableElement(ctx2, _evt, { send }) {
168
151
  return (0, import_dismissable.trackDismissableElement)(dom.getContentEl(ctx2), {
169
152
  pointerBlocking: ctx2.modal,
170
153
  exclude: dom.getTriggerEl(ctx2),
@@ -189,32 +172,18 @@ function machine(userContext) {
189
172
  },
190
173
  onFocusOutside(event) {
191
174
  ctx2.onFocusOutside?.(event);
192
- if (ctx2.currentPortalled) {
193
- event.preventDefault();
194
- }
195
175
  },
196
176
  onDismiss() {
197
177
  send({ type: "REQUEST_CLOSE", src: "#interact-outside" });
198
178
  }
199
179
  });
200
180
  },
201
- trackTabKeyDown(ctx2, _evt, { send }) {
202
- if (ctx2.modal)
181
+ proxyTabFocus(ctx2) {
182
+ if (ctx2.modal || !ctx2.portalled)
203
183
  return;
204
- return (0, import_dom_event.addDomEvent)(
205
- dom.getWin(ctx2),
206
- "keydown",
207
- (event) => {
208
- const isTabKey = event.key === "Tab" && !(0, import_dom_event.isModifiedEvent)(event);
209
- if (!isTabKey)
210
- return;
211
- send({
212
- type: event.shiftKey ? "SHIFT_TAB" : "TAB",
213
- preventDefault: () => event.preventDefault()
214
- });
215
- },
216
- true
217
- );
184
+ return (0, import_tabbable2.proxyTabFocus)(dom.getContentEl(ctx2), dom.getTriggerEl(ctx2), (el) => {
185
+ el.focus({ preventScroll: true });
186
+ });
218
187
  },
219
188
  hideContentBelow(ctx2) {
220
189
  if (!ctx2.modal)
@@ -254,22 +223,22 @@ function machine(userContext) {
254
223
  return () => trap?.deactivate();
255
224
  }
256
225
  },
257
- guards: {
258
- portalled: (ctx2) => ctx2.currentPortalled,
259
- isRelatedTargetWithinContent: (ctx2, evt) => (0, import_dom_query2.contains)(dom.getContentEl(ctx2), evt.target),
260
- closeOnInteractOutside: (ctx2) => !!ctx2.closeOnInteractOutside,
261
- isContentFocused: (ctx2) => dom.getContentEl(ctx2) === dom.getActiveEl(ctx2),
262
- isTriggerFocused: (ctx2) => dom.getTriggerEl(ctx2) === dom.getActiveEl(ctx2),
263
- isFirstTabbableElement: (ctx2) => dom.getFirstTabbableEl(ctx2) === dom.getActiveEl(ctx2),
264
- isLastTabbableElement: (ctx2) => dom.getLastTabbableEl(ctx2) === dom.getActiveEl(ctx2)
265
- },
266
226
  actions: {
227
+ setPositioning(ctx2, evt) {
228
+ (0, import_dom_query2.raf)(() => {
229
+ const anchorEl = dom.getAnchorEl(ctx2) ?? dom.getTriggerEl(ctx2);
230
+ (0, import_popper.getPlacement)(anchorEl, dom.getPositionerEl(ctx2), {
231
+ ...ctx2.positioning,
232
+ ...evt.options,
233
+ listeners: false
234
+ });
235
+ });
236
+ },
267
237
  checkRenderedElements(ctx2) {
268
238
  (0, import_dom_query2.raf)(() => {
269
239
  Object.assign(ctx2.renderedElements, {
270
240
  title: !!dom.getTitleEl(ctx2),
271
- description: !!dom.getDescriptionEl(ctx2),
272
- anchor: !!dom.getAnchorEl(ctx2)
241
+ description: !!dom.getDescriptionEl(ctx2)
273
242
  });
274
243
  });
275
244
  },
@@ -283,38 +252,14 @@ function machine(userContext) {
283
252
  return;
284
253
  (0, import_dom_query2.raf)(() => dom.getTriggerEl(ctx2)?.focus());
285
254
  },
286
- focusFirstTabbableElement(ctx2, evt) {
287
- evt.preventDefault();
288
- dom.getFirstTabbableEl(ctx2)?.focus();
289
- },
290
- invokeOnOpen(ctx2, evt) {
291
- if (evt.type !== "SETUP") {
292
- ctx2.onOpenChange?.(true);
293
- }
255
+ invokeOnOpen(ctx2) {
256
+ ctx2.onOpenChange?.(true);
294
257
  },
295
- invokeOnClose(ctx2, evt) {
296
- if (evt.type !== "SETUP") {
297
- ctx2.onOpenChange?.(false);
298
- }
258
+ invokeOnClose(ctx2) {
259
+ ctx2.onOpenChange?.(false);
299
260
  },
300
- focusNextTabbableElementAfterTrigger(ctx2, evt) {
301
- const content = dom.getContentEl(ctx2);
302
- const button = dom.getTriggerEl(ctx2);
303
- if (!content || !button)
304
- return;
305
- const lastTabbable = dom.getLastTabbableEl(ctx2);
306
- if (lastTabbable !== dom.getActiveEl(ctx2))
307
- return;
308
- let tabbables = dom.getDocTabbableEls(ctx2);
309
- let elementAfterTrigger = (0, import_utils2.next)(tabbables, tabbables.indexOf(button), { loop: false });
310
- if (elementAfterTrigger === content) {
311
- tabbables = tabbables.filter((el) => !(0, import_dom_query2.contains)(content, el));
312
- elementAfterTrigger = (0, import_utils2.next)(tabbables, tabbables.indexOf(button), { loop: false });
313
- }
314
- if (!elementAfterTrigger || elementAfterTrigger === button)
315
- return;
316
- evt.preventDefault();
317
- (0, import_dom_query2.raf)(() => elementAfterTrigger?.focus());
261
+ toggleVisibility(ctx2, _evt, { send }) {
262
+ send({ type: ctx2.open ? "OPEN" : "CLOSE", src: "controlled" });
318
263
  }
319
264
  }
320
265
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  machine
3
- } from "./chunk-JT2RXXW4.mjs";
3
+ } from "./chunk-I6GHRN6K.mjs";
4
4
  import "./chunk-4IGGT6KB.mjs";
5
5
  export {
6
6
  machine
@@ -56,9 +56,9 @@ type PublicContext = DismissableElementHandlers & CommonProperties & {
56
56
  */
57
57
  positioning: PositioningOptions;
58
58
  /**
59
- * Whether to open the popover on page load
59
+ * Whether the popover is open
60
60
  */
61
- defaultOpen?: boolean;
61
+ open?: boolean;
62
62
  };
63
63
  type UserDefinedContext = RequiredBy<PublicContext, "id">;
64
64
  type ComputedContext = Readonly<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popover",
3
- "version": "0.2.13",
3
+ "version": "0.5.0",
4
4
  "description": "Core logic for the popover widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -26,18 +26,17 @@
26
26
  "url": "https://github.com/chakra-ui/zag/issues"
27
27
  },
28
28
  "dependencies": {
29
- "focus-trap": "7.3.1",
29
+ "focus-trap": "7.4.0",
30
30
  "@zag-js/anatomy": "0.1.4",
31
31
  "@zag-js/aria-hidden": "0.2.2",
32
- "@zag-js/core": "0.2.10",
32
+ "@zag-js/core": "0.5.0",
33
33
  "@zag-js/dom-query": "0.1.4",
34
- "@zag-js/dom-event": "0.0.1",
35
- "@zag-js/utils": "0.3.3",
36
- "@zag-js/dismissable": "0.2.4",
37
- "@zag-js/tabbable": "0.0.1",
38
- "@zag-js/popper": "0.2.5",
34
+ "@zag-js/utils": "0.3.4",
35
+ "@zag-js/dismissable": "0.5.0",
36
+ "@zag-js/tabbable": "0.1.1",
37
+ "@zag-js/popper": "0.2.7",
39
38
  "@zag-js/remove-scroll": "0.2.4",
40
- "@zag-js/types": "0.3.4"
39
+ "@zag-js/types": "0.5.0"
41
40
  },
42
41
  "devDependencies": {
43
42
  "clean-package": "2.2.0"