@zag-js/toast 0.70.0 → 0.71.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,403 +0,0 @@
1
- import type { Machine, Ref, StateMachine as S } from "@zag-js/core"
2
- import type { CommonProperties, Direction, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
3
-
4
- /* -----------------------------------------------------------------------------
5
- * Base types
6
- * -----------------------------------------------------------------------------*/
7
-
8
- export type Type = "success" | "error" | "loading" | "info" | (string & {})
9
-
10
- export type Placement = "top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end"
11
-
12
- export type Status = "visible" | "dismissing" | "unmounted"
13
-
14
- export interface GenericOptions<T = string> {
15
- /**
16
- * The title of the toast.
17
- */
18
- title?: T
19
- /**
20
- * The description of the toast.
21
- */
22
- description?: T
23
- }
24
-
25
- export interface StatusChangeDetails {
26
- status: Status
27
- }
28
-
29
- /**
30
- * @internal
31
- */
32
- export interface ToastHeightDetails {
33
- id: string
34
- height: number
35
- placement: Placement
36
- }
37
-
38
- export interface ActionOptions {
39
- /**
40
- * The label of the action
41
- */
42
- label: string
43
- /**
44
- * The function to call when the action is clicked
45
- */
46
- onClick: () => void
47
- }
48
-
49
- export interface Options<T> extends GenericOptions<T> {
50
- /**
51
- * The duration the toast will be visible
52
- */
53
- duration?: number
54
- /**
55
- * The duration for the toast to kept alive before it is removed.
56
- * Useful for exit transitions.
57
- */
58
- removeDelay?: number
59
- /**
60
- * The placement of the toast
61
- */
62
- placement?: Placement
63
- /**
64
- * The unique id of the toast
65
- */
66
- id?: string
67
- /**
68
- * The type of the toast
69
- */
70
- type?: Type
71
- /**
72
- * Function called when the toast is visible
73
- */
74
- onStatusChange?(details: StatusChangeDetails): void
75
- /**
76
- * The action of the toast
77
- */
78
- action?: ActionOptions
79
- /**
80
- * The metadata of the toast
81
- */
82
- meta?: Record<string, any>
83
- }
84
-
85
- /* -----------------------------------------------------------------------------
86
- * Machine context
87
- * -----------------------------------------------------------------------------*/
88
-
89
- export interface MachineContext<T = any>
90
- extends Omit<CommonProperties, "id">,
91
- MachinePrivateContext,
92
- Omit<Options<T>, "removeDelay"> {
93
- /**
94
- * The duration for the toast to kept alive before it is removed.
95
- * Useful for exit transitions.
96
- */
97
- removeDelay: number
98
- /**
99
- * The document's text/writing direction.
100
- */
101
- dir?: Direction
102
- /**
103
- * The time the toast was created
104
- */
105
- createdAt: number
106
- /**
107
- * The time left before the toast is removed
108
- */
109
- remaining: number
110
- }
111
-
112
- interface MachinePrivateContext {
113
- /**
114
- * @internal
115
- * The height of the toast
116
- */
117
- height: number
118
- /**
119
- * @internal
120
- * The absolute height of the toast relative to other toasts
121
- */
122
- offset: number
123
- /**
124
- * @internal
125
- * Whether the toast is in the front
126
- */
127
- frontmost: boolean
128
- /**
129
- * @internal
130
- * The index of the toast in the group
131
- */
132
- index: number
133
- /**
134
- * @internal
135
- * Whether the toast is mounted
136
- */
137
- mounted: boolean
138
- /**
139
- * @internal
140
- * The z-index of the toast
141
- */
142
- zIndex: number
143
- /**
144
- * @internal
145
- * Whether the toast is stacked
146
- */
147
- stacked?: boolean
148
- }
149
-
150
- export interface MachineState {
151
- value: "visible" | "visible:updating" | "dismissing" | "unmounted" | "visible:persist"
152
- tags: "visible" | "paused" | "updating"
153
- }
154
-
155
- export type State<T = any> = S.State<MachineContext<T>, MachineState>
156
-
157
- export type Send = S.Send
158
-
159
- export type Service<T = any> = Machine<MachineContext<T>, MachineState>
160
-
161
- /* -----------------------------------------------------------------------------
162
- * Group machine context
163
- * -----------------------------------------------------------------------------*/
164
-
165
- interface GroupPublicContext extends DirectionProperty, CommonProperties {
166
- /**
167
- * Whether to pause toast when the user leaves the browser tab
168
- * @default false
169
- */
170
- pauseOnPageIdle: boolean
171
- /**
172
- * The gap or spacing between toasts
173
- * @default 16
174
- */
175
- gap: number
176
- /**
177
- * The maximum number of toasts that can be shown at once
178
- * @default Number.MAX_SAFE_INTEGER
179
- */
180
- max: number
181
- /**
182
- * The offset from the safe environment edge of the viewport
183
- * @default "1rem"
184
- */
185
- offsets: string | Record<"left" | "right" | "bottom" | "top", string>
186
- /**
187
- * The hotkey that will move focus to the toast group
188
- * @default '["altKey", "KeyT"]'
189
- */
190
- hotkey: string[]
191
- /**
192
- * Whether the toasts should overlap each other
193
- */
194
- overlap?: boolean
195
- /**
196
- * The placement of the toast
197
- */
198
- placement: Placement
199
- /**
200
- * The duration for the toast to kept alive before it is removed.
201
- * Useful for exit transitions.
202
- *
203
- * @default 200
204
- */
205
- removeDelay: number
206
- /**
207
- * The duration the toast will be visible
208
- */
209
- duration?: number
210
- }
211
-
212
- export interface UserDefinedGroupContext extends RequiredBy<GroupPublicContext, "id"> {}
213
-
214
- type GroupComputedContext = Readonly<{
215
- /**
216
- * @computed
217
- * The total number of toasts in the group
218
- */
219
- count: number
220
- }>
221
-
222
- interface GroupPrivateContext<T> extends GenericOptions<T> {
223
- /**
224
- * @internal
225
- * The child toast machines (spawned by the toast group)
226
- */
227
- toasts: Service<T>[]
228
- /**
229
- * @internal
230
- * The height of each toast
231
- */
232
- heights: ToastHeightDetails[]
233
- /**
234
- * @internal
235
- */
236
- _cleanup?: VoidFunction
237
- /**
238
- * @internal
239
- */
240
- lastFocusedEl: Ref<HTMLElement> | null
241
- /**
242
- * @internal
243
- */
244
- isFocusWithin: boolean
245
- }
246
-
247
- export interface GroupMachineContext<T = any>
248
- extends GroupPublicContext,
249
- GroupPrivateContext<T>,
250
- GroupComputedContext {}
251
-
252
- export interface GroupMachineState {
253
- value: "stack" | "overlap"
254
- }
255
-
256
- export type GroupState<T = any> = S.State<GroupMachineContext<T>>
257
-
258
- export type GroupSend = S.Send
259
-
260
- export type GroupService<T = any> = Machine<GroupMachineContext<T>, GroupMachineState>
261
-
262
- /* -----------------------------------------------------------------------------
263
- * Component API
264
- * -----------------------------------------------------------------------------*/
265
-
266
- type MaybeFunction<Value, Args> = Value | ((arg: Args) => Value)
267
-
268
- export interface PromiseOptions<V, O = any> {
269
- loading: Options<O>
270
- success: MaybeFunction<Options<O>, V>
271
- error: MaybeFunction<Options<O>, Error>
272
- finally?: () => void | Promise<void>
273
- }
274
-
275
- export interface GroupProps {
276
- /**
277
- * The placement of the toast region
278
- */
279
- placement: Placement
280
- /**
281
- * The human-readable label for the toast region
282
- */
283
- label?: string
284
- }
285
-
286
- export interface GroupMachineApi<T extends PropTypes = PropTypes, O = any> {
287
- /**
288
- * The total number of toasts
289
- */
290
- getCount(): number
291
- /**
292
- * The placements of the active toasts
293
- */
294
- getPlacements(): Placement[]
295
- /**
296
- * The active toasts by placement
297
- */
298
- getToastsByPlacement(placement: Placement): Service<O>[]
299
- /**
300
- * Returns whether the toast id is visible
301
- */
302
- isVisible(id: string): boolean
303
- /**
304
- * Function to create a toast.
305
- */
306
- create(options: Options<O>): string | undefined
307
- /**
308
- * Function to create or update a toast.
309
- */
310
- upsert(options: Options<O>): string | undefined
311
- /**
312
- * Function to update a toast's options by id.
313
- */
314
- update(id: string, options: Options<O>): void
315
- /**
316
- * Function to create a success toast.
317
- */
318
- success(options: Options<O>): string | undefined
319
- /**
320
- * Function to create an error toast.
321
- */
322
- error(options: Options<O>): string | undefined
323
- /**
324
- * Function to create a loading toast.
325
- */
326
- loading(options: Options<O>): string | undefined
327
- /**
328
- * Function to resume a toast by id.
329
- */
330
- resume(id?: string | undefined): void
331
- /**
332
- * Function to pause a toast by id.
333
- */
334
- pause(id?: string | undefined): void
335
- /**
336
- * Function to dismiss a toast by id.
337
- * If no id is provided, all toasts will be dismissed.
338
- */
339
- dismiss(id?: string | undefined): void
340
- /**
341
- * Function to dismiss all toasts by placement.
342
- */
343
- dismissByPlacement(placement: Placement): void
344
- /**
345
- * Function to remove a toast by id.
346
- * If no id is provided, all toasts will be removed.
347
- */
348
- remove(id?: string | undefined): void
349
- /**
350
- * Function to create a toast from a promise.
351
- * - When the promise resolves, the toast will be updated with the success options.
352
- * - When the promise rejects, the toast will be updated with the error options.
353
- */
354
- promise<T>(
355
- promise: Promise<T> | (() => Promise<T>),
356
- options: PromiseOptions<T, O>,
357
- shared?: Partial<Options<O>>,
358
- ): string
359
- /**
360
- * Function to subscribe to the toast group.
361
- */
362
- subscribe(callback: (toasts: Options<O>[]) => void): VoidFunction
363
- getGroupProps(options: GroupProps): T["element"]
364
- }
365
-
366
- export interface MachineApi<T extends PropTypes = PropTypes, O = any> extends GenericOptions<O> {
367
- /**
368
- * The type of the toast.
369
- */
370
- type: Type
371
- /**
372
- * The current placement of the toast.
373
- */
374
- placement: Placement
375
- /**
376
- * Whether the toast is visible.
377
- */
378
- visible: boolean
379
- /**
380
- * Whether the toast is paused.
381
- */
382
- paused: boolean
383
- /**
384
- * Function to pause the toast (keeping it visible).
385
- */
386
- pause(): void
387
- /**
388
- * Function to resume the toast dismissing.
389
- */
390
- resume(): void
391
- /**
392
- * Function to instantly dismiss the toast.
393
- */
394
- dismiss(): void
395
-
396
- getRootProps(): T["element"]
397
- getTitleProps(): T["element"]
398
- getGhostBeforeProps(): T["element"]
399
- getGhostAfterProps(): T["element"]
400
- getDescriptionProps(): T["element"]
401
- getCloseTriggerProps(): T["button"]
402
- getActionTriggerProps(): T["button"]
403
- }
@@ -1,192 +0,0 @@
1
- import { MAX_Z_INDEX } from "@zag-js/dom-query"
2
- import type { Style } from "@zag-js/types"
3
- import type { GroupMachineContext, MachineContext, Placement, Service, Type } from "./toast.types"
4
-
5
- export function getToastsByPlacement<T>(toasts: Service<T>[], placement: Placement) {
6
- return toasts.filter((toast) => toast.state.context.placement === placement)
7
- }
8
-
9
- export const defaultTimeouts: Record<Type, number> = {
10
- info: 5000,
11
- error: 5000,
12
- success: 2000,
13
- loading: Infinity,
14
- DEFAULT: 5000,
15
- }
16
-
17
- export function getToastDuration(duration: number | undefined, type: NonNullable<MachineContext["type"]>) {
18
- return duration ?? defaultTimeouts[type] ?? defaultTimeouts.DEFAULT
19
- }
20
-
21
- export function getGroupPlacementStyle<T>(ctx: GroupMachineContext<T>, placement: Placement): Style {
22
- const offset = ctx.offsets
23
- const computedOffset =
24
- typeof offset === "string" ? { left: offset, right: offset, bottom: offset, top: offset } : offset
25
-
26
- const rtl = ctx.dir === "rtl"
27
- const computedPlacement = placement
28
- .replace("-start", rtl ? "-right" : "-left")
29
- .replace("-end", rtl ? "-left" : "-right")
30
-
31
- const isRighty = computedPlacement.includes("right")
32
- const isLefty = computedPlacement.includes("left")
33
-
34
- const styles: Style = {
35
- position: "fixed",
36
- pointerEvents: ctx.count > 0 ? undefined : "none",
37
- display: "flex",
38
- flexDirection: "column",
39
- "--gap": `${ctx.gap}px`,
40
- "--first-height": `${ctx.heights[0]?.height || 0}px`,
41
- zIndex: MAX_Z_INDEX,
42
- }
43
-
44
- let alignItems: Style["alignItems"] = "center"
45
- if (isRighty) alignItems = "flex-end"
46
- if (isLefty) alignItems = "flex-start"
47
-
48
- styles.alignItems = alignItems
49
-
50
- if (computedPlacement.includes("top")) {
51
- const offset = computedOffset.top
52
- styles.top = `max(env(safe-area-inset-top, 0px), ${offset})`
53
- }
54
-
55
- if (computedPlacement.includes("bottom")) {
56
- const offset = computedOffset.bottom
57
- styles.bottom = `max(env(safe-area-inset-bottom, 0px), ${offset})`
58
- }
59
-
60
- if (!computedPlacement.includes("left")) {
61
- const offset = computedOffset.right
62
- styles.insetInlineEnd = `calc(env(safe-area-inset-right, 0px) + ${offset})`
63
- }
64
-
65
- if (!computedPlacement.includes("right")) {
66
- const offset = computedOffset.left
67
- styles.insetInlineStart = `calc(env(safe-area-inset-left, 0px) + ${offset})`
68
- }
69
-
70
- return styles
71
- }
72
-
73
- export function getPlacementStyle<T>(ctx: MachineContext<T>, visible: boolean): Style {
74
- const [side] = ctx.placement!.split("-")
75
- const sibling = !ctx.frontmost
76
- const overlap = !ctx.stacked
77
-
78
- const styles: Style = {
79
- position: "absolute",
80
- pointerEvents: "auto",
81
- "--opacity": "0",
82
- "--remove-delay": `${ctx.removeDelay}ms`,
83
- "--duration": `${ctx.type === "loading" ? Number.MAX_SAFE_INTEGER : ctx.duration}ms`,
84
- "--initial-height": `${ctx.height}px`,
85
- "--offset": `${ctx.offset}px`,
86
- "--index": ctx.index,
87
- "--z-index": ctx.zIndex,
88
- "--lift-amount": "calc(var(--lift) * var(--gap))",
89
- "--y": "100%",
90
- "--x": "0",
91
- }
92
-
93
- const assign = (overrides: Style) => Object.assign(styles, overrides)
94
-
95
- if (side === "top") {
96
- //
97
- assign({
98
- top: "0",
99
- "--sign": "-1",
100
- "--y": "-100%",
101
- "--lift": "1",
102
- })
103
- //
104
- } else if (side === "bottom") {
105
- //
106
- assign({
107
- bottom: "0",
108
- "--sign": "1",
109
- "--y": "100%",
110
- "--lift": "-1",
111
- })
112
- }
113
-
114
- if (ctx.mounted) {
115
- assign({
116
- "--y": "0",
117
- "--opacity": "1",
118
- })
119
-
120
- if (ctx.stacked) {
121
- assign({
122
- "--y": "calc(var(--lift) * var(--offset))",
123
- "--height": "var(--initial-height)",
124
- })
125
- }
126
- }
127
-
128
- if (!visible) {
129
- assign({
130
- "--opacity": "0",
131
- pointerEvents: "none",
132
- })
133
- }
134
-
135
- if (sibling && overlap) {
136
- assign({
137
- "--base-scale": "var(--index) * 0.05 + 1",
138
- "--y": "calc(var(--lift-amount) * var(--index))",
139
- "--scale": "calc(-1 * var(--base-scale))",
140
- "--height": "var(--first-height)",
141
- })
142
-
143
- if (!visible) {
144
- assign({
145
- "--y": "calc(var(--sign) * 40%)",
146
- })
147
- }
148
- }
149
-
150
- if (sibling && ctx.stacked && !visible) {
151
- assign({
152
- "--y": "calc(var(--lift) * var(--offset) + var(--lift) * -100%)",
153
- })
154
- }
155
-
156
- if (ctx.frontmost && !visible) {
157
- assign({
158
- "--y": "calc(var(--lift) * -100%)",
159
- })
160
- }
161
-
162
- return styles
163
- }
164
-
165
- export function getGhostBeforeStyle<T>(ctx: MachineContext<T>, visible: boolean): Style {
166
- const styles: Style = {
167
- position: "absolute",
168
- inset: "0",
169
- scale: "1 2",
170
- pointerEvents: visible ? "none" : "auto",
171
- }
172
-
173
- const assign = (overrides: Style) => Object.assign(styles, overrides)
174
-
175
- if (ctx.frontmost && !visible) {
176
- assign({
177
- height: "calc(var(--initial-height) + 80%)",
178
- })
179
- }
180
-
181
- return styles
182
- }
183
-
184
- export function getGhostAfterStyle<T>(_ctx: MachineContext<T>, _visible: boolean): Style {
185
- return {
186
- position: "absolute",
187
- left: "0",
188
- height: "calc(var(--gap) + 2px)",
189
- bottom: "100%",
190
- width: "100%",
191
- }
192
- }