@zag-js/tooltip 0.34.0 → 0.36.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.
@@ -1,6 +1,6 @@
1
- import { createMachine, subscribe, guards } from "@zag-js/core"
1
+ import { createMachine, guards, subscribe } from "@zag-js/core"
2
2
  import { addDomEvent } from "@zag-js/dom-event"
3
- import { getScrollParents, isHTMLElement, isSafari } from "@zag-js/dom-query"
3
+ import { getOverflowAncestors, isHTMLElement, isSafari } from "@zag-js/dom-query"
4
4
  import { getPlacement } from "@zag-js/popper"
5
5
  import { compact } from "@zag-js/utils"
6
6
  import { dom } from "./tooltip.dom"
@@ -14,7 +14,7 @@ export function machine(userContext: UserDefinedContext) {
14
14
  return createMachine<MachineContext, MachineState>(
15
15
  {
16
16
  id: "tooltip",
17
- initial: "closed",
17
+ initial: ctx.open ? "open" : "closed",
18
18
 
19
19
  context: {
20
20
  openDelay: 1000,
@@ -40,17 +40,16 @@ export function machine(userContext: UserDefinedContext) {
40
40
  open: ["toggleVisibility"],
41
41
  },
42
42
 
43
- on: {
44
- OPEN: "open",
45
- CLOSE: "closed",
46
- },
47
-
48
43
  states: {
49
44
  closed: {
50
45
  tags: ["closed"],
51
- entry: ["clearGlobalId", "invokeOnClose"],
46
+ entry: ["clearGlobalId"],
52
47
  on: {
53
- FOCUS: "open",
48
+ "CONTROLLED.OPEN": "open",
49
+ OPEN: {
50
+ target: "open",
51
+ actions: ["invokeOnOpen"],
52
+ },
54
53
  POINTER_LEAVE: {
55
54
  actions: ["clearPointerMoveOpened"],
56
55
  },
@@ -62,7 +61,7 @@ export function machine(userContext: UserDefinedContext) {
62
61
  {
63
62
  guard: not("hasPointerMoveOpened"),
64
63
  target: "open",
65
- actions: ["setPointerMoveOpened"],
64
+ actions: ["setPointerMoveOpened", "invokeOnOpen"],
66
65
  },
67
66
  ],
68
67
  },
@@ -72,22 +71,43 @@ export function machine(userContext: UserDefinedContext) {
72
71
  tags: ["closed"],
73
72
  activities: ["trackScroll", "trackPointerlockChange"],
74
73
  after: {
75
- OPEN_DELAY: {
76
- target: "open",
77
- actions: ["setPointerMoveOpened"],
78
- },
74
+ OPEN_DELAY: [
75
+ {
76
+ guard: "isOpenControlled",
77
+ actions: ["setPointerMoveOpened", "invokeOnOpen"],
78
+ },
79
+ {
80
+ target: "open",
81
+ actions: ["setPointerMoveOpened", "invokeOnOpen"],
82
+ },
83
+ ],
79
84
  },
80
85
  on: {
81
- POINTER_LEAVE: {
82
- target: "closed",
83
- actions: ["clearPointerMoveOpened"],
84
- },
85
- BLUR: "closed",
86
- SCROLL: "closed",
87
- POINTER_LOCK_CHANGE: "closed",
88
- POINTER_DOWN: {
89
- guard: "closeOnPointerDown",
86
+ "CONTROLLED.OPEN": "open",
87
+ "CONTROLLED.CLOSE": "closed",
88
+ OPEN: [
89
+ {
90
+ guard: "isOpenControlled",
91
+ actions: ["invokeOnOpen"],
92
+ },
93
+ {
94
+ target: "open",
95
+ actions: ["invokeOnOpen"],
96
+ },
97
+ ],
98
+ POINTER_LEAVE: [
99
+ {
100
+ guard: "isOpenControlled",
101
+ actions: ["clearPointerMoveOpened", "invokeOnClose"],
102
+ },
103
+ {
104
+ target: "closed",
105
+ actions: ["clearPointerMoveOpened", "invokeOnClose"],
106
+ },
107
+ ],
108
+ CLOSE: {
90
109
  target: "closed",
110
+ actions: ["invokeOnClose"],
91
111
  },
92
112
  },
93
113
  },
@@ -101,33 +121,34 @@ export function machine(userContext: UserDefinedContext) {
101
121
  "trackPointerlockChange",
102
122
  "trackPositioning",
103
123
  ],
104
- entry: ["setGlobalId", "invokeOnOpen"],
124
+ entry: ["setGlobalId"],
105
125
  on: {
126
+ "CONTROLLED.CLOSE": "closed",
127
+ CLOSE: {
128
+ target: "closed",
129
+ actions: ["invokeOnClose"],
130
+ },
106
131
  POINTER_LEAVE: [
107
132
  {
108
133
  guard: "isVisible",
109
134
  target: "closing",
110
135
  actions: ["clearPointerMoveOpened"],
111
136
  },
137
+ // == group ==
138
+ {
139
+ guard: "isOpenControlled",
140
+ actions: ["clearPointerMoveOpened", "invokeOnClose"],
141
+ },
112
142
  {
113
143
  target: "closed",
114
- actions: ["clearPointerMoveOpened"],
144
+ actions: ["clearPointerMoveOpened", "invokeOnClose"],
115
145
  },
116
146
  ],
117
- BLUR: "closed",
118
- ESCAPE: "closed",
119
- SCROLL: "closed",
120
- POINTER_LOCK_CHANGE: "closed",
121
147
  "CONTENT.POINTER_LEAVE": {
122
148
  guard: "isInteractive",
123
149
  target: "closing",
124
150
  },
125
- POINTER_DOWN: {
126
- guard: "closeOnPointerDown",
127
- target: "closed",
128
- },
129
- CLICK: "closed",
130
- SET_POSITIONING: {
151
+ "POSITIONING.SET": {
131
152
  actions: "reposition",
132
153
  },
133
154
  },
@@ -137,18 +158,47 @@ export function machine(userContext: UserDefinedContext) {
137
158
  tags: ["open"],
138
159
  activities: ["trackStore", "trackPositioning"],
139
160
  after: {
140
- CLOSE_DELAY: "closed",
161
+ CLOSE_DELAY: [
162
+ {
163
+ guard: "isOpenControlled",
164
+ actions: ["invokeOnClose"],
165
+ },
166
+ {
167
+ target: "closed",
168
+ actions: ["invokeOnClose"],
169
+ },
170
+ ],
141
171
  },
142
172
  on: {
143
- FORCE_CLOSE: "closed",
144
- POINTER_MOVE: {
145
- target: "open",
146
- actions: ["setPointerMoveOpened"],
147
- },
173
+ "CONTROLLED.CLOSE": "closed",
174
+ "CONTROLLED.OPEN": "open",
175
+ CLOSE: [
176
+ {
177
+ guard: "isOpenControlled",
178
+ actions: ["invokeOnClose"],
179
+ },
180
+ {
181
+ target: "closed",
182
+ actions: ["invokeOnClose"],
183
+ },
184
+ ],
185
+ POINTER_MOVE: [
186
+ {
187
+ guard: "isOpenControlled",
188
+ actions: ["setPointerMoveOpened", "invokeOnOpen"],
189
+ },
190
+ {
191
+ target: "open",
192
+ actions: ["setPointerMoveOpened", "invokeOnOpen"],
193
+ },
194
+ ],
148
195
  "CONTENT.POINTER_MOVE": {
149
196
  guard: "isInteractive",
150
197
  target: "open",
151
198
  },
199
+ "POSITIONING.SET": {
200
+ actions: "reposition",
201
+ },
152
202
  },
153
203
  },
154
204
  },
@@ -167,16 +217,21 @@ export function machine(userContext: UserDefinedContext) {
167
217
  })
168
218
  },
169
219
  trackPointerlockChange(ctx, _evt, { send }) {
170
- const onChange = () => send("POINTER_LOCK_CHANGE")
220
+ const onChange = () => send({ type: "CLOSE", src: "pointerlock:change" })
171
221
  return addDomEvent(dom.getDoc(ctx), "pointerlockchange", onChange, false)
172
222
  },
173
223
  trackScroll(ctx, _evt, { send }) {
174
- const trigger = dom.getTriggerEl(ctx)
175
- if (!trigger) return
176
- const cleanups = getScrollParents(trigger).map((el) => {
177
- const opts = { passive: true, capture: true } as const
178
- return addDomEvent(el, "scroll", () => send("SCROLL"), opts)
179
- })
224
+ const triggerEl = dom.getTriggerEl(ctx)
225
+ if (!triggerEl) return
226
+
227
+ const overflowParents = getOverflowAncestors(triggerEl)
228
+ const cleanups = overflowParents.map((overflowParent) =>
229
+ addDomEvent(overflowParent, "scroll", () => send({ type: "CLOSE", src: "scroll" }), {
230
+ passive: true,
231
+ capture: true,
232
+ }),
233
+ )
234
+
180
235
  return () => {
181
236
  cleanups.forEach((fn) => fn?.())
182
237
  }
@@ -184,7 +239,7 @@ export function machine(userContext: UserDefinedContext) {
184
239
  trackStore(ctx, _evt, { send }) {
185
240
  return subscribe(store, () => {
186
241
  if (store.id !== ctx.id) {
187
- send("FORCE_CLOSE")
242
+ send({ type: "CLOSE", src: "id:change" })
188
243
  }
189
244
  })
190
245
  },
@@ -202,7 +257,7 @@ export function machine(userContext: UserDefinedContext) {
202
257
  const doc = dom.getDoc(ctx)
203
258
  return addDomEvent(doc, "keydown", (event) => {
204
259
  if (event.key === "Escape") {
205
- send("ESCAPE")
260
+ send("CLOSE")
206
261
  }
207
262
  })
208
263
  },
@@ -216,18 +271,15 @@ export function machine(userContext: UserDefinedContext) {
216
271
  store.setId(null)
217
272
  }
218
273
  },
219
- invokeOnOpen(ctx, evt) {
220
- const omit = ["CONTENT.POINTER_MOVE", "POINTER_MOVE"]
221
- if (!omit.includes(evt.type)) {
222
- ctx.onOpenChange?.({ open: true })
223
- }
274
+ invokeOnOpen(ctx) {
275
+ ctx.onOpenChange?.({ open: true })
224
276
  },
225
277
  invokeOnClose(ctx) {
226
278
  ctx.onOpenChange?.({ open: false })
227
279
  },
228
280
  closeIfDisabled(ctx, _evt, { send }) {
229
281
  if (!ctx.disabled) return
230
- send("CLOSE")
282
+ send({ type: "CLOSE", src: "disabled:change" })
231
283
  },
232
284
  reposition(ctx, evt) {
233
285
  const getPositionerEl = () => dom.getPositionerEl(ctx)
@@ -241,8 +293,8 @@ export function machine(userContext: UserDefinedContext) {
241
293
  },
242
294
  })
243
295
  },
244
- toggleVisibility(ctx, _evt, { send }) {
245
- send({ type: ctx.open ? "OPEN" : "CLOSE", src: "controlled" })
296
+ toggleVisibility(ctx, evt, { send }) {
297
+ send({ type: ctx.open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: evt })
246
298
  },
247
299
  setPointerMoveOpened(ctx) {
248
300
  ctx.hasPointerMoveOpened = true
@@ -252,11 +304,11 @@ export function machine(userContext: UserDefinedContext) {
252
304
  },
253
305
  },
254
306
  guards: {
255
- closeOnPointerDown: (ctx) => ctx.closeOnPointerDown,
256
307
  noVisibleTooltip: () => store.id === null,
257
308
  isVisible: (ctx) => ctx.id === store.id,
258
309
  isInteractive: (ctx) => ctx.interactive,
259
310
  hasPointerMoveOpened: (ctx) => !!ctx.hasPointerMoveOpened,
311
+ isOpenControlled: (ctx) => !!ctx["open.controlled"],
260
312
  },
261
313
  delays: {
262
314
  OPEN_DELAY: (ctx) => ctx.openDelay,
@@ -72,6 +72,10 @@ interface PublicContext extends DirectionProperty, CommonProperties {
72
72
  * Whether the tooltip is open
73
73
  */
74
74
  open?: boolean
75
+ /**
76
+ * Whether the tooltip is controlled by the user
77
+ */
78
+ "open.controlled"?: boolean
75
79
  }
76
80
 
77
81
  export type UserDefinedContext = RequiredBy<PublicContext, "id">