@pixotope/toasts 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,438 @@
1
+ import { EventBus } from "@pixotope/event-bus";
2
+ import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/core.ts
6
+ const DEFAULT_TOAST_DURATION = 3e3;
7
+ let generatedIds = 0;
8
+ const createToastId = () => `px-toast-${Date.now().toString(36)}-${(generatedIds++).toString(36)}`;
9
+ const createToastCore = () => {
10
+ const bus = new EventBus();
11
+ const timers = /* @__PURE__ */ new Map();
12
+ const pendingRemovals = /* @__PURE__ */ new Map();
13
+ let toasts = [];
14
+ const setToasts = (next) => {
15
+ toasts = next;
16
+ bus.emit("toasts:change", toasts);
17
+ };
18
+ const clearTimer = (id) => {
19
+ const timer = timers.get(id);
20
+ if (timer?.handle) clearTimeout(timer.handle);
21
+ timers.delete(id);
22
+ };
23
+ const cancelRemoval = (id) => {
24
+ clearTimeout(pendingRemovals.get(id));
25
+ pendingRemovals.delete(id);
26
+ };
27
+ const remove = (id) => {
28
+ const removed = id === void 0 ? toasts : toasts.filter((t) => t.id === id);
29
+ if (!removed.length) return;
30
+ for (const toast of removed) {
31
+ clearTimer(toast.id);
32
+ cancelRemoval(toast.id);
33
+ }
34
+ setToasts(id === void 0 ? [] : toasts.filter((t) => t.id !== id));
35
+ };
36
+ const dismiss = (id) => {
37
+ const dismissed = toasts.filter((t) => t.visible && (id === void 0 || t.id === id));
38
+ if (!dismissed.length) return;
39
+ const dismissedIds = new Set(dismissed.map((t) => t.id));
40
+ for (const toastId of dismissedIds) {
41
+ clearTimer(toastId);
42
+ pendingRemovals.set(toastId, setTimeout(() => {
43
+ pendingRemovals.delete(toastId);
44
+ remove(toastId);
45
+ }, 300));
46
+ }
47
+ setToasts(toasts.map((t) => dismissedIds.has(t.id) ? {
48
+ ...t,
49
+ visible: false
50
+ } : t));
51
+ for (const toast of dismissed) toast.onClose?.(toast);
52
+ };
53
+ const upsert = ({ id, ...input }) => {
54
+ const now = Date.now();
55
+ if (!toasts.find((t) => t.id === id)) {
56
+ setToasts([...toasts, {
57
+ ...input,
58
+ id,
59
+ type: input.type ?? "warning",
60
+ count: 1,
61
+ createdAt: now,
62
+ updatedAt: now,
63
+ visible: true
64
+ }]);
65
+ return;
66
+ }
67
+ cancelRemoval(id);
68
+ clearTimer(id);
69
+ setToasts(toasts.map((t) => t.id === id ? {
70
+ ...t,
71
+ ...input,
72
+ type: input.type ?? t.type,
73
+ count: t.count + 1,
74
+ updatedAt: now,
75
+ visible: true
76
+ } : t));
77
+ };
78
+ bus.on("toast:upsert", upsert);
79
+ bus.on("toast:dismiss", dismiss);
80
+ bus.on("toast:remove", remove);
81
+ const runTimer = (id, timer) => {
82
+ timer.startedAt = Date.now();
83
+ timer.handle = setTimeout(() => {
84
+ timers.delete(id);
85
+ dismiss(id);
86
+ }, timer.remaining);
87
+ };
88
+ const pause = (id) => {
89
+ const timer = timers.get(id);
90
+ if (!timer?.handle) return;
91
+ clearTimeout(timer.handle);
92
+ timer.handle = void 0;
93
+ timer.remaining -= Date.now() - timer.startedAt;
94
+ };
95
+ const resume = (id) => {
96
+ const timer = timers.get(id);
97
+ if (!timer || timer.handle) return;
98
+ runTimer(id, timer);
99
+ };
100
+ return {
101
+ bus,
102
+ getToasts: () => toasts,
103
+ subscribe: (listener) => bus.on("toasts:change", listener),
104
+ startTimer: (toast, duration) => {
105
+ if (!toast.visible || !Number.isFinite(duration)) return;
106
+ if (timers.get(toast.id)?.version === toast.updatedAt) return;
107
+ clearTimer(toast.id);
108
+ const timer = {
109
+ handle: void 0,
110
+ remaining: duration,
111
+ startedAt: 0,
112
+ version: toast.updatedAt
113
+ };
114
+ timers.set(toast.id, timer);
115
+ runTimer(toast.id, timer);
116
+ },
117
+ pause,
118
+ resume,
119
+ pauseAll: () => timers.forEach((_, id) => pause(id)),
120
+ resumeAll: () => timers.forEach((_, id) => resume(id))
121
+ };
122
+ };
123
+ const coreKey = Symbol.for("@pixotope/toasts");
124
+ const getToastCore = () => globalThis[coreKey] ??= createToastCore();
125
+ //#endregion
126
+ //#region src/toast.ts
127
+ const show = (input) => {
128
+ const id = input.id ?? createToastId();
129
+ getToastCore().bus.emit("toast:upsert", {
130
+ ...input,
131
+ id
132
+ });
133
+ return id;
134
+ };
135
+ const shorthand = (type) => (title, options) => show({
136
+ ...options,
137
+ title,
138
+ type
139
+ });
140
+ /**
141
+ * Shows a toast and returns its id. Works outside React; a mounted
142
+ * `<Toaster>` renders it.
143
+ */
144
+ const toast = Object.assign(show, {
145
+ success: shorthand("success"),
146
+ error: shorthand("error"),
147
+ warning: shorthand("warning"),
148
+ info: shorthand("info"),
149
+ clean: shorthand("clean"),
150
+ insight: shorthand("insight"),
151
+ /** Plays the exit animation, then removes; omit the id to dismiss all. */
152
+ dismiss: (id) => getToastCore().bus.emit("toast:dismiss", id),
153
+ /** Removes immediately without calling `onClose`; omit the id to remove all. */
154
+ remove: (id) => getToastCore().bus.emit("toast:remove", id)
155
+ });
156
+ //#endregion
157
+ //#region src/Toaster.module.scss
158
+ var Toaster_module_default = { "toaster": "NAvrhW_toaster" };
159
+ //#endregion
160
+ //#region src/icons.tsx
161
+ const CloseIcon = (props) => /* @__PURE__ */ jsx("svg", {
162
+ viewBox: "0 0 16 16",
163
+ width: "16",
164
+ height: "16",
165
+ "aria-hidden": true,
166
+ ...props,
167
+ children: /* @__PURE__ */ jsx("path", {
168
+ fillRule: "evenodd",
169
+ clipRule: "evenodd",
170
+ d: "M11.6801 3L13 4.31994L9.31994 8L13 11.6801L11.6801 13L8 9.31994L4.31994 13L3 11.6801L6.68006 8L3 4.31994L4.31994 3L8 6.68006L11.6801 3Z"
171
+ })
172
+ });
173
+ const CopyIcon = (props) => /* @__PURE__ */ jsxs("svg", {
174
+ viewBox: "0 0 16 16",
175
+ width: "16",
176
+ height: "16",
177
+ "aria-hidden": true,
178
+ ...props,
179
+ children: [/* @__PURE__ */ jsx("path", {
180
+ fillRule: "evenodd",
181
+ clipRule: "evenodd",
182
+ d: "M1 10H1.87178e-06L0 2.00842C-2.58519e-07 0.904675 0.894148 0.00958455 1.99789 0.00842272L9.99956 0L10.0006 0.999999L1.99895 1.00842C1.44707 1.009 1 1.45655 1 2.00842L1 10Z"
183
+ }), /* @__PURE__ */ jsx("path", {
184
+ fillRule: "evenodd",
185
+ clipRule: "evenodd",
186
+ d: "M11.0001 3C11.1327 3 11.2599 3.05268 11.3536 3.14645L15.8536 7.64645C15.9474 7.74021 16.0001 7.86739 16.0001 8V14.5C16.0001 15.3284 15.3285 16 14.5001 16H4.50009C3.67166 16 3.00009 15.3284 3.00009 14.5V4.5C3.00009 3.67157 3.67166 3 4.50009 3H11.0001ZM10.0001 4H4.50009C4.22395 4 4.00009 4.22386 4.00009 4.5V14.5C4.00009 14.7761 4.22395 15 4.50009 15H14.5001C14.7762 15 15.0001 14.7761 15.0001 14.5V9.00129H11.0001C10.4478 9.00129 10.0001 8.55357 10.0001 8.00129V4ZM11.0001 4.20711V8.00129H14.7943L11.0001 4.20711Z"
187
+ })]
188
+ });
189
+ const InfoIcon = (props) => /* @__PURE__ */ jsx("svg", {
190
+ viewBox: "0 0 16 16",
191
+ width: "16",
192
+ height: "16",
193
+ "aria-hidden": true,
194
+ ...props,
195
+ children: /* @__PURE__ */ jsx("path", {
196
+ fillRule: "evenodd",
197
+ d: "M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm1-3V7H7v6h2zM8 5a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"
198
+ })
199
+ });
200
+ const WarningIcon = (props) => /* @__PURE__ */ jsx("svg", {
201
+ viewBox: "0 0 17 16",
202
+ width: "17",
203
+ height: "16",
204
+ "aria-hidden": true,
205
+ ...props,
206
+ children: /* @__PURE__ */ jsx("path", {
207
+ fillRule: "evenodd",
208
+ clipRule: "evenodd",
209
+ d: "M0.750968 16C0.193431 16 -0.169191 15.4133 0.0801473 14.9146L7.33015 0.41459C7.60654 -0.138197 8.3954 -0.138197 8.67179 0.41459L15.9218 14.9146C16.1711 15.4133 15.8085 16 15.251 16H0.750968ZM7.25097 6H8.75097V11H7.25097V6ZM8.75097 12.75C8.75097 13.1642 8.41518 13.5 8.00097 13.5C7.58675 13.5 7.25097 13.1642 7.25097 12.75C7.25097 12.3358 7.58675 12 8.00097 12C8.41518 12 8.75097 12.3358 8.75097 12.75Z"
210
+ })
211
+ });
212
+ //#endregion
213
+ //#region src/ToastNotification.module.scss
214
+ var ToastNotification_module_default = {
215
+ "action": "_2Xv1UW_action",
216
+ "buttons": "_2Xv1UW_buttons",
217
+ "content": "_2Xv1UW_content",
218
+ "copyButton": "_2Xv1UW_copyButton",
219
+ "description": "_2Xv1UW_description",
220
+ "enter": "_2Xv1UW_enter",
221
+ "exit": "_2Xv1UW_exit",
222
+ "icon": "_2Xv1UW_icon",
223
+ "iconButton": "_2Xv1UW_iconButton",
224
+ "note": "_2Xv1UW_note",
225
+ "subtitle": "_2Xv1UW_subtitle",
226
+ "time": "_2Xv1UW_time",
227
+ "timer": "_2Xv1UW_timer",
228
+ "title": "_2Xv1UW_title",
229
+ "toast": "_2Xv1UW_toast"
230
+ };
231
+ //#endregion
232
+ //#region src/ToastNotification.tsx
233
+ const joinClassNames = (...names) => names.filter(Boolean).join(" ");
234
+ /** The default Pixotope toast visual, also usable inside `renderToast`. */
235
+ const ToastNotification = ({ toast, onDismiss, className, onMouseDown, onMouseEnter, ...rest }) => {
236
+ const titleRef = useRef(null);
237
+ const subtitleRef = useRef(null);
238
+ const descriptionRef = useRef(null);
239
+ const [copied, setCopied] = useState(false);
240
+ const { type, icon, subtitle, description, note, action, showTime = true, timeLabel, dismissible = true, copyable = true, count, updatedAt, visible } = toast;
241
+ const duration = toast.duration ?? 3e3;
242
+ const formattedTime = new Date(updatedAt).toLocaleTimeString();
243
+ const copyToClipboard = () => {
244
+ const lines = [`${type}: ${titleRef.current?.innerText ?? ""}`];
245
+ if (subtitleRef.current) lines.push(subtitleRef.current.innerText);
246
+ if (descriptionRef.current?.innerText) lines.push(descriptionRef.current.innerText);
247
+ if (showTime) lines.push(` at ${formattedTime}`);
248
+ navigator.clipboard.writeText(lines.join("\n"));
249
+ setCopied(true);
250
+ };
251
+ const Icon = icon === "info" ? InfoIcon : icon === "warning" ? WarningIcon : null;
252
+ return /* @__PURE__ */ jsxs("div", {
253
+ role: type === "error" ? "alert" : "status",
254
+ className: joinClassNames(ToastNotification_module_default.toast, toast.className, className),
255
+ "data-type": type,
256
+ "data-visible": visible,
257
+ onMouseDown: (event) => {
258
+ if (event.button === 1 && dismissible) onDismiss?.();
259
+ onMouseDown?.(event);
260
+ },
261
+ onMouseEnter: (event) => {
262
+ setCopied(false);
263
+ onMouseEnter?.(event);
264
+ },
265
+ ...rest,
266
+ children: [
267
+ /* @__PURE__ */ jsxs("div", {
268
+ className: ToastNotification_module_default.buttons,
269
+ children: [copyable && /* @__PURE__ */ jsx("button", {
270
+ type: "button",
271
+ className: joinClassNames(ToastNotification_module_default.iconButton, ToastNotification_module_default.copyButton),
272
+ "data-copied": copied,
273
+ "aria-label": "Copy notification",
274
+ onClick: copyToClipboard,
275
+ children: copied ? "copied" : /* @__PURE__ */ jsx(CopyIcon, {})
276
+ }), dismissible && onDismiss && /* @__PURE__ */ jsx("button", {
277
+ type: "button",
278
+ className: ToastNotification_module_default.iconButton,
279
+ "aria-label": "Close notification",
280
+ onClick: onDismiss,
281
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
282
+ })]
283
+ }),
284
+ Icon && /* @__PURE__ */ jsx(Icon, { className: ToastNotification_module_default.icon }),
285
+ /* @__PURE__ */ jsxs("div", {
286
+ className: ToastNotification_module_default.content,
287
+ children: [
288
+ /* @__PURE__ */ jsx("h3", {
289
+ ref: titleRef,
290
+ className: ToastNotification_module_default.title,
291
+ children: toast.title
292
+ }),
293
+ subtitle && /* @__PURE__ */ jsx("div", {
294
+ ref: subtitleRef,
295
+ className: ToastNotification_module_default.subtitle,
296
+ children: subtitle
297
+ }),
298
+ /* @__PURE__ */ jsx("div", {
299
+ ref: descriptionRef,
300
+ className: ToastNotification_module_default.description,
301
+ children: description
302
+ }),
303
+ note && /* @__PURE__ */ jsx("div", {
304
+ className: ToastNotification_module_default.note,
305
+ children: note
306
+ }),
307
+ action && /* @__PURE__ */ jsx("button", {
308
+ type: "button",
309
+ className: ToastNotification_module_default.action,
310
+ onClick: () => action.onClick(toast),
311
+ children: action.label
312
+ }),
313
+ showTime && /* @__PURE__ */ jsxs("div", {
314
+ className: ToastNotification_module_default.time,
315
+ children: [/* @__PURE__ */ jsx("div", { children: timeLabel }), /* @__PURE__ */ jsxs("div", { children: [formattedTime, count > 1 && ` (${count})`] })]
316
+ })
317
+ ]
318
+ }),
319
+ visible && Number.isFinite(duration) && /* @__PURE__ */ jsx("span", {
320
+ className: ToastNotification_module_default.timer,
321
+ style: { animationDuration: `${duration}ms` },
322
+ "data-testid": "toast-timer"
323
+ }, updatedAt)
324
+ ]
325
+ });
326
+ };
327
+ //#endregion
328
+ //#region src/useToasts.ts
329
+ const withDefaults = (toast, defaults) => {
330
+ if (!defaults) return toast;
331
+ const merged = { ...toast };
332
+ for (const [key, value] of Object.entries(defaults)) if (merged[key] === void 0) merged[key] = value;
333
+ return merged;
334
+ };
335
+ /**
336
+ * Headless access to the toast store, for rendering toasts with your own
337
+ * components. Starts the auto-dismiss timer of every toast it returns.
338
+ */
339
+ const useToasts = ({ limit = Infinity, toastOptions } = {}) => {
340
+ const core = getToastCore();
341
+ const all = useSyncExternalStore(core.subscribe, core.getToasts, core.getToasts);
342
+ const toasts = useMemo(() => all.slice(0, limit).map((t) => withDefaults(t, toastOptions)), [
343
+ all,
344
+ limit,
345
+ toastOptions
346
+ ]);
347
+ useEffect(() => {
348
+ for (const toast of toasts) core.startTimer(toast, toast.duration ?? 3e3);
349
+ }, [core, toasts]);
350
+ return {
351
+ toasts,
352
+ dismiss: toast.dismiss,
353
+ remove: toast.remove,
354
+ pause: core.pause,
355
+ resume: core.resume,
356
+ pauseAll: core.pauseAll,
357
+ resumeAll: core.resumeAll
358
+ };
359
+ };
360
+ //#endregion
361
+ //#region src/Toaster.tsx
362
+ const DEFAULT_OFFSET = {
363
+ top: 56,
364
+ right: 14,
365
+ bottom: 14,
366
+ left: 14
367
+ };
368
+ const DEFAULT_TOAST_OPTIONS = { timeLabel: "Message received:" };
369
+ const placement = (position, offset) => {
370
+ const [vertical, horizontal] = position.split("-");
371
+ return {
372
+ [vertical]: offset[vertical],
373
+ ...horizontal === "center" ? { left: "50%" } : { [horizontal]: offset[horizontal] }
374
+ };
375
+ };
376
+ /** Renders toasts raised with `toast()`. Mount one per app. */
377
+ const Toaster = ({ position = "top-right", offset, limit = 5, zIndex = 9999, className, toastOptions, renderToast, container }) => {
378
+ const mergedOptions = useMemo(() => ({
379
+ ...DEFAULT_TOAST_OPTIONS,
380
+ ...toastOptions
381
+ }), [toastOptions]);
382
+ const { toasts, dismiss, pause, resume, pauseAll, resumeAll } = useToasts({
383
+ limit,
384
+ toastOptions: mergedOptions
385
+ });
386
+ const [windowFocused, setWindowFocused] = useState(true);
387
+ useEffect(() => {
388
+ const onBlur = () => {
389
+ pauseAll();
390
+ setWindowFocused(false);
391
+ };
392
+ const onFocus = () => {
393
+ resumeAll();
394
+ setWindowFocused(true);
395
+ };
396
+ window.addEventListener("blur", onBlur);
397
+ window.addEventListener("focus", onFocus);
398
+ return () => {
399
+ window.removeEventListener("blur", onBlur);
400
+ window.removeEventListener("focus", onFocus);
401
+ };
402
+ }, [pauseAll, resumeAll]);
403
+ const target = container ?? (typeof document === "undefined" ? null : document.body);
404
+ if (!target) return null;
405
+ return createPortal(/* @__PURE__ */ jsx("section", {
406
+ "aria-live": "polite",
407
+ "aria-label": "Notifications",
408
+ className: [Toaster_module_default.toaster, className].filter(Boolean).join(" "),
409
+ "data-position": position,
410
+ "data-paused": !windowFocused,
411
+ style: {
412
+ ...placement(position, {
413
+ ...DEFAULT_OFFSET,
414
+ ...offset
415
+ }),
416
+ zIndex
417
+ },
418
+ children: toasts.map((toast) => {
419
+ const helpers = {
420
+ dismiss: () => dismiss(toast.id),
421
+ pause: () => pause(toast.id),
422
+ resume: () => resume(toast.id)
423
+ };
424
+ return /* @__PURE__ */ jsx("div", {
425
+ onMouseEnter: helpers.pause,
426
+ onMouseLeave: helpers.resume,
427
+ children: renderToast ? renderToast(toast, helpers) : /* @__PURE__ */ jsx(ToastNotification, {
428
+ toast,
429
+ onDismiss: helpers.dismiss
430
+ })
431
+ }, toast.id);
432
+ })
433
+ }), target);
434
+ };
435
+ //#endregion
436
+ export { DEFAULT_TOAST_DURATION, ToastNotification, Toaster, toast, useToasts };
437
+
438
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["styles","toastApi","styles"],"sources":["../src/core.ts","../src/toast.ts","../src/Toaster.module.scss","../src/icons.tsx","../src/ToastNotification.module.scss","../src/ToastNotification.tsx","../src/useToasts.ts","../src/Toaster.tsx"],"sourcesContent":["import { EventBus } from '@pixotope/event-bus';\nimport type { Toast, ToastInput } from './types';\n\nexport const DEFAULT_TOAST_DURATION = 3000;\n/** Matches the exit animation length in ToastNotification.module.scss. */\nexport const TOAST_EXIT_DURATION = 300;\n\ntype ToastEvents = {\n 'toast:upsert': ToastInput & { id: string };\n 'toast:dismiss': string | undefined;\n 'toast:remove': string | undefined;\n 'toasts:change': readonly Toast[];\n};\n\ninterface ToastTimer {\n handle: ReturnType<typeof setTimeout> | undefined;\n remaining: number;\n startedAt: number;\n /** The `updatedAt` the timer was started for; an update restarts it. */\n version: number;\n}\n\nexport interface ToastCore {\n bus: EventBus<ToastEvents>;\n getToasts: () => readonly Toast[];\n subscribe: (listener: () => void) => () => void;\n startTimer: (toast: Toast, duration: number) => void;\n pause: (id: string) => void;\n resume: (id: string) => void;\n pauseAll: () => void;\n resumeAll: () => void;\n}\n\nlet generatedIds = 0;\nexport const createToastId = () =>\n `px-toast-${Date.now().toString(36)}-${(generatedIds++).toString(36)}`;\n\nexport const createToastCore = (): ToastCore => {\n const bus = new EventBus<ToastEvents>();\n const timers = new Map<string, ToastTimer>();\n const pendingRemovals = new Map<string, ReturnType<typeof setTimeout>>();\n let toasts: readonly Toast[] = [];\n\n const setToasts = (next: readonly Toast[]) => {\n toasts = next;\n bus.emit('toasts:change', toasts);\n };\n\n const clearTimer = (id: string) => {\n const timer = timers.get(id);\n if (timer?.handle) clearTimeout(timer.handle);\n timers.delete(id);\n };\n\n const cancelRemoval = (id: string) => {\n clearTimeout(pendingRemovals.get(id));\n pendingRemovals.delete(id);\n };\n\n const remove = (id: string | undefined) => {\n const removed =\n id === undefined ? toasts : toasts.filter((t) => t.id === id);\n if (!removed.length) return;\n for (const toast of removed) {\n clearTimer(toast.id);\n cancelRemoval(toast.id);\n }\n setToasts(id === undefined ? [] : toasts.filter((t) => t.id !== id));\n };\n\n const dismiss = (id: string | undefined) => {\n const dismissed = toasts.filter(\n (t) => t.visible && (id === undefined || t.id === id)\n );\n if (!dismissed.length) return;\n const dismissedIds = new Set(dismissed.map((t) => t.id));\n for (const toastId of dismissedIds) {\n clearTimer(toastId);\n pendingRemovals.set(\n toastId,\n setTimeout(() => {\n pendingRemovals.delete(toastId);\n remove(toastId);\n }, TOAST_EXIT_DURATION)\n );\n }\n setToasts(\n toasts.map((t) => (dismissedIds.has(t.id) ? { ...t, visible: false } : t))\n );\n for (const toast of dismissed) toast.onClose?.(toast);\n };\n\n const upsert = ({ id, ...input }: ToastInput & { id: string }) => {\n const now = Date.now();\n const existing = toasts.find((t) => t.id === id);\n if (!existing) {\n setToasts([\n ...toasts,\n {\n ...input,\n id,\n type: input.type ?? 'warning',\n count: 1,\n createdAt: now,\n updatedAt: now,\n visible: true,\n },\n ]);\n return;\n }\n cancelRemoval(id);\n clearTimer(id);\n setToasts(\n toasts.map((t) =>\n t.id === id\n ? {\n ...t,\n ...input,\n type: input.type ?? t.type,\n count: t.count + 1,\n updatedAt: now,\n visible: true,\n }\n : t\n )\n );\n };\n\n bus.on('toast:upsert', upsert);\n bus.on('toast:dismiss', dismiss);\n bus.on('toast:remove', remove);\n\n const runTimer = (id: string, timer: ToastTimer) => {\n timer.startedAt = Date.now();\n timer.handle = setTimeout(() => {\n timers.delete(id);\n dismiss(id);\n }, timer.remaining);\n };\n\n const pause = (id: string) => {\n const timer = timers.get(id);\n if (!timer?.handle) return;\n clearTimeout(timer.handle);\n timer.handle = undefined;\n timer.remaining -= Date.now() - timer.startedAt;\n };\n\n const resume = (id: string) => {\n const timer = timers.get(id);\n if (!timer || timer.handle) return;\n runTimer(id, timer);\n };\n\n return {\n bus,\n getToasts: () => toasts,\n subscribe: (listener) => bus.on('toasts:change', listener),\n startTimer: (toast, duration) => {\n if (!toast.visible || !Number.isFinite(duration)) return;\n if (timers.get(toast.id)?.version === toast.updatedAt) return;\n clearTimer(toast.id);\n const timer: ToastTimer = {\n handle: undefined,\n remaining: duration,\n startedAt: 0,\n version: toast.updatedAt,\n };\n timers.set(toast.id, timer);\n runTimer(toast.id, timer);\n },\n pause,\n resume,\n pauseAll: () => timers.forEach((_, id) => pause(id)),\n resumeAll: () => timers.forEach((_, id) => resume(id)),\n };\n};\n\n// Kept on globalThis so toasts raised from a second bundled copy of this\n// package still reach the <Toaster> rendered by the first.\nconst coreKey = Symbol.for('@pixotope/toasts');\ntype GlobalWithCore = typeof globalThis & { [coreKey]?: ToastCore };\n\nexport const getToastCore = (): ToastCore =>\n ((globalThis as GlobalWithCore)[coreKey] ??= createToastCore());\n","import { createToastId, getToastCore } from './core';\nimport type { ToastInput, ToastOptions, ToastType } from './types';\n\nexport type ToastShorthand = (\n title: ToastInput['title'],\n options?: Omit<ToastOptions, 'type'>\n) => string;\n\nconst show = (input: ToastInput): string => {\n const id = input.id ?? createToastId();\n getToastCore().bus.emit('toast:upsert', { ...input, id });\n return id;\n};\n\nconst shorthand =\n (type: ToastType): ToastShorthand =>\n (title, options) =>\n show({ ...options, title, type });\n\n/**\n * Shows a toast and returns its id. Works outside React; a mounted\n * `<Toaster>` renders it.\n */\nexport const toast = Object.assign(show, {\n success: shorthand('success'),\n error: shorthand('error'),\n warning: shorthand('warning'),\n info: shorthand('info'),\n clean: shorthand('clean'),\n insight: shorthand('insight'),\n /** Plays the exit animation, then removes; omit the id to dismiss all. */\n dismiss: (id?: string) => getToastCore().bus.emit('toast:dismiss', id),\n /** Removes immediately without calling `onClose`; omit the id to remove all. */\n remove: (id?: string) => getToastCore().bus.emit('toast:remove', id),\n});\n",".toaster {\n position: fixed;\n display: flex;\n flex-direction: column;\n gap: 4px;\n pointer-events: none;\n}\n.toaster > * {\n pointer-events: auto;\n}\n.toaster[data-position$=-right] {\n align-items: flex-end;\n --px-toast-enter-transform: translateX(100%);\n}\n.toaster[data-position$=-left] {\n align-items: flex-start;\n --px-toast-enter-transform: translateX(-100%);\n}\n.toaster[data-position$=-center] {\n align-items: center;\n transform: translateX(-50%);\n}\n.toaster[data-position=top-center] {\n --px-toast-enter-transform: translateY(-100%);\n}\n.toaster[data-position=bottom-center] {\n --px-toast-enter-transform: translateY(100%);\n}","import type { SVGProps } from 'react';\n\ntype IconProps = SVGProps<SVGSVGElement>;\n\nexport const CloseIcon = (props: IconProps) => (\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden {...props}>\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M11.6801 3L13 4.31994L9.31994 8L13 11.6801L11.6801 13L8 9.31994L4.31994 13L3 11.6801L6.68006 8L3 4.31994L4.31994 3L8 6.68006L11.6801 3Z\"\n />\n </svg>\n);\n\nexport const CopyIcon = (props: IconProps) => (\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden {...props}>\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M1 10H1.87178e-06L0 2.00842C-2.58519e-07 0.904675 0.894148 0.00958455 1.99789 0.00842272L9.99956 0L10.0006 0.999999L1.99895 1.00842C1.44707 1.009 1 1.45655 1 2.00842L1 10Z\"\n />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M11.0001 3C11.1327 3 11.2599 3.05268 11.3536 3.14645L15.8536 7.64645C15.9474 7.74021 16.0001 7.86739 16.0001 8V14.5C16.0001 15.3284 15.3285 16 14.5001 16H4.50009C3.67166 16 3.00009 15.3284 3.00009 14.5V4.5C3.00009 3.67157 3.67166 3 4.50009 3H11.0001ZM10.0001 4H4.50009C4.22395 4 4.00009 4.22386 4.00009 4.5V14.5C4.00009 14.7761 4.22395 15 4.50009 15H14.5001C14.7762 15 15.0001 14.7761 15.0001 14.5V9.00129H11.0001C10.4478 9.00129 10.0001 8.55357 10.0001 8.00129V4ZM11.0001 4.20711V8.00129H14.7943L11.0001 4.20711Z\"\n />\n </svg>\n);\n\nexport const InfoIcon = (props: IconProps) => (\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden {...props}>\n <path\n fillRule=\"evenodd\"\n d=\"M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm1-3V7H7v6h2zM8 5a1 1 0 1 0 0-2 1 1 0 0 0 0 2z\"\n />\n </svg>\n);\n\nexport const WarningIcon = (props: IconProps) => (\n <svg viewBox=\"0 0 17 16\" width=\"17\" height=\"16\" aria-hidden {...props}>\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M0.750968 16C0.193431 16 -0.169191 15.4133 0.0801473 14.9146L7.33015 0.41459C7.60654 -0.138197 8.3954 -0.138197 8.67179 0.41459L15.9218 14.9146C16.1711 15.4133 15.8085 16 15.251 16H0.750968ZM7.25097 6H8.75097V11H7.25097V6ZM8.75097 12.75C8.75097 13.1642 8.41518 13.5 8.00097 13.5C7.58675 13.5 7.25097 13.1642 7.25097 12.75C7.25097 12.3358 7.58675 12 8.00097 12C8.41518 12 8.75097 12.3358 8.75097 12.75Z\"\n />\n </svg>\n);\n","@keyframes enter {\n from {\n transform: var(--px-toast-enter-transform, translateX(100%));\n opacity: 0;\n }\n to {\n transform: none;\n opacity: 1;\n }\n}\n@keyframes exit {\n from {\n transform: none;\n opacity: 1;\n }\n to {\n transform: var(--px-toast-enter-transform, translateX(100%));\n opacity: 0;\n }\n}\n@keyframes timer {\n from {\n width: 100%;\n }\n to {\n width: 0;\n }\n}\n.toast {\n --px-toast-accent: var(--support-03);\n position: relative;\n display: flex;\n gap: 8px;\n width: 320px;\n padding: 12px 24px 12px 20px;\n border: 0 solid var(--px-toast-accent);\n border-left-width: 5px;\n background-color: var(--ui-02);\n user-select: text;\n animation: enter 0.3s ease-in-out;\n}\n.toast[data-type=info] {\n --px-toast-accent: var(--support-04);\n}\n.toast[data-type=error] {\n --px-toast-accent: var(--interactive-07);\n}\n.toast[data-type=success] {\n --px-toast-accent: var(--support-12);\n}\n.toast[data-type=insight] {\n --px-toast-accent: var(--support-13);\n}\n.toast[data-type=clean] {\n --px-toast-accent: transparent;\n}\n.toast[data-visible=false] {\n animation: exit 0.3s ease-in-out forwards;\n}\n\n.timer {\n position: absolute;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 4px;\n background: var(--text-01);\n opacity: 0.3;\n animation: timer linear 1 forwards;\n}\n.toast:hover .timer, [data-paused=true] .timer {\n animation-play-state: paused;\n}\n\n.icon {\n flex-shrink: 0;\n fill: var(--px-toast-accent);\n}\n\n.buttons {\n position: absolute;\n top: 12px;\n right: 0;\n display: flex;\n gap: 8px;\n margin-right: 14px;\n}\n\n.iconButton {\n display: inline-flex;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--text-01);\n font-size: 12px;\n cursor: pointer;\n}\n.iconButton svg {\n fill: currentColor;\n}\n.iconButton:focus-visible {\n outline: 1px solid var(--interactive-01);\n}\n\n.copyButton {\n opacity: 1;\n transition: opacity 0.2s ease-in-out;\n}\n.copyButton svg {\n opacity: 0;\n transition: opacity 0.2s ease-in-out;\n}\n.toast:hover .copyButton svg, .copyButton:focus-visible svg {\n opacity: 1;\n}\n.copyButton[data-copied=true] {\n opacity: 0;\n transition-duration: 1s;\n}\n\n.content {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n width: 100%;\n color: var(--text-01);\n}\n\n.title {\n margin: 0 0 4px;\n padding-bottom: 4px;\n color: var(--text-01);\n font-size: 16px;\n font-weight: 600;\n line-height: 22px;\n letter-spacing: 0.16px;\n text-align: left;\n}\n\n.subtitle {\n margin-top: -4px;\n margin-bottom: 8px;\n color: var(--text-03);\n font-size: 12px;\n font-weight: 400;\n line-height: 16px;\n letter-spacing: 0.32px;\n}\n\n.description {\n margin-bottom: 8px;\n color: var(--text-01);\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n letter-spacing: 0.16px;\n text-align: left;\n overflow-wrap: anywhere;\n}\n\n.note {\n align-self: stretch;\n margin-bottom: 8px;\n padding: 4px 8px;\n border-radius: 2px;\n background-color: var(--faded-light-green-2);\n color: var(--text-01);\n font-family: \"IBM Plex Mono\", monospace;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: 8px;\n padding: 10px 22px;\n border: 2px solid var(--interactive-01);\n background: var(--interactive-01);\n color: var(--ui-01);\n font: inherit;\n font-size: 14px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n cursor: pointer;\n transition: opacity 0.3s ease, background 0.3s ease;\n}\n.action:hover {\n opacity: 0.9;\n}\n.action:active {\n opacity: 0.8;\n}\n.action:focus {\n outline: none;\n}\n.action:focus-visible {\n outline: 2px solid var(--interactive-03-hover);\n}\n\n.time {\n display: flex;\n justify-content: space-between;\n width: 100%;\n color: var(--text-03);\n font-size: 12px;\n font-weight: 400;\n line-height: 16px;\n}","import { useRef, useState, type HTMLAttributes } from 'react';\nimport { CloseIcon, CopyIcon, InfoIcon, WarningIcon } from './icons';\nimport styles from './ToastNotification.module.scss';\nimport type { Toast } from './types';\nimport { DEFAULT_TOAST_DURATION } from './core';\n\nexport interface ToastNotificationProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n toast: Toast;\n onDismiss?: () => void;\n}\n\nconst joinClassNames = (...names: (string | false | undefined)[]) =>\n names.filter(Boolean).join(' ');\n\n/** The default Pixotope toast visual, also usable inside `renderToast`. */\nexport const ToastNotification = ({\n toast,\n onDismiss,\n className,\n onMouseDown,\n onMouseEnter,\n ...rest\n}: ToastNotificationProps) => {\n const titleRef = useRef<HTMLHeadingElement>(null);\n const subtitleRef = useRef<HTMLDivElement>(null);\n const descriptionRef = useRef<HTMLDivElement>(null);\n const [copied, setCopied] = useState(false);\n\n const {\n type,\n icon,\n subtitle,\n description,\n note,\n action,\n showTime = true,\n timeLabel,\n dismissible = true,\n copyable = true,\n count,\n updatedAt,\n visible,\n } = toast;\n const duration = toast.duration ?? DEFAULT_TOAST_DURATION;\n const formattedTime = new Date(updatedAt).toLocaleTimeString();\n\n const copyToClipboard = () => {\n const lines = [`${type}: ${titleRef.current?.innerText ?? ''}`];\n if (subtitleRef.current) lines.push(subtitleRef.current.innerText);\n if (descriptionRef.current?.innerText) {\n lines.push(descriptionRef.current.innerText);\n }\n if (showTime) lines.push(` at ${formattedTime}`);\n void navigator.clipboard.writeText(lines.join('\\n'));\n setCopied(true);\n };\n\n const Icon =\n icon === 'info' ? InfoIcon : icon === 'warning' ? WarningIcon : null;\n\n return (\n <div\n role={type === 'error' ? 'alert' : 'status'}\n className={joinClassNames(styles.toast, toast.className, className)}\n data-type={type}\n data-visible={visible}\n onMouseDown={(event) => {\n if (event.button === 1 && dismissible) onDismiss?.();\n onMouseDown?.(event);\n }}\n onMouseEnter={(event) => {\n setCopied(false);\n onMouseEnter?.(event);\n }}\n {...rest}\n >\n <div className={styles.buttons}>\n {copyable && (\n <button\n type=\"button\"\n className={joinClassNames(styles.iconButton, styles.copyButton)}\n data-copied={copied}\n aria-label=\"Copy notification\"\n onClick={copyToClipboard}\n >\n {copied ? 'copied' : <CopyIcon />}\n </button>\n )}\n {dismissible && onDismiss && (\n <button\n type=\"button\"\n className={styles.iconButton}\n aria-label=\"Close notification\"\n onClick={onDismiss}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n {Icon && <Icon className={styles.icon} />}\n <div className={styles.content}>\n <h3 ref={titleRef} className={styles.title}>\n {toast.title}\n </h3>\n {subtitle && (\n <div ref={subtitleRef} className={styles.subtitle}>\n {subtitle}\n </div>\n )}\n <div ref={descriptionRef} className={styles.description}>\n {description}\n </div>\n {note && <div className={styles.note}>{note}</div>}\n {action && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => action.onClick(toast)}\n >\n {action.label}\n </button>\n )}\n {showTime && (\n <div className={styles.time}>\n <div>{timeLabel}</div>\n <div>\n {formattedTime}\n {count > 1 && ` (${count})`}\n </div>\n </div>\n )}\n </div>\n {visible && Number.isFinite(duration) && (\n <span\n key={updatedAt}\n className={styles.timer}\n style={{ animationDuration: `${duration}ms` }}\n data-testid=\"toast-timer\"\n />\n )}\n </div>\n );\n};\n","import { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { DEFAULT_TOAST_DURATION, getToastCore } from './core';\nimport { toast as toastApi } from './toast';\nimport type { Toast, ToastOptions } from './types';\n\nexport type ToastDefaults = Omit<ToastOptions, 'id'>;\n\nexport interface UseToastsOptions {\n /** Most toasts returned at once; the rest wait, and their timers do not run yet. */\n limit?: number;\n /** Defaults for fields a toast leaves undefined. */\n toastOptions?: ToastDefaults;\n}\n\nexport interface UseToastsResult {\n toasts: Toast[];\n dismiss: (id?: string) => void;\n remove: (id?: string) => void;\n pause: (id: string) => void;\n resume: (id: string) => void;\n pauseAll: () => void;\n resumeAll: () => void;\n}\n\nconst withDefaults = (toast: Toast, defaults: ToastDefaults | undefined) => {\n if (!defaults) return toast;\n const merged: Record<string, unknown> = { ...toast };\n for (const [key, value] of Object.entries(defaults)) {\n if (merged[key] === undefined) merged[key] = value;\n }\n return merged as unknown as Toast;\n};\n\n/**\n * Headless access to the toast store, for rendering toasts with your own\n * components. Starts the auto-dismiss timer of every toast it returns.\n */\nexport const useToasts = ({\n limit = Infinity,\n toastOptions,\n}: UseToastsOptions = {}): UseToastsResult => {\n const core = getToastCore();\n const all = useSyncExternalStore(\n core.subscribe,\n core.getToasts,\n core.getToasts\n );\n\n const toasts = useMemo(\n () => all.slice(0, limit).map((t) => withDefaults(t, toastOptions)),\n [all, limit, toastOptions]\n );\n\n useEffect(() => {\n for (const toast of toasts) {\n core.startTimer(toast, toast.duration ?? DEFAULT_TOAST_DURATION);\n }\n }, [core, toasts]);\n\n return {\n toasts,\n dismiss: toastApi.dismiss,\n remove: toastApi.remove,\n pause: core.pause,\n resume: core.resume,\n pauseAll: core.pauseAll,\n resumeAll: core.resumeAll,\n };\n};\n","import {\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport styles from './Toaster.module.scss';\nimport { ToastNotification } from './ToastNotification';\nimport type { Toast, ToastPosition } from './types';\nimport { useToasts, type ToastDefaults } from './useToasts';\n\nexport interface ToastOffset {\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}\n\nexport interface RenderToastHelpers {\n dismiss: () => void;\n pause: () => void;\n resume: () => void;\n}\n\nexport interface ToasterProps {\n position?: ToastPosition;\n /** Distance in px from the viewport edges the position touches. */\n offset?: ToastOffset;\n /** Most toasts shown at once; later ones wait for a free slot. */\n limit?: number;\n zIndex?: number;\n className?: string;\n /** Defaults for fields a toast leaves undefined. */\n toastOptions?: ToastDefaults;\n /** Replaces the default visual; render `ToastNotification` to wrap it. */\n renderToast?: (toast: Toast, helpers: RenderToastHelpers) => ReactNode;\n /** Portal target; defaults to `document.body`. */\n container?: Element | null;\n}\n\nconst DEFAULT_OFFSET: Required<ToastOffset> = {\n top: 56,\n right: 14,\n bottom: 14,\n left: 14,\n};\n\nconst DEFAULT_TOAST_OPTIONS: ToastDefaults = {\n timeLabel: 'Message received:',\n};\n\nconst placement = (\n position: ToastPosition,\n offset: Required<ToastOffset>\n): CSSProperties => {\n const [vertical, horizontal] = position.split('-') as [\n 'top' | 'bottom',\n 'left' | 'center' | 'right',\n ];\n return {\n [vertical]: offset[vertical],\n ...(horizontal === 'center'\n ? { left: '50%' }\n : { [horizontal]: offset[horizontal] }),\n };\n};\n\n/** Renders toasts raised with `toast()`. Mount one per app. */\nexport const Toaster = ({\n position = 'top-right',\n offset,\n limit = 5,\n zIndex = 9999,\n className,\n toastOptions,\n renderToast,\n container,\n}: ToasterProps) => {\n const mergedOptions = useMemo(\n () => ({ ...DEFAULT_TOAST_OPTIONS, ...toastOptions }),\n [toastOptions]\n );\n\n const { toasts, dismiss, pause, resume, pauseAll, resumeAll } = useToasts({\n limit,\n toastOptions: mergedOptions,\n });\n const [windowFocused, setWindowFocused] = useState(true);\n\n // Timers stop while the window is in the background, as in ToastsPortal\n useEffect(() => {\n const onBlur = () => {\n pauseAll();\n setWindowFocused(false);\n };\n const onFocus = () => {\n resumeAll();\n setWindowFocused(true);\n };\n window.addEventListener('blur', onBlur);\n window.addEventListener('focus', onFocus);\n return () => {\n window.removeEventListener('blur', onBlur);\n window.removeEventListener('focus', onFocus);\n };\n }, [pauseAll, resumeAll]);\n\n const target =\n container ?? (typeof document === 'undefined' ? null : document.body);\n if (!target) return null;\n\n return createPortal(\n <section\n aria-live=\"polite\"\n aria-label=\"Notifications\"\n className={[styles.toaster, className].filter(Boolean).join(' ')}\n data-position={position}\n data-paused={!windowFocused}\n style={{\n ...placement(position, { ...DEFAULT_OFFSET, ...offset }),\n zIndex,\n }}\n >\n {toasts.map((toast) => {\n const helpers: RenderToastHelpers = {\n dismiss: () => dismiss(toast.id),\n pause: () => pause(toast.id),\n resume: () => resume(toast.id),\n };\n return (\n <div\n key={toast.id}\n onMouseEnter={helpers.pause}\n onMouseLeave={helpers.resume}\n >\n {renderToast ? (\n renderToast(toast, helpers)\n ) : (\n <ToastNotification toast={toast} onDismiss={helpers.dismiss} />\n )}\n </div>\n );\n })}\n </section>,\n target\n );\n};\n"],"mappings":"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"}