your-toast 0.1.0 → 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,214 +18,355 @@ 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
  }
106
166
  );
107
167
 
168
+ // src/components/YourToastProvider.tsx
169
+ var import_react2 = require("react");
170
+
108
171
  // src/hooks/useToast.ts
109
172
  var import_react = require("react");
110
173
  function useToast() {
111
- const [toasts2, setToasts] = (0, import_react.useState)(
112
- toastStore.getToasts()
174
+ const toasts2 = (0, import_react.useSyncExternalStore)(
175
+ toastStore.subscribe,
176
+ toastStore.getSnapshot,
177
+ toastStore.getServerSnapshot
113
178
  );
114
- (0, import_react.useEffect)(() => {
115
- const unsubscribe = toastStore.subscribe(setToasts);
116
- return unsubscribe;
117
- }, []);
118
179
  return { toasts: toasts2 };
119
180
  }
120
181
 
121
182
  // src/components/YourToastProvider.tsx
122
183
  var import_jsx_runtime = require("react/jsx-runtime");
123
- var YourToastProvider = ({
124
- children
125
- }) => {
184
+ function YourToastProvider() {
126
185
  const { toasts: toasts2 } = useToast();
127
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
128
- children,
129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: containerStyle, children: toasts2.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
130
- "div",
131
- {
132
- style: {
133
- ...toastStyle,
134
- ...getVariantStyle(toast2.type)
186
+ (0, import_react2.useEffect)(() => {
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
+ }
200
+
201
+ to {
202
+ opacity: 1;
203
+ transform: translate3d(0, 0, 0) scale(1);
204
+ }
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
+ };
229
+ }, []);
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",
252
+ {
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"
267
+ }
268
+ )
269
+ ]
135
270
  },
136
- children: [
137
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column" }, children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontWeight: 600 }, children: toast2.title }),
139
- toast2.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.7, fontSize: "13px" }, children: toast2.description })
140
- ] }),
141
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
142
- "button",
143
- {
144
- onClick: () => toastStore.remove(toast2.id),
145
- style: closeBtn,
146
- children: "\u2715"
147
- }
148
- ),
149
- toast2.action && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
150
- "button",
151
- {
152
- onClick: toast2.action.onClick,
153
- style: {
154
- background: "rgba(255,255,255,0.1)",
155
- border: "none",
156
- padding: "6px 10px",
157
- borderRadius: "8px",
158
- color: "#fff",
159
- cursor: "pointer",
160
- fontSize: "12px"
161
- },
162
- children: toast2.action.label
163
- }
164
- )
165
- ]
166
- },
167
- toast2.id
168
- )) })
169
- ] });
170
- };
271
+ toast2.id
272
+ ))
273
+ }
274
+ );
275
+ }
171
276
  function getVariantStyle(type) {
172
277
  switch (type) {
173
278
  case "success":
174
- return { background: "#16a34a" };
279
+ return {
280
+ background: "linear-gradient(135deg, rgba(22,163,74,.9), rgba(21,128,61,.8))"
281
+ };
175
282
  case "error":
176
- return { background: "#dc2626" };
283
+ return {
284
+ background: "linear-gradient(135deg, rgba(220,38,38,.9), rgba(185,28,28,.8))"
285
+ };
177
286
  case "loading":
178
- return { background: "#2563eb" };
287
+ return {
288
+ background: "linear-gradient(135deg, rgba(37,99,235,.9), rgba(29,78,216,.8))"
289
+ };
179
290
  case "action":
180
- return { background: "#7c3aed" };
291
+ return {
292
+ background: "linear-gradient(135deg, rgba(124,58,237,.9), rgba(109,40,217,.8))"
293
+ };
181
294
  default:
182
- return { background: "#111" };
295
+ return {
296
+ background: "rgba(20,20,25,.82)"
297
+ };
183
298
  }
184
299
  }
185
300
  var containerStyle = {
186
301
  position: "fixed",
187
- top: 20,
188
- right: 20,
302
+ top: "20px",
303
+ right: "20px",
304
+ zIndex: 99999,
189
305
  display: "flex",
190
306
  flexDirection: "column",
307
+ alignItems: "flex-end",
191
308
  gap: "10px",
192
- zIndex: 9999
309
+ width: "min(420px, calc(100vw - 40px))",
310
+ pointerEvents: "none"
193
311
  };
194
312
  var toastStyle = {
195
- backdropFilter: "blur(16px)",
196
- background: "rgba(255, 255, 255, 0.08)",
197
- color: "#fff",
313
+ width: "100%",
314
+ boxSizing: "border-box",
315
+ display: "flex",
316
+ alignItems: "center",
317
+ gap: "12px",
198
318
  padding: "14px 16px",
199
- borderRadius: "14px",
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,
200
330
  display: "flex",
201
- justifyContent: "space-between",
202
- alignItems: "flex-start",
203
- minWidth: "260px",
204
- gap: "10px",
205
- border: "1px solid rgba(255,255,255,0.1)",
206
- boxShadow: "0 8px 30px rgba(0,0,0,0.3)",
207
- animation: "yt-slide-in 0.3s ease"
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
208
344
  };
209
345
  var closeBtn = {
210
- background: "transparent",
346
+ flexShrink: 0,
347
+ width: "28px",
348
+ height: "28px",
349
+ display: "grid",
350
+ placeItems: "center",
211
351
  border: "none",
352
+ borderRadius: "8px",
353
+ background: "rgba(255,255,255,.08)",
212
354
  color: "#fff",
213
- cursor: "pointer"
355
+ cursor: "pointer",
356
+ fontSize: "18px",
357
+ lineHeight: 1
358
+ };
359
+ var actionBtn = {
360
+ flexShrink: 0,
361
+ border: "1px solid rgba(255,255,255,.12)",
362
+ borderRadius: "8px",
363
+ padding: "7px 10px",
364
+ background: "rgba(255,255,255,.1)",
365
+ color: "#fff",
366
+ cursor: "pointer",
367
+ fontSize: "12px",
368
+ fontWeight: 500
214
369
  };
215
- var style = document.createElement("style");
216
- style.innerHTML = `
217
- @keyframes yt-slide-in {
218
- from {
219
- opacity: 0;
220
- transform: translateY(-10px) translateX(10px);
221
- }
222
- to {
223
- opacity: 1;
224
- transform: translateY(0) translateX(0);
225
- }
226
- }
227
- `;
228
- document.head.appendChild(style);
229
370
  // Annotate the CommonJS export names for ESM import in node:
230
371
  0 && (module.exports = {
231
372
  YourToastProvider,
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 };