@rozie-ui/toast-solid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/index.cjs +482 -0
- package/dist/index.d.cts +51 -0
- package/dist/index.d.mts +51 -0
- package/dist/index.mjs +477 -0
- package/package.json +66 -0
- package/src/Toaster.tsx +683 -0
- package/src/index.ts +5 -0
- package/src/themes/base.css +57 -0
- package/src/themes/bootstrap.css +27 -0
- package/src/themes/material.css +28 -0
- package/src/themes/shadcn.css +29 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { JSX } from "solid-js";
|
|
2
|
+
|
|
3
|
+
//#region src/Toaster.d.ts
|
|
4
|
+
interface ToastSlotCtx {
|
|
5
|
+
toast: any;
|
|
6
|
+
dismiss: any;
|
|
7
|
+
}
|
|
8
|
+
interface ToasterProps {
|
|
9
|
+
/**
|
|
10
|
+
* Which corner the toast stack renders in: `'top-left'`, `'top-right'`, `'top-center'`, `'bottom-left'`, `'bottom-right'`, or `'bottom-center'`. Drives the fixed-position layout and the stack direction.
|
|
11
|
+
*/
|
|
12
|
+
position?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Default auto-dismiss time in milliseconds, applied to any toast that does not pass its own `duration`. `0` (or a per-toast `duration` of `0`) makes the toast sticky — it stays until explicitly dismissed.
|
|
15
|
+
*/
|
|
16
|
+
duration?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of visible toasts (`0` = unlimited). When the queue exceeds this, the oldest toasts drop off the stack.
|
|
19
|
+
*/
|
|
20
|
+
max?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Opt **out** of pausing the auto-dismiss timers while the pointer is over the stack. By default hovering pauses every timer and leaving restarts them; set this to keep toasts dismissing on schedule regardless of hover.
|
|
23
|
+
*/
|
|
24
|
+
disablePauseOnHover?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Accessible name for the live region (`role="region"`), applied as its `aria-label`. Defaults to `'Notifications'` when not set, so assistive tech can navigate to the toast stack as a landmark.
|
|
27
|
+
*/
|
|
28
|
+
ariaLabel?: (string) | null;
|
|
29
|
+
/**
|
|
30
|
+
* Opt **out** of pointer swipe-to-dismiss. By default, dragging a toast past 45% of its own width/height (direction auto-derived from `position`) or a fast flick dismisses it with reason `'swipe'`; a short drag springs back. A drag starting on the close button (or any button/link) never swipes.
|
|
31
|
+
*/
|
|
32
|
+
disableSwipe?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Opt **in** to a sonner-style collapsed stack: a single-cell grid overlay with depth-driven transforms (toasts at depth 3+ fade to invisible), newest on top. Hovering the region or moving keyboard focus into it expands to the normal flex-column stack; leaving re-collapses. `false` (default) renders the plain flex column at all times.
|
|
35
|
+
*/
|
|
36
|
+
stacked?: boolean;
|
|
37
|
+
onDismissed?: (...args: unknown[]) => void;
|
|
38
|
+
toastSlot?: (ctx: ToastSlotCtx) => JSX.Element;
|
|
39
|
+
slots?: Record<string, (ctx: any) => JSX.Element>;
|
|
40
|
+
ref?: (h: ToasterHandle) => void;
|
|
41
|
+
}
|
|
42
|
+
interface ToasterHandle {
|
|
43
|
+
show: (...args: any[]) => any;
|
|
44
|
+
dismiss: (...args: any[]) => any;
|
|
45
|
+
clear: (...args: any[]) => any;
|
|
46
|
+
patch: (...args: any[]) => any;
|
|
47
|
+
promise: (...args: any[]) => any;
|
|
48
|
+
}
|
|
49
|
+
declare function Toaster(_props: ToasterProps): JSX.Element;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { Toaster, Toaster as default, type ToasterHandle };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { Show, createSignal, mergeProps, onCleanup, onMount, splitProps } from "solid-js";
|
|
2
|
+
import { Key } from "@solid-primitives/keyed";
|
|
3
|
+
import { __rozieInjectStyle, mergeListeners, parseInlineStyle, rozieAttr, rozieClass, rozieDisplay } from "@rozie/runtime-solid";
|
|
4
|
+
//#region src/Toaster.tsx
|
|
5
|
+
__rozieInjectStyle("Toaster-12d4265c", `@media (prefers-reduced-motion: reduce) {
|
|
6
|
+
.rozie-toast[data-rozie-s-12d4265c] {
|
|
7
|
+
animation-name: rozie-toast-fade-in;
|
|
8
|
+
animation-duration: 1ms;
|
|
9
|
+
}
|
|
10
|
+
.rozie-toast--exiting[data-rozie-s-12d4265c] {
|
|
11
|
+
animation-name: rozie-toast-fade-out;
|
|
12
|
+
animation-duration: 1ms;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
.rozie-toaster[data-rozie-s-12d4265c] {
|
|
16
|
+
position: fixed;
|
|
17
|
+
z-index: var(--rozie-toast-z, 9999);
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--rozie-toast-gap, 0.5rem);
|
|
21
|
+
padding: var(--rozie-toast-region-padding, 1rem);
|
|
22
|
+
max-width: var(--rozie-toast-max-width, calc(100vw - 2rem));
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
font: var(--rozie-toast-font, inherit);
|
|
25
|
+
}
|
|
26
|
+
.rozie-toaster[data-rozie-s-12d4265c] > *[data-rozie-s-12d4265c] {
|
|
27
|
+
pointer-events: auto;
|
|
28
|
+
}
|
|
29
|
+
.rozie-toaster--top-left[data-rozie-s-12d4265c] { top: 0; left: 0; align-items: flex-start; }
|
|
30
|
+
.rozie-toaster--top-right[data-rozie-s-12d4265c] { top: 0; right: 0; align-items: flex-end; }
|
|
31
|
+
.rozie-toaster--top-center[data-rozie-s-12d4265c] { top: 0; left: 50%; transform: translateX(-50%); align-items: center; }
|
|
32
|
+
.rozie-toaster--bottom-left[data-rozie-s-12d4265c] { bottom: 0; left: 0; align-items: flex-start; flex-direction: column-reverse; }
|
|
33
|
+
.rozie-toaster--bottom-right[data-rozie-s-12d4265c] { bottom: 0; right: 0; align-items: flex-end; flex-direction: column-reverse; }
|
|
34
|
+
.rozie-toaster--bottom-center[data-rozie-s-12d4265c] { bottom: 0; left: 50%; transform: translateX(-50%); align-items: center; flex-direction: column-reverse; }
|
|
35
|
+
.rozie-toaster--stacked[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c] {
|
|
36
|
+
grid-area: 1 / 1;
|
|
37
|
+
z-index: calc(100 - var(--rozie-toast-depth, 0));
|
|
38
|
+
}
|
|
39
|
+
.rozie-toaster--stacked[data-rozie-s-12d4265c]:not([data-rozie-s-12d4265c]:hover):not([data-rozie-s-12d4265c]:focus-within) {
|
|
40
|
+
display: grid;
|
|
41
|
+
}
|
|
42
|
+
.rozie-toaster--stacked[data-rozie-s-12d4265c]:not([data-rozie-s-12d4265c]:hover):not([data-rozie-s-12d4265c]:focus-within) .rozie-toast[data-rozie-s-12d4265c] {
|
|
43
|
+
transform:
|
|
44
|
+
translateY(calc(var(--rozie-toast-depth, 0) * var(--rozie-toast-stack-offset, 8px)))
|
|
45
|
+
scale(calc(1 - var(--rozie-toast-depth, 0) * var(--rozie-toast-stack-scale-step, 0.05)));
|
|
46
|
+
opacity: calc(1 - min(1, max(0, var(--rozie-toast-depth, 0) - 2)));
|
|
47
|
+
}
|
|
48
|
+
.rozie-toaster--stacked.rozie-toaster--bottom-left[data-rozie-s-12d4265c]:not([data-rozie-s-12d4265c]:hover):not([data-rozie-s-12d4265c]:focus-within) .rozie-toast[data-rozie-s-12d4265c],
|
|
49
|
+
.rozie-toaster--stacked.rozie-toaster--bottom-right[data-rozie-s-12d4265c]:not([data-rozie-s-12d4265c]:hover):not([data-rozie-s-12d4265c]:focus-within) .rozie-toast[data-rozie-s-12d4265c],
|
|
50
|
+
.rozie-toaster--stacked.rozie-toaster--bottom-center[data-rozie-s-12d4265c]:not([data-rozie-s-12d4265c]:hover):not([data-rozie-s-12d4265c]:focus-within) .rozie-toast[data-rozie-s-12d4265c] {
|
|
51
|
+
transform:
|
|
52
|
+
translateY(calc(var(--rozie-toast-depth, 0) * var(--rozie-toast-stack-offset, 8px) * -1))
|
|
53
|
+
scale(calc(1 - var(--rozie-toast-depth, 0) * var(--rozie-toast-stack-scale-step, 0.05)));
|
|
54
|
+
}
|
|
55
|
+
.rozie-toast[data-rozie-s-12d4265c] {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
gap: var(--rozie-toast-content-gap, 0.75rem);
|
|
59
|
+
min-width: var(--rozie-toast-min-width, 16rem);
|
|
60
|
+
max-width: var(--rozie-toast-toast-max-width, 24rem);
|
|
61
|
+
padding: var(--rozie-toast-padding, 0.75rem 1rem);
|
|
62
|
+
color: var(--rozie-toast-color, #fff);
|
|
63
|
+
background: var(--rozie-toast-bg, #333);
|
|
64
|
+
border-radius: var(--rozie-toast-radius, 0.5rem);
|
|
65
|
+
box-shadow: var(--rozie-toast-shadow, 0 6px 20px rgba(0, 0, 0, 0.25));
|
|
66
|
+
/* Swipe: page scroll stays alive on touch along the axis the toast does
|
|
67
|
+
NOT move on. The transition here drives the spring-back (the active-drag
|
|
68
|
+
:style sets an inline \`transition: none\` to track the finger 1:1;
|
|
69
|
+
releasing it without a further gesture falls back to this transition). */
|
|
70
|
+
touch-action: pan-y;
|
|
71
|
+
transition: transform 200ms ease, opacity 200ms ease;
|
|
72
|
+
}
|
|
73
|
+
.rozie-toaster--top-center[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c],
|
|
74
|
+
.rozie-toaster--bottom-center[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c] {
|
|
75
|
+
touch-action: pan-x;
|
|
76
|
+
}
|
|
77
|
+
.rozie-toast--success[data-rozie-s-12d4265c] { background: var(--rozie-toast-success-bg, #16a34a); }
|
|
78
|
+
.rozie-toast--error[data-rozie-s-12d4265c] { background: var(--rozie-toast-error-bg, #dc2626); }
|
|
79
|
+
.rozie-toast--warning[data-rozie-s-12d4265c] { background: var(--rozie-toast-warning-bg, #ca8a04); }
|
|
80
|
+
.rozie-toast--info[data-rozie-s-12d4265c] { background: var(--rozie-toast-info-bg, var(--rozie-toast-bg, #333)); }
|
|
81
|
+
from[data-rozie-s-12d4265c] { opacity: 0; transform: translateY(-0.5rem); }
|
|
82
|
+
to[data-rozie-s-12d4265c] { opacity: 1; transform: translateY(0); }
|
|
83
|
+
from[data-rozie-s-12d4265c] { opacity: 0; transform: translateY(0.5rem); }
|
|
84
|
+
to[data-rozie-s-12d4265c] { opacity: 1; transform: translateY(0); }
|
|
85
|
+
from[data-rozie-s-12d4265c] { opacity: 1; transform: translateY(0); }
|
|
86
|
+
to[data-rozie-s-12d4265c] { opacity: 0; transform: translateY(-0.5rem); }
|
|
87
|
+
from[data-rozie-s-12d4265c] { opacity: 1; transform: translateY(0); }
|
|
88
|
+
to[data-rozie-s-12d4265c] { opacity: 0; transform: translateY(0.5rem); }
|
|
89
|
+
.rozie-toast[data-rozie-s-12d4265c] {
|
|
90
|
+
animation: rozie-toast-enter var(--rozie-toast-enter-duration, 200ms) ease-out;
|
|
91
|
+
}
|
|
92
|
+
.rozie-toaster--bottom-left[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c],
|
|
93
|
+
.rozie-toaster--bottom-right[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c],
|
|
94
|
+
.rozie-toaster--bottom-center[data-rozie-s-12d4265c] .rozie-toast[data-rozie-s-12d4265c] {
|
|
95
|
+
animation-name: rozie-toast-enter-from-bottom;
|
|
96
|
+
}
|
|
97
|
+
.rozie-toast--exiting[data-rozie-s-12d4265c] {
|
|
98
|
+
animation: rozie-toast-exit var(--rozie-toast-exit-duration, 200ms) ease-in forwards;
|
|
99
|
+
}
|
|
100
|
+
.rozie-toaster--bottom-left[data-rozie-s-12d4265c] .rozie-toast--exiting[data-rozie-s-12d4265c],
|
|
101
|
+
.rozie-toaster--bottom-right[data-rozie-s-12d4265c] .rozie-toast--exiting[data-rozie-s-12d4265c],
|
|
102
|
+
.rozie-toaster--bottom-center[data-rozie-s-12d4265c] .rozie-toast--exiting[data-rozie-s-12d4265c] {
|
|
103
|
+
animation-name: rozie-toast-exit-to-bottom;
|
|
104
|
+
}
|
|
105
|
+
from[data-rozie-s-12d4265c] { opacity: 0; }
|
|
106
|
+
to[data-rozie-s-12d4265c] { opacity: 1; }
|
|
107
|
+
from[data-rozie-s-12d4265c] { opacity: 1; }
|
|
108
|
+
to[data-rozie-s-12d4265c] { opacity: 0; }
|
|
109
|
+
from[data-rozie-s-12d4265c] { opacity: 1; transform: translateX(0); }
|
|
110
|
+
to[data-rozie-s-12d4265c] { opacity: 0; transform: translateX(calc(var(--rozie-toast-swipe-exit, 1) * 100%)); }
|
|
111
|
+
from[data-rozie-s-12d4265c] { opacity: 1; transform: translateY(0); }
|
|
112
|
+
to[data-rozie-s-12d4265c] { opacity: 0; transform: translateY(calc(var(--rozie-toast-swipe-exit, 1) * 100%)); }
|
|
113
|
+
.rozie-toast--exiting.rozie-toast--swipe-exit[data-rozie-s-12d4265c] {
|
|
114
|
+
animation-name: rozie-toast-swipe-exit-x;
|
|
115
|
+
}
|
|
116
|
+
.rozie-toaster--top-center[data-rozie-s-12d4265c] .rozie-toast--exiting.rozie-toast--swipe-exit[data-rozie-s-12d4265c],
|
|
117
|
+
.rozie-toaster--bottom-center[data-rozie-s-12d4265c] .rozie-toast--exiting.rozie-toast--swipe-exit[data-rozie-s-12d4265c] {
|
|
118
|
+
animation-name: rozie-toast-swipe-exit-y;
|
|
119
|
+
}
|
|
120
|
+
.rozie-toast-spinner[data-rozie-s-12d4265c] {
|
|
121
|
+
flex: 0 0 auto;
|
|
122
|
+
width: var(--rozie-toast-spinner-size, 1em);
|
|
123
|
+
height: var(--rozie-toast-spinner-size, 1em);
|
|
124
|
+
border: 2px solid color-mix(in srgb, var(--rozie-toast-spinner-color, currentColor) 25%, transparent);
|
|
125
|
+
border-top-color: var(--rozie-toast-spinner-color, currentColor);
|
|
126
|
+
border-radius: 50%;
|
|
127
|
+
animation: rozie-toast-spin 0.75s linear infinite;
|
|
128
|
+
}
|
|
129
|
+
to[data-rozie-s-12d4265c] { transform: rotate(360deg); }
|
|
130
|
+
.rozie-toast-message[data-rozie-s-12d4265c] {
|
|
131
|
+
flex: 1 1 auto;
|
|
132
|
+
font-size: var(--rozie-toast-font-size, 0.9rem);
|
|
133
|
+
}
|
|
134
|
+
.rozie-toast-close[data-rozie-s-12d4265c] {
|
|
135
|
+
flex: 0 0 auto;
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
139
|
+
width: var(--rozie-toast-close-size, 1.25rem);
|
|
140
|
+
height: var(--rozie-toast-close-size, 1.25rem);
|
|
141
|
+
padding: 0;
|
|
142
|
+
font-size: 1.1rem;
|
|
143
|
+
line-height: 1;
|
|
144
|
+
color: inherit;
|
|
145
|
+
background: transparent;
|
|
146
|
+
border: none;
|
|
147
|
+
border-radius: 0.25rem;
|
|
148
|
+
opacity: var(--rozie-toast-close-opacity, 0.75);
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
}
|
|
151
|
+
.rozie-toast-close[data-rozie-s-12d4265c]:hover {
|
|
152
|
+
opacity: 1;
|
|
153
|
+
}`);
|
|
154
|
+
function Toaster(_props) {
|
|
155
|
+
const [local, attrs] = splitProps(mergeProps({
|
|
156
|
+
position: "bottom-right",
|
|
157
|
+
duration: 4e3,
|
|
158
|
+
max: 0,
|
|
159
|
+
disablePauseOnHover: false,
|
|
160
|
+
ariaLabel: null,
|
|
161
|
+
disableSwipe: false,
|
|
162
|
+
stacked: false
|
|
163
|
+
}, _props), [
|
|
164
|
+
"position",
|
|
165
|
+
"duration",
|
|
166
|
+
"max",
|
|
167
|
+
"disablePauseOnHover",
|
|
168
|
+
"ariaLabel",
|
|
169
|
+
"disableSwipe",
|
|
170
|
+
"stacked",
|
|
171
|
+
"ref"
|
|
172
|
+
]);
|
|
173
|
+
onMount(() => {
|
|
174
|
+
local.ref?.({
|
|
175
|
+
show,
|
|
176
|
+
dismiss,
|
|
177
|
+
clear,
|
|
178
|
+
patch,
|
|
179
|
+
promise
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
const [toasts, setToasts] = createSignal([]);
|
|
183
|
+
const [seq, setSeq] = createSignal(0);
|
|
184
|
+
const [swipe, setSwipe] = createSignal(null);
|
|
185
|
+
const [swipeGesture, setSwipeGesture] = createSignal(null);
|
|
186
|
+
onCleanup(() => {
|
|
187
|
+
unmounted = true;
|
|
188
|
+
teardownTimers();
|
|
189
|
+
});
|
|
190
|
+
let timers = {};
|
|
191
|
+
let exitFailsafes = {};
|
|
192
|
+
let unmounted = false;
|
|
193
|
+
let seqLocal = 0;
|
|
194
|
+
let paused = false;
|
|
195
|
+
function startTimer(toast) {
|
|
196
|
+
if (!toast || !toast.duration || toast.duration <= 0) return;
|
|
197
|
+
if (typeof window === "undefined") return;
|
|
198
|
+
const existing = timers[toast.id];
|
|
199
|
+
if (existing && existing.handle != null) window.clearTimeout(existing.handle);
|
|
200
|
+
const remaining = toast.duration;
|
|
201
|
+
const handle = window.setTimeout(() => dismissBegin(toast.id, "timeout"), remaining);
|
|
202
|
+
timers[toast.id] = {
|
|
203
|
+
handle,
|
|
204
|
+
startedAt: Date.now(),
|
|
205
|
+
remaining
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function clearTimer(id) {
|
|
209
|
+
const entry = timers[id];
|
|
210
|
+
if (entry && entry.handle != null && typeof window !== "undefined") window.clearTimeout(entry.handle);
|
|
211
|
+
delete timers[id];
|
|
212
|
+
}
|
|
213
|
+
function pauseTimers() {
|
|
214
|
+
paused = true;
|
|
215
|
+
if (typeof window === "undefined") return;
|
|
216
|
+
for (const id in timers) {
|
|
217
|
+
const entry = timers[id];
|
|
218
|
+
if (entry.handle == null) continue;
|
|
219
|
+
window.clearTimeout(entry.handle);
|
|
220
|
+
const elapsed = Date.now() - entry.startedAt;
|
|
221
|
+
const remaining = Math.max(0, entry.remaining - elapsed);
|
|
222
|
+
timers[id] = {
|
|
223
|
+
handle: null,
|
|
224
|
+
startedAt: entry.startedAt,
|
|
225
|
+
remaining
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function resumeTimers() {
|
|
230
|
+
paused = false;
|
|
231
|
+
if (typeof window === "undefined") return;
|
|
232
|
+
for (const id in timers) {
|
|
233
|
+
const entry = timers[id];
|
|
234
|
+
if (entry.handle != null) continue;
|
|
235
|
+
if (entry.remaining == null || entry.remaining <= 0) {
|
|
236
|
+
dismissBegin(id, "timeout");
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const remaining = entry.remaining;
|
|
240
|
+
const handle = window.setTimeout(() => dismissBegin(id, "timeout"), remaining);
|
|
241
|
+
timers[id] = {
|
|
242
|
+
handle,
|
|
243
|
+
startedAt: Date.now(),
|
|
244
|
+
remaining
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function teardownTimers() {
|
|
249
|
+
if (typeof window !== "undefined") {
|
|
250
|
+
for (const id in timers) {
|
|
251
|
+
const entry = timers[id];
|
|
252
|
+
if (entry.handle != null) window.clearTimeout(entry.handle);
|
|
253
|
+
}
|
|
254
|
+
for (const id in exitFailsafes) if (exitFailsafes[id] != null) window.clearTimeout(exitFailsafes[id]);
|
|
255
|
+
}
|
|
256
|
+
timers = {};
|
|
257
|
+
exitFailsafes = {};
|
|
258
|
+
}
|
|
259
|
+
function show(input) {
|
|
260
|
+
const t = input || {};
|
|
261
|
+
let id;
|
|
262
|
+
if (t.id != null) id = String(t.id);
|
|
263
|
+
else {
|
|
264
|
+
const s = Math.max(seq(), seqLocal);
|
|
265
|
+
id = "t" + s;
|
|
266
|
+
seqLocal = s + 1;
|
|
267
|
+
setSeq(s + 1);
|
|
268
|
+
}
|
|
269
|
+
const toast = {
|
|
270
|
+
id,
|
|
271
|
+
message: t.message != null ? t.message : "",
|
|
272
|
+
type: t.type || "info",
|
|
273
|
+
duration: t.duration != null ? t.duration : local.duration
|
|
274
|
+
};
|
|
275
|
+
setToasts(toasts().concat([toast]).slice(local.max > 0 ? Math.max(0, toasts().length + 1 - local.max) : 0));
|
|
276
|
+
startTimer(toast);
|
|
277
|
+
return id;
|
|
278
|
+
}
|
|
279
|
+
const EXIT_FAILSAFE_MS = 350;
|
|
280
|
+
function removeToast(id) {
|
|
281
|
+
if (typeof window !== "undefined" && exitFailsafes[id] != null) window.clearTimeout(exitFailsafes[id]);
|
|
282
|
+
delete exitFailsafes[id];
|
|
283
|
+
setToasts(toasts().filter((t) => t.id !== id));
|
|
284
|
+
}
|
|
285
|
+
function dismissBegin(id, reason, extra) {
|
|
286
|
+
const entry = toasts().find((t) => t.id === id);
|
|
287
|
+
if (!entry || entry.exiting) return;
|
|
288
|
+
clearTimer(id);
|
|
289
|
+
_props.onDismissed?.({
|
|
290
|
+
toast: entry,
|
|
291
|
+
reason
|
|
292
|
+
});
|
|
293
|
+
setToasts(toasts().map((t) => t.id === id ? {
|
|
294
|
+
...t,
|
|
295
|
+
exiting: true,
|
|
296
|
+
...extra || {}
|
|
297
|
+
} : t));
|
|
298
|
+
if (typeof window === "undefined") removeToast(id);
|
|
299
|
+
else exitFailsafes[id] = window.setTimeout(() => removeToast(id), EXIT_FAILSAFE_MS);
|
|
300
|
+
}
|
|
301
|
+
function dismiss(id) {
|
|
302
|
+
dismissBegin(id, "api");
|
|
303
|
+
}
|
|
304
|
+
function clear() {
|
|
305
|
+
teardownTimers();
|
|
306
|
+
setToasts([]);
|
|
307
|
+
}
|
|
308
|
+
function patch(id, changes) {
|
|
309
|
+
const c = changes || {};
|
|
310
|
+
let existed = false;
|
|
311
|
+
const next = toasts().map((t) => {
|
|
312
|
+
if (t.id !== id) return t;
|
|
313
|
+
if (t.exiting) return t;
|
|
314
|
+
existed = true;
|
|
315
|
+
const merged = { ...t };
|
|
316
|
+
if (c.message !== void 0) merged.message = c.message;
|
|
317
|
+
if (c.type !== void 0) merged.type = c.type;
|
|
318
|
+
if (c.duration !== void 0) merged.duration = c.duration;
|
|
319
|
+
return merged;
|
|
320
|
+
});
|
|
321
|
+
if (!existed) return false;
|
|
322
|
+
setToasts(next);
|
|
323
|
+
if (c.duration !== void 0) {
|
|
324
|
+
clearTimer(id);
|
|
325
|
+
const patched = next.find((t) => t.id === id);
|
|
326
|
+
if (paused) {
|
|
327
|
+
if (patched && patched.duration > 0 && typeof window !== "undefined") timers[id] = {
|
|
328
|
+
handle: null,
|
|
329
|
+
startedAt: Date.now(),
|
|
330
|
+
remaining: patched.duration
|
|
331
|
+
};
|
|
332
|
+
} else startTimer(patched);
|
|
333
|
+
}
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
function settlePromise(id, type, messageOrFn, value) {
|
|
337
|
+
if (unmounted) return;
|
|
338
|
+
const entry = toasts().find((t) => t.id === id);
|
|
339
|
+
if (!entry || entry.exiting) return;
|
|
340
|
+
patch(id, {
|
|
341
|
+
type,
|
|
342
|
+
message: typeof messageOrFn === "function" ? messageOrFn(value) : messageOrFn,
|
|
343
|
+
duration: local.duration
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function promise(p, opts) {
|
|
347
|
+
const o = opts || {};
|
|
348
|
+
const id = show({
|
|
349
|
+
type: "loading",
|
|
350
|
+
duration: 0,
|
|
351
|
+
message: o.loading
|
|
352
|
+
});
|
|
353
|
+
if (p && typeof p.then === "function") p.then((value) => settlePromise(id, "success", o.success, value)).catch((err) => settlePromise(id, "error", o.error, err));
|
|
354
|
+
return id;
|
|
355
|
+
}
|
|
356
|
+
function swipeAxisFor(position) {
|
|
357
|
+
return position === "top-center" || position === "bottom-center" ? "y" : "x";
|
|
358
|
+
}
|
|
359
|
+
function swipeSignFor(position) {
|
|
360
|
+
if (position === "top-right" || position === "bottom-right") return 1;
|
|
361
|
+
if (position === "top-left" || position === "bottom-left") return -1;
|
|
362
|
+
if (position === "bottom-center") return 1;
|
|
363
|
+
return -1;
|
|
364
|
+
}
|
|
365
|
+
function onToastPointerDown(t, event) {
|
|
366
|
+
if (local.disableSwipe) return;
|
|
367
|
+
if (event.button != null && event.button !== 0) return;
|
|
368
|
+
if (event.target && event.target.closest ? event.target.closest("button, a") : null) return;
|
|
369
|
+
const axis = swipeAxisFor(local.position);
|
|
370
|
+
const sign = swipeSignFor(local.position);
|
|
371
|
+
const el = event.currentTarget;
|
|
372
|
+
const size = axis === "x" ? el.offsetWidth : el.offsetHeight;
|
|
373
|
+
setSwipeGesture({
|
|
374
|
+
id: t.id,
|
|
375
|
+
axis,
|
|
376
|
+
sign,
|
|
377
|
+
size,
|
|
378
|
+
startX: event.clientX,
|
|
379
|
+
startY: event.clientY,
|
|
380
|
+
startTime: Date.now()
|
|
381
|
+
});
|
|
382
|
+
if (el && el.setPointerCapture) try {
|
|
383
|
+
el.setPointerCapture(event.pointerId);
|
|
384
|
+
} catch (e) {}
|
|
385
|
+
}
|
|
386
|
+
function onToastPointerMove(t, event) {
|
|
387
|
+
if (local.disableSwipe) return;
|
|
388
|
+
const gesture = swipeGesture();
|
|
389
|
+
if (!gesture || gesture.id !== t.id) return;
|
|
390
|
+
const raw = gesture.axis === "x" ? event.clientX - gesture.startX : event.clientY - gesture.startY;
|
|
391
|
+
const d = raw * gesture.sign > 0 ? raw : raw * .15;
|
|
392
|
+
setSwipe({
|
|
393
|
+
id: t.id,
|
|
394
|
+
d,
|
|
395
|
+
axis: gesture.axis,
|
|
396
|
+
sign: gesture.sign,
|
|
397
|
+
size: gesture.size
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function onToastPointerUp(t, event) {
|
|
401
|
+
if (local.disableSwipe) return;
|
|
402
|
+
const gesture = swipeGesture();
|
|
403
|
+
setSwipeGesture(null);
|
|
404
|
+
const dragState = swipe();
|
|
405
|
+
setSwipe(null);
|
|
406
|
+
if (!gesture || gesture.id !== t.id || !dragState) return;
|
|
407
|
+
const elapsed = Math.max(1, Date.now() - gesture.startTime);
|
|
408
|
+
const magnitude = dragState.d * gesture.sign;
|
|
409
|
+
const velocity = magnitude / elapsed;
|
|
410
|
+
if (magnitude > 0 && (magnitude > gesture.size * .45 || velocity > .11)) dismissBegin(t.id, "swipe", { swipeExitSign: gesture.sign });
|
|
411
|
+
}
|
|
412
|
+
function onToastPointerCancel(t) {
|
|
413
|
+
if (local.disableSwipe) return;
|
|
414
|
+
if (swipeGesture() && swipeGesture().id === t.id) setSwipeGesture(null);
|
|
415
|
+
if (swipe() && swipe().id === t.id) setSwipe(null);
|
|
416
|
+
}
|
|
417
|
+
function depth(t) {
|
|
418
|
+
const idx = toasts().findIndex((x) => x.id === t.id);
|
|
419
|
+
return idx === -1 ? 0 : toasts().length - 1 - idx;
|
|
420
|
+
}
|
|
421
|
+
function toastStyle(t) {
|
|
422
|
+
const depthDecl = "--rozie-toast-depth: " + depth(t) + ";";
|
|
423
|
+
if (t.exiting) return t.swipeExitSign != null ? depthDecl + " --rozie-toast-swipe-exit: " + t.swipeExitSign + ";" : depthDecl;
|
|
424
|
+
const dragState = swipe();
|
|
425
|
+
if (!dragState || dragState.id !== t.id) return depthDecl;
|
|
426
|
+
const translate = dragState.axis === "x" ? "translateX(" + dragState.d + "px)" : "translateY(" + dragState.d + "px)";
|
|
427
|
+
const magnitude = dragState.d * dragState.sign;
|
|
428
|
+
const opacity = magnitude > 0 && dragState.size > 0 ? Math.max(.3, 1 - magnitude / dragState.size) : 1;
|
|
429
|
+
return depthDecl + " transform: " + translate + "; opacity: " + opacity + "; transition: none;";
|
|
430
|
+
}
|
|
431
|
+
function onMouseEnter() {
|
|
432
|
+
if (local.disablePauseOnHover) return;
|
|
433
|
+
pauseTimers();
|
|
434
|
+
}
|
|
435
|
+
function onMouseLeave() {
|
|
436
|
+
if (local.disablePauseOnHover) return;
|
|
437
|
+
resumeTimers();
|
|
438
|
+
}
|
|
439
|
+
function regionLabel() {
|
|
440
|
+
return local.ariaLabel != null ? local.ariaLabel : "Notifications";
|
|
441
|
+
}
|
|
442
|
+
function liveFor(type) {
|
|
443
|
+
return type === "error" || type === "warning" ? "assertive" : "polite";
|
|
444
|
+
}
|
|
445
|
+
return <>
|
|
446
|
+
<div role="region" aria-label={rozieAttr(regionLabel())} {...attrs} class={"rozie-toaster " + rozieClass("rozie-toaster--" + local.position + (local.stacked ? " rozie-toaster--stacked" : "")) + (attrs.class ? " " + attrs.class : "")} {...mergeListeners({
|
|
447
|
+
onMouseEnter: ($event) => {
|
|
448
|
+
onMouseEnter();
|
|
449
|
+
},
|
|
450
|
+
onMouseLeave: ($event) => {
|
|
451
|
+
onMouseLeave();
|
|
452
|
+
}
|
|
453
|
+
}, attrs)} data-rozie-s-12d4265c="">
|
|
454
|
+
|
|
455
|
+
<Key each={toasts()} by={(t) => t.id}>{(t) => <div role="status" aria-live={rozieAttr(liveFor(t().type))} class={"rozie-toast " + rozieClass("rozie-toast--" + t().type + (t().exiting ? " rozie-toast--exiting" : "") + (t().swipeExitSign != null ? " rozie-toast--swipe-exit" : ""))} style={parseInlineStyle(toastStyle(t()))} onAnimationEnd={($event) => {
|
|
456
|
+
t().exiting && removeToast(t().id);
|
|
457
|
+
}} onPointerDown={($event) => {
|
|
458
|
+
onToastPointerDown(t(), $event);
|
|
459
|
+
}} onPointerMove={($event) => {
|
|
460
|
+
onToastPointerMove(t(), $event);
|
|
461
|
+
}} onPointerUp={($event) => {
|
|
462
|
+
onToastPointerUp(t(), $event);
|
|
463
|
+
}} onPointerCancel={($event) => {
|
|
464
|
+
onToastPointerCancel(t());
|
|
465
|
+
}} data-rozie-s-12d4265c="">
|
|
466
|
+
{(_props.toastSlot ?? _props.slots?.["toast"])?.({
|
|
467
|
+
toast: t(),
|
|
468
|
+
dismiss
|
|
469
|
+
}) ?? <>{<Show when={t().type === "loading"}><span class={"rozie-toast-spinner"} aria-hidden="true" data-rozie-s-12d4265c="" /></Show>}<span class={"rozie-toast-message"} data-rozie-s-12d4265c="">{rozieDisplay(t().message)}</span><button type="button" aria-label="Dismiss" class={"rozie-toast-close"} onClick={($event) => {
|
|
470
|
+
dismissBegin(t().id, "close");
|
|
471
|
+
}} data-rozie-s-12d4265c="">×</button></>}
|
|
472
|
+
</div>}</Key>
|
|
473
|
+
</div>
|
|
474
|
+
</>;
|
|
475
|
+
}
|
|
476
|
+
//#endregion
|
|
477
|
+
export { Toaster, Toaster as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rozie-ui/toast-solid",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Idiomatic Solid headless, accessible toast / notification host (queue, auto-dismiss, hover-pause, positioning, imperative show/dismiss/clear handle) — one accessible Rozie source compiled to Solid.",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"rozie",
|
|
10
|
+
"rozie-ui",
|
|
11
|
+
"toast",
|
|
12
|
+
"toaster",
|
|
13
|
+
"notification",
|
|
14
|
+
"snackbar",
|
|
15
|
+
"headless",
|
|
16
|
+
"accessibility",
|
|
17
|
+
"aria",
|
|
18
|
+
"component",
|
|
19
|
+
"solid"
|
|
20
|
+
],
|
|
21
|
+
"author": "One Learning Community (https://github.com/One-Learning-Community)",
|
|
22
|
+
"homepage": "https://github.com/One-Learning-Community/rozie.js#readme",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/One-Learning-Community/rozie.js.git",
|
|
26
|
+
"directory": "packages/ui/toast/packages/solid"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/One-Learning-Community/rozie.js/issues"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.mts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.mts",
|
|
37
|
+
"import": "./dist/index.mjs",
|
|
38
|
+
"require": "./dist/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./source": "./src/Toaster.tsx",
|
|
41
|
+
"./themes/*": "./src/themes/*"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"src"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@solid-primitives/keyed": "^1.5.3",
|
|
49
|
+
"@rozie/runtime-solid": "0.2.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"solid-js": "^1.8"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"solid-js": {
|
|
56
|
+
"optional": false
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsdown",
|
|
64
|
+
"typecheck": "tsc --noEmit"
|
|
65
|
+
}
|
|
66
|
+
}
|