solid-drift 0.11.0 → 0.12.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/README.md +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/toast.d.ts +81 -0
- package/dist/toast.js +167 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1094,6 +1094,42 @@ const connect = createConnectButton(() => btn, { strength: 0.35 });
|
|
|
1094
1094
|
|
|
1095
1095
|
Returns `{ copyTick, chainPulse, status }`. `status()` is `"idle"`, `"ticking"` (check visible), or `"pulsing"` (ring expanding). Call `chainPulse()` after a successful connection or network switch. Under reduced motion there is no magnetic pull or scale; `copyTick()` and `chainPulse()` still show their overlays statically.
|
|
1096
1096
|
|
|
1097
|
+
### `createToast(options?)`
|
|
1098
|
+
|
|
1099
|
+
A signal-native toast queue with choreographed lifecycle. The primitive owns timing and state; you own the rendering, so no component opinions leak into your design system. Each toast moves through `"entering"` to `"visible"` to `"leaving"` to removed on the shared animation clock: bind `state` to CSS classes or drift values for enter/exit motion without any timers of your own.
|
|
1100
|
+
|
|
1101
|
+
```tsx
|
|
1102
|
+
import { createToast } from "solid-drift"
|
|
1103
|
+
|
|
1104
|
+
const { toasts, success, dismiss } = createToast()
|
|
1105
|
+
success("Payment sent", { description: "0.5 SOL to alice.sol" })
|
|
1106
|
+
|
|
1107
|
+
<For each={toasts()}>
|
|
1108
|
+
{(t) => (
|
|
1109
|
+
<div
|
|
1110
|
+
class="toast"
|
|
1111
|
+
classList={{
|
|
1112
|
+
"toast-enter": t.state === "entering",
|
|
1113
|
+
"toast-leave": t.state === "leaving",
|
|
1114
|
+
}}
|
|
1115
|
+
>
|
|
1116
|
+
<strong>{t.title}</strong>
|
|
1117
|
+
{t.description && <p>{t.description}</p>}
|
|
1118
|
+
<button onClick={() => dismiss(t.id)}>Dismiss</button>
|
|
1119
|
+
</div>
|
|
1120
|
+
)}
|
|
1121
|
+
</For>
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
| Option | Default | Description |
|
|
1125
|
+
| ---------- | ------- | ---------------------------------------------------------------- |
|
|
1126
|
+
| `max` | `5` | Max toasts in the queue; older ones are dismissed first |
|
|
1127
|
+
| `enterMs` | `250` | Enter transition time in milliseconds |
|
|
1128
|
+
| `leaveMs` | `200` | Leave transition time in milliseconds |
|
|
1129
|
+
| `duration` | `4000` | Default auto-dismiss time in milliseconds |
|
|
1130
|
+
|
|
1131
|
+
Push helpers: `toast(title, options?)`, `info(...)`, `success(...)`, `warning(...)`, `error(...)`. Each returns the toast id. Per-toast options: `kind`, `description`, `duration` (ms; `0` means sticky). `dismiss(id)` starts the leave transition for one toast; `clear()` dismisses all. SSR-safe: toasts pushed on the server start `"visible"`. Under reduced motion the enter and leave transitions are instant, but auto-dismiss timing still applies.
|
|
1132
|
+
|
|
1097
1133
|
### Easings
|
|
1098
1134
|
|
|
1099
1135
|
Named easings: `linear`, `easeInQuad`, `easeOutQuad`, `easeInOutQuad`, `easeInCubic`, `easeOutCubic`, `easeInOutCubic`, `easeInQuart`, `easeOutQuart`, `easeInOutQuart`, `easeOutExpo`, `easeOutBack`, plus the cartoon set: `easeInBack` (anticipation dip before movement), `easeInOutBack` (wind-up, overshoot, settle), `easeOutElastic` (decaying rubber-band oscillation), `easeOutBounce` (shrinking cartoon bounces). Also `cubicBezier(x1, y1, x2, y2)` for CSS-style curves. Pass a name or a custom `(t) => number` function anywhere an easing is accepted.
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { createMagnetic, type MagneticOptions, type MagneticResult, createTilt,
|
|
|
20
20
|
export { createTrail, type TrailOptions } from "./trail.js";
|
|
21
21
|
export { createTimeline, type TimelineStep, type TimelineStatus, type TimelineControls, } from "./timeline.js";
|
|
22
22
|
export { animateFlip, type FlipOptions, createSharedLayout, type SharedLayoutOptions, type SharedLayoutResult, } from "./flip.js";
|
|
23
|
+
export { createToast, type Toast, type ToastControls, type ToastKind, type ToastOptions, type ToastQueueOptions, type ToastState, } from "./toast.js";
|
|
23
24
|
export { createSquashStretch, type SquashStretchOptions, type SquashStretchResult, createFollowThrough, type FollowThroughOptions, createAnticipation, type AnticipationOptions, createWobble, type WobbleOptions, type WobbleResult, } from "./cartoon.js";
|
|
24
25
|
export { createGravity, type GravityOptions, type GravityResult, createPendulum, type PendulumOptions, type PendulumResult, createFling, type FlingOptions, type FlingResult, } from "./physics.js";
|
|
25
26
|
export { createFontSwap, type FontSwapOptions, type FontSwapResult, createTyping, type TypingOptions, type TypingResult, createTextPhysics, type TextPhysicsOptions, type TextPhysicsResult, createTextTunnel, type TextTunnelOptions, type TextTunnelResult, createTextCutout, type TextCutoutOptions, createTextGradient, type TextGradientOptions, createTextScramble, type TextScrambleOptions, type TextScrambleResult, createTextWave, type TextWaveOptions, createCountUp, type CountUpOptions, } from "./typography.js";
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ export { createMagnetic, createTilt, } from "./pointer.js";
|
|
|
20
20
|
export { createTrail } from "./trail.js";
|
|
21
21
|
export { createTimeline, } from "./timeline.js";
|
|
22
22
|
export { animateFlip, createSharedLayout, } from "./flip.js";
|
|
23
|
+
export { createToast, } from "./toast.js";
|
|
23
24
|
export { createSquashStretch, createFollowThrough, createAnticipation, createWobble, } from "./cartoon.js";
|
|
24
25
|
export { createGravity, createPendulum, createFling, } from "./physics.js";
|
|
25
26
|
export { createFontSwap, createTyping, createTextPhysics, createTextTunnel, createTextCutout, createTextGradient, createTextScramble, createTextWave, createCountUp, } from "./typography.js";
|
package/dist/toast.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type Accessor } from "solid-js";
|
|
2
|
+
export type ToastKind = "info" | "success" | "warning" | "error";
|
|
3
|
+
/** Lifecycle state of a toast. Bind it to your enter/exit animation. */
|
|
4
|
+
export type ToastState = "entering" | "visible" | "leaving";
|
|
5
|
+
export interface Toast {
|
|
6
|
+
/** Unique id, returned by the push helpers. */
|
|
7
|
+
id: number;
|
|
8
|
+
kind: ToastKind;
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
state: ToastState;
|
|
12
|
+
/** Millisecond timestamp when the toast entered the queue. */
|
|
13
|
+
createdAt: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ToastOptions {
|
|
16
|
+
/** Visual kind. Default "info" (or the shortcut you called). */
|
|
17
|
+
kind?: ToastKind;
|
|
18
|
+
description?: string;
|
|
19
|
+
/** Auto-dismiss after this many ms. Default 4000. 0 means sticky. */
|
|
20
|
+
duration?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ToastQueueOptions {
|
|
23
|
+
/** Max toasts in the queue; older ones are dismissed first. Default 5. */
|
|
24
|
+
max?: number;
|
|
25
|
+
/** Enter transition time in ms. Default 250. */
|
|
26
|
+
enterMs?: number;
|
|
27
|
+
/** Leave transition time in ms. Default 200. */
|
|
28
|
+
leaveMs?: number;
|
|
29
|
+
/** Default auto-dismiss time in ms. Default 4000. */
|
|
30
|
+
duration?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface ToastControls {
|
|
33
|
+
/** Reactive list of toasts, oldest first. */
|
|
34
|
+
toasts: Accessor<Toast[]>;
|
|
35
|
+
/** Push a toast. Returns its id. */
|
|
36
|
+
toast: (title: string, options?: ToastOptions) => number;
|
|
37
|
+
info: (title: string, options?: ToastOptions) => number;
|
|
38
|
+
success: (title: string, options?: ToastOptions) => number;
|
|
39
|
+
warning: (title: string, options?: ToastOptions) => number;
|
|
40
|
+
error: (title: string, options?: ToastOptions) => number;
|
|
41
|
+
/** Start the leave transition for one toast. */
|
|
42
|
+
dismiss: (id: number) => void;
|
|
43
|
+
/** Dismiss every toast. */
|
|
44
|
+
clear: () => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A signal-native toast queue with choreographed lifecycle.
|
|
48
|
+
*
|
|
49
|
+
* The primitive owns timing and state; you own the rendering. Each toast
|
|
50
|
+
* moves through `entering` -> `visible` -> `leaving` -> removed on the
|
|
51
|
+
* shared animation clock, so you can bind `state` to CSS classes or drift
|
|
52
|
+
* values for enter/exit motion without any timers of your own.
|
|
53
|
+
*
|
|
54
|
+
* SSR-safe: toasts pushed on the server start `visible`. Under reduced
|
|
55
|
+
* motion the enter and leave transitions are instant, but auto-dismiss
|
|
56
|
+
* timing still applies.
|
|
57
|
+
*
|
|
58
|
+
* ```tsx
|
|
59
|
+
* import { createToast } from "solid-drift"
|
|
60
|
+
*
|
|
61
|
+
* const { toasts, success, dismiss } = createToast()
|
|
62
|
+
* success("Payment sent", { description: "0.5 SOL to alice.sol" })
|
|
63
|
+
*
|
|
64
|
+
* <For each={toasts()}>
|
|
65
|
+
* {(t) => (
|
|
66
|
+
* <div
|
|
67
|
+
* class="toast"
|
|
68
|
+
* classList={{
|
|
69
|
+
* "toast-enter": t.state === "entering",
|
|
70
|
+
* "toast-leave": t.state === "leaving",
|
|
71
|
+
* }}
|
|
72
|
+
* >
|
|
73
|
+
* <strong>{t.title}</strong>
|
|
74
|
+
* {t.description && <p>{t.description}</p>}
|
|
75
|
+
* <button onClick={() => dismiss(t.id)}>Dismiss</button>
|
|
76
|
+
* </div>
|
|
77
|
+
* )}
|
|
78
|
+
* </For>
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function createToast(options?: ToastQueueOptions): ToastControls;
|
package/dist/toast.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createSignal, onCleanup, untrack } from "solid-js";
|
|
2
|
+
import { now, schedule } from "./engine.js";
|
|
3
|
+
import { prefersReducedMotion } from "./reduced-motion.js";
|
|
4
|
+
/**
|
|
5
|
+
* A signal-native toast queue with choreographed lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* The primitive owns timing and state; you own the rendering. Each toast
|
|
8
|
+
* moves through `entering` -> `visible` -> `leaving` -> removed on the
|
|
9
|
+
* shared animation clock, so you can bind `state` to CSS classes or drift
|
|
10
|
+
* values for enter/exit motion without any timers of your own.
|
|
11
|
+
*
|
|
12
|
+
* SSR-safe: toasts pushed on the server start `visible`. Under reduced
|
|
13
|
+
* motion the enter and leave transitions are instant, but auto-dismiss
|
|
14
|
+
* timing still applies.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { createToast } from "solid-drift"
|
|
18
|
+
*
|
|
19
|
+
* const { toasts, success, dismiss } = createToast()
|
|
20
|
+
* success("Payment sent", { description: "0.5 SOL to alice.sol" })
|
|
21
|
+
*
|
|
22
|
+
* <For each={toasts()}>
|
|
23
|
+
* {(t) => (
|
|
24
|
+
* <div
|
|
25
|
+
* class="toast"
|
|
26
|
+
* classList={{
|
|
27
|
+
* "toast-enter": t.state === "entering",
|
|
28
|
+
* "toast-leave": t.state === "leaving",
|
|
29
|
+
* }}
|
|
30
|
+
* >
|
|
31
|
+
* <strong>{t.title}</strong>
|
|
32
|
+
* {t.description && <p>{t.description}</p>}
|
|
33
|
+
* <button onClick={() => dismiss(t.id)}>Dismiss</button>
|
|
34
|
+
* </div>
|
|
35
|
+
* )}
|
|
36
|
+
* </For>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createToast(options = {}) {
|
|
40
|
+
const { max = 5, enterMs = 250, leaveMs = 200, duration: defaultDuration = 4000, } = options;
|
|
41
|
+
const [toasts, setToasts] = createSignal([]);
|
|
42
|
+
const timers = new Map();
|
|
43
|
+
let nextId = 1;
|
|
44
|
+
let cancelDriver = null;
|
|
45
|
+
const tick = (t) => {
|
|
46
|
+
const list = untrack(toasts);
|
|
47
|
+
if (list.length === 0) {
|
|
48
|
+
cancelDriver = null;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const em = prefersReducedMotion() ? 0 : enterMs;
|
|
52
|
+
const lm = prefersReducedMotion() ? 0 : leaveMs;
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const toast of list) {
|
|
55
|
+
const meta = timers.get(toast.id);
|
|
56
|
+
if (!meta)
|
|
57
|
+
continue;
|
|
58
|
+
if (toast.state === "entering" && t - meta.enteredAt >= em) {
|
|
59
|
+
out.push({ ...toast, state: "visible" });
|
|
60
|
+
}
|
|
61
|
+
else if (toast.state === "visible" &&
|
|
62
|
+
meta.deadline > 0 &&
|
|
63
|
+
t >= meta.deadline) {
|
|
64
|
+
meta.leavingAt = t;
|
|
65
|
+
out.push({ ...toast, state: "leaving" });
|
|
66
|
+
}
|
|
67
|
+
else if (toast.state === "leaving" && t - meta.leavingAt >= lm) {
|
|
68
|
+
timers.delete(toast.id);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
out.push(toast);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const changed = out.length !== list.length ||
|
|
75
|
+
out.some((toast, i) => toast !== list[i]);
|
|
76
|
+
if (changed)
|
|
77
|
+
setToasts(out);
|
|
78
|
+
const busy = out.some((toast) => {
|
|
79
|
+
const meta = timers.get(toast.id);
|
|
80
|
+
return (toast.state === "entering" ||
|
|
81
|
+
toast.state === "leaving" ||
|
|
82
|
+
(toast.state === "visible" && !!meta && meta.deadline > 0));
|
|
83
|
+
});
|
|
84
|
+
if (!busy) {
|
|
85
|
+
cancelDriver = null;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
};
|
|
90
|
+
const ensureDriver = () => {
|
|
91
|
+
if (!cancelDriver && typeof window !== "undefined") {
|
|
92
|
+
cancelDriver = schedule(tick);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const push = (kind, title, opts = {}) => {
|
|
96
|
+
const id = nextId++;
|
|
97
|
+
const t = now();
|
|
98
|
+
const duration = opts.duration ?? defaultDuration;
|
|
99
|
+
const toast = {
|
|
100
|
+
id,
|
|
101
|
+
kind: opts.kind ?? kind,
|
|
102
|
+
title,
|
|
103
|
+
description: opts.description,
|
|
104
|
+
state: typeof window === "undefined" ? "visible" : "entering",
|
|
105
|
+
createdAt: t,
|
|
106
|
+
};
|
|
107
|
+
timers.set(id, {
|
|
108
|
+
enteredAt: t,
|
|
109
|
+
deadline: duration > 0 ? t + duration : 0,
|
|
110
|
+
leavingAt: 0,
|
|
111
|
+
});
|
|
112
|
+
setToasts((prev) => {
|
|
113
|
+
const next = [...prev, toast];
|
|
114
|
+
if (next.length > max) {
|
|
115
|
+
const excess = next.length - max;
|
|
116
|
+
return next.map((item, i) => {
|
|
117
|
+
if (i < excess && item.state !== "leaving") {
|
|
118
|
+
const meta = timers.get(item.id);
|
|
119
|
+
if (meta)
|
|
120
|
+
meta.leavingAt = now();
|
|
121
|
+
return { ...item, state: "leaving" };
|
|
122
|
+
}
|
|
123
|
+
return item;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return next;
|
|
127
|
+
});
|
|
128
|
+
ensureDriver();
|
|
129
|
+
return id;
|
|
130
|
+
};
|
|
131
|
+
const dismiss = (id) => {
|
|
132
|
+
const meta = timers.get(id);
|
|
133
|
+
if (!meta)
|
|
134
|
+
return;
|
|
135
|
+
meta.leavingAt = now();
|
|
136
|
+
setToasts((prev) => prev.map((toast) => toast.id === id && toast.state !== "leaving"
|
|
137
|
+
? { ...toast, state: "leaving" }
|
|
138
|
+
: toast));
|
|
139
|
+
ensureDriver();
|
|
140
|
+
};
|
|
141
|
+
const clear = () => {
|
|
142
|
+
const t = now();
|
|
143
|
+
setToasts((prev) => prev.map((toast) => {
|
|
144
|
+
if (toast.state === "leaving")
|
|
145
|
+
return toast;
|
|
146
|
+
const meta = timers.get(toast.id);
|
|
147
|
+
if (meta)
|
|
148
|
+
meta.leavingAt = t;
|
|
149
|
+
return { ...toast, state: "leaving" };
|
|
150
|
+
}));
|
|
151
|
+
ensureDriver();
|
|
152
|
+
};
|
|
153
|
+
onCleanup(() => {
|
|
154
|
+
cancelDriver?.();
|
|
155
|
+
cancelDriver = null;
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
toasts,
|
|
159
|
+
toast: (title, opts) => push("info", title, opts),
|
|
160
|
+
info: (title, opts) => push("info", title, opts),
|
|
161
|
+
success: (title, opts) => push("success", title, opts),
|
|
162
|
+
warning: (title, opts) => push("warning", title, opts),
|
|
163
|
+
error: (title, opts) => push("error", title, opts),
|
|
164
|
+
dismiss,
|
|
165
|
+
clear,
|
|
166
|
+
};
|
|
167
|
+
}
|
package/package.json
CHANGED