kmod-cli 1.0.10
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/README.md +53 -0
- package/bin/gen-components.js +68 -0
- package/bin/index.js +153 -0
- package/component-templates/components/access-denied.tsx +130 -0
- package/component-templates/components/breadcumb.tsx +42 -0
- package/component-templates/components/count-down.tsx +94 -0
- package/component-templates/components/count-input.tsx +221 -0
- package/component-templates/components/date-range-calendar/button.tsx +61 -0
- package/component-templates/components/date-range-calendar/calendar.tsx +132 -0
- package/component-templates/components/date-range-calendar/date-input.tsx +259 -0
- package/component-templates/components/date-range-calendar/date-range-picker.tsx +594 -0
- package/component-templates/components/date-range-calendar/label.tsx +31 -0
- package/component-templates/components/date-range-calendar/popover.tsx +32 -0
- package/component-templates/components/date-range-calendar/select.tsx +125 -0
- package/component-templates/components/date-range-calendar/switch.tsx +30 -0
- package/component-templates/components/datetime-picker/button.tsx +61 -0
- package/component-templates/components/datetime-picker/calendar.tsx +156 -0
- package/component-templates/components/datetime-picker/datetime-picker.tsx +75 -0
- package/component-templates/components/datetime-picker/input.tsx +20 -0
- package/component-templates/components/datetime-picker/label.tsx +18 -0
- package/component-templates/components/datetime-picker/period-input.tsx +62 -0
- package/component-templates/components/datetime-picker/popover.tsx +32 -0
- package/component-templates/components/datetime-picker/select.tsx +125 -0
- package/component-templates/components/datetime-picker/time-picker-input.tsx +131 -0
- package/component-templates/components/datetime-picker/time-picker-utils.tsx +204 -0
- package/component-templates/components/datetime-picker/time-picker.tsx +59 -0
- package/component-templates/components/gradient-outline.tsx +233 -0
- package/component-templates/components/gradient-svg.tsx +157 -0
- package/component-templates/components/grid-layout.tsx +69 -0
- package/component-templates/components/hydrate-guard.tsx +40 -0
- package/component-templates/components/image.tsx +92 -0
- package/component-templates/components/loader-slash-gradient.tsx +85 -0
- package/component-templates/components/masonry-gallery.tsx +221 -0
- package/component-templates/components/modal.tsx +110 -0
- package/component-templates/components/multi-select.tsx +447 -0
- package/component-templates/components/non-hydration.tsx +27 -0
- package/component-templates/components/portal.tsx +34 -0
- package/component-templates/components/segments-circle.tsx +235 -0
- package/component-templates/components/single-select.tsx +248 -0
- package/component-templates/components/stroke-circle.tsx +57 -0
- package/component-templates/components/table/column-table.tsx +15 -0
- package/component-templates/components/table/data-table.tsx +339 -0
- package/component-templates/components/table/readme.tsx +95 -0
- package/component-templates/components/table/table.tsx +60 -0
- package/component-templates/components/text-hover-effect.tsx +120 -0
- package/component-templates/components/timout-loader.tsx +52 -0
- package/component-templates/components/toast.tsx +994 -0
- package/component-templates/configs/config.ts +33 -0
- package/component-templates/configs/feature-config.tsx +432 -0
- package/component-templates/configs/keys.ts +7 -0
- package/component-templates/core/api-service.ts +202 -0
- package/component-templates/core/calculate.ts +18 -0
- package/component-templates/core/idb.ts +166 -0
- package/component-templates/core/storage.ts +213 -0
- package/component-templates/hooks/count-down.ts +38 -0
- package/component-templates/hooks/fade-on-scroll.ts +52 -0
- package/component-templates/hooks/safe-action.ts +59 -0
- package/component-templates/hooks/spam-guard.ts +31 -0
- package/component-templates/lib/utils.ts +6 -0
- package/component-templates/providers/feature-guard.tsx +432 -0
- package/component-templates/queries/query.tsx +775 -0
- package/component-templates/utils/colors/color-by-text.ts +307 -0
- package/component-templates/utils/colors/stripe-effect.ts +100 -0
- package/component-templates/utils/hash/hash-aes.ts +35 -0
- package/components.json +348 -0
- package/package.json +60 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, {
|
|
3
|
+
createContext,
|
|
4
|
+
type CSSProperties,
|
|
5
|
+
type PropsWithChildren,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
useCallback,
|
|
8
|
+
useContext,
|
|
9
|
+
useEffect,
|
|
10
|
+
useLayoutEffect,
|
|
11
|
+
useMemo,
|
|
12
|
+
useReducer,
|
|
13
|
+
useRef,
|
|
14
|
+
useState,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import { createPortal } from 'react-dom';
|
|
17
|
+
|
|
18
|
+
import { XIcon } from 'lucide-react';
|
|
19
|
+
|
|
20
|
+
import { cn } from '../lib/utils';
|
|
21
|
+
|
|
22
|
+
// ------------------------------------------------------------
|
|
23
|
+
// Types
|
|
24
|
+
// ------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
export type ToastType =
|
|
27
|
+
| "blank"
|
|
28
|
+
| "success"
|
|
29
|
+
| "error"
|
|
30
|
+
| "warning"
|
|
31
|
+
| "info"
|
|
32
|
+
| "loading";
|
|
33
|
+
|
|
34
|
+
export type ToastPosition =
|
|
35
|
+
| "top-left"
|
|
36
|
+
| "top-right"
|
|
37
|
+
| "top-center"
|
|
38
|
+
| "bottom-left"
|
|
39
|
+
| "bottom-right"
|
|
40
|
+
| "bottom-center";
|
|
41
|
+
|
|
42
|
+
export type ToastTheme = "glass" | "liquid" | "minimal";
|
|
43
|
+
export type ToastClassNames = {
|
|
44
|
+
container?: string;
|
|
45
|
+
body?: string;
|
|
46
|
+
icon?: string;
|
|
47
|
+
content?: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
desc?: string;
|
|
50
|
+
close?: string;
|
|
51
|
+
progress?: string;
|
|
52
|
+
progress_bar?: string;
|
|
53
|
+
actions?: string;
|
|
54
|
+
}
|
|
55
|
+
export type ToastAction = {
|
|
56
|
+
label: string;
|
|
57
|
+
onClick: (id: string) => void;
|
|
58
|
+
className?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type Toast = {
|
|
62
|
+
id: string;
|
|
63
|
+
title?: ReactNode;
|
|
64
|
+
description?: ReactNode;
|
|
65
|
+
type: ToastType;
|
|
66
|
+
position: ToastPosition;
|
|
67
|
+
theme: ToastTheme;
|
|
68
|
+
duration: number; // ms; 0 = sticky
|
|
69
|
+
createdAt: number;
|
|
70
|
+
icon?: ReactNode;
|
|
71
|
+
dismissible?: boolean;
|
|
72
|
+
dismissRender?: ReactNode;
|
|
73
|
+
closeOnClick?: boolean;
|
|
74
|
+
pauseOnHover?: boolean;
|
|
75
|
+
showProgress?: boolean;
|
|
76
|
+
classNames?: ToastClassNames;
|
|
77
|
+
style?: CSSProperties;
|
|
78
|
+
ariaLive?: "polite" | "assertive" | "off";
|
|
79
|
+
role?: "status" | "alert";
|
|
80
|
+
actions?: ToastAction[];
|
|
81
|
+
// Internal runtime
|
|
82
|
+
_remaining?: number;
|
|
83
|
+
_paused?: boolean;
|
|
84
|
+
_hovering?: boolean;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type ToastOptions = Partial<Omit<Toast, "id" | "createdAt">> & {
|
|
88
|
+
id?: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ------------------------------------------------------------
|
|
92
|
+
// Default config
|
|
93
|
+
// ------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
const DEFAULTS: Required<
|
|
96
|
+
Pick<
|
|
97
|
+
Toast,
|
|
98
|
+
| "type"
|
|
99
|
+
| "position"
|
|
100
|
+
| "theme"
|
|
101
|
+
| "duration"
|
|
102
|
+
| "dismissible"
|
|
103
|
+
| "closeOnClick"
|
|
104
|
+
| "pauseOnHover"
|
|
105
|
+
| "showProgress"
|
|
106
|
+
| "ariaLive"
|
|
107
|
+
| "role"
|
|
108
|
+
>
|
|
109
|
+
> = {
|
|
110
|
+
type: "blank",
|
|
111
|
+
position: "top-right",
|
|
112
|
+
theme: "glass",
|
|
113
|
+
duration: 3000,
|
|
114
|
+
dismissible: true,
|
|
115
|
+
closeOnClick: true,
|
|
116
|
+
pauseOnHover: true,
|
|
117
|
+
showProgress: false,
|
|
118
|
+
ariaLive: "polite",
|
|
119
|
+
role: "status",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// ------------------------------------------------------------
|
|
123
|
+
// Store (Context + Reducer) — no external state libs
|
|
124
|
+
// ------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
type State = {
|
|
127
|
+
toasts: Toast[];
|
|
128
|
+
limit: number; // max on screen
|
|
129
|
+
queue: Toast[];
|
|
130
|
+
zIndexBase: number;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const initialState: State = {
|
|
134
|
+
toasts: [],
|
|
135
|
+
limit: 5,
|
|
136
|
+
queue: [],
|
|
137
|
+
zIndexBase: 9999,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
type Action =
|
|
141
|
+
| { type: "SHOW"; toast: Toast }
|
|
142
|
+
| { type: "DISMISS"; id?: string }
|
|
143
|
+
| { type: "UPDATE"; id: string; patch: Partial<Toast> }
|
|
144
|
+
| { type: "CONFIG"; limit?: number; zIndexBase?: number };
|
|
145
|
+
|
|
146
|
+
function reducer(state: State, action: Action): State {
|
|
147
|
+
switch (action.type) {
|
|
148
|
+
case "CONFIG": {
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
limit: action.limit ?? state.limit,
|
|
152
|
+
zIndexBase: action.zIndexBase ?? state.zIndexBase,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
case "SHOW": {
|
|
156
|
+
const active = state.toasts;
|
|
157
|
+
if (active.length >= state.limit) {
|
|
158
|
+
return { ...state, queue: [...state.queue, action.toast] };
|
|
159
|
+
}
|
|
160
|
+
return { ...state, toasts: [...active, action.toast] };
|
|
161
|
+
}
|
|
162
|
+
case "DISMISS": {
|
|
163
|
+
const id = action.id;
|
|
164
|
+
const remaining = id ? state.toasts.filter((t) => t.id !== id) : [];
|
|
165
|
+
// Pull next from queue if any
|
|
166
|
+
const next = [...remaining];
|
|
167
|
+
while (next.length < state.limit && state.queue.length > 0) {
|
|
168
|
+
const q0 = state.queue[0];
|
|
169
|
+
state.queue.shift();
|
|
170
|
+
next.push(q0);
|
|
171
|
+
}
|
|
172
|
+
return { ...state, toasts: next, queue: [...state.queue] };
|
|
173
|
+
}
|
|
174
|
+
case "UPDATE": {
|
|
175
|
+
const idx = state.toasts.findIndex((t) => t.id === action.id);
|
|
176
|
+
if (idx === -1) return state;
|
|
177
|
+
const clone = [...state.toasts];
|
|
178
|
+
clone[idx] = { ...clone[idx], ...action.patch };
|
|
179
|
+
return { ...state, toasts: clone };
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
return state;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const Ctx = createContext<{
|
|
187
|
+
state: State;
|
|
188
|
+
dispatch: React.Dispatch<Action>;
|
|
189
|
+
} | null>(null);
|
|
190
|
+
|
|
191
|
+
export function ToastProvider({
|
|
192
|
+
children,
|
|
193
|
+
limit,
|
|
194
|
+
zIndexBase,
|
|
195
|
+
}: PropsWithChildren<{ limit?: number; zIndexBase?: number }>) {
|
|
196
|
+
const [state, dispatch] = useReducer(reducer, {
|
|
197
|
+
...initialState,
|
|
198
|
+
limit: limit ?? initialState.limit,
|
|
199
|
+
zIndexBase: zIndexBase ?? initialState.zIndexBase,
|
|
200
|
+
});
|
|
201
|
+
const [mounted, setMounted] = useState(false);
|
|
202
|
+
|
|
203
|
+
// Inject styles once
|
|
204
|
+
useToastGlobalStyles();
|
|
205
|
+
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
setMounted(true);
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
const value = useMemo(() => ({ state, dispatch }), [state]);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<Ctx.Provider value={value}>
|
|
214
|
+
{children}
|
|
215
|
+
{mounted && createPortal(<ToastViewport />, document.body)}
|
|
216
|
+
</Ctx.Provider>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function useCtx() {
|
|
221
|
+
const ctx = useContext(Ctx);
|
|
222
|
+
if (!ctx) throw new Error("ToastProvider is missing");
|
|
223
|
+
return ctx;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ------------------------------------------------------------
|
|
227
|
+
// Public API
|
|
228
|
+
// ------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
function uid() {
|
|
231
|
+
// RFC4122-ish; crypto if available
|
|
232
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto)
|
|
233
|
+
return crypto.randomUUID();
|
|
234
|
+
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export const t = Object.assign(
|
|
238
|
+
(message: ReactNode, opts?: ToastOptions) =>
|
|
239
|
+
show({ description: message, ...(opts ?? {}) }),
|
|
240
|
+
{
|
|
241
|
+
show,
|
|
242
|
+
success: (message: ReactNode, opts?: ToastOptions) =>
|
|
243
|
+
show({ type: "success", description: message, ...(opts ?? {}) }),
|
|
244
|
+
error: (message: ReactNode, opts?: ToastOptions) =>
|
|
245
|
+
show({ type: "error", description: message, ...(opts ?? {}) }),
|
|
246
|
+
warning: (message: ReactNode, opts?: ToastOptions) =>
|
|
247
|
+
show({ type: "warning", description: message, ...(opts ?? {}) }),
|
|
248
|
+
info: (message: ReactNode, opts?: ToastOptions) =>
|
|
249
|
+
show({ type: "info", description: message, ...(opts ?? {}) }),
|
|
250
|
+
loading: (message: ReactNode, opts?: ToastOptions) =>
|
|
251
|
+
show({
|
|
252
|
+
type: "loading",
|
|
253
|
+
description: message,
|
|
254
|
+
duration: 0,
|
|
255
|
+
...(opts ?? {}),
|
|
256
|
+
}),
|
|
257
|
+
update,
|
|
258
|
+
dismiss,
|
|
259
|
+
dismissAll: () => dismiss(),
|
|
260
|
+
promise: ultraPromise,
|
|
261
|
+
config: (cfg: { limit?: number; zIndexBase?: number }) =>
|
|
262
|
+
getDispatch()?.({ type: "CONFIG", ...cfg }),
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
function getDispatch() {
|
|
267
|
+
// Late binding via a hidden ref set by viewport
|
|
268
|
+
return (window as any).__ultra_toast_dispatch as
|
|
269
|
+
| React.Dispatch<Action>
|
|
270
|
+
| undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function getState() {
|
|
274
|
+
return (window as any).__ultra_toast_state as State | undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function show(opts: ToastOptions) {
|
|
278
|
+
const dispatch = getDispatch();
|
|
279
|
+
if (!dispatch) throw new Error("ToastProvider not mounted");
|
|
280
|
+
|
|
281
|
+
const id = opts.id ?? uid();
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
const base: Toast = {
|
|
284
|
+
id,
|
|
285
|
+
createdAt: now,
|
|
286
|
+
title: opts.title,
|
|
287
|
+
description: opts.description,
|
|
288
|
+
type: opts.type ?? DEFAULTS.type,
|
|
289
|
+
position: opts.position ?? DEFAULTS.position,
|
|
290
|
+
theme: opts.theme ?? DEFAULTS.theme,
|
|
291
|
+
duration: opts.duration ?? DEFAULTS.duration,
|
|
292
|
+
icon: opts.icon,
|
|
293
|
+
dismissible: opts.dismissible ?? DEFAULTS.dismissible,
|
|
294
|
+
closeOnClick: opts.closeOnClick ?? DEFAULTS.closeOnClick,
|
|
295
|
+
pauseOnHover: opts.pauseOnHover ?? DEFAULTS.pauseOnHover,
|
|
296
|
+
showProgress: opts.showProgress ?? DEFAULTS.showProgress,
|
|
297
|
+
classNames: opts.classNames,
|
|
298
|
+
style: opts.style,
|
|
299
|
+
ariaLive: opts.ariaLive ?? DEFAULTS.ariaLive,
|
|
300
|
+
role: opts.role ?? DEFAULTS.role,
|
|
301
|
+
actions: opts.actions,
|
|
302
|
+
_remaining: opts.duration ?? DEFAULTS.duration,
|
|
303
|
+
_paused: false,
|
|
304
|
+
_hovering: false,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
dispatch({ type: "SHOW", toast: base });
|
|
308
|
+
return id;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function update(id: string, patch: Partial<Toast>) {
|
|
312
|
+
const dispatch = getDispatch();
|
|
313
|
+
if (!dispatch) throw new Error("ToastProvider not mounted");
|
|
314
|
+
dispatch({ type: "UPDATE", id, patch });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function dismiss(id?: string) {
|
|
318
|
+
const dispatch = getDispatch();
|
|
319
|
+
if (!dispatch) throw new Error("ToastProvider not mounted");
|
|
320
|
+
dispatch({ type: "DISMISS", id });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function ultraPromise<T>(
|
|
324
|
+
promiseOrFn: Promise<T> | (() => Promise<T>),
|
|
325
|
+
msgs: {
|
|
326
|
+
loading?: ReactNode;
|
|
327
|
+
success: (value: T) => ReactNode | ReactNode;
|
|
328
|
+
error: (err: unknown) => ReactNode | ReactNode;
|
|
329
|
+
},
|
|
330
|
+
opts?: ToastOptions
|
|
331
|
+
) {
|
|
332
|
+
const id = show({
|
|
333
|
+
...opts,
|
|
334
|
+
type: "loading",
|
|
335
|
+
description: msgs.loading ?? "Loading...",
|
|
336
|
+
duration: 0,
|
|
337
|
+
});
|
|
338
|
+
try {
|
|
339
|
+
const value =
|
|
340
|
+
typeof promiseOrFn === "function"
|
|
341
|
+
? await promiseOrFn()
|
|
342
|
+
: await promiseOrFn;
|
|
343
|
+
update(id, {
|
|
344
|
+
type: "success",
|
|
345
|
+
description: msgs.success(value),
|
|
346
|
+
duration: opts?.duration ?? 2500,
|
|
347
|
+
showProgress: true,
|
|
348
|
+
});
|
|
349
|
+
// auto hide timer will take over
|
|
350
|
+
return value;
|
|
351
|
+
} catch (err) {
|
|
352
|
+
update(id, {
|
|
353
|
+
type: "error",
|
|
354
|
+
description: msgs.error(err),
|
|
355
|
+
duration: opts?.duration ?? 3500,
|
|
356
|
+
showProgress: true,
|
|
357
|
+
});
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ------------------------------------------------------------
|
|
363
|
+
// Viewport & Item
|
|
364
|
+
// ------------------------------------------------------------
|
|
365
|
+
|
|
366
|
+
function ToastViewport() {
|
|
367
|
+
const { state, dispatch } = useCtx();
|
|
368
|
+
|
|
369
|
+
// Expose for API (imperative) — safe since it's just a reference
|
|
370
|
+
useLayoutEffect(() => {
|
|
371
|
+
(window as any).__ultra_toast_dispatch = dispatch;
|
|
372
|
+
(window as any).__ultra_toast_state = state;
|
|
373
|
+
}, [state, dispatch]);
|
|
374
|
+
|
|
375
|
+
const byPosition = useMemo(() => {
|
|
376
|
+
const map: Record<ToastPosition, Toast[]> = {
|
|
377
|
+
"top-left": [],
|
|
378
|
+
"top-right": [],
|
|
379
|
+
"top-center": [],
|
|
380
|
+
"bottom-left": [],
|
|
381
|
+
"bottom-right": [],
|
|
382
|
+
"bottom-center": [],
|
|
383
|
+
};
|
|
384
|
+
for (const t of state.toasts) map[t.position].push(t);
|
|
385
|
+
return map;
|
|
386
|
+
}, [state.toasts]);
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<>
|
|
390
|
+
{Object.entries(byPosition).map(
|
|
391
|
+
([pos, list]) =>
|
|
392
|
+
list.length > 0 && (
|
|
393
|
+
<ToastStack
|
|
394
|
+
key={pos}
|
|
395
|
+
position={pos as ToastPosition}
|
|
396
|
+
items={list}
|
|
397
|
+
zIndexBase={state.zIndexBase}
|
|
398
|
+
/>
|
|
399
|
+
)
|
|
400
|
+
)}
|
|
401
|
+
</>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function ToastStack({
|
|
406
|
+
position,
|
|
407
|
+
items,
|
|
408
|
+
zIndexBase,
|
|
409
|
+
}: {
|
|
410
|
+
position: ToastPosition;
|
|
411
|
+
items: Toast[];
|
|
412
|
+
zIndexBase: number;
|
|
413
|
+
}) {
|
|
414
|
+
const style: CSSProperties = stackPositionStyle(position);
|
|
415
|
+
return (
|
|
416
|
+
<div
|
|
417
|
+
style={{
|
|
418
|
+
position: "fixed",
|
|
419
|
+
zIndex: zIndexBase,
|
|
420
|
+
pointerEvents: "none",
|
|
421
|
+
...style,
|
|
422
|
+
}}
|
|
423
|
+
>
|
|
424
|
+
<div className="ultra-stack" style={{ pointerEvents: "auto" }}>
|
|
425
|
+
{items.map((t) => (
|
|
426
|
+
<ToastItem key={t.id} toast={t} />
|
|
427
|
+
))}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function stackPositionStyle(position: ToastPosition): CSSProperties {
|
|
434
|
+
const base: CSSProperties = {};
|
|
435
|
+
switch (position) {
|
|
436
|
+
case "top-left":
|
|
437
|
+
return { top: 16, left: 16, ...base };
|
|
438
|
+
case "top-right":
|
|
439
|
+
return { top: 16, right: 16, ...base };
|
|
440
|
+
case "top-center":
|
|
441
|
+
return { top: 16, left: "50%", transform: "translateX(-50%)", ...base };
|
|
442
|
+
case "bottom-left":
|
|
443
|
+
return { bottom: 16, left: 16, ...base };
|
|
444
|
+
case "bottom-right":
|
|
445
|
+
return { bottom: 16, right: 16, ...base };
|
|
446
|
+
case "bottom-center":
|
|
447
|
+
return {
|
|
448
|
+
bottom: 16,
|
|
449
|
+
left: "50%",
|
|
450
|
+
transform: "translateX(-50%)",
|
|
451
|
+
...base,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function ToastItem({ toast }: { toast: Toast }) {
|
|
457
|
+
const { dispatch } = useCtx();
|
|
458
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
459
|
+
const [mounted, setMounted] = useState(false);
|
|
460
|
+
const [leaving, setLeaving] = useState(false);
|
|
461
|
+
const [progress, setProgress] = useState(100);
|
|
462
|
+
const rafRef = useRef<number | null>(null);
|
|
463
|
+
const lastTick = useRef<number>(0);
|
|
464
|
+
|
|
465
|
+
const stopRaf = () => {
|
|
466
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
467
|
+
rafRef.current = null;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const startTimer = useCallback(() => {
|
|
471
|
+
if (toast.duration === 0) return; // sticky
|
|
472
|
+
if (toast._paused) return;
|
|
473
|
+
lastTick.current = performance.now();
|
|
474
|
+
const step = (now: number) => {
|
|
475
|
+
const dt = now - lastTick.current;
|
|
476
|
+
lastTick.current = now;
|
|
477
|
+
const remaining = Math.max(0, (toast._remaining ?? toast.duration) - dt);
|
|
478
|
+
const pct = Math.max(0, (remaining / (toast.duration || 1)) * 100);
|
|
479
|
+
setProgress(pct);
|
|
480
|
+
toast._remaining = remaining;
|
|
481
|
+
if (remaining <= 0) {
|
|
482
|
+
triggerDismiss();
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
rafRef.current = requestAnimationFrame(step);
|
|
486
|
+
};
|
|
487
|
+
rafRef.current = requestAnimationFrame(step);
|
|
488
|
+
}, [toast]);
|
|
489
|
+
|
|
490
|
+
const pauseTimer = useCallback(() => {
|
|
491
|
+
toast._paused = true;
|
|
492
|
+
stopRaf();
|
|
493
|
+
}, [toast]);
|
|
494
|
+
|
|
495
|
+
const resumeTimer = useCallback(() => {
|
|
496
|
+
toast._paused = false;
|
|
497
|
+
startTimer();
|
|
498
|
+
}, [startTimer, toast]);
|
|
499
|
+
|
|
500
|
+
useEffect(() => {
|
|
501
|
+
setMounted(true);
|
|
502
|
+
startTimer();
|
|
503
|
+
return stopRaf;
|
|
504
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
505
|
+
}, []);
|
|
506
|
+
|
|
507
|
+
// Hover pause
|
|
508
|
+
const onMouseEnter = () => {
|
|
509
|
+
if (!toast.pauseOnHover) return;
|
|
510
|
+
toast._hovering = true;
|
|
511
|
+
pauseTimer();
|
|
512
|
+
};
|
|
513
|
+
const onMouseLeave = () => {
|
|
514
|
+
if (!toast.pauseOnHover) return;
|
|
515
|
+
toast._hovering = false;
|
|
516
|
+
resumeTimer();
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const triggerDismiss = () => {
|
|
520
|
+
setLeaving(true);
|
|
521
|
+
// Wait for exit animation
|
|
522
|
+
setTimeout(() => dispatch({ type: "DISMISS", id: toast.id }), 220);
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const onClick = () => {
|
|
526
|
+
if (toast.closeOnClick) triggerDismiss();
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const themeClasses = getThemeClasses(toast.theme, toast.type);
|
|
530
|
+
|
|
531
|
+
return (
|
|
532
|
+
<div
|
|
533
|
+
ref={ref}
|
|
534
|
+
role={toast.role}
|
|
535
|
+
aria-live={toast.ariaLive}
|
|
536
|
+
onMouseEnter={onMouseEnter}
|
|
537
|
+
onMouseLeave={onMouseLeave}
|
|
538
|
+
onClick={onClick}
|
|
539
|
+
className={[
|
|
540
|
+
"ultra-toast",
|
|
541
|
+
themeClasses,
|
|
542
|
+
mounted && !leaving ? "ultra-enter" : "",
|
|
543
|
+
leaving ? "ultra-leave" : "",
|
|
544
|
+
toast.classNames?.container ?? "",
|
|
545
|
+
].join(" ")}
|
|
546
|
+
style={toast.style}
|
|
547
|
+
>
|
|
548
|
+
<div className={cn("ultra-toast-body", toast.classNames?.body)}>
|
|
549
|
+
{toast.icon ? (
|
|
550
|
+
<div className={cn("ultra-toast-icon", toast.classNames?.icon)}>{toast.icon}</div>
|
|
551
|
+
) : (
|
|
552
|
+
<DefaultIcon type={toast.type} />
|
|
553
|
+
)}
|
|
554
|
+
<div className={cn("ultra-toast-content", toast.classNames?.content)}>
|
|
555
|
+
{toast.title && (
|
|
556
|
+
<div className={cn("ultra-toast-title", toast.classNames?.title)}>{toast.title}</div>
|
|
557
|
+
)}
|
|
558
|
+
{toast.description && (
|
|
559
|
+
<div className={cn("ultra-toast-desc", toast.classNames?.desc)}>{toast.description}</div>
|
|
560
|
+
)}
|
|
561
|
+
</div>
|
|
562
|
+
{toast.dismissible && (
|
|
563
|
+
<button
|
|
564
|
+
className={cn("ultra-toast-close", toast.classNames?.close)}
|
|
565
|
+
aria-label="Dismiss"
|
|
566
|
+
onClick={(e) => {
|
|
567
|
+
e.stopPropagation();
|
|
568
|
+
triggerDismiss();
|
|
569
|
+
}}
|
|
570
|
+
>
|
|
571
|
+
{toast.dismissRender ? toast.dismissRender : <XIcon size={10} />}
|
|
572
|
+
</button>
|
|
573
|
+
)}
|
|
574
|
+
</div>
|
|
575
|
+
|
|
576
|
+
{toast.showProgress && toast.duration > 0 && (
|
|
577
|
+
<div className={cn("ultra-toast-progress", toast.classNames?.progress)}>
|
|
578
|
+
<div
|
|
579
|
+
className={cn("ultra-toast-progress-bar", toast.classNames?.progress_bar)}
|
|
580
|
+
style={{ width: `${progress}%` }}
|
|
581
|
+
/>
|
|
582
|
+
</div>
|
|
583
|
+
)}
|
|
584
|
+
|
|
585
|
+
{toast.actions && toast.actions.length > 0 && (
|
|
586
|
+
<div className={cn("ultra-toast-actions", toast.classNames?.actions)}>
|
|
587
|
+
{toast.actions.map((a, i) => (
|
|
588
|
+
<button
|
|
589
|
+
key={i}
|
|
590
|
+
className={cn("ultra-toast-action", a.className)}
|
|
591
|
+
onClick={(e) => {
|
|
592
|
+
e.stopPropagation();
|
|
593
|
+
a.onClick(toast.id);
|
|
594
|
+
}}
|
|
595
|
+
>
|
|
596
|
+
{a.label}
|
|
597
|
+
</button>
|
|
598
|
+
))}
|
|
599
|
+
</div>
|
|
600
|
+
)}
|
|
601
|
+
{/* Liquid filter scope (only adds if theme is liquid) */}
|
|
602
|
+
{toast.theme === "liquid" && <LiquidDefs />}
|
|
603
|
+
{toast.theme === "liquid" && <div className="ultra-liquid-effect" />}
|
|
604
|
+
</div>
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function DefaultIcon({ type }: { type: ToastType }) {
|
|
609
|
+
const map: Record<ToastType, ReactNode> = {
|
|
610
|
+
blank: null,
|
|
611
|
+
success: (
|
|
612
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
613
|
+
<path
|
|
614
|
+
d="M20 6L9 17l-5-5"
|
|
615
|
+
stroke="currentColor"
|
|
616
|
+
strokeWidth="2"
|
|
617
|
+
strokeLinecap="round"
|
|
618
|
+
strokeLinejoin="round"
|
|
619
|
+
/>
|
|
620
|
+
</svg>
|
|
621
|
+
),
|
|
622
|
+
error: (
|
|
623
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
624
|
+
<path
|
|
625
|
+
d="M18 6L6 18M6 6l12 12"
|
|
626
|
+
stroke="currentColor"
|
|
627
|
+
strokeWidth="2"
|
|
628
|
+
strokeLinecap="round"
|
|
629
|
+
/>
|
|
630
|
+
</svg>
|
|
631
|
+
),
|
|
632
|
+
warning: (
|
|
633
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
634
|
+
<path
|
|
635
|
+
d="M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"
|
|
636
|
+
stroke="currentColor"
|
|
637
|
+
strokeWidth="2"
|
|
638
|
+
strokeLinecap="round"
|
|
639
|
+
strokeLinejoin="round"
|
|
640
|
+
/>
|
|
641
|
+
</svg>
|
|
642
|
+
),
|
|
643
|
+
info: (
|
|
644
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
645
|
+
<path
|
|
646
|
+
d="M13 16h-1v-4h-1m1-4h.01"
|
|
647
|
+
stroke="currentColor"
|
|
648
|
+
strokeWidth="2"
|
|
649
|
+
strokeLinecap="round"
|
|
650
|
+
strokeLinejoin="round"
|
|
651
|
+
/>
|
|
652
|
+
</svg>
|
|
653
|
+
),
|
|
654
|
+
loading: <span className="ultra-spinner" aria-hidden />,
|
|
655
|
+
};
|
|
656
|
+
return <div className="ultra-toast-icon">{map[type]}</div>;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function LiquidDefs() {
|
|
660
|
+
// SVG gooey filter for liquid theme
|
|
661
|
+
return (
|
|
662
|
+
<>
|
|
663
|
+
<svg
|
|
664
|
+
className="ultra-svg-defs"
|
|
665
|
+
aria-hidden
|
|
666
|
+
style={{ position: "absolute", width: 0, height: 0 }}
|
|
667
|
+
>
|
|
668
|
+
<defs>
|
|
669
|
+
{/* <filter id="ultra-goo"> */}
|
|
670
|
+
{/* <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" />
|
|
671
|
+
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10" result="goo" />
|
|
672
|
+
<feComposite in="SourceGraphic" in2="goo" operator="atop" /> */}
|
|
673
|
+
{/* <feTurbulence
|
|
674
|
+
type="turbulence"
|
|
675
|
+
baseFrequency="0.01 0.1"
|
|
676
|
+
numOctaves="1"
|
|
677
|
+
result="turbulence"
|
|
678
|
+
/>
|
|
679
|
+
<feDisplacementMap
|
|
680
|
+
in2="turbulence"
|
|
681
|
+
in="SourceGraphic"
|
|
682
|
+
scale="8"
|
|
683
|
+
xChannelSelector="R"
|
|
684
|
+
yChannelSelector="G"
|
|
685
|
+
/> */}
|
|
686
|
+
|
|
687
|
+
{/* </filter> */}
|
|
688
|
+
{/* <filter id="ultra-goo" primitiveUnits="objectBoundingBox">
|
|
689
|
+
<feImage href="data:image/png;base64,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" x="0" y="0" width="1" height="1" result="map"/>
|
|
690
|
+
<feGaussianBlur in="SourceGraphic" stdDeviation="0.02" result="blur"/>
|
|
691
|
+
<feDisplacementMap id="disp" in="blur" in2="map" scale="1" xChannelSelector="R" yChannelSelector="G">
|
|
692
|
+
<animate attributeName="scale" to="1.4" dur="0.3s" begin="btn.mouseover" fill="freeze"/>
|
|
693
|
+
<animate attributeName="scale" to="1" dur="0.3s" begin="btn.mouseout" fill="freeze"/>
|
|
694
|
+
</feDisplacementMap>
|
|
695
|
+
</filter> */}
|
|
696
|
+
<filter
|
|
697
|
+
id="ultra-goo"
|
|
698
|
+
x="0%"
|
|
699
|
+
y="0%"
|
|
700
|
+
width="100%"
|
|
701
|
+
height="100%"
|
|
702
|
+
filterUnits="objectBoundingBox"
|
|
703
|
+
>
|
|
704
|
+
<feTurbulence
|
|
705
|
+
type="fractalNoise"
|
|
706
|
+
baseFrequency="0.001 0.005"
|
|
707
|
+
numOctaves="1"
|
|
708
|
+
seed="17"
|
|
709
|
+
result="turbulence"
|
|
710
|
+
/>
|
|
711
|
+
<feComponentTransfer in="turbulence" result="mapped">
|
|
712
|
+
<feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5" />
|
|
713
|
+
<feFuncG type="gamma" amplitude="0" exponent="1" offset="0" />
|
|
714
|
+
<feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5" />
|
|
715
|
+
</feComponentTransfer>
|
|
716
|
+
<feGaussianBlur in="turbulence" stdDeviation="6" result="softMap" />
|
|
717
|
+
<feSpecularLighting
|
|
718
|
+
in="softMap"
|
|
719
|
+
surfaceScale="5"
|
|
720
|
+
specularConstant="1"
|
|
721
|
+
specularExponent="100"
|
|
722
|
+
lightingColor="white"
|
|
723
|
+
result="specLight"
|
|
724
|
+
>
|
|
725
|
+
<fePointLight x="-200" y="-200" z="300" />
|
|
726
|
+
</feSpecularLighting>
|
|
727
|
+
<feComposite
|
|
728
|
+
in="specLight"
|
|
729
|
+
operator="arithmetic"
|
|
730
|
+
k1="0"
|
|
731
|
+
k2="1"
|
|
732
|
+
k3="1"
|
|
733
|
+
k4="0"
|
|
734
|
+
result="litImage"
|
|
735
|
+
/>
|
|
736
|
+
<feDisplacementMap
|
|
737
|
+
in="SourceGraphic"
|
|
738
|
+
in2="softMap"
|
|
739
|
+
scale="200"
|
|
740
|
+
xChannelSelector="R"
|
|
741
|
+
yChannelSelector="G"
|
|
742
|
+
/>
|
|
743
|
+
</filter>
|
|
744
|
+
</defs>
|
|
745
|
+
</svg>
|
|
746
|
+
</>
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function getThemeClasses(theme: ToastTheme, type: ToastType) {
|
|
751
|
+
// const colorVars = {
|
|
752
|
+
// success: "72, 187, 120",
|
|
753
|
+
// error: "239, 68, 68",
|
|
754
|
+
// warning: "245, 158, 11",
|
|
755
|
+
// info: "59, 130, 246",
|
|
756
|
+
// loading: "148, 163, 184",
|
|
757
|
+
// blank: "156, 163, 175",
|
|
758
|
+
// }[type];
|
|
759
|
+
|
|
760
|
+
const baseClass = "ultra-card";
|
|
761
|
+
|
|
762
|
+
if (theme === "glass") return `${baseClass} ultra-glass ultra-${type}`;
|
|
763
|
+
if (theme === "liquid") return `${baseClass} ultra-liquid ultra-${type}`;
|
|
764
|
+
return `${baseClass} ultra-minimal ultra-${type}`;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// ------------------------------------------------------------
|
|
768
|
+
// Global Styles (Tailwind-like utility via CSS vars)
|
|
769
|
+
// ------------------------------------------------------------
|
|
770
|
+
|
|
771
|
+
function useToastGlobalStyles() {
|
|
772
|
+
useEffect(() => {
|
|
773
|
+
if (document.getElementById("ultra-toast-styles")) return;
|
|
774
|
+
const style = document.createElement("style");
|
|
775
|
+
style.id = "ultra-toast-styles";
|
|
776
|
+
style.textContent = `
|
|
777
|
+
:root {
|
|
778
|
+
--ultra-bg: 255, 255, 255;
|
|
779
|
+
--ultra-ink: 15, 23, 42;
|
|
780
|
+
--ultra-success: 72, 187, 120;
|
|
781
|
+
--ultra-error: 239, 68, 68;
|
|
782
|
+
--ultra-warning: 245, 158, 11;
|
|
783
|
+
--ultra-info: 59, 130, 246;
|
|
784
|
+
--ultra-loading: 148, 163, 184;
|
|
785
|
+
--ultra-blank: 156, 163, 175;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.ultra-stack {
|
|
789
|
+
display: flex;
|
|
790
|
+
flex-direction: column;
|
|
791
|
+
gap: 12px;
|
|
792
|
+
align-items: stretch;
|
|
793
|
+
max-width: min(92vw, 420px);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.ultra-card {
|
|
797
|
+
position: relative;
|
|
798
|
+
border-radius: 16px;
|
|
799
|
+
padding: 12px 14px;
|
|
800
|
+
display: flex;
|
|
801
|
+
flex-direction: column;
|
|
802
|
+
gap: 8px;
|
|
803
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.14);
|
|
804
|
+
transform-origin: center;
|
|
805
|
+
overflow: hidden;
|
|
806
|
+
cursor: pointer;
|
|
807
|
+
transition: all 0.2s ease;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.ultra-glass {
|
|
811
|
+
background: rgba(255,255,255,0.3);
|
|
812
|
+
backdrop-filter: blur(8px) saturate(150%);
|
|
813
|
+
color: rgb(var(--ultra-ink));
|
|
814
|
+
border: 1px solid rgba(255,255,255,0.35);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.ultra-liquid {
|
|
818
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.14);
|
|
819
|
+
color: rgb(var(--ultra-ink));
|
|
820
|
+
backdrop-filter: url(#ultra-goo);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.ultra-liquid-effect {
|
|
824
|
+
background: rgba(255,255,255,0.5);
|
|
825
|
+
backdrop-filter: blur(18px) saturate(160%);
|
|
826
|
+
width: 100%;
|
|
827
|
+
height: 100%;
|
|
828
|
+
top: 0;
|
|
829
|
+
left:0;
|
|
830
|
+
position: absolute;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.ultra-minimal {
|
|
834
|
+
background: white;
|
|
835
|
+
border: 1px solid rgba(0,0,0,0.08);
|
|
836
|
+
color: rgb(var(--ultra-ink));
|
|
837
|
+
border: 1px solid rgba(255,255,255,0.35);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/* Type-specific colors */
|
|
841
|
+
.ultra-success { --ultra-accent: var(--ultra-success); }
|
|
842
|
+
.ultra-error { --ultra-accent: var(--ultra-error); }
|
|
843
|
+
.ultra-warning { --ultra-accent: var(--ultra-warning); }
|
|
844
|
+
.ultra-info { --ultra-accent: var(--ultra-info); }
|
|
845
|
+
.ultra-loading { --ultra-accent: var(--ultra-loading); }
|
|
846
|
+
.ultra-blank { --ultra-accent: var(--ultra-blank); }
|
|
847
|
+
|
|
848
|
+
.ultra-enter {
|
|
849
|
+
animation: ultra-enter 0.22s cubic-bezier(0,1.95,1,1.44) both;
|
|
850
|
+
}
|
|
851
|
+
.ultra-leave {
|
|
852
|
+
animation: ultra-leave 0.22s ease forwards;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
@keyframes ultra-enter {
|
|
856
|
+
from {
|
|
857
|
+
opacity: 0;
|
|
858
|
+
transform: translateY(-8px) scale(0.96);
|
|
859
|
+
}
|
|
860
|
+
to {
|
|
861
|
+
opacity: 1;
|
|
862
|
+
transform: translateY(0) scale(1);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
@keyframes ultra-leave {
|
|
866
|
+
to {
|
|
867
|
+
opacity: 0;
|
|
868
|
+
transform: translateY(-8px) scale(0.96);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.ultra-toast-body {
|
|
873
|
+
display: grid;
|
|
874
|
+
grid-template-columns: auto 1fr auto;
|
|
875
|
+
align-items: center;
|
|
876
|
+
gap: 10px;
|
|
877
|
+
z-index: 1;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.ultra-toast-icon {
|
|
881
|
+
display: grid;
|
|
882
|
+
place-items: center;
|
|
883
|
+
color: rgb(var(--ultra-accent));
|
|
884
|
+
flex-shrink: 0;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.ultra-toast-content {
|
|
888
|
+
min-width: 0;
|
|
889
|
+
flex: 1;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.ultra-toast-title {
|
|
893
|
+
font-weight: 700;
|
|
894
|
+
font-size: 14px;
|
|
895
|
+
line-height: 1.2;
|
|
896
|
+
color: rgb(var(--ultra-ink));
|
|
897
|
+
margin-bottom: 2px;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.ultra-toast-desc {
|
|
901
|
+
font-size: 13px;
|
|
902
|
+
color: rgba(var(--ultra-ink), 0.8);
|
|
903
|
+
overflow-wrap: anywhere;
|
|
904
|
+
line-height: 1.4;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.ultra-toast-close {
|
|
908
|
+
border: 0;
|
|
909
|
+
background: transparent;
|
|
910
|
+
color: rgba(var(--ultra-ink), 0.6);
|
|
911
|
+
font-size: 18px;
|
|
912
|
+
line-height: 1;
|
|
913
|
+
cursor: pointer;
|
|
914
|
+
opacity: 0.8;
|
|
915
|
+
padding: 2px;
|
|
916
|
+
border-radius: 4px;
|
|
917
|
+
transition: all 0.15s ease;
|
|
918
|
+
flex-shrink: 0;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.ultra-toast-close:hover {
|
|
922
|
+
opacity: 1;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.ultra-toast-progress {
|
|
926
|
+
height: 3px;
|
|
927
|
+
background: rgba(var(--ultra-accent), 0.2);
|
|
928
|
+
border-radius: 99px;
|
|
929
|
+
overflow: hidden;
|
|
930
|
+
margin-top: 4px;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.ultra-toast-progress-bar {
|
|
934
|
+
height: 100%;
|
|
935
|
+
background: linear-gradient(90deg, rgb(var(--ultra-accent)), rgba(var(--ultra-accent), 0.6));
|
|
936
|
+
transition: width 0.1s ease;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.ultra-toast-actions {
|
|
940
|
+
display: flex;
|
|
941
|
+
gap: 8px;
|
|
942
|
+
margin-top: 8px;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.ultra-toast-action {
|
|
946
|
+
font-size: 12px;
|
|
947
|
+
border-radius: 99px;
|
|
948
|
+
padding: 6px 10px;
|
|
949
|
+
border: 1px solid rgba(var(--ultra-accent), 0.35);
|
|
950
|
+
color: rgb(var(--ultra-accent));
|
|
951
|
+
background: rgba(var(--ultra-accent), 0.08);
|
|
952
|
+
cursor: pointer;
|
|
953
|
+
transition: all 0.15s ease;
|
|
954
|
+
font-weight: 500;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.ultra-toast-action:hover {
|
|
958
|
+
background: rgba(var(--ultra-accent), 0.14);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/* Spinner */
|
|
962
|
+
.ultra-spinner {
|
|
963
|
+
width: 18px;
|
|
964
|
+
height: 18px;
|
|
965
|
+
border-radius: 99px;
|
|
966
|
+
border: 2px solid rgba(var(--ultra-accent), 0.35);
|
|
967
|
+
border-top-color: rgb(var(--ultra-accent));
|
|
968
|
+
display: inline-block;
|
|
969
|
+
animation: ultra-spin 1s linear infinite;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
@keyframes ultra-spin {
|
|
973
|
+
to { transform: rotate(360deg); }
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/* SVG filter defs */
|
|
977
|
+
.ultra-svg-defs {
|
|
978
|
+
position: absolute;
|
|
979
|
+
width: 0;
|
|
980
|
+
height: 0;
|
|
981
|
+
pointer-events: none;
|
|
982
|
+
}
|
|
983
|
+
`;
|
|
984
|
+
document.head.appendChild(style);
|
|
985
|
+
}, []);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// ------------------------------------------------------------
|
|
989
|
+
// Helper hooks (optional)
|
|
990
|
+
// ------------------------------------------------------------
|
|
991
|
+
|
|
992
|
+
export function useToast() {
|
|
993
|
+
return t;
|
|
994
|
+
}
|