solid-drift 0.10.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 +55 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/toast.d.ts +81 -0
- package/dist/toast.js +167 -0
- package/dist/typography.d.ts +39 -0
- package/dist/typography.js +45 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -776,6 +776,25 @@ createTextWave(() => headline, {
|
|
|
776
776
|
|
|
777
777
|
Options: `amplitude`, `wavelength` (characters per wave), `period`, `tilt` (default true), `progress`. Returns `{ stop }`. Under reduced motion the text sits still.
|
|
778
778
|
|
|
779
|
+
### `createCountUp(source, options?)`
|
|
780
|
+
|
|
781
|
+
A signal that counts toward a source number with an eased tween, rendering as a formatted string: dashboard stats, prices, scores. When the source changes mid-count the tween retargets from the current displayed value, with no snapping.
|
|
782
|
+
|
|
783
|
+
```tsx
|
|
784
|
+
import { createCountUp } from "solid-drift"
|
|
785
|
+
|
|
786
|
+
const [revenue, setRevenue] = createSignal(0)
|
|
787
|
+
const display = createCountUp(revenue, {
|
|
788
|
+
decimals: 2,
|
|
789
|
+
prefix: "$",
|
|
790
|
+
separator: ",",
|
|
791
|
+
})
|
|
792
|
+
<div>{display()}</div> // "$1,234.50" gliding up from "$0.00"
|
|
793
|
+
setRevenue(1234.5)
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Options: `decimals` (default 0), `duration` (ms, default 1000), `easing` (default `"easeOutExpo"`), `prefix` (default `""`), `suffix` (default `""`), `separator` (thousands separator, default `""`). Returns an accessor of the formatted string. SSR-safe: renders the formatted source value. Under reduced motion the text jumps straight to each new value.
|
|
797
|
+
|
|
779
798
|
### `createKineticType(ref, options?)`
|
|
780
799
|
|
|
781
800
|
Kinetic typography: each character (or word) flies in with position, blur, scale, and opacity, staggered for that showreel title feel. One master clock drives every unit, so a 40-character headline costs a single rAF task.
|
|
@@ -1075,6 +1094,42 @@ const connect = createConnectButton(() => btn, { strength: 0.35 });
|
|
|
1075
1094
|
|
|
1076
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.
|
|
1077
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
|
+
|
|
1078
1133
|
### Easings
|
|
1079
1134
|
|
|
1080
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,9 +20,10 @@ 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
|
-
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, } from "./typography.js";
|
|
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";
|
|
26
27
|
export { easings, cubicBezier, linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, easeOutExpo, easeOutBack, easeInBack, easeInOutBack, easeOutElastic, easeOutBounce, resolveEasing, type Easing, type EasingName, } from "./easing.js";
|
|
27
28
|
export { createKineticType, createScenePlayer, createShowreel, createCamera, createColorShift, createTransition, createBeat, createBeatCuts, } from "./motion.js";
|
|
28
29
|
export type { KineticTypeFrom, KineticTypeOptions, KineticTypeStatus, KineticTypeControls, MotionScene, ScenePlayerStatus, ScenePlayerControls, ShowreelScene, ShowreelSceneKind, CameraKeyframe, CameraOptions, ColorShiftOptions, ColorShiftStatus, ColorShiftControls, TransitionType, TransitionDirection, TransitionOptions, TransitionLayerStyle, TransitionStatus, TransitionControls, BeatOptions, BeatStatus, BeatControls, BeatCutOptions, } from "./motion.js";
|
package/dist/index.js
CHANGED
|
@@ -20,9 +20,10 @@ 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
|
-
export { createFontSwap, createTyping, createTextPhysics, createTextTunnel, createTextCutout, createTextGradient, createTextScramble, createTextWave, } from "./typography.js";
|
|
26
|
+
export { createFontSwap, createTyping, createTextPhysics, createTextTunnel, createTextCutout, createTextGradient, createTextScramble, createTextWave, createCountUp, } from "./typography.js";
|
|
26
27
|
export { easings, cubicBezier, linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, easeOutExpo, easeOutBack, easeInBack, easeInOutBack, easeOutElastic, easeOutBounce, resolveEasing, } from "./easing.js";
|
|
27
28
|
export { createKineticType, createScenePlayer, createShowreel, createCamera, createColorShift, createTransition, createBeat, createBeatCuts, } from "./motion.js";
|
|
28
29
|
export { createDrag, } from "./gesture.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/dist/typography.d.ts
CHANGED
|
@@ -238,4 +238,43 @@ export interface TextWaveOptions {
|
|
|
238
238
|
export declare function createTextWave(ref: MaybeElement, options?: TextWaveOptions): {
|
|
239
239
|
stop: () => void;
|
|
240
240
|
};
|
|
241
|
+
export interface CountUpOptions {
|
|
242
|
+
/** Decimal places in the output. Default 0. */
|
|
243
|
+
decimals?: number;
|
|
244
|
+
/** Tween duration in milliseconds when the source changes. Default 1000. */
|
|
245
|
+
duration?: number;
|
|
246
|
+
/** Easing for the count. Default "easeOutExpo". */
|
|
247
|
+
easing?: Easing | EasingName;
|
|
248
|
+
/** String placed before the number, e.g. "$". Default "". */
|
|
249
|
+
prefix?: string;
|
|
250
|
+
/** String placed after the number, e.g. "%". Default "". */
|
|
251
|
+
suffix?: string;
|
|
252
|
+
/** Thousands separator, e.g. ",". Default "" (no grouping). */
|
|
253
|
+
separator?: string;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* A signal that counts toward a source number with an eased tween,
|
|
257
|
+
* rendering as a formatted string: dashboard stats, prices, scores.
|
|
258
|
+
*
|
|
259
|
+
* When the source changes mid-count the tween retargets from the
|
|
260
|
+
* current displayed value, with no snapping. Formatting (decimals,
|
|
261
|
+
* prefix, suffix, thousands separator) applies to every frame.
|
|
262
|
+
*
|
|
263
|
+
* SSR-safe: renders the formatted source value. Under reduced motion
|
|
264
|
+
* the text jumps straight to each new value with no tween.
|
|
265
|
+
*
|
|
266
|
+
* ```tsx
|
|
267
|
+
* import { createCountUp } from "solid-drift"
|
|
268
|
+
*
|
|
269
|
+
* const [revenue, setRevenue] = createSignal(0)
|
|
270
|
+
* const display = createCountUp(revenue, {
|
|
271
|
+
* decimals: 2,
|
|
272
|
+
* prefix: "$",
|
|
273
|
+
* separator: ",",
|
|
274
|
+
* })
|
|
275
|
+
* <div>{display()}</div> // "$1,234.50" gliding up from "$0.00"
|
|
276
|
+
* setRevenue(1234.5)
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export declare function createCountUp(source: Accessor<number>, options?: CountUpOptions): Accessor<string>;
|
|
241
280
|
export {};
|
package/dist/typography.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createEffect, createSignal, onCleanup, untrack, } from "solid-js";
|
|
2
2
|
import { animate } from "./animate.js";
|
|
3
|
+
import { createTween } from "./tween.js";
|
|
3
4
|
import { resolveEasing } from "./easing.js";
|
|
4
5
|
import { now, schedule } from "./engine.js";
|
|
5
6
|
import { prefersReducedMotion } from "./reduced-motion.js";
|
|
@@ -810,3 +811,47 @@ export function createTextWave(ref, options = {}) {
|
|
|
810
811
|
},
|
|
811
812
|
};
|
|
812
813
|
}
|
|
814
|
+
function groupInteger(int, separator) {
|
|
815
|
+
if (!separator)
|
|
816
|
+
return int;
|
|
817
|
+
const negative = int.startsWith("-");
|
|
818
|
+
const digits = negative ? int.slice(1) : int;
|
|
819
|
+
const grouped = digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
820
|
+
return (negative ? "-" : "") + grouped;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* A signal that counts toward a source number with an eased tween,
|
|
824
|
+
* rendering as a formatted string: dashboard stats, prices, scores.
|
|
825
|
+
*
|
|
826
|
+
* When the source changes mid-count the tween retargets from the
|
|
827
|
+
* current displayed value, with no snapping. Formatting (decimals,
|
|
828
|
+
* prefix, suffix, thousands separator) applies to every frame.
|
|
829
|
+
*
|
|
830
|
+
* SSR-safe: renders the formatted source value. Under reduced motion
|
|
831
|
+
* the text jumps straight to each new value with no tween.
|
|
832
|
+
*
|
|
833
|
+
* ```tsx
|
|
834
|
+
* import { createCountUp } from "solid-drift"
|
|
835
|
+
*
|
|
836
|
+
* const [revenue, setRevenue] = createSignal(0)
|
|
837
|
+
* const display = createCountUp(revenue, {
|
|
838
|
+
* decimals: 2,
|
|
839
|
+
* prefix: "$",
|
|
840
|
+
* separator: ",",
|
|
841
|
+
* })
|
|
842
|
+
* <div>{display()}</div> // "$1,234.50" gliding up from "$0.00"
|
|
843
|
+
* setRevenue(1234.5)
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
export function createCountUp(source, options = {}) {
|
|
847
|
+
const { decimals = 0, duration = 1000, easing = "easeOutExpo", prefix = "", suffix = "", separator = "", } = options;
|
|
848
|
+
const tweened = createTween(source, { duration, easing });
|
|
849
|
+
const format = (value) => {
|
|
850
|
+
const fixed = value.toFixed(decimals);
|
|
851
|
+
const dot = fixed.indexOf(".");
|
|
852
|
+
const head = dot === -1 ? fixed : fixed.slice(0, dot);
|
|
853
|
+
const tail = dot === -1 ? "" : fixed.slice(dot);
|
|
854
|
+
return prefix + groupInteger(head, separator) + tail + suffix;
|
|
855
|
+
};
|
|
856
|
+
return () => format(tweened());
|
|
857
|
+
}
|
package/package.json
CHANGED