@zag-js/tooltip 0.82.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,468 +1,495 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
- import { createScope, dataAttr, addDomEvent, getOverflowAncestors, isComposingEvent } from '@zag-js/dom-query';
3
- import { isFocusVisible, trackFocusVisible } from '@zag-js/focus-visible';
4
- import { getPlacementStyles, getPlacement } from '@zag-js/popper';
5
- import { proxy, createMachine, subscribe, guards } from '@zag-js/core';
6
- import { createSplitProps, compact } from '@zag-js/utils';
2
+ import { addDomEvent, getOverflowAncestors, dataAttr, isComposingEvent } from '@zag-js/dom-query';
3
+ import { trackFocusVisible, isFocusVisible } from '@zag-js/focus-visible';
4
+ import { getPlacement, getPlacementStyles } from '@zag-js/popper';
5
+ import { proxy, subscribe } from '@zag-js/store';
6
+ import { createGuards, createMachine } from '@zag-js/core';
7
7
  import { createProps } from '@zag-js/types';
8
+ import { createSplitProps } from '@zag-js/utils';
8
9
 
9
10
  // src/tooltip.anatomy.ts
10
11
  var anatomy = createAnatomy("tooltip").parts("trigger", "arrow", "arrowTip", "positioner", "content");
11
12
  var parts = anatomy.build();
12
- var dom = createScope({
13
- getTriggerId: (ctx) => ctx.ids?.trigger ?? `tooltip:${ctx.id}:trigger`,
14
- getContentId: (ctx) => ctx.ids?.content ?? `tooltip:${ctx.id}:content`,
15
- getArrowId: (ctx) => ctx.ids?.arrow ?? `tooltip:${ctx.id}:arrow`,
16
- getPositionerId: (ctx) => ctx.ids?.positioner ?? `tooltip:${ctx.id}:popper`,
17
- getTriggerEl: (ctx) => dom.getById(ctx, dom.getTriggerId(ctx)),
18
- getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
19
- getPositionerEl: (ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
20
- getArrowEl: (ctx) => dom.getById(ctx, dom.getArrowId(ctx))
21
- });
22
- var store = proxy({
23
- id: null,
24
- prevId: null,
25
- setId(val) {
26
- this.prevId = this.id;
27
- this.id = val;
28
- }
29
- });
13
+
14
+ // src/tooltip.dom.ts
15
+ var getTriggerId = (scope) => scope.ids?.trigger ?? `tooltip:${scope.id}:trigger`;
16
+ var getContentId = (scope) => scope.ids?.content ?? `tooltip:${scope.id}:content`;
17
+ var getArrowId = (scope) => scope.ids?.arrow ?? `tooltip:${scope.id}:arrow`;
18
+ var getPositionerId = (scope) => scope.ids?.positioner ?? `tooltip:${scope.id}:popper`;
19
+ var getTriggerEl = (scope) => scope.getById(getTriggerId(scope));
20
+ var getPositionerEl = (scope) => scope.getById(getPositionerId(scope));
21
+ var store = proxy({ id: null });
30
22
 
31
23
  // src/tooltip.connect.ts
32
- function connect(state, send, normalize) {
33
- const id = state.context.id;
34
- const hasAriaLabel = state.context.hasAriaLabel;
35
- const open = state.hasTag("open");
36
- const triggerId = dom.getTriggerId(state.context);
37
- const contentId = dom.getContentId(state.context);
38
- const disabled = state.context.disabled;
24
+ function connect(service, normalize) {
25
+ const { state, context, send, scope, prop, event: _event } = service;
26
+ const id = prop("id");
27
+ const hasAriaLabel = !!prop("aria-label");
28
+ const open = state.matches("open", "closing");
29
+ const triggerId = getTriggerId(scope);
30
+ const contentId = getContentId(scope);
31
+ const disabled = prop("disabled");
39
32
  const popperStyles = getPlacementStyles({
40
- ...state.context.positioning,
41
- placement: state.context.currentPlacement
33
+ ...prop("positioning"),
34
+ placement: context.get("currentPlacement")
42
35
  });
43
36
  return {
44
37
  open,
45
38
  setOpen(nextOpen) {
46
39
  if (nextOpen === open) return;
47
- send(nextOpen ? "OPEN" : "CLOSE");
40
+ send({ type: nextOpen ? "open" : "close" });
48
41
  },
49
42
  reposition(options = {}) {
50
- send({ type: "POSITIONING.SET", options });
43
+ send({ type: "positioning.set", options });
51
44
  },
52
45
  getTriggerProps() {
53
46
  return normalize.button({
54
47
  ...parts.trigger.attrs,
55
48
  id: triggerId,
56
- dir: state.context.dir,
49
+ dir: prop("dir"),
57
50
  "data-expanded": dataAttr(open),
58
51
  "data-state": open ? "open" : "closed",
59
52
  "aria-describedby": open ? contentId : void 0,
60
53
  onClick(event) {
61
54
  if (event.defaultPrevented) return;
62
55
  if (disabled) return;
63
- if (!state.context.closeOnClick) return;
64
- send({ type: "CLOSE", src: "trigger.click" });
56
+ if (!prop("closeOnClick")) return;
57
+ send({ type: "close", src: "trigger.click" });
65
58
  },
66
59
  onFocus(event) {
67
- if (event.defaultPrevented) return;
68
- if (disabled) return;
69
- if (state.event.src === "trigger.pointerdown") return;
70
- if (!isFocusVisible()) return;
71
- send({ type: "OPEN", src: "trigger.focus" });
60
+ queueMicrotask(() => {
61
+ if (event.defaultPrevented) return;
62
+ if (disabled) return;
63
+ if (_event.src === "trigger.pointerdown") return;
64
+ if (!isFocusVisible()) return;
65
+ send({ type: "open", src: "trigger.focus" });
66
+ });
72
67
  },
73
68
  onBlur(event) {
74
69
  if (event.defaultPrevented) return;
75
70
  if (disabled) return;
76
71
  if (id === store.id) {
77
- send({ type: "CLOSE", src: "trigger.blur" });
72
+ send({ type: "close", src: "trigger.blur" });
78
73
  }
79
74
  },
80
75
  onPointerDown(event) {
81
76
  if (event.defaultPrevented) return;
82
77
  if (disabled) return;
83
- if (!state.context.closeOnPointerDown) return;
78
+ if (!prop("closeOnPointerDown")) return;
84
79
  if (id === store.id) {
85
- send({ type: "CLOSE", src: "trigger.pointerdown" });
80
+ send({ type: "close", src: "trigger.pointerdown" });
86
81
  }
87
82
  },
88
83
  onPointerMove(event) {
89
84
  if (event.defaultPrevented) return;
90
85
  if (disabled) return;
91
86
  if (event.pointerType === "touch") return;
92
- send("POINTER_MOVE");
87
+ send({ type: "pointer.move" });
93
88
  },
94
89
  onPointerLeave() {
95
90
  if (disabled) return;
96
- send("POINTER_LEAVE");
91
+ send({ type: "pointer.leave" });
97
92
  },
98
93
  onPointerCancel() {
99
94
  if (disabled) return;
100
- send("POINTER_LEAVE");
95
+ send({ type: "pointer.leave" });
101
96
  }
102
97
  });
103
98
  },
104
99
  getArrowProps() {
105
100
  return normalize.element({
106
- id: dom.getArrowId(state.context),
101
+ id: getArrowId(scope),
107
102
  ...parts.arrow.attrs,
108
- dir: state.context.dir,
103
+ dir: prop("dir"),
109
104
  style: popperStyles.arrow
110
105
  });
111
106
  },
112
107
  getArrowTipProps() {
113
108
  return normalize.element({
114
109
  ...parts.arrowTip.attrs,
115
- dir: state.context.dir,
110
+ dir: prop("dir"),
116
111
  style: popperStyles.arrowTip
117
112
  });
118
113
  },
119
114
  getPositionerProps() {
120
115
  return normalize.element({
121
- id: dom.getPositionerId(state.context),
116
+ id: getPositionerId(scope),
122
117
  ...parts.positioner.attrs,
123
- dir: state.context.dir,
118
+ dir: prop("dir"),
124
119
  style: popperStyles.floating
125
120
  });
126
121
  },
127
122
  getContentProps() {
128
123
  return normalize.element({
129
124
  ...parts.content.attrs,
130
- dir: state.context.dir,
125
+ dir: prop("dir"),
131
126
  hidden: !open,
132
127
  "data-state": open ? "open" : "closed",
133
128
  role: hasAriaLabel ? void 0 : "tooltip",
134
129
  id: hasAriaLabel ? void 0 : contentId,
135
- "data-placement": state.context.currentPlacement,
130
+ "data-placement": context.get("currentPlacement"),
136
131
  onPointerEnter() {
137
- send("CONTENT.POINTER_MOVE");
132
+ send({ type: "content.pointer.move" });
138
133
  },
139
134
  onPointerLeave() {
140
- send("CONTENT.POINTER_LEAVE");
135
+ send({ type: "content.pointer.leave" });
141
136
  },
142
137
  style: {
143
- pointerEvents: state.context.interactive ? "auto" : "none"
138
+ pointerEvents: prop("interactive") ? "auto" : "none"
144
139
  }
145
140
  });
146
141
  }
147
142
  };
148
143
  }
149
- var { and, not } = guards;
150
- function machine(userContext) {
151
- const ctx = compact(userContext);
152
- return createMachine(
153
- {
154
- id: "tooltip",
155
- initial: ctx.open ? "open" : "closed",
156
- activities: ["trackFocusVisible"],
157
- context: {
158
- openDelay: 1e3,
159
- closeDelay: 500,
160
- closeOnPointerDown: true,
161
- closeOnEscape: true,
162
- interactive: false,
163
- closeOnScroll: true,
164
- closeOnClick: true,
165
- disabled: false,
166
- ...ctx,
167
- currentPlacement: void 0,
168
- hasPointerMoveOpened: false,
169
- positioning: {
170
- placement: "bottom",
171
- ...ctx.positioning
172
- }
173
- },
174
- computed: {
175
- hasAriaLabel: (ctx2) => !!ctx2["aria-label"]
176
- },
177
- watch: {
178
- disabled: ["closeIfDisabled"],
179
- open: ["toggleVisibility"]
180
- },
181
- states: {
182
- closed: {
183
- tags: ["closed"],
184
- entry: ["clearGlobalId"],
185
- on: {
186
- "CONTROLLED.OPEN": "open",
187
- OPEN: [
188
- {
189
- guard: "isOpenControlled",
190
- actions: ["invokeOnOpen"]
191
- },
192
- {
193
- target: "open",
194
- actions: ["invokeOnOpen"]
195
- }
196
- ],
197
- POINTER_LEAVE: {
198
- actions: ["clearPointerMoveOpened"]
199
- },
200
- POINTER_MOVE: [
201
- {
202
- guard: and("noVisibleTooltip", not("hasPointerMoveOpened")),
203
- target: "opening"
204
- },
205
- {
206
- guard: not("hasPointerMoveOpened"),
207
- target: "open",
208
- actions: ["setPointerMoveOpened", "invokeOnOpen"]
209
- }
210
- ]
144
+ var { and, not } = createGuards();
145
+ var machine = createMachine({
146
+ initialState: ({ prop }) => {
147
+ const open = prop("open") || prop("defaultOpen");
148
+ return open ? "open" : "closed";
149
+ },
150
+ props({ props: props2 }) {
151
+ return {
152
+ id: "x",
153
+ openDelay: 1e3,
154
+ closeDelay: 500,
155
+ closeOnPointerDown: true,
156
+ closeOnEscape: true,
157
+ interactive: false,
158
+ closeOnScroll: true,
159
+ closeOnClick: true,
160
+ disabled: false,
161
+ ...props2,
162
+ positioning: {
163
+ placement: "bottom",
164
+ ...props2.positioning
165
+ }
166
+ };
167
+ },
168
+ effects: ["trackFocusVisible", "trackStore"],
169
+ context: ({ bindable }) => ({
170
+ currentPlacement: bindable(() => ({ defaultValue: void 0 })),
171
+ hasPointerMoveOpened: bindable(() => ({ defaultValue: false }))
172
+ }),
173
+ watch({ track, action, prop }) {
174
+ track([() => prop("disabled")], () => {
175
+ action(["closeIfDisabled"]);
176
+ });
177
+ track([() => prop("open")], () => {
178
+ action(["toggleVisibility"]);
179
+ });
180
+ },
181
+ states: {
182
+ closed: {
183
+ entry: ["clearGlobalId"],
184
+ on: {
185
+ "controlled.open": {
186
+ target: "open"
187
+ },
188
+ open: [
189
+ {
190
+ guard: "isOpenControlled",
191
+ actions: ["invokeOnOpen"]
192
+ },
193
+ {
194
+ target: "open",
195
+ actions: ["invokeOnOpen"]
211
196
  }
197
+ ],
198
+ "pointer.leave": {
199
+ actions: ["clearPointerMoveOpened"]
212
200
  },
213
- opening: {
214
- tags: ["closed"],
215
- activities: ["trackScroll", "trackPointerlockChange"],
216
- after: {
217
- OPEN_DELAY: [
218
- {
219
- guard: "isOpenControlled",
220
- actions: ["setPointerMoveOpened", "invokeOnOpen"]
221
- },
222
- {
223
- target: "open",
224
- actions: ["setPointerMoveOpened", "invokeOnOpen"]
225
- }
226
- ]
201
+ "pointer.move": [
202
+ {
203
+ guard: and("noVisibleTooltip", not("hasPointerMoveOpened")),
204
+ target: "opening"
205
+ },
206
+ {
207
+ guard: not("hasPointerMoveOpened"),
208
+ target: "open",
209
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
210
+ }
211
+ ]
212
+ }
213
+ },
214
+ opening: {
215
+ effects: ["trackScroll", "trackPointerlockChange", "waitForOpenDelay"],
216
+ on: {
217
+ "after.openDelay": [
218
+ {
219
+ guard: "isOpenControlled",
220
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
227
221
  },
228
- on: {
229
- "CONTROLLED.OPEN": "open",
230
- "CONTROLLED.CLOSE": "closed",
231
- OPEN: [
232
- {
233
- guard: "isOpenControlled",
234
- actions: ["invokeOnOpen"]
235
- },
236
- {
237
- target: "open",
238
- actions: ["invokeOnOpen"]
239
- }
240
- ],
241
- POINTER_LEAVE: [
242
- {
243
- guard: "isOpenControlled",
244
- // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
245
- actions: ["clearPointerMoveOpened", "invokeOnClose", "toggleVisibility"]
246
- },
247
- {
248
- target: "closed",
249
- actions: ["clearPointerMoveOpened", "invokeOnClose"]
250
- }
251
- ],
252
- CLOSE: [
253
- {
254
- guard: "isOpenControlled",
255
- // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
256
- actions: ["invokeOnClose", "toggleVisibility"]
257
- },
258
- {
259
- target: "closed",
260
- actions: ["invokeOnClose"]
261
- }
262
- ]
222
+ {
223
+ target: "open",
224
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
263
225
  }
226
+ ],
227
+ "controlled.open": {
228
+ target: "open"
264
229
  },
265
- open: {
266
- tags: ["open"],
267
- activities: ["trackEscapeKey", "trackScroll", "trackPointerlockChange", "trackPositioning"],
268
- entry: ["setGlobalId"],
269
- on: {
270
- "CONTROLLED.CLOSE": "closed",
271
- CLOSE: [
272
- {
273
- guard: "isOpenControlled",
274
- actions: ["invokeOnClose"]
275
- },
276
- {
277
- target: "closed",
278
- actions: ["invokeOnClose"]
279
- }
280
- ],
281
- POINTER_LEAVE: [
282
- {
283
- guard: "isVisible",
284
- target: "closing",
285
- actions: ["clearPointerMoveOpened"]
286
- },
287
- // == group ==
288
- {
289
- guard: "isOpenControlled",
290
- actions: ["clearPointerMoveOpened", "invokeOnClose"]
291
- },
292
- {
293
- target: "closed",
294
- actions: ["clearPointerMoveOpened", "invokeOnClose"]
295
- }
296
- ],
297
- "CONTENT.POINTER_LEAVE": {
298
- guard: "isInteractive",
299
- target: "closing"
300
- },
301
- "POSITIONING.SET": {
302
- actions: "reposition"
303
- }
230
+ "controlled.close": {
231
+ target: "closed"
232
+ },
233
+ open: [
234
+ {
235
+ guard: "isOpenControlled",
236
+ actions: ["invokeOnOpen"]
237
+ },
238
+ {
239
+ target: "open",
240
+ actions: ["invokeOnOpen"]
241
+ }
242
+ ],
243
+ "pointer.leave": [
244
+ {
245
+ guard: "isOpenControlled",
246
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
247
+ actions: ["clearPointerMoveOpened", "invokeOnClose", "toggleVisibility"]
248
+ },
249
+ {
250
+ target: "closed",
251
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
304
252
  }
253
+ ],
254
+ close: [
255
+ {
256
+ guard: "isOpenControlled",
257
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
258
+ actions: ["invokeOnClose", "toggleVisibility"]
259
+ },
260
+ {
261
+ target: "closed",
262
+ actions: ["invokeOnClose"]
263
+ }
264
+ ]
265
+ }
266
+ },
267
+ open: {
268
+ effects: ["trackEscapeKey", "trackScroll", "trackPointerlockChange", "trackPositioning"],
269
+ entry: ["setGlobalId"],
270
+ on: {
271
+ "controlled.close": {
272
+ target: "closed"
305
273
  },
306
- closing: {
307
- tags: ["open"],
308
- activities: ["trackStore", "trackPositioning"],
309
- after: {
310
- CLOSE_DELAY: [
311
- {
312
- guard: "isOpenControlled",
313
- actions: ["invokeOnClose"]
314
- },
315
- {
316
- target: "closed",
317
- actions: ["invokeOnClose"]
318
- }
319
- ]
274
+ close: [
275
+ {
276
+ guard: "isOpenControlled",
277
+ actions: ["invokeOnClose"]
320
278
  },
321
- on: {
322
- "CONTROLLED.CLOSE": "closed",
323
- "CONTROLLED.OPEN": "open",
324
- CLOSE: [
325
- {
326
- guard: "isOpenControlled",
327
- actions: ["invokeOnClose"]
328
- },
329
- {
330
- target: "closed",
331
- actions: ["invokeOnClose"]
332
- }
333
- ],
334
- POINTER_MOVE: [
335
- {
336
- guard: "isOpenControlled",
337
- // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
338
- actions: ["setPointerMoveOpened", "invokeOnOpen", "toggleVisibility"]
339
- },
340
- {
341
- target: "open",
342
- actions: ["setPointerMoveOpened", "invokeOnOpen"]
343
- }
344
- ],
345
- "CONTENT.POINTER_MOVE": {
346
- guard: "isInteractive",
347
- target: "open"
348
- },
349
- "POSITIONING.SET": {
350
- actions: "reposition"
351
- }
279
+ {
280
+ target: "closed",
281
+ actions: ["invokeOnClose"]
282
+ }
283
+ ],
284
+ "pointer.leave": [
285
+ {
286
+ guard: "isVisible",
287
+ target: "closing",
288
+ actions: ["clearPointerMoveOpened"]
289
+ },
290
+ // == group ==
291
+ {
292
+ guard: "isOpenControlled",
293
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
294
+ },
295
+ {
296
+ target: "closed",
297
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
352
298
  }
299
+ ],
300
+ "content.pointer.leave": {
301
+ guard: "isInteractive",
302
+ target: "closing"
303
+ },
304
+ "positioning.set": {
305
+ actions: ["reposition"]
353
306
  }
354
307
  }
355
308
  },
356
- {
357
- activities: {
358
- trackFocusVisible(ctx2) {
359
- return trackFocusVisible({ root: dom.getRootNode(ctx2) });
309
+ closing: {
310
+ effects: ["trackPositioning", "waitForCloseDelay"],
311
+ on: {
312
+ "after.closeDelay": [
313
+ {
314
+ guard: "isOpenControlled",
315
+ actions: ["invokeOnClose"]
316
+ },
317
+ {
318
+ target: "closed",
319
+ actions: ["invokeOnClose"]
320
+ }
321
+ ],
322
+ "controlled.close": {
323
+ target: "closed"
360
324
  },
361
- trackPositioning(ctx2) {
362
- ctx2.currentPlacement || (ctx2.currentPlacement = ctx2.positioning.placement);
363
- const getPositionerEl = () => dom.getPositionerEl(ctx2);
364
- return getPlacement(dom.getTriggerEl(ctx2), getPositionerEl, {
365
- ...ctx2.positioning,
366
- defer: true,
367
- onComplete(data) {
368
- ctx2.currentPlacement = data.placement;
369
- }
370
- });
325
+ "controlled.open": {
326
+ target: "open"
371
327
  },
372
- trackPointerlockChange(ctx2, _evt, { send }) {
373
- const onChange = () => send({ type: "CLOSE", src: "pointerlock:change" });
374
- return addDomEvent(dom.getDoc(ctx2), "pointerlockchange", onChange, false);
328
+ close: [
329
+ {
330
+ guard: "isOpenControlled",
331
+ actions: ["invokeOnClose"]
332
+ },
333
+ {
334
+ target: "closed",
335
+ actions: ["invokeOnClose"]
336
+ }
337
+ ],
338
+ "pointer.move": [
339
+ {
340
+ guard: "isOpenControlled",
341
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
342
+ actions: ["setPointerMoveOpened", "invokeOnOpen", "toggleVisibility"]
343
+ },
344
+ {
345
+ target: "open",
346
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
347
+ }
348
+ ],
349
+ "content.pointer.move": {
350
+ guard: "isInteractive",
351
+ target: "open"
375
352
  },
376
- trackScroll(ctx2, _evt, { send }) {
377
- if (!ctx2.closeOnScroll) return;
378
- const triggerEl = dom.getTriggerEl(ctx2);
379
- if (!triggerEl) return;
380
- const overflowParents = getOverflowAncestors(triggerEl);
381
- const cleanups = overflowParents.map((overflowParent) => {
382
- const onScroll = () => {
383
- send({ type: "CLOSE", src: "scroll" });
384
- };
385
- return addDomEvent(overflowParent, "scroll", onScroll, { passive: true, capture: true });
353
+ "positioning.set": {
354
+ actions: ["reposition"]
355
+ }
356
+ }
357
+ }
358
+ },
359
+ implementations: {
360
+ guards: {
361
+ noVisibleTooltip: () => store.id === null,
362
+ isVisible: ({ prop }) => prop("id") === store.id,
363
+ isInteractive: ({ prop }) => !!prop("interactive"),
364
+ hasPointerMoveOpened: ({ context }) => context.get("hasPointerMoveOpened"),
365
+ isOpenControlled: ({ prop }) => prop("open") !== void 0
366
+ },
367
+ actions: {
368
+ setGlobalId: ({ prop }) => {
369
+ store.id = prop("id");
370
+ },
371
+ clearGlobalId: ({ prop }) => {
372
+ if (prop("id") === store.id) {
373
+ store.id = null;
374
+ }
375
+ },
376
+ invokeOnOpen: ({ prop }) => {
377
+ prop("onOpenChange")?.({ open: true });
378
+ },
379
+ invokeOnClose: ({ prop }) => {
380
+ prop("onOpenChange")?.({ open: false });
381
+ },
382
+ closeIfDisabled: ({ prop, send }) => {
383
+ if (!prop("disabled")) return;
384
+ send({ type: "close", src: "disabled.change" });
385
+ },
386
+ reposition: ({ context, event, prop, scope }) => {
387
+ if (event.type !== "positioning.set") return;
388
+ const getPositionerEl2 = () => getPositionerEl(scope);
389
+ return getPlacement(getTriggerEl(scope), getPositionerEl2, {
390
+ ...prop("positioning"),
391
+ ...event.options,
392
+ defer: true,
393
+ listeners: false,
394
+ onComplete(data) {
395
+ context.set("currentPlacement", data.placement);
396
+ }
397
+ });
398
+ },
399
+ toggleVisibility: ({ prop, event, send }) => {
400
+ queueMicrotask(() => {
401
+ send({
402
+ type: prop("open") ? "controlled.open" : "controlled.close",
403
+ previousEvent: event
386
404
  });
387
- return () => {
388
- cleanups.forEach((fn) => fn?.());
405
+ });
406
+ },
407
+ setPointerMoveOpened: ({ context }) => {
408
+ context.set("hasPointerMoveOpened", true);
409
+ },
410
+ clearPointerMoveOpened: ({ context }) => {
411
+ context.set("hasPointerMoveOpened", false);
412
+ }
413
+ },
414
+ effects: {
415
+ trackFocusVisible: ({ scope }) => {
416
+ return trackFocusVisible({ root: scope.getRootNode?.() });
417
+ },
418
+ trackPositioning: ({ flush, context, prop, scope }) => {
419
+ if (!context.get("currentPlacement")) {
420
+ const positioning = prop("positioning");
421
+ context.set("currentPlacement", positioning.placement);
422
+ }
423
+ const getPositionerEl2 = () => getPositionerEl(scope);
424
+ return getPlacement(getTriggerEl(scope), getPositionerEl2, {
425
+ ...prop("positioning"),
426
+ defer: true,
427
+ onComplete(data) {
428
+ flush(() => {
429
+ context.set("currentPlacement", data.placement);
430
+ });
431
+ }
432
+ });
433
+ },
434
+ trackPointerlockChange: ({ send, scope }) => {
435
+ const doc = scope.getDoc();
436
+ const onChange = () => send({ type: "close", src: "pointerlock:change" });
437
+ return addDomEvent(doc, "pointerlockchange", onChange, false);
438
+ },
439
+ trackScroll: ({ send, prop, scope }) => {
440
+ if (!prop("closeOnScroll")) return;
441
+ const triggerEl = getTriggerEl(scope);
442
+ if (!triggerEl) return;
443
+ const overflowParents = getOverflowAncestors(triggerEl);
444
+ const cleanups = overflowParents.map((overflowParent) => {
445
+ const onScroll = () => {
446
+ send({ type: "close", src: "scroll" });
389
447
  };
390
- },
391
- trackStore(ctx2, _evt, { send }) {
392
- return subscribe(store, () => {
393
- if (store.id !== ctx2.id) {
394
- send({ type: "CLOSE", src: "id.change" });
395
- }
448
+ return addDomEvent(overflowParent, "scroll", onScroll, {
449
+ passive: true,
450
+ capture: true
396
451
  });
397
- },
398
- trackEscapeKey(ctx2, _evt, { send }) {
399
- if (!ctx2.closeOnEscape) return;
400
- const onKeyDown = (event) => {
401
- if (isComposingEvent(event)) return;
402
- if (event.key !== "Escape") return;
403
- event.stopPropagation();
404
- send({ type: "CLOSE", src: "keydown.escape" });
405
- };
406
- return addDomEvent(dom.getDoc(ctx2), "keydown", onKeyDown, true);
407
- }
452
+ });
453
+ return () => {
454
+ cleanups.forEach((fn) => fn?.());
455
+ };
408
456
  },
409
- actions: {
410
- setGlobalId(ctx2) {
411
- store.setId(ctx2.id);
412
- },
413
- clearGlobalId(ctx2) {
414
- if (ctx2.id === store.id) {
415
- store.setId(null);
416
- }
417
- },
418
- invokeOnOpen(ctx2) {
419
- ctx2.onOpenChange?.({ open: true });
420
- },
421
- invokeOnClose(ctx2) {
422
- ctx2.onOpenChange?.({ open: false });
423
- },
424
- closeIfDisabled(ctx2, _evt, { send }) {
425
- if (!ctx2.disabled) return;
426
- send({ type: "CLOSE", src: "disabled.change" });
427
- },
428
- reposition(ctx2, evt) {
429
- const getPositionerEl = () => dom.getPositionerEl(ctx2);
430
- getPlacement(dom.getTriggerEl(ctx2), getPositionerEl, {
431
- ...ctx2.positioning,
432
- ...evt.options,
433
- defer: true,
434
- listeners: false,
435
- onComplete(data) {
436
- ctx2.currentPlacement = data.placement;
457
+ trackStore: ({ prop, send }) => {
458
+ let cleanup;
459
+ queueMicrotask(() => {
460
+ cleanup = subscribe(store, () => {
461
+ if (store.id !== prop("id")) {
462
+ send({ type: "close", src: "id.change" });
437
463
  }
438
464
  });
439
- },
440
- toggleVisibility(ctx2, evt, { send }) {
441
- queueMicrotask(() => {
442
- send({ type: ctx2.open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: evt });
443
- });
444
- },
445
- setPointerMoveOpened(ctx2) {
446
- ctx2.hasPointerMoveOpened = true;
447
- },
448
- clearPointerMoveOpened(ctx2) {
449
- ctx2.hasPointerMoveOpened = false;
450
- }
465
+ });
466
+ return () => cleanup?.();
467
+ },
468
+ trackEscapeKey: ({ send, prop }) => {
469
+ if (!prop("closeOnEscape")) return;
470
+ const onKeyDown = (event) => {
471
+ if (isComposingEvent(event)) return;
472
+ if (event.key !== "Escape") return;
473
+ event.stopPropagation();
474
+ send({ type: "close", src: "keydown.escape" });
475
+ };
476
+ return addDomEvent(document, "keydown", onKeyDown, true);
451
477
  },
452
- guards: {
453
- noVisibleTooltip: () => store.id === null,
454
- isVisible: (ctx2) => ctx2.id === store.id,
455
- isInteractive: (ctx2) => ctx2.interactive,
456
- hasPointerMoveOpened: (ctx2) => !!ctx2.hasPointerMoveOpened,
457
- isOpenControlled: (ctx2) => !!ctx2["open.controlled"]
478
+ waitForOpenDelay: ({ send, prop }) => {
479
+ const id = setTimeout(() => {
480
+ send({ type: "after.openDelay" });
481
+ }, prop("openDelay"));
482
+ return () => clearTimeout(id);
458
483
  },
459
- delays: {
460
- OPEN_DELAY: (ctx2) => ctx2.openDelay,
461
- CLOSE_DELAY: (ctx2) => ctx2.closeDelay
484
+ waitForCloseDelay: ({ send, prop }) => {
485
+ const id = setTimeout(() => {
486
+ send({ type: "after.closeDelay" });
487
+ }, prop("closeDelay"));
488
+ return () => clearTimeout(id);
462
489
  }
463
490
  }
464
- );
465
- }
491
+ }
492
+ });
466
493
  var props = createProps()([
467
494
  "aria-label",
468
495
  "closeDelay",
@@ -477,7 +504,7 @@ var props = createProps()([
477
504
  "ids",
478
505
  "interactive",
479
506
  "onOpenChange",
480
- "open.controlled",
507
+ "defaultOpen",
481
508
  "open",
482
509
  "openDelay",
483
510
  "positioning"