fancy-ui-react 0.2.1 → 0.4.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 +56 -20
- package/dist/components/animated-beam/AnimatedBeam.d.ts +47 -11
- package/dist/components/animated-beam/AnimatedBeam.js +293 -118
- package/dist/components/animated-tooltip/AnimatedTooltip.d.ts +10 -1
- package/dist/components/animated-tooltip/AnimatedTooltip.js +170 -101
- package/dist/components/bento-grid/BentoFrame.d.ts +16 -0
- package/dist/components/bento-grid/BentoFrame.js +28 -0
- package/dist/components/bento-grid/BentoGrid.d.ts +5 -1
- package/dist/components/bento-grid/BentoGrid.js +71 -2
- package/dist/components/bento-grid/BentoGridCard.js +34 -26
- package/dist/components/bento-grid/BentoGridItem.js +10 -10
- package/dist/components/border-beam/BorderBeam.d.ts +55 -10
- package/dist/components/border-beam/BorderBeam.js +93 -16
- package/dist/components/button/Button.js +5 -1
- package/dist/components/compare/Compare.d.ts +32 -8
- package/dist/components/compare/Compare.js +213 -95
- package/dist/components/container-scroll/ContainerScroll.d.ts +26 -4
- package/dist/components/container-scroll/ContainerScroll.js +88 -49
- package/dist/components/datamosh-transition/DatamoshTransition.d.ts +76 -0
- package/dist/components/datamosh-transition/DatamoshTransition.js +368 -0
- package/dist/components/datamosh-transition/datamosh-transition-core.d.ts +180 -0
- package/dist/components/datamosh-transition/datamosh-transition-core.js +370 -0
- package/dist/components/datamosh-transition/index.d.ts +3 -0
- package/dist/components/dock/Dock.d.ts +10 -7
- package/dist/components/dock/Dock.js +76 -31
- package/dist/components/dock/DockIcon.d.ts +14 -3
- package/dist/components/dock/DockIcon.js +23 -19
- package/dist/components/dock/DockSeparator.d.ts +4 -1
- package/dist/components/dock/DockSeparator.js +5 -2
- package/dist/components/dock/types.d.ts +13 -6
- package/dist/components/dropdown-menu/menu-shared.d.ts +11 -8
- package/dist/components/editorial-engine/engine.js +1 -14
- package/dist/components/fireworks-hdr/webgl2-renderer.js +0 -19
- package/dist/components/fireworks-hdr/webgpu-renderer.js +3 -22
- package/dist/components/flip-card/FlipCard.d.ts +42 -1
- package/dist/components/flip-card/FlipCard.js +178 -25
- package/dist/components/flip-card/index.d.ts +1 -1
- package/dist/components/fluid-cursor/FluidCursor.js +5 -4
- package/dist/components/fluid-cursor/dev.d.ts +1 -0
- package/dist/components/fluid-cursor/dev.js +8 -0
- package/dist/components/fluid-cursor/fluid-shared.d.ts +0 -2
- package/dist/components/fluid-cursor/fluid-shared.js +0 -2
- package/dist/components/fluid-cursor/webgpu-engine.js +10 -24
- package/dist/components/glow-border/GlowBorder.d.ts +41 -1
- package/dist/components/glow-border/GlowBorder.js +96 -16
- package/dist/components/glow-border/index.d.ts +2 -2
- package/dist/components/gradient-button/GradientButton.d.ts +10 -5
- package/dist/components/gradient-button/GradientButton.js +25 -18
- package/dist/components/image-trail-cursor/ImageTrailCursor.js +1 -0
- package/dist/components/image-trail-cursor/trail-variants.d.ts +37 -1
- package/dist/components/image-trail-cursor/trail-variants.js +331 -8
- package/dist/components/interactive-hover-button/InteractiveHoverButton.js +30 -35
- package/dist/components/marquee/Marquee.d.ts +7 -3
- package/dist/components/marquee/Marquee.js +134 -2
- package/dist/components/marquee/ReviewCard.d.ts +2 -1
- package/dist/components/marquee/ReviewCard.js +20 -9
- package/dist/components/meteors/Meteors.d.ts +32 -10
- package/dist/components/meteors/Meteors.js +53 -20
- package/dist/components/mosaic-glow/mosaic-glow-core.d.ts +98 -10
- package/dist/components/neon-border/NeonBorder.d.ts +24 -5
- package/dist/components/neon-border/NeonBorder.js +37 -7
- package/dist/components/pagination/Pagination.js +220 -82
- package/dist/components/rainbow-button/RainbowButton.js +13 -5
- package/dist/components/ripple-button/RippleButton.d.ts +20 -1
- package/dist/components/ripple-button/RippleButton.js +44 -21
- package/dist/components/shimmer-button/ShimmerButton.d.ts +5 -5
- package/dist/components/shimmer-button/ShimmerButton.js +17 -19
- package/dist/components/smooth-cursor/SmoothCursor.d.ts +13 -9
- package/dist/components/smooth-cursor/SmoothCursor.js +114 -163
- package/dist/components/smooth-cursor/smooth-cursor-core.d.ts +85 -0
- package/dist/components/smooth-cursor/smooth-cursor-core.js +327 -0
- package/dist/components/stepper/Step.js +32 -19
- package/dist/components/stepper/Stepper.js +18 -2
- package/dist/components/stepper/types.d.ts +13 -0
- package/dist/components/timeline/Timeline.d.ts +8 -1
- package/dist/components/timeline/Timeline.js +165 -76
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -1
- package/dist/internals/markdown.d.ts +5 -0
- package/dist/internals/motion/haptics.d.ts +0 -4
- package/dist/internals/motion/presence-core.d.ts +59 -0
- package/dist/internals/motion/presence-core.js +162 -0
- package/dist/internals/motion/presence.d.ts +2 -4
- package/dist/internals/motion/presence.js +1 -156
- package/dist/internals/motion/raf.d.ts +1 -1
- package/dist/internals/motion/stagger.d.ts +3 -4
- package/dist/internals/motion/tokens.js +4 -0
- package/dist/internals/motion/transitions.d.ts +17 -17
- package/dist/internals/motion/types.d.ts +6 -7
- package/dist/internals/relative-time.d.ts +1 -1
- package/dist/styles.css +3545 -204
- package/package.json +18 -2
- package/dist/components/fireworks-hdr/webgpu-types.d.ts +0 -159
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const DEFAULT_DAMPING = 45;
|
|
3
|
+
const DEFAULT_STIFFNESS = 400;
|
|
4
|
+
const DEFAULT_MASS = 1;
|
|
5
|
+
const LABEL_STIFFNESS_SCALE = 0.55;
|
|
6
|
+
const LABEL_DAMPING_SCALE = 1.1;
|
|
7
|
+
const MAX_DT = 0.064;
|
|
8
|
+
const WAKE_DT = 1 / 60;
|
|
9
|
+
const MIN_ROTATION_DISTANCE = 0.1;
|
|
10
|
+
const ROTATION_EASE = 0.3;
|
|
11
|
+
const LABEL_TETHER = 24;
|
|
12
|
+
const LABEL_TILT_PER_VELOCITY = 35e-4;
|
|
13
|
+
const LABEL_MAX_TILT = 5;
|
|
14
|
+
const SETTLE_DISTANCE = 0.05;
|
|
15
|
+
const SETTLE_VELOCITY = 1;
|
|
16
|
+
const SETTLE_ROTATION = 0.05;
|
|
17
|
+
function resolveConfig(springConfig) {
|
|
18
|
+
return {
|
|
19
|
+
damping: springConfig?.damping ?? DEFAULT_DAMPING,
|
|
20
|
+
stiffness: springConfig?.stiffness ?? DEFAULT_STIFFNESS,
|
|
21
|
+
mass: springConfig?.mass ?? DEFAULT_MASS
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function resolveLabelConfig(labelSpring, springConfig) {
|
|
25
|
+
const base = resolveConfig(springConfig);
|
|
26
|
+
return {
|
|
27
|
+
damping: labelSpring?.damping ?? base.damping * LABEL_DAMPING_SCALE,
|
|
28
|
+
stiffness: labelSpring?.stiffness ?? base.stiffness * LABEL_STIFFNESS_SCALE,
|
|
29
|
+
mass: labelSpring?.mass ?? base.mass
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function createSmoothCursor(el, options) {
|
|
33
|
+
const { cursor } = el;
|
|
34
|
+
let label = el.label ?? null;
|
|
35
|
+
const onVisibleChange = options.onVisibleChange;
|
|
36
|
+
const onIdleChange = options.onIdleChange;
|
|
37
|
+
let springConfig = options.springConfig;
|
|
38
|
+
let labelSpring = options.labelSpring;
|
|
39
|
+
let rotate = options.rotate ?? true;
|
|
40
|
+
let idleFade = Math.max(0, options.idleFade ?? 0);
|
|
41
|
+
let reducedMotion = options.reducedMotion ?? false;
|
|
42
|
+
let posX = 0;
|
|
43
|
+
let posY = 0;
|
|
44
|
+
let velX = 0;
|
|
45
|
+
let velY = 0;
|
|
46
|
+
let targetX = 0;
|
|
47
|
+
let targetY = 0;
|
|
48
|
+
let labelX = 0;
|
|
49
|
+
let labelY = 0;
|
|
50
|
+
let labelVelX = 0;
|
|
51
|
+
let labelVelY = 0;
|
|
52
|
+
let rotation = 0;
|
|
53
|
+
let prevX = 0;
|
|
54
|
+
let prevY = 0;
|
|
55
|
+
let visible = false;
|
|
56
|
+
let idle = false;
|
|
57
|
+
let idleTimer = null;
|
|
58
|
+
let rafId = null;
|
|
59
|
+
let lastTime = 0;
|
|
60
|
+
let sleeping = false;
|
|
61
|
+
let waking = false;
|
|
62
|
+
let destroyed = false;
|
|
63
|
+
function setVisible(next) {
|
|
64
|
+
if (visible === next) return;
|
|
65
|
+
visible = next;
|
|
66
|
+
onVisibleChange?.(visible);
|
|
67
|
+
}
|
|
68
|
+
function setIdle(next) {
|
|
69
|
+
if (idle === next) return;
|
|
70
|
+
idle = next;
|
|
71
|
+
onIdleChange?.(idle);
|
|
72
|
+
}
|
|
73
|
+
function clearIdleTimer() {
|
|
74
|
+
if (idleTimer !== null) {
|
|
75
|
+
clearTimeout(idleTimer);
|
|
76
|
+
idleTimer = null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function markActive() {
|
|
80
|
+
clearIdleTimer();
|
|
81
|
+
setIdle(false);
|
|
82
|
+
if (idleFade > 0) {
|
|
83
|
+
idleTimer = setTimeout(() => {
|
|
84
|
+
idleTimer = null;
|
|
85
|
+
setIdle(true);
|
|
86
|
+
}, idleFade);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function startAnimation() {
|
|
90
|
+
if (rafId === null) {
|
|
91
|
+
if (sleeping) {
|
|
92
|
+
waking = true;
|
|
93
|
+
} else {
|
|
94
|
+
lastTime = 0;
|
|
95
|
+
}
|
|
96
|
+
sleeping = false;
|
|
97
|
+
rafId = requestAnimationFrame(animate);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function stopAnimation() {
|
|
101
|
+
sleeping = false;
|
|
102
|
+
waking = false;
|
|
103
|
+
if (rafId !== null) {
|
|
104
|
+
cancelAnimationFrame(rafId);
|
|
105
|
+
rafId = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function writeLabel(tilt) {
|
|
109
|
+
if (!label) return;
|
|
110
|
+
label.style.transform = tilt === 0 ? `translate3d(${labelX}px, ${labelY}px, 0)` : `translate3d(${labelX}px, ${labelY}px, 0) rotate(${tilt}deg)`;
|
|
111
|
+
}
|
|
112
|
+
function snapLabel() {
|
|
113
|
+
labelX = posX;
|
|
114
|
+
labelY = posY;
|
|
115
|
+
labelVelX = 0;
|
|
116
|
+
labelVelY = 0;
|
|
117
|
+
writeLabel(0);
|
|
118
|
+
}
|
|
119
|
+
function snapToTarget() {
|
|
120
|
+
posX = targetX;
|
|
121
|
+
posY = targetY;
|
|
122
|
+
velX = 0;
|
|
123
|
+
velY = 0;
|
|
124
|
+
if (!rotate) rotation = 0;
|
|
125
|
+
cursor.style.transform = `translate3d(${posX}px, ${posY}px, 0)`;
|
|
126
|
+
snapLabel();
|
|
127
|
+
}
|
|
128
|
+
function placeAtTarget() {
|
|
129
|
+
posX = targetX;
|
|
130
|
+
posY = targetY;
|
|
131
|
+
prevX = targetX;
|
|
132
|
+
prevY = targetY;
|
|
133
|
+
labelX = targetX;
|
|
134
|
+
labelY = targetY;
|
|
135
|
+
labelVelX = 0;
|
|
136
|
+
labelVelY = 0;
|
|
137
|
+
}
|
|
138
|
+
function onMouseMove(e) {
|
|
139
|
+
targetX = e.clientX;
|
|
140
|
+
targetY = e.clientY;
|
|
141
|
+
if (!visible) {
|
|
142
|
+
placeAtTarget();
|
|
143
|
+
setVisible(true);
|
|
144
|
+
}
|
|
145
|
+
markActive();
|
|
146
|
+
if (reducedMotion) {
|
|
147
|
+
snapToTarget();
|
|
148
|
+
} else {
|
|
149
|
+
startAnimation();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function onMouseLeave() {
|
|
153
|
+
setVisible(false);
|
|
154
|
+
clearIdleTimer();
|
|
155
|
+
setIdle(false);
|
|
156
|
+
stopAnimation();
|
|
157
|
+
}
|
|
158
|
+
function onMouseEnter(e) {
|
|
159
|
+
targetX = e.clientX;
|
|
160
|
+
targetY = e.clientY;
|
|
161
|
+
placeAtTarget();
|
|
162
|
+
setVisible(true);
|
|
163
|
+
markActive();
|
|
164
|
+
if (!reducedMotion) {
|
|
165
|
+
startAnimation();
|
|
166
|
+
} else {
|
|
167
|
+
snapToTarget();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function animate(time) {
|
|
171
|
+
rafId = null;
|
|
172
|
+
if (!visible) return;
|
|
173
|
+
if (lastTime === 0) {
|
|
174
|
+
lastTime = time;
|
|
175
|
+
rafId = requestAnimationFrame(animate);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
let dt = Math.min((time - lastTime) / 1e3, MAX_DT);
|
|
179
|
+
if (waking) {
|
|
180
|
+
dt = Math.min(dt, WAKE_DT);
|
|
181
|
+
waking = false;
|
|
182
|
+
}
|
|
183
|
+
lastTime = time;
|
|
184
|
+
const { stiffness, damping, mass } = resolveConfig(springConfig);
|
|
185
|
+
const forceX = -stiffness * (posX - targetX) - damping * velX;
|
|
186
|
+
const forceY = -stiffness * (posY - targetY) - damping * velY;
|
|
187
|
+
const accX = forceX / mass;
|
|
188
|
+
const accY = forceY / mass;
|
|
189
|
+
velX += accX * dt;
|
|
190
|
+
velY += accY * dt;
|
|
191
|
+
posX += velX * dt;
|
|
192
|
+
posY += velY * dt;
|
|
193
|
+
if (rotate) {
|
|
194
|
+
const dx = posX - prevX;
|
|
195
|
+
const dy = posY - prevY;
|
|
196
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
197
|
+
if (distance > MIN_ROTATION_DISTANCE) {
|
|
198
|
+
const targetRotation = Math.atan2(dy, dx) * (180 / Math.PI) + 90;
|
|
199
|
+
let diff = targetRotation - rotation;
|
|
200
|
+
while (diff > 180) diff -= 360;
|
|
201
|
+
while (diff < -180) diff += 360;
|
|
202
|
+
rotation += diff * ROTATION_EASE;
|
|
203
|
+
}
|
|
204
|
+
} else if (rotation !== 0) {
|
|
205
|
+
rotation += -rotation * ROTATION_EASE;
|
|
206
|
+
if (Math.abs(rotation) < SETTLE_ROTATION) rotation = 0;
|
|
207
|
+
}
|
|
208
|
+
prevX = posX;
|
|
209
|
+
prevY = posY;
|
|
210
|
+
let tilt = 0;
|
|
211
|
+
let labelSettled = true;
|
|
212
|
+
if (label) {
|
|
213
|
+
const lc = resolveLabelConfig(labelSpring, springConfig);
|
|
214
|
+
const lfx = -lc.stiffness * (labelX - posX) - lc.damping * labelVelX;
|
|
215
|
+
const lfy = -lc.stiffness * (labelY - posY) - lc.damping * labelVelY;
|
|
216
|
+
labelVelX += lfx / lc.mass * dt;
|
|
217
|
+
labelVelY += lfy / lc.mass * dt;
|
|
218
|
+
labelX += labelVelX * dt;
|
|
219
|
+
labelY += labelVelY * dt;
|
|
220
|
+
const gapX = labelX - posX;
|
|
221
|
+
const gapY = labelY - posY;
|
|
222
|
+
const gap = Math.sqrt(gapX * gapX + gapY * gapY);
|
|
223
|
+
if (gap > LABEL_TETHER) {
|
|
224
|
+
const k = LABEL_TETHER / gap;
|
|
225
|
+
labelX = posX + gapX * k;
|
|
226
|
+
labelY = posY + gapY * k;
|
|
227
|
+
}
|
|
228
|
+
tilt = Math.max(
|
|
229
|
+
-LABEL_MAX_TILT,
|
|
230
|
+
Math.min(LABEL_MAX_TILT, labelVelX * LABEL_TILT_PER_VELOCITY)
|
|
231
|
+
);
|
|
232
|
+
if (Math.abs(tilt) < 0.01) tilt = 0;
|
|
233
|
+
labelSettled = Math.abs(labelX - targetX) < SETTLE_DISTANCE && Math.abs(labelY - targetY) < SETTLE_DISTANCE && Math.abs(labelVelX) < SETTLE_VELOCITY && Math.abs(labelVelY) < SETTLE_VELOCITY;
|
|
234
|
+
}
|
|
235
|
+
const settled = labelSettled && Math.abs(posX - targetX) < SETTLE_DISTANCE && Math.abs(posY - targetY) < SETTLE_DISTANCE && Math.abs(velX) < SETTLE_VELOCITY && Math.abs(velY) < SETTLE_VELOCITY && (rotate || rotation === 0);
|
|
236
|
+
if (settled) {
|
|
237
|
+
posX = targetX;
|
|
238
|
+
posY = targetY;
|
|
239
|
+
velX = 0;
|
|
240
|
+
velY = 0;
|
|
241
|
+
prevX = posX;
|
|
242
|
+
prevY = posY;
|
|
243
|
+
if (label) {
|
|
244
|
+
labelX = targetX;
|
|
245
|
+
labelY = targetY;
|
|
246
|
+
labelVelX = 0;
|
|
247
|
+
labelVelY = 0;
|
|
248
|
+
tilt = 0;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
cursor.style.transform = `translate3d(${posX}px, ${posY}px, 0) rotate(${rotation}deg)`;
|
|
252
|
+
writeLabel(tilt);
|
|
253
|
+
if (settled) {
|
|
254
|
+
sleeping = true;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
rafId = requestAnimationFrame(animate);
|
|
258
|
+
}
|
|
259
|
+
const previousBodyCursor = document.body.style.cursor;
|
|
260
|
+
document.body.style.cursor = "none";
|
|
261
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
262
|
+
document.documentElement.addEventListener("mouseleave", onMouseLeave);
|
|
263
|
+
document.documentElement.addEventListener("mouseenter", onMouseEnter);
|
|
264
|
+
return {
|
|
265
|
+
setOptions(next) {
|
|
266
|
+
if (destroyed) return;
|
|
267
|
+
if ("springConfig" in next) {
|
|
268
|
+
springConfig = next.springConfig;
|
|
269
|
+
}
|
|
270
|
+
if ("labelSpring" in next) {
|
|
271
|
+
labelSpring = next.labelSpring;
|
|
272
|
+
}
|
|
273
|
+
if ("rotate" in next) {
|
|
274
|
+
rotate = next.rotate ?? true;
|
|
275
|
+
if (!rotate && rotation !== 0) {
|
|
276
|
+
if (reducedMotion) {
|
|
277
|
+
rotation = 0;
|
|
278
|
+
cursor.style.transform = `translate3d(${posX}px, ${posY}px, 0)`;
|
|
279
|
+
} else if (visible) {
|
|
280
|
+
startAnimation();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if ("idleFade" in next) {
|
|
285
|
+
idleFade = Math.max(0, next.idleFade ?? 0);
|
|
286
|
+
clearIdleTimer();
|
|
287
|
+
if (idleFade === 0) {
|
|
288
|
+
setIdle(false);
|
|
289
|
+
} else if (visible && !idle) {
|
|
290
|
+
markActive();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if ("reducedMotion" in next) {
|
|
294
|
+
reducedMotion = next.reducedMotion ?? false;
|
|
295
|
+
if (reducedMotion) {
|
|
296
|
+
stopAnimation();
|
|
297
|
+
snapToTarget();
|
|
298
|
+
} else if (visible) {
|
|
299
|
+
startAnimation();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
setLabel(next) {
|
|
304
|
+
if (destroyed || next === label) return;
|
|
305
|
+
label = next;
|
|
306
|
+
if (!label) return;
|
|
307
|
+
snapLabel();
|
|
308
|
+
},
|
|
309
|
+
resize() {
|
|
310
|
+
},
|
|
311
|
+
destroy() {
|
|
312
|
+
if (destroyed) return;
|
|
313
|
+
destroyed = true;
|
|
314
|
+
document.body.style.cursor = previousBodyCursor;
|
|
315
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
316
|
+
document.documentElement.removeEventListener("mouseleave", onMouseLeave);
|
|
317
|
+
document.documentElement.removeEventListener("mouseenter", onMouseEnter);
|
|
318
|
+
clearIdleTimer();
|
|
319
|
+
stopAnimation();
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
export {
|
|
324
|
+
createSmoothCursor,
|
|
325
|
+
resolveConfig,
|
|
326
|
+
resolveLabelConfig
|
|
327
|
+
};
|
|
@@ -30,32 +30,40 @@ const Step = forwardRef(function Step2({ label, description, children, className
|
|
|
30
30
|
const clickable = stepper?.clickable ?? false;
|
|
31
31
|
const isFirst = index === 0;
|
|
32
32
|
const isLast = stepper ? index === stepper.count - 1 : true;
|
|
33
|
-
const
|
|
33
|
+
const railFrom = orientation === "vertical" ? index : index - 1;
|
|
34
|
+
const connectorDone = stepper && index !== -1 ? railFrom < stepper.current : false;
|
|
35
|
+
const animate = stepper?.animate ?? false;
|
|
36
|
+
const origin = stepper?.origin ?? 0;
|
|
37
|
+
const forward = stepper ? stepper.current >= origin : true;
|
|
38
|
+
const railOrder = Math.max(0, forward ? railFrom - origin : origin - 1 - railFrom);
|
|
39
|
+
const arrival = stepper && forward && index > origin && index <= stepper.current ? index - origin - 1 + 0.6 : void 0;
|
|
40
|
+
const railStyle = { "--ft-step-order": railOrder };
|
|
41
|
+
const bulletStyle = arrival === void 0 ? void 0 : { "--ft-step-arrival": arrival };
|
|
34
42
|
function handleClick() {
|
|
35
43
|
if (!stepper || !clickable || index === -1) return;
|
|
36
44
|
stepper.select(index);
|
|
37
45
|
}
|
|
38
46
|
const bulletClasses = cn(
|
|
39
|
-
"ft-step-bullet relative inline-flex size-7 shrink-0 items-center justify-center rounded-full text-[
|
|
47
|
+
"ft-step-bullet relative inline-flex size-7 shrink-0 items-center justify-center rounded-full text-[12px] font-semibold tabular-nums",
|
|
40
48
|
status === "done" && "ft-step-bullet-done",
|
|
41
|
-
status === "current" && "ft-step-bullet-current",
|
|
42
|
-
status === "upcoming" && "
|
|
49
|
+
status === "current" && "ft-step-bullet-current ft-step-bullet-halo",
|
|
50
|
+
status === "upcoming" && "ft-step-bullet-upcoming text-muted-foreground"
|
|
43
51
|
);
|
|
44
52
|
const defaultBullet = status === "done" ? /* @__PURE__ */ jsx(
|
|
45
53
|
"svg",
|
|
46
54
|
{
|
|
47
|
-
className: "size-3.5",
|
|
55
|
+
className: "ft-step-glyph ft-step-check size-3.5",
|
|
48
56
|
viewBox: "0 0 24 24",
|
|
49
57
|
fill: "none",
|
|
50
58
|
stroke: "currentColor",
|
|
51
|
-
strokeWidth: "2.
|
|
59
|
+
strokeWidth: "2.75",
|
|
52
60
|
strokeLinecap: "round",
|
|
53
61
|
strokeLinejoin: "round",
|
|
54
62
|
"aria-hidden": "true",
|
|
55
|
-
children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
|
|
63
|
+
children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5", pathLength: 1 })
|
|
56
64
|
}
|
|
57
|
-
) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: index + 1 });
|
|
58
|
-
const bulletContent = /* @__PURE__ */ jsxs("span", { className: bulletClasses, "data-status": status, children: [
|
|
65
|
+
) : /* @__PURE__ */ jsx("span", { className: "ft-step-glyph", "aria-hidden": "true", children: index + 1 });
|
|
66
|
+
const bulletContent = /* @__PURE__ */ jsxs("span", { className: bulletClasses, "data-status": status, style: bulletStyle, children: [
|
|
59
67
|
children ?? defaultBullet,
|
|
60
68
|
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
61
69
|
" ",
|
|
@@ -71,13 +79,15 @@ const Step = forwardRef(function Step2({ label, description, children, className
|
|
|
71
79
|
"span",
|
|
72
80
|
{
|
|
73
81
|
className: cn(
|
|
74
|
-
"text-xs",
|
|
75
|
-
status === "current"
|
|
82
|
+
"ft-step-label text-xs transition-colors",
|
|
83
|
+
status === "current" && "text-foreground font-medium",
|
|
84
|
+
status === "done" && "text-foreground/80",
|
|
85
|
+
status === "upcoming" && "text-muted-foreground"
|
|
76
86
|
),
|
|
77
87
|
children: label
|
|
78
88
|
}
|
|
79
89
|
) : null,
|
|
80
|
-
description ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-
|
|
90
|
+
description ? /* @__PURE__ */ jsx("span", { className: "ft-step-description text-muted-foreground/80 text-[11px] leading-snug", children: description }) : null
|
|
81
91
|
]
|
|
82
92
|
}
|
|
83
93
|
);
|
|
@@ -87,20 +97,22 @@ const Step = forwardRef(function Step2({ label, description, children, className
|
|
|
87
97
|
ref,
|
|
88
98
|
className: cn(
|
|
89
99
|
"ft-step flex",
|
|
90
|
-
orientation === "vertical" ? cn("flex-row items-stretch gap-3", isLast ? "pb-0" : "pb-
|
|
100
|
+
orientation === "vertical" ? cn("flex-row items-stretch gap-3", isLast ? "pb-0" : "pb-7") : cn("flex-col items-center", isFirst ? "flex-none" : "flex-1"),
|
|
101
|
+
animate && "ft-step-animate",
|
|
91
102
|
className
|
|
92
103
|
),
|
|
93
104
|
"data-status": status,
|
|
94
105
|
"data-orientation": orientation,
|
|
95
106
|
"aria-current": status === "current" ? "step" : void 0,
|
|
96
|
-
children: orientation === "horizontal" ? /* @__PURE__ */ jsxs("div", { className: "flex w-full items-
|
|
107
|
+
children: orientation === "horizontal" ? /* @__PURE__ */ jsxs("div", { className: "flex w-full items-start", children: [
|
|
97
108
|
!isFirst && /* @__PURE__ */ jsx(
|
|
98
109
|
"span",
|
|
99
110
|
{
|
|
100
111
|
className: cn(
|
|
101
|
-
"ft-step-connector mt-[13px] h-0.5 flex-1",
|
|
102
|
-
connectorDone
|
|
112
|
+
"ft-step-connector bg-border relative mx-1.5 mt-[13px] h-0.5 flex-1 rounded-full",
|
|
113
|
+
connectorDone && "ft-step-connector-done ft-step-connector-lit"
|
|
103
114
|
),
|
|
115
|
+
style: railStyle,
|
|
104
116
|
"aria-hidden": "true"
|
|
105
117
|
}
|
|
106
118
|
),
|
|
@@ -120,15 +132,16 @@ const Step = forwardRef(function Step2({ label, description, children, className
|
|
|
120
132
|
textContent
|
|
121
133
|
] })
|
|
122
134
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center self-stretch", children: [
|
|
135
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center self-stretch", !isLast && "-mb-7"), children: [
|
|
124
136
|
bulletContent,
|
|
125
137
|
!isLast && /* @__PURE__ */ jsx(
|
|
126
138
|
"span",
|
|
127
139
|
{
|
|
128
140
|
className: cn(
|
|
129
|
-
"ft-step-connector my-1 w-0.5 flex-1",
|
|
130
|
-
connectorDone
|
|
141
|
+
"ft-step-connector bg-border relative my-1 w-0.5 flex-1 rounded-full",
|
|
142
|
+
connectorDone && "ft-step-connector-done ft-step-connector-lit"
|
|
131
143
|
),
|
|
144
|
+
style: railStyle,
|
|
132
145
|
"aria-hidden": "true"
|
|
133
146
|
}
|
|
134
147
|
)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { forwardRef, useState, useRef, useCallback, useMemo } from "react";
|
|
3
|
+
import { forwardRef, useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
4
4
|
import { cn } from "../../utils.js";
|
|
5
5
|
import { useSoundCue } from "../../sound/use-sound.js";
|
|
6
|
+
import { useReducedMotion } from "../../internals/motion/media-query.js";
|
|
6
7
|
import { STEPPER_KEY } from "./types.js";
|
|
7
8
|
const Stepper = forwardRef(function Stepper2({
|
|
8
9
|
current,
|
|
@@ -35,6 +36,18 @@ const Stepper = forwardRef(function Stepper2({
|
|
|
35
36
|
};
|
|
36
37
|
}, []);
|
|
37
38
|
const indexOf = useCallback((id) => registered.indexOf(id), [registered]);
|
|
39
|
+
const reduced = useReducedMotion();
|
|
40
|
+
const [asked, setAsked] = useState(false);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setAsked(true);
|
|
43
|
+
}, []);
|
|
44
|
+
const animate = asked && !reduced;
|
|
45
|
+
const [run, setRun] = useState(() => ({ settled: activeIndex, origin: 0 }));
|
|
46
|
+
let origin = run.origin;
|
|
47
|
+
if (run.settled !== activeIndex) {
|
|
48
|
+
origin = run.settled;
|
|
49
|
+
setRun({ settled: activeIndex, origin: run.settled });
|
|
50
|
+
}
|
|
38
51
|
const playCue = useSoundCue(sound);
|
|
39
52
|
const select = useCallback(
|
|
40
53
|
(index) => {
|
|
@@ -52,11 +65,13 @@ const Stepper = forwardRef(function Stepper2({
|
|
|
52
65
|
clickable,
|
|
53
66
|
current: activeIndex,
|
|
54
67
|
count: registered.length,
|
|
68
|
+
origin,
|
|
69
|
+
animate,
|
|
55
70
|
register,
|
|
56
71
|
indexOf,
|
|
57
72
|
select
|
|
58
73
|
}),
|
|
59
|
-
[orientation, clickable, activeIndex, registered, register, indexOf, select]
|
|
74
|
+
[orientation, clickable, activeIndex, registered, origin, animate, register, indexOf, select]
|
|
60
75
|
);
|
|
61
76
|
return /* @__PURE__ */ jsx(STEPPER_KEY.Provider, { value: context, children: /* @__PURE__ */ jsx(
|
|
62
77
|
"ol",
|
|
@@ -69,6 +84,7 @@ const Stepper = forwardRef(function Stepper2({
|
|
|
69
84
|
className
|
|
70
85
|
),
|
|
71
86
|
"data-orientation": orientation,
|
|
87
|
+
"data-motion": animate ? "full" : "reduced",
|
|
72
88
|
children
|
|
73
89
|
}
|
|
74
90
|
) });
|
|
@@ -34,6 +34,19 @@ export interface StepperContext {
|
|
|
34
34
|
readonly current: number;
|
|
35
35
|
/** Total number of currently registered steps. */
|
|
36
36
|
readonly count: number;
|
|
37
|
+
/**
|
|
38
|
+
* The active index before the latest change (0 on first paint). Steps
|
|
39
|
+
* order their rail sweep and bullet arrival by their distance from it.
|
|
40
|
+
* Optional so a hand-rolled context without it still type-checks; a
|
|
41
|
+
* missing value reads as "no run to order".
|
|
42
|
+
*/
|
|
43
|
+
readonly origin?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the motion layer (rail sweep, breathing halo, check draw) is
|
|
46
|
+
* on: true on the client once `prefers-reduced-motion` has been read and
|
|
47
|
+
* is not `reduce`. Optional for the same reason as `origin`.
|
|
48
|
+
*/
|
|
49
|
+
readonly animate?: boolean;
|
|
37
50
|
/**
|
|
38
51
|
* Registers a step under a stable per-instance id (typically
|
|
39
52
|
* `useFancyId()`). Returns an unregister function; call it on unmount.
|
|
@@ -14,5 +14,12 @@ export interface TimelineProps {
|
|
|
14
14
|
className?: string;
|
|
15
15
|
/** Content render prop, called for each item */
|
|
16
16
|
content?: (item: TimelineItem) => ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Colour of the travelling head, its trail and the lit dots. Any CSS
|
|
19
|
+
* colour. Unset, it follows the theme's `--primary` token and falls back
|
|
20
|
+
* to a soft violet when no theme is present. Also settable from CSS via
|
|
21
|
+
* `--timeline-accent`.
|
|
22
|
+
*/
|
|
23
|
+
accent?: string;
|
|
17
24
|
}
|
|
18
|
-
export declare function Timeline({ items, title, description, className, content }: TimelineProps): import("react").JSX.Element;
|
|
25
|
+
export declare function Timeline({ items, title, description, className, content, accent, }: TimelineProps): import("react").JSX.Element;
|