your-toast 0.1.1 → 0.1.2

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.cjs CHANGED
@@ -18,88 +18,148 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
23
  YourToastProvider: () => YourToastProvider,
24
24
  toast: () => toast
25
25
  });
26
- module.exports = __toCommonJS(index_exports);
26
+ module.exports = __toCommonJS(src_exports);
27
27
 
28
28
  // src/store/toastStore.ts
29
29
  var toasts = [];
30
- var listeners = [];
31
- function notify() {
32
- listeners.forEach((listener) => listener(toasts));
30
+ var listeners = /* @__PURE__ */ new Set();
31
+ function emit() {
32
+ listeners.forEach((listener) => listener());
33
33
  }
34
34
  var toastStore = {
35
35
  subscribe(listener) {
36
- listeners.push(listener);
36
+ listeners.add(listener);
37
37
  return () => {
38
- listeners = listeners.filter((l) => l !== listener);
38
+ listeners.delete(listener);
39
39
  };
40
40
  },
41
- getToasts() {
41
+ getSnapshot() {
42
42
  return toasts;
43
43
  },
44
+ getServerSnapshot() {
45
+ return [];
46
+ },
44
47
  add(toast2) {
45
48
  toasts = [toast2, ...toasts].slice(0, 5);
46
- notify();
49
+ emit();
47
50
  },
48
51
  remove(id) {
49
- toasts = toasts.filter((t) => t.id !== id);
50
- notify();
52
+ toasts = toasts.filter((toast2) => toast2.id !== id);
53
+ emit();
51
54
  },
52
55
  update(id, updated) {
53
56
  toasts = toasts.map(
54
- (t) => t.id === id ? { ...t, ...updated } : t
57
+ (toast2) => toast2.id === id ? { ...toast2, ...updated } : toast2
55
58
  );
56
- notify();
59
+ emit();
60
+ },
61
+ clear() {
62
+ toasts = [];
63
+ emit();
64
+ },
65
+ getToasts() {
66
+ return toasts;
57
67
  }
58
68
  };
59
69
 
60
70
  // src/core/toast.ts
61
- function createToast(message, type = "default") {
62
- const id = crypto.randomUUID();
63
- const duration = 4e3;
71
+ function isBrowser() {
72
+ return typeof window !== "undefined";
73
+ }
74
+ function generateId() {
75
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
76
+ return crypto.randomUUID();
77
+ }
78
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
79
+ }
80
+ function createToast(message, type = "default", options = {}) {
81
+ if (!isBrowser()) {
82
+ return "";
83
+ }
84
+ const id = generateId();
85
+ const duration = options.duration ?? 4e3;
64
86
  toastStore.add({
65
87
  id,
66
88
  title: message,
67
89
  type,
68
- duration
90
+ duration,
91
+ description: options.description,
92
+ action: options.action
69
93
  });
70
- setTimeout(() => {
71
- toastStore.remove(id);
72
- }, duration);
94
+ if (duration !== Infinity && duration > 0) {
95
+ setTimeout(() => {
96
+ toastStore.remove(id);
97
+ }, duration);
98
+ }
73
99
  return id;
74
100
  }
101
+ function createTypedToast(type, message, options) {
102
+ return createToast(message, type, options);
103
+ }
75
104
  var toast = Object.assign(
76
- (message) => createToast(message, "default"),
105
+ (message, options) => createToast(message, "default", options),
77
106
  {
78
- success: (message) => createToast(message, "success"),
79
- error: (message) => createToast(message, "error"),
80
- loading: (message) => createToast(message, "loading"),
81
- action: (message) => createToast(message, "action"),
82
- dismiss: (id) => {
83
- toastStore.remove(id);
107
+ success: (message, options) => createTypedToast("success", message, options),
108
+ error: (message, options) => createTypedToast("error", message, options),
109
+ warning: (message, options) => createTypedToast("action", message, options),
110
+ info: (message, options) => createTypedToast("default", message, options),
111
+ loading: (message, options) => createTypedToast("loading", message, {
112
+ duration: Infinity,
113
+ ...options
114
+ }),
115
+ action: (message, options) => createTypedToast("action", message, options),
116
+ dismiss(id) {
117
+ if (!isBrowser()) return;
118
+ if (id) {
119
+ toastStore.remove(id);
120
+ } else {
121
+ toastStore.clear();
122
+ }
84
123
  },
85
- update: (id, data) => {
124
+ update(id, data) {
125
+ if (!isBrowser()) return;
86
126
  toastStore.update(id, data);
87
127
  },
88
- promise: async (promise, messages) => {
89
- const id = createToast(messages.loading, "loading");
128
+ async promise(promise, messages) {
129
+ const id = createToast(
130
+ messages.loading,
131
+ "loading",
132
+ {
133
+ duration: Infinity
134
+ }
135
+ );
90
136
  try {
91
137
  const result = await promise;
92
- toastStore.update(id, {
93
- title: messages.success,
94
- type: "success"
95
- });
138
+ const successMessage = typeof messages.success === "function" ? messages.success(result) : messages.success;
139
+ if (id) {
140
+ toastStore.update(id, {
141
+ title: successMessage,
142
+ type: "success",
143
+ duration: 4e3
144
+ });
145
+ setTimeout(() => {
146
+ toastStore.remove(id);
147
+ }, 4e3);
148
+ }
96
149
  return result;
97
- } catch (err) {
98
- toastStore.update(id, {
99
- title: messages.error,
100
- type: "error"
101
- });
102
- throw err;
150
+ } catch (error) {
151
+ const errorMessage = typeof messages.error === "function" ? messages.error(error) : messages.error;
152
+ if (id) {
153
+ toastStore.update(id, {
154
+ title: errorMessage,
155
+ type: "error",
156
+ duration: 4e3
157
+ });
158
+ setTimeout(() => {
159
+ toastStore.remove(id);
160
+ }, 4e3);
161
+ }
162
+ throw error;
103
163
  }
104
164
  }
105
165
  }
@@ -111,147 +171,201 @@ var import_react2 = require("react");
111
171
  // src/hooks/useToast.ts
112
172
  var import_react = require("react");
113
173
  function useToast() {
114
- const [toasts2, setToasts] = (0, import_react.useState)(
115
- toastStore.getToasts()
174
+ const toasts2 = (0, import_react.useSyncExternalStore)(
175
+ toastStore.subscribe,
176
+ toastStore.getSnapshot,
177
+ toastStore.getServerSnapshot
116
178
  );
117
- (0, import_react.useEffect)(() => {
118
- const unsubscribe = toastStore.subscribe(setToasts);
119
- return unsubscribe;
120
- }, []);
121
179
  return { toasts: toasts2 };
122
180
  }
123
181
 
124
182
  // src/components/YourToastProvider.tsx
125
183
  var import_jsx_runtime = require("react/jsx-runtime");
126
- var YourToastProvider = ({
127
- children
128
- }) => {
184
+ function YourToastProvider() {
129
185
  const { toasts: toasts2 } = useToast();
130
- const [mounted, setMounted] = (0, import_react2.useState)(false);
131
186
  (0, import_react2.useEffect)(() => {
132
- setMounted(true);
133
- if (typeof document !== "undefined") {
134
- const style = document.createElement("style");
135
- style.innerHTML = `
136
- @keyframes yt-slide-in {
137
- from {
138
- opacity: 0;
139
- transform: translateY(-10px) translateX(10px);
140
- }
187
+ if (typeof document === "undefined") return;
188
+ const styleId = "your-toast-styles";
189
+ if (document.getElementById(styleId)) {
190
+ return;
191
+ }
192
+ const style = document.createElement("style");
193
+ style.id = styleId;
194
+ style.textContent = `
195
+ @keyframes yt-slide-in {
196
+ from {
197
+ opacity: 0;
198
+ transform: translate3d(20px, -10px, 0) scale(0.96);
199
+ }
141
200
 
142
- to {
143
- opacity: 1;
144
- transform: translateY(0) translateX(0);
145
- }
201
+ to {
202
+ opacity: 1;
203
+ transform: translate3d(0, 0, 0) scale(1);
146
204
  }
147
- `;
148
- document.head.appendChild(style);
149
- return () => {
150
- document.head.removeChild(style);
151
- };
152
- }
205
+ }
206
+
207
+ @keyframes yt-slide-out {
208
+ from {
209
+ opacity: 1;
210
+ transform: translate3d(0, 0, 0) scale(1);
211
+ }
212
+
213
+ to {
214
+ opacity: 0;
215
+ transform: translate3d(20px, -10px, 0) scale(0.96);
216
+ }
217
+ }
218
+
219
+ @media (prefers-reduced-motion: reduce) {
220
+ .yt-toast {
221
+ animation: none !important;
222
+ }
223
+ }
224
+ `;
225
+ document.head.appendChild(style);
226
+ return () => {
227
+ style.remove();
228
+ };
153
229
  }, []);
154
- if (!mounted) {
155
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
156
- }
157
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
158
- children,
159
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: containerStyle, children: toasts2.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
160
- "div",
161
- {
162
- style: {
163
- ...toastStyle,
164
- ...getVariantStyle(toast2.type)
165
- },
166
- children: [
167
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column" }, children: [
168
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontWeight: 600 }, children: toast2.title }),
169
- toast2.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
170
- "span",
230
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
231
+ "div",
232
+ {
233
+ "aria-live": "polite",
234
+ "aria-atomic": "false",
235
+ style: containerStyle,
236
+ children: toasts2.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
237
+ "div",
238
+ {
239
+ className: "yt-toast",
240
+ role: toast2.type === "error" ? "alert" : "status",
241
+ style: {
242
+ ...toastStyle,
243
+ ...getVariantStyle(toast2.type)
244
+ },
245
+ children: [
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: contentStyle, children: [
247
+ toast2.title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: titleStyle, children: toast2.title }),
248
+ toast2.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: descriptionStyle, children: toast2.description })
249
+ ] }),
250
+ toast2.action && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
251
+ "button",
171
252
  {
172
- style: {
173
- opacity: 0.7,
174
- fontSize: "13px"
175
- },
176
- children: toast2.description
253
+ type: "button",
254
+ onClick: toast2.action.onClick,
255
+ style: actionBtn,
256
+ children: toast2.action.label
257
+ }
258
+ ),
259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
260
+ "button",
261
+ {
262
+ type: "button",
263
+ "aria-label": "Dismiss notification",
264
+ onClick: () => toastStore.remove(toast2.id),
265
+ style: closeBtn,
266
+ children: "\xD7"
177
267
  }
178
268
  )
179
- ] }),
180
- toast2.action && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
181
- "button",
182
- {
183
- onClick: toast2.action.onClick,
184
- style: actionBtn,
185
- children: toast2.action.label
186
- }
187
- ),
188
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
189
- "button",
190
- {
191
- onClick: () => toastStore.remove(toast2.id),
192
- style: closeBtn,
193
- children: "\u2715"
194
- }
195
- )
196
- ]
197
- },
198
- toast2.id
199
- )) })
200
- ] });
201
- };
269
+ ]
270
+ },
271
+ toast2.id
272
+ ))
273
+ }
274
+ );
275
+ }
202
276
  function getVariantStyle(type) {
203
277
  switch (type) {
204
278
  case "success":
205
- return { background: "#16a34a" };
279
+ return {
280
+ background: "linear-gradient(135deg, rgba(22,163,74,.9), rgba(21,128,61,.8))"
281
+ };
206
282
  case "error":
207
- return { background: "#dc2626" };
283
+ return {
284
+ background: "linear-gradient(135deg, rgba(220,38,38,.9), rgba(185,28,28,.8))"
285
+ };
208
286
  case "loading":
209
- return { background: "#2563eb" };
287
+ return {
288
+ background: "linear-gradient(135deg, rgba(37,99,235,.9), rgba(29,78,216,.8))"
289
+ };
210
290
  case "action":
211
- return { background: "#7c3aed" };
291
+ return {
292
+ background: "linear-gradient(135deg, rgba(124,58,237,.9), rgba(109,40,217,.8))"
293
+ };
212
294
  default:
213
295
  return {
214
- background: "rgba(255,255,255,0.08)"
296
+ background: "rgba(20,20,25,.82)"
215
297
  };
216
298
  }
217
299
  }
218
300
  var containerStyle = {
219
301
  position: "fixed",
220
- top: 20,
221
- right: 20,
302
+ top: "20px",
303
+ right: "20px",
304
+ zIndex: 99999,
222
305
  display: "flex",
223
306
  flexDirection: "column",
307
+ alignItems: "flex-end",
224
308
  gap: "10px",
225
- zIndex: 9999
309
+ width: "min(420px, calc(100vw - 40px))",
310
+ pointerEvents: "none"
226
311
  };
227
312
  var toastStyle = {
228
- backdropFilter: "blur(16px)",
229
- color: "#fff",
230
- padding: "14px 16px",
231
- borderRadius: "14px",
313
+ width: "100%",
314
+ boxSizing: "border-box",
232
315
  display: "flex",
233
- justifyContent: "space-between",
234
316
  alignItems: "center",
235
- minWidth: "260px",
236
- gap: "10px",
237
- border: "1px solid rgba(255,255,255,0.1)",
238
- boxShadow: "0 8px 30px rgba(0,0,0,0.3)",
239
- animation: "yt-slide-in 0.3s ease"
317
+ gap: "12px",
318
+ padding: "14px 16px",
319
+ borderRadius: "16px",
320
+ color: "#fff",
321
+ border: "1px solid rgba(255,255,255,.14)",
322
+ boxShadow: "0 12px 40px rgba(0,0,0,.28)",
323
+ backdropFilter: "blur(18px)",
324
+ WebkitBackdropFilter: "blur(18px)",
325
+ animation: "yt-slide-in .3s cubic-bezier(.16,1,.3,1)",
326
+ pointerEvents: "auto"
327
+ };
328
+ var contentStyle = {
329
+ flex: 1,
330
+ display: "flex",
331
+ flexDirection: "column",
332
+ minWidth: 0,
333
+ gap: "3px"
334
+ };
335
+ var titleStyle = {
336
+ fontSize: "14px",
337
+ fontWeight: 600,
338
+ lineHeight: 1.4
339
+ };
340
+ var descriptionStyle = {
341
+ fontSize: "13px",
342
+ lineHeight: 1.4,
343
+ opacity: 0.75
240
344
  };
241
345
  var closeBtn = {
242
- background: "transparent",
346
+ flexShrink: 0,
347
+ width: "28px",
348
+ height: "28px",
349
+ display: "grid",
350
+ placeItems: "center",
243
351
  border: "none",
352
+ borderRadius: "8px",
353
+ background: "rgba(255,255,255,.08)",
244
354
  color: "#fff",
245
- cursor: "pointer"
355
+ cursor: "pointer",
356
+ fontSize: "18px",
357
+ lineHeight: 1
246
358
  };
247
359
  var actionBtn = {
248
- background: "rgba(255,255,255,0.1)",
249
- border: "none",
250
- padding: "6px 10px",
360
+ flexShrink: 0,
361
+ border: "1px solid rgba(255,255,255,.12)",
251
362
  borderRadius: "8px",
363
+ padding: "7px 10px",
364
+ background: "rgba(255,255,255,.1)",
252
365
  color: "#fff",
253
366
  cursor: "pointer",
254
- fontSize: "12px"
367
+ fontSize: "12px",
368
+ fontWeight: 500
255
369
  };
256
370
  // Annotate the CommonJS export names for ESM import in node:
257
371
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -1,22 +1,46 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
1
+ export { YourToastProvider } from './provider.cjs';
2
+ import 'react/jsx-runtime';
3
3
 
4
- declare const toast: ((message: string) => `${string}-${string}-${string}-${string}-${string}`) & {
5
- success: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
6
- error: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
7
- loading: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
8
- action: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
9
- dismiss: (id: string) => void;
10
- update: (id: string, data: Partial<any>) => void;
11
- promise: <T>(promise: Promise<T>, messages: {
12
- loading: string;
13
- success: string;
14
- error: string;
15
- }) => Promise<T>;
16
- };
4
+ type ToastType = "default" | "success" | "error" | "loading" | "action";
5
+ interface ToastAction {
6
+ label: string;
7
+ onClick: () => void;
8
+ }
9
+ interface Toast {
10
+ id: string;
11
+ title?: string;
12
+ description?: string;
13
+ type: ToastType;
14
+ duration?: number;
15
+ action?: ToastAction;
16
+ }
17
+ interface ToastOptions {
18
+ description?: string;
19
+ duration?: number;
20
+ action?: ToastAction;
21
+ }
22
+ interface ToastPromiseMessages<T = unknown> {
23
+ loading: string;
24
+ success: string | ((data: T) => string);
25
+ error: string | ((error: unknown) => string);
26
+ }
17
27
 
18
- declare const YourToastProvider: ({ children, }: {
19
- children: React.ReactNode;
20
- }) => react_jsx_runtime.JSX.Element;
28
+ declare const toast: ((message: string, options?: ToastOptions) => string) & {
29
+ success: (message: string, options?: ToastOptions) => string;
30
+ error: (message: string, options?: ToastOptions) => string;
31
+ warning: (message: string, options?: ToastOptions) => string;
32
+ info: (message: string, options?: ToastOptions) => string;
33
+ loading: (message: string, options?: ToastOptions) => string;
34
+ action: (message: string, options?: ToastOptions) => string;
35
+ dismiss(id?: string): void;
36
+ update(id: string, data: Partial<{
37
+ title: string;
38
+ description: string;
39
+ type: ToastType;
40
+ duration: number;
41
+ action: ToastOptions["action"];
42
+ }>): void;
43
+ promise<T>(promise: Promise<T>, messages: ToastPromiseMessages<T>): Promise<T>;
44
+ };
21
45
 
22
- export { YourToastProvider, toast };
46
+ export { type Toast, type ToastAction, type ToastOptions, type ToastPromiseMessages, type ToastType, toast };
package/dist/index.d.ts CHANGED
@@ -1,22 +1,46 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
1
+ export { YourToastProvider } from './provider.js';
2
+ import 'react/jsx-runtime';
3
3
 
4
- declare const toast: ((message: string) => `${string}-${string}-${string}-${string}-${string}`) & {
5
- success: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
6
- error: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
7
- loading: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
8
- action: (message: string) => `${string}-${string}-${string}-${string}-${string}`;
9
- dismiss: (id: string) => void;
10
- update: (id: string, data: Partial<any>) => void;
11
- promise: <T>(promise: Promise<T>, messages: {
12
- loading: string;
13
- success: string;
14
- error: string;
15
- }) => Promise<T>;
16
- };
4
+ type ToastType = "default" | "success" | "error" | "loading" | "action";
5
+ interface ToastAction {
6
+ label: string;
7
+ onClick: () => void;
8
+ }
9
+ interface Toast {
10
+ id: string;
11
+ title?: string;
12
+ description?: string;
13
+ type: ToastType;
14
+ duration?: number;
15
+ action?: ToastAction;
16
+ }
17
+ interface ToastOptions {
18
+ description?: string;
19
+ duration?: number;
20
+ action?: ToastAction;
21
+ }
22
+ interface ToastPromiseMessages<T = unknown> {
23
+ loading: string;
24
+ success: string | ((data: T) => string);
25
+ error: string | ((error: unknown) => string);
26
+ }
17
27
 
18
- declare const YourToastProvider: ({ children, }: {
19
- children: React.ReactNode;
20
- }) => react_jsx_runtime.JSX.Element;
28
+ declare const toast: ((message: string, options?: ToastOptions) => string) & {
29
+ success: (message: string, options?: ToastOptions) => string;
30
+ error: (message: string, options?: ToastOptions) => string;
31
+ warning: (message: string, options?: ToastOptions) => string;
32
+ info: (message: string, options?: ToastOptions) => string;
33
+ loading: (message: string, options?: ToastOptions) => string;
34
+ action: (message: string, options?: ToastOptions) => string;
35
+ dismiss(id?: string): void;
36
+ update(id: string, data: Partial<{
37
+ title: string;
38
+ description: string;
39
+ type: ToastType;
40
+ duration: number;
41
+ action: ToastOptions["action"];
42
+ }>): void;
43
+ promise<T>(promise: Promise<T>, messages: ToastPromiseMessages<T>): Promise<T>;
44
+ };
21
45
 
22
- export { YourToastProvider, toast };
46
+ export { type Toast, type ToastAction, type ToastOptions, type ToastPromiseMessages, type ToastType, toast };