@zag-js/tooltip 1.34.0 → 1.35.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.
@@ -0,0 +1,361 @@
1
+ // src/tooltip.machine.ts
2
+ import { createGuards, createMachine } from "@zag-js/core";
3
+ import { addDomEvent, getOverflowAncestors, isComposingEvent } from "@zag-js/dom-query";
4
+ import { trackFocusVisible } from "@zag-js/focus-visible";
5
+ import { getPlacement } from "@zag-js/popper";
6
+ import * as dom from "./tooltip.dom.mjs";
7
+ import { store } from "./tooltip.store.mjs";
8
+ import { ensureProps } from "@zag-js/utils";
9
+ var { and, not } = createGuards();
10
+ var machine = createMachine({
11
+ initialState: ({ prop }) => {
12
+ const open = prop("open") || prop("defaultOpen");
13
+ return open ? "open" : "closed";
14
+ },
15
+ props({ props }) {
16
+ ensureProps(props, ["id"]);
17
+ const closeOnClick = props.closeOnClick ?? true;
18
+ const closeOnPointerDown = props.closeOnPointerDown ?? closeOnClick;
19
+ return {
20
+ openDelay: 400,
21
+ closeDelay: 150,
22
+ closeOnEscape: true,
23
+ interactive: false,
24
+ closeOnScroll: true,
25
+ disabled: false,
26
+ ...props,
27
+ closeOnPointerDown,
28
+ closeOnClick,
29
+ positioning: {
30
+ placement: "bottom",
31
+ ...props.positioning
32
+ }
33
+ };
34
+ },
35
+ effects: ["trackFocusVisible", "trackStore"],
36
+ context: ({ bindable }) => ({
37
+ currentPlacement: bindable(() => ({ defaultValue: void 0 })),
38
+ hasPointerMoveOpened: bindable(() => ({ defaultValue: false }))
39
+ }),
40
+ watch({ track, action, prop }) {
41
+ track([() => prop("disabled")], () => {
42
+ action(["closeIfDisabled"]);
43
+ });
44
+ track([() => prop("open")], () => {
45
+ action(["toggleVisibility"]);
46
+ });
47
+ },
48
+ states: {
49
+ closed: {
50
+ entry: ["clearGlobalId"],
51
+ on: {
52
+ "controlled.open": {
53
+ target: "open"
54
+ },
55
+ open: [
56
+ {
57
+ guard: "isOpenControlled",
58
+ actions: ["invokeOnOpen"]
59
+ },
60
+ {
61
+ target: "open",
62
+ actions: ["invokeOnOpen"]
63
+ }
64
+ ],
65
+ "pointer.leave": {
66
+ actions: ["clearPointerMoveOpened"]
67
+ },
68
+ "pointer.move": [
69
+ {
70
+ guard: and("noVisibleTooltip", not("hasPointerMoveOpened")),
71
+ target: "opening"
72
+ },
73
+ {
74
+ guard: not("hasPointerMoveOpened"),
75
+ target: "open",
76
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
77
+ }
78
+ ]
79
+ }
80
+ },
81
+ opening: {
82
+ effects: ["trackScroll", "trackPointerlockChange", "waitForOpenDelay"],
83
+ on: {
84
+ "after.openDelay": [
85
+ {
86
+ guard: "isOpenControlled",
87
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
88
+ },
89
+ {
90
+ target: "open",
91
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
92
+ }
93
+ ],
94
+ "controlled.open": {
95
+ target: "open"
96
+ },
97
+ "controlled.close": {
98
+ target: "closed"
99
+ },
100
+ open: [
101
+ {
102
+ guard: "isOpenControlled",
103
+ actions: ["invokeOnOpen"]
104
+ },
105
+ {
106
+ target: "open",
107
+ actions: ["invokeOnOpen"]
108
+ }
109
+ ],
110
+ "pointer.leave": [
111
+ {
112
+ guard: "isOpenControlled",
113
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
114
+ actions: ["clearPointerMoveOpened", "invokeOnClose", "toggleVisibility"]
115
+ },
116
+ {
117
+ target: "closed",
118
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
119
+ }
120
+ ],
121
+ close: [
122
+ {
123
+ guard: "isOpenControlled",
124
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
125
+ actions: ["invokeOnClose", "toggleVisibility"]
126
+ },
127
+ {
128
+ target: "closed",
129
+ actions: ["invokeOnClose"]
130
+ }
131
+ ]
132
+ }
133
+ },
134
+ open: {
135
+ effects: ["trackEscapeKey", "trackScroll", "trackPointerlockChange", "trackPositioning"],
136
+ entry: ["setGlobalId"],
137
+ on: {
138
+ "controlled.close": {
139
+ target: "closed"
140
+ },
141
+ close: [
142
+ {
143
+ guard: "isOpenControlled",
144
+ actions: ["invokeOnClose"]
145
+ },
146
+ {
147
+ target: "closed",
148
+ actions: ["invokeOnClose"]
149
+ }
150
+ ],
151
+ "pointer.leave": [
152
+ {
153
+ guard: "isVisible",
154
+ target: "closing",
155
+ actions: ["clearPointerMoveOpened"]
156
+ },
157
+ // == group ==
158
+ {
159
+ guard: "isOpenControlled",
160
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
161
+ },
162
+ {
163
+ target: "closed",
164
+ actions: ["clearPointerMoveOpened", "invokeOnClose"]
165
+ }
166
+ ],
167
+ "content.pointer.leave": {
168
+ guard: "isInteractive",
169
+ target: "closing"
170
+ },
171
+ "positioning.set": {
172
+ actions: ["reposition"]
173
+ }
174
+ }
175
+ },
176
+ closing: {
177
+ effects: ["trackPositioning", "waitForCloseDelay"],
178
+ on: {
179
+ "after.closeDelay": [
180
+ {
181
+ guard: "isOpenControlled",
182
+ actions: ["invokeOnClose"]
183
+ },
184
+ {
185
+ target: "closed",
186
+ actions: ["invokeOnClose"]
187
+ }
188
+ ],
189
+ "controlled.close": {
190
+ target: "closed"
191
+ },
192
+ "controlled.open": {
193
+ target: "open"
194
+ },
195
+ close: [
196
+ {
197
+ guard: "isOpenControlled",
198
+ actions: ["invokeOnClose"]
199
+ },
200
+ {
201
+ target: "closed",
202
+ actions: ["invokeOnClose"]
203
+ }
204
+ ],
205
+ "pointer.move": [
206
+ {
207
+ guard: "isOpenControlled",
208
+ // We trigger toggleVisibility manually since the `ctx.open` has not changed yet (at this point)
209
+ actions: ["setPointerMoveOpened", "invokeOnOpen", "toggleVisibility"]
210
+ },
211
+ {
212
+ target: "open",
213
+ actions: ["setPointerMoveOpened", "invokeOnOpen"]
214
+ }
215
+ ],
216
+ "content.pointer.move": {
217
+ guard: "isInteractive",
218
+ target: "open"
219
+ },
220
+ "positioning.set": {
221
+ actions: ["reposition"]
222
+ }
223
+ }
224
+ }
225
+ },
226
+ implementations: {
227
+ guards: {
228
+ noVisibleTooltip: () => store.get("id") === null,
229
+ isVisible: ({ prop }) => prop("id") === store.get("id"),
230
+ isInteractive: ({ prop }) => !!prop("interactive"),
231
+ hasPointerMoveOpened: ({ context }) => context.get("hasPointerMoveOpened"),
232
+ isOpenControlled: ({ prop }) => prop("open") !== void 0
233
+ },
234
+ actions: {
235
+ setGlobalId: ({ prop }) => {
236
+ const prevId = store.get("id");
237
+ const isInstant = prevId !== null && prevId !== prop("id");
238
+ store.update({ id: prop("id"), prevId: isInstant ? prevId : null, instant: isInstant });
239
+ },
240
+ clearGlobalId: ({ prop }) => {
241
+ if (prop("id") === store.get("id")) {
242
+ store.update({ id: null, prevId: null, instant: false });
243
+ }
244
+ },
245
+ invokeOnOpen: ({ prop }) => {
246
+ prop("onOpenChange")?.({ open: true });
247
+ },
248
+ invokeOnClose: ({ prop }) => {
249
+ prop("onOpenChange")?.({ open: false });
250
+ },
251
+ closeIfDisabled: ({ prop, send }) => {
252
+ if (!prop("disabled")) return;
253
+ send({ type: "close", src: "disabled.change" });
254
+ },
255
+ reposition: ({ context, event, prop, scope }) => {
256
+ if (event.type !== "positioning.set") return;
257
+ const getPositionerEl2 = () => dom.getPositionerEl(scope);
258
+ return getPlacement(dom.getTriggerEl(scope), getPositionerEl2, {
259
+ ...prop("positioning"),
260
+ ...event.options,
261
+ defer: true,
262
+ listeners: false,
263
+ onComplete(data) {
264
+ context.set("currentPlacement", data.placement);
265
+ }
266
+ });
267
+ },
268
+ toggleVisibility: ({ prop, event, send }) => {
269
+ queueMicrotask(() => {
270
+ send({
271
+ type: prop("open") ? "controlled.open" : "controlled.close",
272
+ previousEvent: event
273
+ });
274
+ });
275
+ },
276
+ setPointerMoveOpened: ({ context }) => {
277
+ context.set("hasPointerMoveOpened", true);
278
+ },
279
+ clearPointerMoveOpened: ({ context }) => {
280
+ context.set("hasPointerMoveOpened", false);
281
+ }
282
+ },
283
+ effects: {
284
+ trackFocusVisible: ({ scope }) => {
285
+ return trackFocusVisible({ root: scope.getRootNode?.() });
286
+ },
287
+ trackPositioning: ({ context, prop, scope }) => {
288
+ if (!context.get("currentPlacement")) {
289
+ context.set("currentPlacement", prop("positioning").placement);
290
+ }
291
+ const getPositionerEl2 = () => dom.getPositionerEl(scope);
292
+ return getPlacement(dom.getTriggerEl(scope), getPositionerEl2, {
293
+ ...prop("positioning"),
294
+ defer: true,
295
+ onComplete(data) {
296
+ context.set("currentPlacement", data.placement);
297
+ }
298
+ });
299
+ },
300
+ trackPointerlockChange: ({ send, scope }) => {
301
+ const doc = scope.getDoc();
302
+ const onChange = () => send({ type: "close", src: "pointerlock:change" });
303
+ return addDomEvent(doc, "pointerlockchange", onChange, false);
304
+ },
305
+ trackScroll: ({ send, prop, scope }) => {
306
+ if (!prop("closeOnScroll")) return;
307
+ const triggerEl = dom.getTriggerEl(scope);
308
+ if (!triggerEl) return;
309
+ const overflowParents = getOverflowAncestors(triggerEl);
310
+ const cleanups = overflowParents.map((overflowParent) => {
311
+ const onScroll = () => {
312
+ send({ type: "close", src: "scroll" });
313
+ };
314
+ return addDomEvent(overflowParent, "scroll", onScroll, {
315
+ passive: true,
316
+ capture: true
317
+ });
318
+ });
319
+ return () => {
320
+ cleanups.forEach((fn) => fn?.());
321
+ };
322
+ },
323
+ trackStore: ({ prop, send }) => {
324
+ let cleanup;
325
+ queueMicrotask(() => {
326
+ cleanup = store.subscribe(() => {
327
+ if (store.get("id") !== prop("id")) {
328
+ send({ type: "close", src: "id.change" });
329
+ }
330
+ });
331
+ });
332
+ return () => cleanup?.();
333
+ },
334
+ trackEscapeKey: ({ send, prop }) => {
335
+ if (!prop("closeOnEscape")) return;
336
+ const onKeyDown = (event) => {
337
+ if (isComposingEvent(event)) return;
338
+ if (event.key !== "Escape") return;
339
+ event.stopPropagation();
340
+ send({ type: "close", src: "keydown.escape" });
341
+ };
342
+ return addDomEvent(document, "keydown", onKeyDown, true);
343
+ },
344
+ waitForOpenDelay: ({ send, prop }) => {
345
+ const id = setTimeout(() => {
346
+ send({ type: "after.openDelay" });
347
+ }, prop("openDelay"));
348
+ return () => clearTimeout(id);
349
+ },
350
+ waitForCloseDelay: ({ send, prop }) => {
351
+ const id = setTimeout(() => {
352
+ send({ type: "after.closeDelay" });
353
+ }, prop("closeDelay"));
354
+ return () => clearTimeout(id);
355
+ }
356
+ }
357
+ }
358
+ });
359
+ export {
360
+ machine
361
+ };
@@ -0,0 +1,9 @@
1
+ import { TooltipProps } from './tooltip.types.mjs';
2
+ import '@zag-js/core';
3
+ import '@zag-js/popper';
4
+ import '@zag-js/types';
5
+
6
+ declare const props: (keyof TooltipProps)[];
7
+ declare const splitProps: <Props extends Partial<TooltipProps>>(props: Props) => [Partial<TooltipProps>, Omit<Props, keyof TooltipProps>];
8
+
9
+ export { props, splitProps };
@@ -0,0 +1,9 @@
1
+ import { TooltipProps } from './tooltip.types.js';
2
+ import '@zag-js/core';
3
+ import '@zag-js/popper';
4
+ import '@zag-js/types';
5
+
6
+ declare const props: (keyof TooltipProps)[];
7
+ declare const splitProps: <Props extends Partial<TooltipProps>>(props: Props) => [Partial<TooltipProps>, Omit<Props, keyof TooltipProps>];
8
+
9
+ export { props, splitProps };
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/tooltip.props.ts
21
+ var tooltip_props_exports = {};
22
+ __export(tooltip_props_exports, {
23
+ props: () => props,
24
+ splitProps: () => splitProps
25
+ });
26
+ module.exports = __toCommonJS(tooltip_props_exports);
27
+ var import_types = require("@zag-js/types");
28
+ var import_utils = require("@zag-js/utils");
29
+ var props = (0, import_types.createProps)()([
30
+ "aria-label",
31
+ "closeDelay",
32
+ "closeOnEscape",
33
+ "closeOnPointerDown",
34
+ "closeOnScroll",
35
+ "closeOnClick",
36
+ "dir",
37
+ "disabled",
38
+ "getRootNode",
39
+ "id",
40
+ "ids",
41
+ "interactive",
42
+ "onOpenChange",
43
+ "defaultOpen",
44
+ "open",
45
+ "openDelay",
46
+ "positioning"
47
+ ]);
48
+ var splitProps = (0, import_utils.createSplitProps)(props);
49
+ // Annotate the CommonJS export names for ESM import in node:
50
+ 0 && (module.exports = {
51
+ props,
52
+ splitProps
53
+ });
@@ -0,0 +1,27 @@
1
+ // src/tooltip.props.ts
2
+ import { createProps } from "@zag-js/types";
3
+ import { createSplitProps } from "@zag-js/utils";
4
+ var props = createProps()([
5
+ "aria-label",
6
+ "closeDelay",
7
+ "closeOnEscape",
8
+ "closeOnPointerDown",
9
+ "closeOnScroll",
10
+ "closeOnClick",
11
+ "dir",
12
+ "disabled",
13
+ "getRootNode",
14
+ "id",
15
+ "ids",
16
+ "interactive",
17
+ "onOpenChange",
18
+ "defaultOpen",
19
+ "open",
20
+ "openDelay",
21
+ "positioning"
22
+ ]);
23
+ var splitProps = createSplitProps(props);
24
+ export {
25
+ props,
26
+ splitProps
27
+ };
@@ -0,0 +1,9 @@
1
+ import * as _zag_js_utils from '@zag-js/utils';
2
+
3
+ declare const store: _zag_js_utils.Store<{
4
+ id: string | null;
5
+ prevId: string | null;
6
+ instant: boolean;
7
+ }>;
8
+
9
+ export { store };
@@ -0,0 +1,9 @@
1
+ import * as _zag_js_utils from '@zag-js/utils';
2
+
3
+ declare const store: _zag_js_utils.Store<{
4
+ id: string | null;
5
+ prevId: string | null;
6
+ instant: boolean;
7
+ }>;
8
+
9
+ export { store };
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/tooltip.store.ts
21
+ var tooltip_store_exports = {};
22
+ __export(tooltip_store_exports, {
23
+ store: () => store
24
+ });
25
+ module.exports = __toCommonJS(tooltip_store_exports);
26
+ var import_utils = require("@zag-js/utils");
27
+ var store = (0, import_utils.createStore)({
28
+ id: null,
29
+ prevId: null,
30
+ instant: false
31
+ });
32
+ // Annotate the CommonJS export names for ESM import in node:
33
+ 0 && (module.exports = {
34
+ store
35
+ });
@@ -0,0 +1,10 @@
1
+ // src/tooltip.store.ts
2
+ import { createStore } from "@zag-js/utils";
3
+ var store = createStore({
4
+ id: null,
5
+ prevId: null,
6
+ instant: false
7
+ });
8
+ export {
9
+ store
10
+ };
@@ -0,0 +1,119 @@
1
+ import { Machine, EventObject, Service } from '@zag-js/core';
2
+ import { PositioningOptions, Placement } from '@zag-js/popper';
3
+ export { Placement, PositioningOptions } from '@zag-js/popper';
4
+ import { PropTypes, RequiredBy, DirectionProperty, CommonProperties } from '@zag-js/types';
5
+
6
+ interface OpenChangeDetails {
7
+ open: boolean;
8
+ }
9
+ type ElementIds = Partial<{
10
+ trigger: string;
11
+ content: string;
12
+ arrow: string;
13
+ positioner: string;
14
+ }>;
15
+ interface TooltipProps extends DirectionProperty, CommonProperties {
16
+ /**
17
+ * The ids of the elements in the tooltip. Useful for composition.
18
+ */
19
+ ids?: ElementIds | undefined;
20
+ /**
21
+ * The open delay of the tooltip.
22
+ * @default 400
23
+ */
24
+ openDelay?: number | undefined;
25
+ /**
26
+ * The close delay of the tooltip.
27
+ * @default 150
28
+ */
29
+ closeDelay?: number | undefined;
30
+ /**
31
+ * Whether to close the tooltip on pointerdown.
32
+ * @default true
33
+ */
34
+ closeOnPointerDown?: boolean | undefined;
35
+ /**
36
+ * Whether to close the tooltip when the Escape key is pressed.
37
+ * @default true
38
+ */
39
+ closeOnEscape?: boolean | undefined;
40
+ /**
41
+ * Whether the tooltip should close on scroll
42
+ * @default true
43
+ */
44
+ closeOnScroll?: boolean | undefined;
45
+ /**
46
+ * Whether the tooltip should close on click
47
+ * @default true
48
+ */
49
+ closeOnClick?: boolean | undefined;
50
+ /**
51
+ * Whether the tooltip's content is interactive.
52
+ * In this mode, the tooltip will remain open when user hovers over the content.
53
+ * @see https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus
54
+ *
55
+ * @default false
56
+ */
57
+ interactive?: boolean | undefined;
58
+ /**
59
+ * Function called when the tooltip is opened.
60
+ */
61
+ onOpenChange?: ((details: OpenChangeDetails) => void) | undefined;
62
+ /**
63
+ * Custom label for the tooltip.
64
+ */
65
+ "aria-label"?: string | undefined;
66
+ /**
67
+ * The user provided options used to position the popover content
68
+ */
69
+ positioning?: PositioningOptions | undefined;
70
+ /**
71
+ * Whether the tooltip is disabled
72
+ */
73
+ disabled?: boolean | undefined;
74
+ /**
75
+ * The controlled open state of the tooltip
76
+ */
77
+ open?: boolean | undefined;
78
+ /**
79
+ * The initial open state of the tooltip when rendered.
80
+ * Use when you don't need to control the open state of the tooltip.
81
+ */
82
+ defaultOpen?: boolean | undefined;
83
+ }
84
+ type PropsWithDefault = "openDelay" | "closeDelay" | "closeOnPointerDown" | "closeOnEscape" | "closeOnScroll" | "closeOnClick" | "interactive" | "id" | "positioning";
85
+ interface TooltipSchema {
86
+ state: "open" | "closed" | "opening" | "closing";
87
+ props: RequiredBy<TooltipProps, PropsWithDefault>;
88
+ context: {
89
+ currentPlacement: Placement | undefined;
90
+ hasPointerMoveOpened: boolean;
91
+ };
92
+ event: EventObject;
93
+ action: string;
94
+ effect: string;
95
+ guard: string;
96
+ }
97
+ type TooltipService = Service<TooltipSchema>;
98
+ type TooltipMachine = Machine<TooltipSchema>;
99
+ interface TooltipApi<T extends PropTypes = PropTypes> {
100
+ /**
101
+ * Whether the tooltip is open.
102
+ */
103
+ open: boolean;
104
+ /**
105
+ * Function to open the tooltip.
106
+ */
107
+ setOpen: (open: boolean) => void;
108
+ /**
109
+ * Function to reposition the popover
110
+ */
111
+ reposition: (options?: Partial<PositioningOptions>) => void;
112
+ getTriggerProps: () => T["button"];
113
+ getArrowProps: () => T["element"];
114
+ getArrowTipProps: () => T["element"];
115
+ getPositionerProps: () => T["element"];
116
+ getContentProps: () => T["element"];
117
+ }
118
+
119
+ export type { ElementIds, OpenChangeDetails, TooltipApi, TooltipMachine, TooltipProps, TooltipSchema, TooltipService };