@zag-js/toast 1.44.0 → 1.46.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.
@@ -89,6 +89,12 @@ var groupMachine = createMachine({
89
89
  },
90
90
  exit: ["clearDismissableBranch", "clearLastFocusedEl", "clearMouseEventTimer"],
91
91
  on: {
92
+ PAUSE_ALL: {
93
+ actions: ["pauseToasts"]
94
+ },
95
+ RESUME_ALL: {
96
+ actions: ["resumeToasts"]
97
+ },
92
98
  "DOC.HOTKEY": {
93
99
  actions: ["focusRegionEl"]
94
100
  },
@@ -192,6 +198,7 @@ var groupMachine = createMachine({
192
198
  const { pauseOnPageIdle } = prop("store").attrs;
193
199
  if (!pauseOnPageIdle) return;
194
200
  const doc = scope.getDoc();
201
+ if (doc.visibilityState === "hidden") send({ type: "PAUSE_ALL" });
195
202
  return (0, import_dom_query.addDomEvent)(doc, "visibilitychange", () => {
196
203
  const isHidden = doc.visibilityState === "hidden";
197
204
  send({ type: isHidden ? "PAUSE_ALL" : "RESUME_ALL" });
@@ -205,6 +212,7 @@ var groupMachine = createMachine({
205
212
  const hasToasts = toasts.length > 0;
206
213
  if (!hasToasts) {
207
214
  refs.get("dismissableCleanup")?.();
215
+ refs.set("dismissableCleanup", void 0);
208
216
  return;
209
217
  }
210
218
  if (hasToasts && refs.get("dismissableCleanup")) {
@@ -216,6 +224,7 @@ var groupMachine = createMachine({
216
224
  },
217
225
  clearDismissableBranch({ refs }) {
218
226
  refs.get("dismissableCleanup")?.();
227
+ refs.set("dismissableCleanup", void 0);
219
228
  },
220
229
  focusRegionEl({ scope, computed }) {
221
230
  queueMicrotask(() => {
@@ -225,7 +234,10 @@ var groupMachine = createMachine({
225
234
  pauseToasts({ prop }) {
226
235
  prop("store").pause();
227
236
  },
228
- resumeToasts({ prop }) {
237
+ resumeToasts({ prop, refs, scope, event }) {
238
+ if (event.type === "REGION.BLUR") refs.set("isFocusWithin", false);
239
+ const idle = prop("store").attrs.pauseOnPageIdle && scope.getDoc().visibilityState === "hidden";
240
+ if (idle || refs.get("isPointerWithin") || refs.get("isFocusWithin")) return;
229
241
  prop("store").resume();
230
242
  },
231
243
  expandToasts({ prop }) {
@@ -248,9 +260,9 @@ var groupMachine = createMachine({
248
260
  send({ type: "REGION.OVERLAP" });
249
261
  },
250
262
  setLastFocusedEl({ refs, event }) {
251
- if (refs.get("isFocusWithin") || !event.target) return;
263
+ if (refs.get("isFocusWithin")) return;
252
264
  refs.set("isFocusWithin", true);
253
- refs.set("lastFocusedEl", event.target);
265
+ refs.set("lastFocusedEl", event.target ?? null);
254
266
  },
255
267
  restoreFocusIfPointerOut({ refs }) {
256
268
  if (!refs.get("lastFocusedEl") || refs.get("isPointerWithin")) return;
@@ -55,6 +55,12 @@ var groupMachine = createMachine({
55
55
  },
56
56
  exit: ["clearDismissableBranch", "clearLastFocusedEl", "clearMouseEventTimer"],
57
57
  on: {
58
+ PAUSE_ALL: {
59
+ actions: ["pauseToasts"]
60
+ },
61
+ RESUME_ALL: {
62
+ actions: ["resumeToasts"]
63
+ },
58
64
  "DOC.HOTKEY": {
59
65
  actions: ["focusRegionEl"]
60
66
  },
@@ -158,6 +164,7 @@ var groupMachine = createMachine({
158
164
  const { pauseOnPageIdle } = prop("store").attrs;
159
165
  if (!pauseOnPageIdle) return;
160
166
  const doc = scope.getDoc();
167
+ if (doc.visibilityState === "hidden") send({ type: "PAUSE_ALL" });
161
168
  return addDomEvent(doc, "visibilitychange", () => {
162
169
  const isHidden = doc.visibilityState === "hidden";
163
170
  send({ type: isHidden ? "PAUSE_ALL" : "RESUME_ALL" });
@@ -171,6 +178,7 @@ var groupMachine = createMachine({
171
178
  const hasToasts = toasts.length > 0;
172
179
  if (!hasToasts) {
173
180
  refs.get("dismissableCleanup")?.();
181
+ refs.set("dismissableCleanup", void 0);
174
182
  return;
175
183
  }
176
184
  if (hasToasts && refs.get("dismissableCleanup")) {
@@ -182,6 +190,7 @@ var groupMachine = createMachine({
182
190
  },
183
191
  clearDismissableBranch({ refs }) {
184
192
  refs.get("dismissableCleanup")?.();
193
+ refs.set("dismissableCleanup", void 0);
185
194
  },
186
195
  focusRegionEl({ scope, computed }) {
187
196
  queueMicrotask(() => {
@@ -191,7 +200,10 @@ var groupMachine = createMachine({
191
200
  pauseToasts({ prop }) {
192
201
  prop("store").pause();
193
202
  },
194
- resumeToasts({ prop }) {
203
+ resumeToasts({ prop, refs, scope, event }) {
204
+ if (event.type === "REGION.BLUR") refs.set("isFocusWithin", false);
205
+ const idle = prop("store").attrs.pauseOnPageIdle && scope.getDoc().visibilityState === "hidden";
206
+ if (idle || refs.get("isPointerWithin") || refs.get("isFocusWithin")) return;
195
207
  prop("store").resume();
196
208
  },
197
209
  expandToasts({ prop }) {
@@ -214,9 +226,9 @@ var groupMachine = createMachine({
214
226
  send({ type: "REGION.OVERLAP" });
215
227
  },
216
228
  setLastFocusedEl({ refs, event }) {
217
- if (refs.get("isFocusWithin") || !event.target) return;
229
+ if (refs.get("isFocusWithin")) return;
218
230
  refs.set("isFocusWithin", true);
219
- refs.set("lastFocusedEl", event.target);
231
+ refs.set("lastFocusedEl", event.target ?? null);
220
232
  },
221
233
  restoreFocusIfPointerOut({ refs }) {
222
234
  if (!refs.get("lastFocusedEl") || refs.get("isPointerWithin")) return;
@@ -47,6 +47,7 @@ function connect(service, normalize) {
47
47
  const visible = state.hasTag("visible");
48
48
  const paused = state.hasTag("paused");
49
49
  const mounted = context.get("mounted");
50
+ const rendered = context.get("rendered");
50
51
  const frontmost = computed("frontmost");
51
52
  const placement = prop("parent").computed("placement");
52
53
  const type = prop("type");
@@ -64,10 +65,10 @@ function connect(service, normalize) {
64
65
  paused,
65
66
  closable: !!prop("closable"),
66
67
  pause() {
67
- send({ type: "PAUSE" });
68
+ prop("parent").prop("store").pause(prop("id"));
68
69
  },
69
70
  resume() {
70
- send({ type: "RESUME" });
71
+ prop("parent").prop("store").resume(prop("id"));
71
72
  },
72
73
  dismiss() {
73
74
  send({ type: "DISMISS", src: "programmatic" });
@@ -90,8 +91,8 @@ function connect(service, normalize) {
90
91
  "data-overlap": (0, import_dom_query.dataAttr)(!stacked),
91
92
  role: "status",
92
93
  "aria-atomic": "true",
93
- "aria-describedby": description ? dom.getDescriptionId(scope) : void 0,
94
- "aria-labelledby": title ? dom.getTitleId(scope) : void 0,
94
+ "aria-describedby": description && rendered.description ? dom.getDescriptionId(scope) : void 0,
95
+ "aria-labelledby": title && rendered.title ? dom.getTitleId(scope) : void 0,
95
96
  tabIndex: 0,
96
97
  style: (0, import_toast2.getPlacementStyle)(service, visible),
97
98
  onKeyDown(event) {
@@ -13,6 +13,7 @@ function connect(service, normalize) {
13
13
  const visible = state.hasTag("visible");
14
14
  const paused = state.hasTag("paused");
15
15
  const mounted = context.get("mounted");
16
+ const rendered = context.get("rendered");
16
17
  const frontmost = computed("frontmost");
17
18
  const placement = prop("parent").computed("placement");
18
19
  const type = prop("type");
@@ -30,10 +31,10 @@ function connect(service, normalize) {
30
31
  paused,
31
32
  closable: !!prop("closable"),
32
33
  pause() {
33
- send({ type: "PAUSE" });
34
+ prop("parent").prop("store").pause(prop("id"));
34
35
  },
35
36
  resume() {
36
- send({ type: "RESUME" });
37
+ prop("parent").prop("store").resume(prop("id"));
37
38
  },
38
39
  dismiss() {
39
40
  send({ type: "DISMISS", src: "programmatic" });
@@ -56,8 +57,8 @@ function connect(service, normalize) {
56
57
  "data-overlap": dataAttr(!stacked),
57
58
  role: "status",
58
59
  "aria-atomic": "true",
59
- "aria-describedby": description ? dom.getDescriptionId(scope) : void 0,
60
- "aria-labelledby": title ? dom.getTitleId(scope) : void 0,
60
+ "aria-describedby": description && rendered.description ? dom.getDescriptionId(scope) : void 0,
61
+ "aria-labelledby": title && rendered.title ? dom.getTitleId(scope) : void 0,
61
62
  tabIndex: 0,
62
63
  style: getPlacementStyle(service, visible),
63
64
  onKeyDown(event) {
@@ -9,6 +9,8 @@ declare const getRootId: (ctx: Scope) => string;
9
9
  declare const getRootEl: (ctx: Scope) => HTMLElement | null;
10
10
  declare const getTitleId: (ctx: Scope) => string;
11
11
  declare const getDescriptionId: (ctx: Scope) => string;
12
+ declare const getTitleEl: (ctx: Scope) => HTMLElement | null;
13
+ declare const getDescriptionEl: (ctx: Scope) => HTMLElement | null;
12
14
  declare const getCloseTriggerId: (ctx: Scope) => string;
13
15
 
14
- export { getCloseTriggerId, getDescriptionId, getRegionEl, getRegionId, getRootEl, getRootId, getTitleId };
16
+ export { getCloseTriggerId, getDescriptionEl, getDescriptionId, getRegionEl, getRegionId, getRootEl, getRootId, getTitleEl, getTitleId };
@@ -9,6 +9,8 @@ declare const getRootId: (ctx: Scope) => string;
9
9
  declare const getRootEl: (ctx: Scope) => HTMLElement | null;
10
10
  declare const getTitleId: (ctx: Scope) => string;
11
11
  declare const getDescriptionId: (ctx: Scope) => string;
12
+ declare const getTitleEl: (ctx: Scope) => HTMLElement | null;
13
+ declare const getDescriptionEl: (ctx: Scope) => HTMLElement | null;
12
14
  declare const getCloseTriggerId: (ctx: Scope) => string;
13
15
 
14
- export { getCloseTriggerId, getDescriptionId, getRegionEl, getRegionId, getRootEl, getRootId, getTitleId };
16
+ export { getCloseTriggerId, getDescriptionEl, getDescriptionId, getRegionEl, getRegionId, getRootEl, getRootId, getTitleEl, getTitleId };
package/dist/toast.dom.js CHANGED
@@ -21,11 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var toast_dom_exports = {};
22
22
  __export(toast_dom_exports, {
23
23
  getCloseTriggerId: () => getCloseTriggerId,
24
+ getDescriptionEl: () => getDescriptionEl,
24
25
  getDescriptionId: () => getDescriptionId,
25
26
  getRegionEl: () => getRegionEl,
26
27
  getRegionId: () => getRegionId,
27
28
  getRootEl: () => getRootEl,
28
29
  getRootId: () => getRootId,
30
+ getTitleEl: () => getTitleEl,
29
31
  getTitleId: () => getTitleId
30
32
  });
31
33
  module.exports = __toCommonJS(toast_dom_exports);
@@ -35,14 +37,18 @@ var getRootId = (ctx) => `toast:${ctx.id}`;
35
37
  var getRootEl = (ctx) => ctx.getById(getRootId(ctx));
36
38
  var getTitleId = (ctx) => `toast:${ctx.id}:title`;
37
39
  var getDescriptionId = (ctx) => `toast:${ctx.id}:description`;
40
+ var getTitleEl = (ctx) => ctx.getById(getTitleId(ctx));
41
+ var getDescriptionEl = (ctx) => ctx.getById(getDescriptionId(ctx));
38
42
  var getCloseTriggerId = (ctx) => `toast${ctx.id}:close`;
39
43
  // Annotate the CommonJS export names for ESM import in node:
40
44
  0 && (module.exports = {
41
45
  getCloseTriggerId,
46
+ getDescriptionEl,
42
47
  getDescriptionId,
43
48
  getRegionEl,
44
49
  getRegionId,
45
50
  getRootEl,
46
51
  getRootId,
52
+ getTitleEl,
47
53
  getTitleId
48
54
  });
@@ -5,13 +5,17 @@ var getRootId = (ctx) => `toast:${ctx.id}`;
5
5
  var getRootEl = (ctx) => ctx.getById(getRootId(ctx));
6
6
  var getTitleId = (ctx) => `toast:${ctx.id}:title`;
7
7
  var getDescriptionId = (ctx) => `toast:${ctx.id}:description`;
8
+ var getTitleEl = (ctx) => ctx.getById(getTitleId(ctx));
9
+ var getDescriptionEl = (ctx) => ctx.getById(getDescriptionId(ctx));
8
10
  var getCloseTriggerId = (ctx) => `toast${ctx.id}:close`;
9
11
  export {
10
12
  getCloseTriggerId,
13
+ getDescriptionEl,
11
14
  getDescriptionId,
12
15
  getRegionEl,
13
16
  getRegionId,
14
17
  getRootEl,
15
18
  getRootId,
19
+ getTitleEl,
16
20
  getTitleId
17
21
  };
@@ -38,7 +38,7 @@ var import_dom_query = require("@zag-js/dom-query");
38
38
  var import_utils = require("@zag-js/utils");
39
39
  var dom = __toESM(require("./toast.dom.js"));
40
40
  var import_toast = require("./toast.utils.js");
41
- var { not } = (0, import_core.createGuards)();
41
+ var { not, and } = (0, import_core.createGuards)();
42
42
  var machine = (0, import_core.createMachine)({
43
43
  props({ props }) {
44
44
  (0, import_utils.ensureProps)(props, ["id", "type", "parent", "removeDelay"], "toast");
@@ -49,7 +49,8 @@ var machine = (0, import_core.createMachine)({
49
49
  };
50
50
  },
51
51
  initialState({ prop }) {
52
- const persist = prop("type") === "loading" || prop("duration") === Infinity;
52
+ if (prop("message") === "DISMISS") return "dismissing";
53
+ const persist = prop("type") === "loading" || prop("duration") === Infinity || prop("paused");
53
54
  return persist ? "visible:persist" : "visible";
54
55
  },
55
56
  context({ prop, bindable }) {
@@ -65,6 +66,9 @@ var machine = (0, import_core.createMachine)({
65
66
  })),
66
67
  initialHeight: bindable(() => ({
67
68
  defaultValue: 0
69
+ })),
70
+ rendered: bindable(() => ({
71
+ defaultValue: { title: true, description: true }
68
72
  }))
69
73
  };
70
74
  },
@@ -105,6 +109,9 @@ var machine = (0, import_core.createMachine)({
105
109
  const message = prop("message");
106
110
  if (message) send({ type: message, src: "programmatic" });
107
111
  });
112
+ track([() => prop("paused")], () => {
113
+ send({ type: prop("paused") ? "PAUSE" : "RESUME" });
114
+ });
108
115
  track([() => prop("type"), () => prop("duration")], () => {
109
116
  send({ type: "UPDATE" });
110
117
  });
@@ -112,11 +119,12 @@ var machine = (0, import_core.createMachine)({
112
119
  on: {
113
120
  UPDATE: [
114
121
  {
115
- guard: "shouldPersist",
122
+ guard: and("isVisible", "shouldPause"),
116
123
  target: "visible:persist",
117
124
  actions: ["resetCloseTimer"]
118
125
  },
119
126
  {
127
+ guard: "isVisible",
120
128
  target: "visible:updating",
121
129
  actions: ["resetCloseTimer"]
122
130
  }
@@ -126,12 +134,18 @@ var machine = (0, import_core.createMachine)({
126
134
  }
127
135
  },
128
136
  entry: ["setMounted", "measureHeight", "invokeOnVisible"],
129
- effects: ["trackHeight"],
137
+ effects: ["trackContent"],
130
138
  states: {
131
139
  "visible:updating": {
132
140
  tags: ["visible", "updating"],
133
141
  effects: ["waitForNextTick"],
134
142
  on: {
143
+ DISMISS: {
144
+ target: "dismissing"
145
+ },
146
+ PAUSE: {
147
+ target: "visible:persist"
148
+ },
135
149
  SHOW: {
136
150
  target: "visible"
137
151
  }
@@ -141,7 +155,7 @@ var machine = (0, import_core.createMachine)({
141
155
  tags: ["visible", "paused"],
142
156
  on: {
143
157
  RESUME: {
144
- guard: not("isLoadingType"),
158
+ guard: not("shouldPause"),
145
159
  target: "visible",
146
160
  actions: ["setCloseTimer"]
147
161
  },
@@ -195,9 +209,8 @@ var machine = (0, import_core.createMachine)({
195
209
  send({ type: "SHOW", src: "timer" });
196
210
  }, 0);
197
211
  },
198
- trackHeight({ scope, prop }) {
199
- let cleanup;
200
- (0, import_dom_query.raf)(() => {
212
+ trackContent({ scope, prop, context }) {
213
+ return (0, import_dom_query.raf)(() => {
201
214
  const rootEl = dom.getRootEl(scope);
202
215
  if (!rootEl) return;
203
216
  const syncHeight = () => {
@@ -205,21 +218,30 @@ var machine = (0, import_core.createMachine)({
205
218
  const item = { id: prop("id"), height };
206
219
  setHeight(prop("parent"), item);
207
220
  };
221
+ const syncRenderedElements = () => {
222
+ context.set("rendered", {
223
+ title: !!dom.getTitleEl(scope),
224
+ description: !!dom.getDescriptionEl(scope)
225
+ });
226
+ };
227
+ syncRenderedElements();
208
228
  const win = scope.getWin();
209
- const observer = new win.MutationObserver(syncHeight);
229
+ const observer = new win.MutationObserver(() => {
230
+ syncHeight();
231
+ syncRenderedElements();
232
+ });
210
233
  observer.observe(rootEl, {
211
234
  childList: true,
212
235
  subtree: true,
213
236
  characterData: true
214
237
  });
215
- cleanup = () => observer.disconnect();
238
+ return () => observer.disconnect();
216
239
  });
217
- return () => cleanup?.();
218
240
  }
219
241
  },
220
242
  guards: {
221
- isLoadingType: ({ prop }) => prop("type") === "loading",
222
- shouldPersist: ({ computed }) => computed("shouldPersist")
243
+ isVisible: ({ state }) => state.hasTag("visible"),
244
+ shouldPause: ({ computed, prop }) => computed("shouldPersist") || !!prop("paused")
223
245
  },
224
246
  actions: {
225
247
  setMounted({ context }) {
@@ -257,6 +279,7 @@ var machine = (0, import_core.createMachine)({
257
279
  parent.send({ type: "TOAST.REMOVE", id: prop("id") });
258
280
  },
259
281
  invokeOnDismiss({ prop, event }) {
282
+ prop("parent").prop("store").dismiss(prop("id"));
260
283
  prop("onStatusChange")?.({ status: "dismissing", src: event.src });
261
284
  },
262
285
  invokeOnUnmount({ prop }) {
@@ -4,7 +4,7 @@ import { raf } from "@zag-js/dom-query";
4
4
  import { ensureProps, setRafTimeout } from "@zag-js/utils";
5
5
  import * as dom from "./toast.dom.mjs";
6
6
  import { getToastDuration } from "./toast.utils.mjs";
7
- var { not } = createGuards();
7
+ var { not, and } = createGuards();
8
8
  var machine = createMachine({
9
9
  props({ props }) {
10
10
  ensureProps(props, ["id", "type", "parent", "removeDelay"], "toast");
@@ -15,7 +15,8 @@ var machine = createMachine({
15
15
  };
16
16
  },
17
17
  initialState({ prop }) {
18
- const persist = prop("type") === "loading" || prop("duration") === Infinity;
18
+ if (prop("message") === "DISMISS") return "dismissing";
19
+ const persist = prop("type") === "loading" || prop("duration") === Infinity || prop("paused");
19
20
  return persist ? "visible:persist" : "visible";
20
21
  },
21
22
  context({ prop, bindable }) {
@@ -31,6 +32,9 @@ var machine = createMachine({
31
32
  })),
32
33
  initialHeight: bindable(() => ({
33
34
  defaultValue: 0
35
+ })),
36
+ rendered: bindable(() => ({
37
+ defaultValue: { title: true, description: true }
34
38
  }))
35
39
  };
36
40
  },
@@ -71,6 +75,9 @@ var machine = createMachine({
71
75
  const message = prop("message");
72
76
  if (message) send({ type: message, src: "programmatic" });
73
77
  });
78
+ track([() => prop("paused")], () => {
79
+ send({ type: prop("paused") ? "PAUSE" : "RESUME" });
80
+ });
74
81
  track([() => prop("type"), () => prop("duration")], () => {
75
82
  send({ type: "UPDATE" });
76
83
  });
@@ -78,11 +85,12 @@ var machine = createMachine({
78
85
  on: {
79
86
  UPDATE: [
80
87
  {
81
- guard: "shouldPersist",
88
+ guard: and("isVisible", "shouldPause"),
82
89
  target: "visible:persist",
83
90
  actions: ["resetCloseTimer"]
84
91
  },
85
92
  {
93
+ guard: "isVisible",
86
94
  target: "visible:updating",
87
95
  actions: ["resetCloseTimer"]
88
96
  }
@@ -92,12 +100,18 @@ var machine = createMachine({
92
100
  }
93
101
  },
94
102
  entry: ["setMounted", "measureHeight", "invokeOnVisible"],
95
- effects: ["trackHeight"],
103
+ effects: ["trackContent"],
96
104
  states: {
97
105
  "visible:updating": {
98
106
  tags: ["visible", "updating"],
99
107
  effects: ["waitForNextTick"],
100
108
  on: {
109
+ DISMISS: {
110
+ target: "dismissing"
111
+ },
112
+ PAUSE: {
113
+ target: "visible:persist"
114
+ },
101
115
  SHOW: {
102
116
  target: "visible"
103
117
  }
@@ -107,7 +121,7 @@ var machine = createMachine({
107
121
  tags: ["visible", "paused"],
108
122
  on: {
109
123
  RESUME: {
110
- guard: not("isLoadingType"),
124
+ guard: not("shouldPause"),
111
125
  target: "visible",
112
126
  actions: ["setCloseTimer"]
113
127
  },
@@ -161,9 +175,8 @@ var machine = createMachine({
161
175
  send({ type: "SHOW", src: "timer" });
162
176
  }, 0);
163
177
  },
164
- trackHeight({ scope, prop }) {
165
- let cleanup;
166
- raf(() => {
178
+ trackContent({ scope, prop, context }) {
179
+ return raf(() => {
167
180
  const rootEl = dom.getRootEl(scope);
168
181
  if (!rootEl) return;
169
182
  const syncHeight = () => {
@@ -171,21 +184,30 @@ var machine = createMachine({
171
184
  const item = { id: prop("id"), height };
172
185
  setHeight(prop("parent"), item);
173
186
  };
187
+ const syncRenderedElements = () => {
188
+ context.set("rendered", {
189
+ title: !!dom.getTitleEl(scope),
190
+ description: !!dom.getDescriptionEl(scope)
191
+ });
192
+ };
193
+ syncRenderedElements();
174
194
  const win = scope.getWin();
175
- const observer = new win.MutationObserver(syncHeight);
195
+ const observer = new win.MutationObserver(() => {
196
+ syncHeight();
197
+ syncRenderedElements();
198
+ });
176
199
  observer.observe(rootEl, {
177
200
  childList: true,
178
201
  subtree: true,
179
202
  characterData: true
180
203
  });
181
- cleanup = () => observer.disconnect();
204
+ return () => observer.disconnect();
182
205
  });
183
- return () => cleanup?.();
184
206
  }
185
207
  },
186
208
  guards: {
187
- isLoadingType: ({ prop }) => prop("type") === "loading",
188
- shouldPersist: ({ computed }) => computed("shouldPersist")
209
+ isVisible: ({ state }) => state.hasTag("visible"),
210
+ shouldPause: ({ computed, prop }) => computed("shouldPersist") || !!prop("paused")
189
211
  },
190
212
  actions: {
191
213
  setMounted({ context }) {
@@ -223,6 +245,7 @@ var machine = createMachine({
223
245
  parent.send({ type: "TOAST.REMOVE", id: prop("id") });
224
246
  },
225
247
  invokeOnDismiss({ prop, event }) {
248
+ prop("parent").prop("store").dismiss(prop("id"));
226
249
  prop("onStatusChange")?.({ status: "dismissing", src: event.src });
227
250
  },
228
251
  invokeOnUnmount({ prop }) {
@@ -36,7 +36,8 @@ var priorities = {
36
36
  };
37
37
  var DEFAULT_TYPE = "info";
38
38
  var getPriorityForType = (type, hasAction) => {
39
- const [actionable, nonActionable] = priorities[type ?? DEFAULT_TYPE];
39
+ const key = type ?? DEFAULT_TYPE;
40
+ const [actionable, nonActionable] = (0, import_utils.hasProp)(priorities, key) ? priorities[key] : priorities[DEFAULT_TYPE];
40
41
  return hasAction ? actionable : nonActionable;
41
42
  };
42
43
  var sortToastsByPriority = (toastArray) => {
@@ -61,15 +62,34 @@ function createToastStore(props = {}) {
61
62
  let toasts = [];
62
63
  let dismissedToasts = /* @__PURE__ */ new Set();
63
64
  let toastQueue = [];
65
+ const closingToasts = /* @__PURE__ */ new Set();
66
+ const promiseTokens = /* @__PURE__ */ new Map();
67
+ let paused = false;
64
68
  const subscribe = (subscriber) => {
65
69
  subscribers.push(subscriber);
70
+ let active = true;
66
71
  return () => {
72
+ if (!active) return;
73
+ active = false;
67
74
  const index = subscribers.indexOf(subscriber);
68
- subscribers.splice(index, 1);
75
+ if (index !== -1) subscribers.splice(index, 1);
69
76
  };
70
77
  };
71
- const publish = (data) => {
72
- subscribers.forEach((subscriber) => subscriber(data));
78
+ const notifications = [];
79
+ let publishing = false;
80
+ const publish = (...data) => {
81
+ notifications.push(...data);
82
+ if (publishing) return data;
83
+ publishing = true;
84
+ try {
85
+ while (notifications.length) {
86
+ const next = notifications.shift();
87
+ subscribers.slice().forEach((subscriber) => subscriber(next));
88
+ }
89
+ } finally {
90
+ publishing = false;
91
+ notifications.length = 0;
92
+ }
73
93
  return data;
74
94
  };
75
95
  const addToast = (data) => {
@@ -77,30 +97,34 @@ function createToastStore(props = {}) {
77
97
  toastQueue.push(data);
78
98
  return;
79
99
  }
80
- publish(data);
81
100
  toasts.unshift(data);
101
+ publish(data);
82
102
  };
83
103
  const processQueue = () => {
84
104
  toastQueue = sortToastsByPriority(toastQueue);
85
105
  while (toastQueue.length > 0 && toasts.length < attrs.max) {
86
106
  const nextToast = toastQueue.shift();
87
107
  if (nextToast) {
88
- publish(nextToast);
89
- toasts.unshift(nextToast);
108
+ const toast = { ...nextToast, paused: paused || nextToast.paused };
109
+ toasts.unshift(toast);
110
+ publish(toast);
90
111
  }
91
112
  }
92
113
  };
93
114
  const create = (data) => {
94
115
  const id = data.id ?? `toast:${(0, import_utils.uuid)()}`;
95
- const exists = toasts.find((toast) => toast.id === id);
96
- if (dismissedToasts.has(id)) dismissedToasts.delete(id);
116
+ const exists = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
117
+ if (closingToasts.has(id)) return id;
118
+ if ((0, import_utils.hasProp)(data, "promise") && data.promise !== exists?.promise) promiseTokens.delete(id);
119
+ dismissedToasts.delete(id);
97
120
  if (exists) {
98
- toasts = toasts.map((toast) => {
99
- if (toast.id === id) {
100
- return publish({ ...toast, ...data, id });
101
- }
102
- return toast;
103
- });
121
+ const next = { ...exists, ...data, id };
122
+ if (toasts.some((toast) => toast.id === id)) {
123
+ toasts = toasts.map((toast) => toast.id === id ? next : toast);
124
+ publish(next);
125
+ } else {
126
+ toastQueue = toastQueue.map((toast) => toast.id === id ? next : toast);
127
+ }
104
128
  } else {
105
129
  const newToast = {
106
130
  id,
@@ -109,7 +133,8 @@ function createToastStore(props = {}) {
109
133
  type: DEFAULT_TYPE,
110
134
  ...data,
111
135
  stacked: !attrs.overlap,
112
- gap: attrs.gap
136
+ gap: attrs.gap,
137
+ paused
113
138
  };
114
139
  const priority = newToast.priority ?? getPriorityForType(newToast.type, !!newToast.action);
115
140
  addToast({ ...newToast, priority });
@@ -117,18 +142,19 @@ function createToastStore(props = {}) {
117
142
  return id;
118
143
  };
119
144
  const remove = (id) => {
120
- dismissedToasts.add(id);
121
- if (!id) {
122
- toasts.forEach((toast) => {
123
- subscribers.forEach((subscriber) => subscriber({ id: toast.id, dismiss: true }));
124
- });
125
- toasts = [];
126
- toastQueue = [];
127
- } else {
128
- subscribers.forEach((subscriber) => subscriber({ id, dismiss: true }));
129
- toasts = toasts.filter((toast) => toast.id !== id);
130
- processQueue();
131
- }
145
+ const removed = id == null ? [...toasts, ...toastQueue] : [...toasts, ...toastQueue].filter((toast) => toast.id === id);
146
+ const visible = id == null ? toasts : toasts.filter((toast) => toast.id === id);
147
+ removed.forEach((toast) => {
148
+ dismissedToasts.add(toast.id);
149
+ closingToasts.delete(toast.id);
150
+ promiseTokens.delete(toast.id);
151
+ });
152
+ if (id != null) dismissedToasts.add(id);
153
+ toasts = id == null ? [] : toasts.filter((toast) => toast.id !== id);
154
+ toastQueue = id == null ? [] : toastQueue.filter((toast) => toast.id !== id);
155
+ if (id != null && !visible.length) publish({ id, dismiss: true });
156
+ publish(...visible.map((toast) => ({ id: toast.id, dismiss: true })));
157
+ processQueue();
132
158
  return id;
133
159
  };
134
160
  const error = (data) => {
@@ -163,6 +189,10 @@ function createToastStore(props = {}) {
163
189
  promise: promise2,
164
190
  type: "loading"
165
191
  });
192
+ const token = /* @__PURE__ */ Symbol();
193
+ const current = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
194
+ if (current?.promise === promise2 && !closingToasts.has(id)) promiseTokens.set(id, token);
195
+ const isCurrent = () => promiseTokens.get(id) === token && !closingToasts.has(id);
166
196
  let removable = true;
167
197
  let result;
168
198
  const prom = (0, import_utils.runIfFn)(promise2).then(async (response) => {
@@ -170,23 +200,24 @@ function createToastStore(props = {}) {
170
200
  if (isHttpResponse(response) && !response.ok) {
171
201
  removable = false;
172
202
  const errorOptions = (0, import_utils.runIfFn)(options.error, `HTTP Error! status: ${response.status}`);
173
- create({ ...shared, ...errorOptions, id, type: "error" });
203
+ if (isCurrent()) create({ ...shared, ...errorOptions, id, type: "error" });
174
204
  } else if (options.success !== void 0) {
175
205
  removable = false;
176
206
  const successOptions = (0, import_utils.runIfFn)(options.success, response);
177
- create({ ...shared, ...successOptions, id, type: successOptions.type ?? "success" });
207
+ if (isCurrent()) create({ ...shared, ...successOptions, id, type: successOptions.type ?? "success" });
178
208
  }
179
209
  }).catch(async (error2) => {
180
210
  result = ["reject", error2];
181
211
  if (options.error !== void 0) {
182
212
  removable = false;
183
213
  const errorOptions = (0, import_utils.runIfFn)(options.error, error2);
184
- create({ ...shared, ...errorOptions, id, type: "error" });
214
+ if (isCurrent()) create({ ...shared, ...errorOptions, id, type: "error" });
185
215
  }
186
216
  }).finally(() => {
187
- if (removable) {
217
+ if (removable && isCurrent()) {
188
218
  remove(id);
189
219
  }
220
+ if (promiseTokens.get(id) === token) promiseTokens.delete(id);
190
221
  options.finally?.();
191
222
  });
192
223
  const unwrap = () => new Promise(
@@ -195,37 +226,46 @@ function createToastStore(props = {}) {
195
226
  return { id, unwrap };
196
227
  };
197
228
  const update = (id, data) => {
198
- return create({ id, ...data });
229
+ if (closingToasts.has(id)) return id;
230
+ if (typeof data !== "function") return create({ ...data, id });
231
+ const current = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
232
+ if (!current) return id;
233
+ const next = { ...data({ ...current }), id };
234
+ if (closingToasts.has(id)) return id;
235
+ if (toasts.some((toast) => toast.id === id)) return create(next);
236
+ if (!toastQueue.some((toast) => toast.id === id)) return id;
237
+ return create(next);
238
+ };
239
+ const changeToasts = (data, id) => {
240
+ const changed = [];
241
+ toasts = toasts.map((toast) => {
242
+ if (id != null && toast.id !== id || closingToasts.has(toast.id)) return toast;
243
+ const next = { ...toast, ...data };
244
+ changed.push(next);
245
+ return next;
246
+ });
247
+ toastQueue = toastQueue.map((toast) => id == null || toast.id === id ? { ...toast, ...data } : toast);
248
+ publish(...changed);
199
249
  };
200
250
  const pause = (id) => {
201
- if (id != null) {
202
- toasts = toasts.map((toast) => {
203
- if (toast.id === id) return publish({ ...toast, message: "PAUSE" });
204
- return toast;
205
- });
206
- } else {
207
- toasts = toasts.map((toast) => publish({ ...toast, message: "PAUSE" }));
208
- }
251
+ if (id == null) paused = true;
252
+ changeToasts({ paused: true }, id);
209
253
  };
210
254
  const resume = (id) => {
211
- if (id != null) {
212
- toasts = toasts.map((toast) => {
213
- if (toast.id === id) return publish({ ...toast, message: "RESUME" });
214
- return toast;
215
- });
216
- } else {
217
- toasts = toasts.map((toast) => publish({ ...toast, message: "RESUME" }));
218
- }
255
+ if (id == null) paused = false;
256
+ changeToasts({ paused: false }, id);
219
257
  };
220
258
  const dismiss = (id) => {
221
- if (id != null) {
222
- toasts = toasts.map((toast) => {
223
- if (toast.id === id) return publish({ ...toast, message: "DISMISS" });
224
- return toast;
225
- });
226
- } else {
227
- toasts = toasts.map((toast) => publish({ ...toast, message: "DISMISS" }));
228
- }
259
+ const queued = toastQueue.filter((toast) => id == null || toast.id === id);
260
+ toastQueue = toastQueue.filter((toast) => id != null && toast.id !== id);
261
+ queued.forEach((toast) => {
262
+ dismissedToasts.add(toast.id);
263
+ promiseTokens.delete(toast.id);
264
+ });
265
+ const changed = toasts.filter((toast) => (id == null || toast.id === id) && !closingToasts.has(toast.id));
266
+ changed.forEach((toast) => closingToasts.add(toast.id));
267
+ toasts = toasts.map((toast) => closingToasts.has(toast.id) ? { ...toast, message: "DISMISS" } : toast);
268
+ publish(...changed.map((toast) => ({ ...toast, message: "DISMISS" })));
229
269
  };
230
270
  const isVisible = (id) => {
231
271
  return !dismissedToasts.has(id) && !!toasts.find((toast) => toast.id === id);
@@ -233,12 +273,8 @@ function createToastStore(props = {}) {
233
273
  const isDismissed = (id) => {
234
274
  return dismissedToasts.has(id);
235
275
  };
236
- const expand = () => {
237
- toasts = toasts.map((toast) => publish({ ...toast, stacked: true }));
238
- };
239
- const collapse = () => {
240
- toasts = toasts.map((toast) => publish({ ...toast, stacked: false }));
241
- };
276
+ const expand = () => changeToasts({ stacked: true });
277
+ const collapse = () => changeToasts({ stacked: false });
242
278
  return {
243
279
  attrs,
244
280
  subscribe,
@@ -1,5 +1,5 @@
1
1
  // src/toast.store.ts
2
- import { compact, runIfFn, uuid, warn } from "@zag-js/utils";
2
+ import { compact, hasProp, runIfFn, uuid, warn } from "@zag-js/utils";
3
3
  var withDefaults = (options, defaults) => {
4
4
  return { ...defaults, ...compact(options) };
5
5
  };
@@ -12,7 +12,8 @@ var priorities = {
12
12
  };
13
13
  var DEFAULT_TYPE = "info";
14
14
  var getPriorityForType = (type, hasAction) => {
15
- const [actionable, nonActionable] = priorities[type ?? DEFAULT_TYPE];
15
+ const key = type ?? DEFAULT_TYPE;
16
+ const [actionable, nonActionable] = hasProp(priorities, key) ? priorities[key] : priorities[DEFAULT_TYPE];
16
17
  return hasAction ? actionable : nonActionable;
17
18
  };
18
19
  var sortToastsByPriority = (toastArray) => {
@@ -37,15 +38,34 @@ function createToastStore(props = {}) {
37
38
  let toasts = [];
38
39
  let dismissedToasts = /* @__PURE__ */ new Set();
39
40
  let toastQueue = [];
41
+ const closingToasts = /* @__PURE__ */ new Set();
42
+ const promiseTokens = /* @__PURE__ */ new Map();
43
+ let paused = false;
40
44
  const subscribe = (subscriber) => {
41
45
  subscribers.push(subscriber);
46
+ let active = true;
42
47
  return () => {
48
+ if (!active) return;
49
+ active = false;
43
50
  const index = subscribers.indexOf(subscriber);
44
- subscribers.splice(index, 1);
51
+ if (index !== -1) subscribers.splice(index, 1);
45
52
  };
46
53
  };
47
- const publish = (data) => {
48
- subscribers.forEach((subscriber) => subscriber(data));
54
+ const notifications = [];
55
+ let publishing = false;
56
+ const publish = (...data) => {
57
+ notifications.push(...data);
58
+ if (publishing) return data;
59
+ publishing = true;
60
+ try {
61
+ while (notifications.length) {
62
+ const next = notifications.shift();
63
+ subscribers.slice().forEach((subscriber) => subscriber(next));
64
+ }
65
+ } finally {
66
+ publishing = false;
67
+ notifications.length = 0;
68
+ }
49
69
  return data;
50
70
  };
51
71
  const addToast = (data) => {
@@ -53,30 +73,34 @@ function createToastStore(props = {}) {
53
73
  toastQueue.push(data);
54
74
  return;
55
75
  }
56
- publish(data);
57
76
  toasts.unshift(data);
77
+ publish(data);
58
78
  };
59
79
  const processQueue = () => {
60
80
  toastQueue = sortToastsByPriority(toastQueue);
61
81
  while (toastQueue.length > 0 && toasts.length < attrs.max) {
62
82
  const nextToast = toastQueue.shift();
63
83
  if (nextToast) {
64
- publish(nextToast);
65
- toasts.unshift(nextToast);
84
+ const toast = { ...nextToast, paused: paused || nextToast.paused };
85
+ toasts.unshift(toast);
86
+ publish(toast);
66
87
  }
67
88
  }
68
89
  };
69
90
  const create = (data) => {
70
91
  const id = data.id ?? `toast:${uuid()}`;
71
- const exists = toasts.find((toast) => toast.id === id);
72
- if (dismissedToasts.has(id)) dismissedToasts.delete(id);
92
+ const exists = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
93
+ if (closingToasts.has(id)) return id;
94
+ if (hasProp(data, "promise") && data.promise !== exists?.promise) promiseTokens.delete(id);
95
+ dismissedToasts.delete(id);
73
96
  if (exists) {
74
- toasts = toasts.map((toast) => {
75
- if (toast.id === id) {
76
- return publish({ ...toast, ...data, id });
77
- }
78
- return toast;
79
- });
97
+ const next = { ...exists, ...data, id };
98
+ if (toasts.some((toast) => toast.id === id)) {
99
+ toasts = toasts.map((toast) => toast.id === id ? next : toast);
100
+ publish(next);
101
+ } else {
102
+ toastQueue = toastQueue.map((toast) => toast.id === id ? next : toast);
103
+ }
80
104
  } else {
81
105
  const newToast = {
82
106
  id,
@@ -85,7 +109,8 @@ function createToastStore(props = {}) {
85
109
  type: DEFAULT_TYPE,
86
110
  ...data,
87
111
  stacked: !attrs.overlap,
88
- gap: attrs.gap
112
+ gap: attrs.gap,
113
+ paused
89
114
  };
90
115
  const priority = newToast.priority ?? getPriorityForType(newToast.type, !!newToast.action);
91
116
  addToast({ ...newToast, priority });
@@ -93,18 +118,19 @@ function createToastStore(props = {}) {
93
118
  return id;
94
119
  };
95
120
  const remove = (id) => {
96
- dismissedToasts.add(id);
97
- if (!id) {
98
- toasts.forEach((toast) => {
99
- subscribers.forEach((subscriber) => subscriber({ id: toast.id, dismiss: true }));
100
- });
101
- toasts = [];
102
- toastQueue = [];
103
- } else {
104
- subscribers.forEach((subscriber) => subscriber({ id, dismiss: true }));
105
- toasts = toasts.filter((toast) => toast.id !== id);
106
- processQueue();
107
- }
121
+ const removed = id == null ? [...toasts, ...toastQueue] : [...toasts, ...toastQueue].filter((toast) => toast.id === id);
122
+ const visible = id == null ? toasts : toasts.filter((toast) => toast.id === id);
123
+ removed.forEach((toast) => {
124
+ dismissedToasts.add(toast.id);
125
+ closingToasts.delete(toast.id);
126
+ promiseTokens.delete(toast.id);
127
+ });
128
+ if (id != null) dismissedToasts.add(id);
129
+ toasts = id == null ? [] : toasts.filter((toast) => toast.id !== id);
130
+ toastQueue = id == null ? [] : toastQueue.filter((toast) => toast.id !== id);
131
+ if (id != null && !visible.length) publish({ id, dismiss: true });
132
+ publish(...visible.map((toast) => ({ id: toast.id, dismiss: true })));
133
+ processQueue();
108
134
  return id;
109
135
  };
110
136
  const error = (data) => {
@@ -139,6 +165,10 @@ function createToastStore(props = {}) {
139
165
  promise: promise2,
140
166
  type: "loading"
141
167
  });
168
+ const token = /* @__PURE__ */ Symbol();
169
+ const current = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
170
+ if (current?.promise === promise2 && !closingToasts.has(id)) promiseTokens.set(id, token);
171
+ const isCurrent = () => promiseTokens.get(id) === token && !closingToasts.has(id);
142
172
  let removable = true;
143
173
  let result;
144
174
  const prom = runIfFn(promise2).then(async (response) => {
@@ -146,23 +176,24 @@ function createToastStore(props = {}) {
146
176
  if (isHttpResponse(response) && !response.ok) {
147
177
  removable = false;
148
178
  const errorOptions = runIfFn(options.error, `HTTP Error! status: ${response.status}`);
149
- create({ ...shared, ...errorOptions, id, type: "error" });
179
+ if (isCurrent()) create({ ...shared, ...errorOptions, id, type: "error" });
150
180
  } else if (options.success !== void 0) {
151
181
  removable = false;
152
182
  const successOptions = runIfFn(options.success, response);
153
- create({ ...shared, ...successOptions, id, type: successOptions.type ?? "success" });
183
+ if (isCurrent()) create({ ...shared, ...successOptions, id, type: successOptions.type ?? "success" });
154
184
  }
155
185
  }).catch(async (error2) => {
156
186
  result = ["reject", error2];
157
187
  if (options.error !== void 0) {
158
188
  removable = false;
159
189
  const errorOptions = runIfFn(options.error, error2);
160
- create({ ...shared, ...errorOptions, id, type: "error" });
190
+ if (isCurrent()) create({ ...shared, ...errorOptions, id, type: "error" });
161
191
  }
162
192
  }).finally(() => {
163
- if (removable) {
193
+ if (removable && isCurrent()) {
164
194
  remove(id);
165
195
  }
196
+ if (promiseTokens.get(id) === token) promiseTokens.delete(id);
166
197
  options.finally?.();
167
198
  });
168
199
  const unwrap = () => new Promise(
@@ -171,37 +202,46 @@ function createToastStore(props = {}) {
171
202
  return { id, unwrap };
172
203
  };
173
204
  const update = (id, data) => {
174
- return create({ id, ...data });
205
+ if (closingToasts.has(id)) return id;
206
+ if (typeof data !== "function") return create({ ...data, id });
207
+ const current = toasts.find((toast) => toast.id === id) ?? toastQueue.find((toast) => toast.id === id);
208
+ if (!current) return id;
209
+ const next = { ...data({ ...current }), id };
210
+ if (closingToasts.has(id)) return id;
211
+ if (toasts.some((toast) => toast.id === id)) return create(next);
212
+ if (!toastQueue.some((toast) => toast.id === id)) return id;
213
+ return create(next);
214
+ };
215
+ const changeToasts = (data, id) => {
216
+ const changed = [];
217
+ toasts = toasts.map((toast) => {
218
+ if (id != null && toast.id !== id || closingToasts.has(toast.id)) return toast;
219
+ const next = { ...toast, ...data };
220
+ changed.push(next);
221
+ return next;
222
+ });
223
+ toastQueue = toastQueue.map((toast) => id == null || toast.id === id ? { ...toast, ...data } : toast);
224
+ publish(...changed);
175
225
  };
176
226
  const pause = (id) => {
177
- if (id != null) {
178
- toasts = toasts.map((toast) => {
179
- if (toast.id === id) return publish({ ...toast, message: "PAUSE" });
180
- return toast;
181
- });
182
- } else {
183
- toasts = toasts.map((toast) => publish({ ...toast, message: "PAUSE" }));
184
- }
227
+ if (id == null) paused = true;
228
+ changeToasts({ paused: true }, id);
185
229
  };
186
230
  const resume = (id) => {
187
- if (id != null) {
188
- toasts = toasts.map((toast) => {
189
- if (toast.id === id) return publish({ ...toast, message: "RESUME" });
190
- return toast;
191
- });
192
- } else {
193
- toasts = toasts.map((toast) => publish({ ...toast, message: "RESUME" }));
194
- }
231
+ if (id == null) paused = false;
232
+ changeToasts({ paused: false }, id);
195
233
  };
196
234
  const dismiss = (id) => {
197
- if (id != null) {
198
- toasts = toasts.map((toast) => {
199
- if (toast.id === id) return publish({ ...toast, message: "DISMISS" });
200
- return toast;
201
- });
202
- } else {
203
- toasts = toasts.map((toast) => publish({ ...toast, message: "DISMISS" }));
204
- }
235
+ const queued = toastQueue.filter((toast) => id == null || toast.id === id);
236
+ toastQueue = toastQueue.filter((toast) => id != null && toast.id !== id);
237
+ queued.forEach((toast) => {
238
+ dismissedToasts.add(toast.id);
239
+ promiseTokens.delete(toast.id);
240
+ });
241
+ const changed = toasts.filter((toast) => (id == null || toast.id === id) && !closingToasts.has(toast.id));
242
+ changed.forEach((toast) => closingToasts.add(toast.id));
243
+ toasts = toasts.map((toast) => closingToasts.has(toast.id) ? { ...toast, message: "DISMISS" } : toast);
244
+ publish(...changed.map((toast) => ({ ...toast, message: "DISMISS" })));
205
245
  };
206
246
  const isVisible = (id) => {
207
247
  return !dismissedToasts.has(id) && !!toasts.find((toast) => toast.id === id);
@@ -209,12 +249,8 @@ function createToastStore(props = {}) {
209
249
  const isDismissed = (id) => {
210
250
  return dismissedToasts.has(id);
211
251
  };
212
- const expand = () => {
213
- toasts = toasts.map((toast) => publish({ ...toast, stacked: true }));
214
- };
215
- const collapse = () => {
216
- toasts = toasts.map((toast) => publish({ ...toast, stacked: false }));
217
- };
252
+ const expand = () => changeToasts({ stacked: true });
253
+ const collapse = () => changeToasts({ stacked: false });
218
254
  return {
219
255
  attrs,
220
256
  subscribe,
@@ -115,6 +115,10 @@ type ToastSchema<O = any> = {
115
115
  mounted: boolean;
116
116
  initialHeight: number;
117
117
  remainingTime: number;
118
+ rendered: {
119
+ title: boolean;
120
+ description: boolean;
121
+ };
118
122
  };
119
123
  computed: {
120
124
  height: number;
@@ -181,7 +185,7 @@ interface ToastStoreProps {
181
185
  removeDelay?: number | undefined;
182
186
  /**
183
187
  * Whether to pause toast when the user leaves the browser tab
184
- * @default false
188
+ * @default true
185
189
  */
186
190
  pauseOnPageIdle?: boolean | undefined;
187
191
  }
@@ -227,13 +231,16 @@ interface ToastStore<V = any> {
227
231
  */
228
232
  subscribe: (subscriber: (...args: any[]) => void) => VoidFunction;
229
233
  /**
230
- * Create a new toast with the given options
234
+ * Create a toast or update an existing toast with the same ID.
235
+ * Calls targeting closing toasts are ignored.
231
236
  */
232
237
  create: (data: Options<V>) => string;
233
238
  /**
234
- * Update an existing toast with new properties
239
+ * Update a toast with new properties or a function of its current properties.
240
+ * Updates to closing toasts are ignored.
241
+ * Functional updates are also ignored when the toast does not exist.
235
242
  */
236
- update: (id: string, data: Partial<ToastProps<V>>) => string;
243
+ update: (id: string, data: MaybeFunction<Partial<ToastProps<V>>, Partial<ToastProps<V>>>) => string;
237
244
  /**
238
245
  * Remove a toast by its ID
239
246
  */
@@ -115,6 +115,10 @@ type ToastSchema<O = any> = {
115
115
  mounted: boolean;
116
116
  initialHeight: number;
117
117
  remainingTime: number;
118
+ rendered: {
119
+ title: boolean;
120
+ description: boolean;
121
+ };
118
122
  };
119
123
  computed: {
120
124
  height: number;
@@ -181,7 +185,7 @@ interface ToastStoreProps {
181
185
  removeDelay?: number | undefined;
182
186
  /**
183
187
  * Whether to pause toast when the user leaves the browser tab
184
- * @default false
188
+ * @default true
185
189
  */
186
190
  pauseOnPageIdle?: boolean | undefined;
187
191
  }
@@ -227,13 +231,16 @@ interface ToastStore<V = any> {
227
231
  */
228
232
  subscribe: (subscriber: (...args: any[]) => void) => VoidFunction;
229
233
  /**
230
- * Create a new toast with the given options
234
+ * Create a toast or update an existing toast with the same ID.
235
+ * Calls targeting closing toasts are ignored.
231
236
  */
232
237
  create: (data: Options<V>) => string;
233
238
  /**
234
- * Update an existing toast with new properties
239
+ * Update a toast with new properties or a function of its current properties.
240
+ * Updates to closing toasts are ignored.
241
+ * Functional updates are also ignored when the toast does not exist.
235
242
  */
236
- update: (id: string, data: Partial<ToastProps<V>>) => string;
243
+ update: (id: string, data: MaybeFunction<Partial<ToastProps<V>>, Partial<ToastProps<V>>>) => string;
237
244
  /**
238
245
  * Remove a toast by its ID
239
246
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/toast",
3
- "version": "1.44.0",
3
+ "version": "1.46.0",
4
4
  "description": "Core logic for the toast widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -29,12 +29,12 @@
29
29
  "url": "https://github.com/chakra-ui/zag/issues"
30
30
  },
31
31
  "dependencies": {
32
- "@zag-js/anatomy": "1.44.0",
33
- "@zag-js/core": "1.44.0",
34
- "@zag-js/dom-query": "1.44.0",
35
- "@zag-js/dismissable": "1.44.0",
36
- "@zag-js/utils": "1.44.0",
37
- "@zag-js/types": "1.44.0"
32
+ "@zag-js/anatomy": "1.46.0",
33
+ "@zag-js/core": "1.46.0",
34
+ "@zag-js/dom-query": "1.46.0",
35
+ "@zag-js/dismissable": "1.46.0",
36
+ "@zag-js/utils": "1.46.0",
37
+ "@zag-js/types": "1.46.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "clean-package": "2.2.0"