@pelatform/ui.components 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.
@@ -0,0 +1,1960 @@
1
+ "use client";
2
+ import {
3
+ cn
4
+ } from "./chunk-2TQSHVXA.js";
5
+
6
+ // src/ui/animation/avatar-group.tsx
7
+ import * as React from "react";
8
+ import {
9
+ AnimatePresence,
10
+ motion,
11
+ useMotionValue,
12
+ useSpring,
13
+ useTransform
14
+ } from "motion/react";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var AvatarGroupContext = React.createContext(null);
17
+ var StaggeredContent = ({ content }) => {
18
+ const children = React.Children.toArray(content);
19
+ return /* @__PURE__ */ jsx(
20
+ motion.div,
21
+ {
22
+ initial: "initial",
23
+ animate: "animate",
24
+ exit: "exit",
25
+ variants: {
26
+ animate: { transition: { staggerChildren: 0.08 } }
27
+ },
28
+ children: children.map((child, i) => /* @__PURE__ */ jsx(
29
+ motion.div,
30
+ {
31
+ variants: {
32
+ initial: { opacity: 0, y: 20 },
33
+ animate: {
34
+ opacity: 1,
35
+ y: 0,
36
+ transition: { duration: 0.3, ease: "easeOut" }
37
+ },
38
+ exit: {
39
+ opacity: 0,
40
+ y: -20,
41
+ transition: { duration: 0.2, ease: "easeIn" }
42
+ }
43
+ },
44
+ children: child
45
+ },
46
+ i
47
+ ))
48
+ }
49
+ );
50
+ };
51
+ function AvatarGroup({
52
+ children,
53
+ className,
54
+ tooltipClassName,
55
+ animation = "default"
56
+ }) {
57
+ const contextValue = {
58
+ tooltipClassName,
59
+ animation
60
+ };
61
+ return /* @__PURE__ */ jsx(AvatarGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { className: cn("flex -space-x-2.5", className), children }) });
62
+ }
63
+ function AvatarGroupItem({
64
+ children,
65
+ className,
66
+ tooltipClassName,
67
+ animation: itemAnimation
68
+ }) {
69
+ const context = React.useContext(AvatarGroupContext);
70
+ const [hoveredIndex, setHoveredIndex] = React.useState(false);
71
+ const springConfig = { stiffness: 100, damping: 5 };
72
+ const x = useMotionValue(0);
73
+ const animation = itemAnimation || context?.animation || "default";
74
+ const finalTooltipClassName = tooltipClassName || context?.tooltipClassName;
75
+ const rotate = useSpring(useTransform(x, [-100, 100], [-45, 45]), springConfig);
76
+ const translateX = useSpring(useTransform(x, [-100, 100], [-50, 50]), springConfig);
77
+ const tooltipChild = React.Children.toArray(children).find(
78
+ (child) => React.isValidElement(child) && child.type === AvatarGroupTooltip
79
+ );
80
+ const otherChildren = React.Children.toArray(children).filter(
81
+ (child) => !(React.isValidElement(child) && child.type === AvatarGroupTooltip)
82
+ );
83
+ const tooltipContent = tooltipChild && React.isValidElement(tooltipChild) ? tooltipChild.props.children : null;
84
+ const handleMouseMove = (event) => {
85
+ const halfWidth = event.target.offsetWidth / 2;
86
+ x.set(event.nativeEvent.offsetX - halfWidth);
87
+ };
88
+ const animationVariants = {
89
+ default: {
90
+ initial: { opacity: 0, y: 20, scale: 0.6 },
91
+ animate: {
92
+ opacity: 1,
93
+ y: 0,
94
+ scale: 1,
95
+ transition: {
96
+ type: "spring",
97
+ stiffness: 260,
98
+ damping: 10
99
+ }
100
+ },
101
+ exit: {
102
+ opacity: 0,
103
+ y: 20,
104
+ scale: 0.6,
105
+ transition: {
106
+ duration: 0.2,
107
+ ease: "easeInOut"
108
+ }
109
+ }
110
+ },
111
+ flip: {
112
+ initial: { opacity: 0, rotateX: -90 },
113
+ animate: {
114
+ opacity: 1,
115
+ rotateX: 0,
116
+ transition: {
117
+ type: "spring",
118
+ stiffness: 180,
119
+ damping: 25
120
+ }
121
+ },
122
+ exit: {
123
+ opacity: 0,
124
+ rotateX: -90,
125
+ transition: {
126
+ duration: 0.3,
127
+ ease: "easeInOut"
128
+ }
129
+ }
130
+ },
131
+ reveal: {
132
+ initial: { opacity: 0, scale: 0.95 },
133
+ animate: {
134
+ opacity: 1,
135
+ scale: 1,
136
+ transition: { duration: 0.15, ease: "easeOut" }
137
+ },
138
+ exit: {
139
+ opacity: 0,
140
+ scale: 0.95,
141
+ transition: { duration: 0.1, ease: "easeIn" }
142
+ }
143
+ }
144
+ };
145
+ const selectedVariant = animationVariants[animation];
146
+ return /* @__PURE__ */ jsxs(
147
+ "div",
148
+ {
149
+ className: cn("group relative", className),
150
+ onMouseEnter: () => setHoveredIndex(true),
151
+ onMouseLeave: () => setHoveredIndex(false),
152
+ children: [
153
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: hoveredIndex && tooltipContent && /* @__PURE__ */ jsxs(
154
+ motion.div,
155
+ {
156
+ initial: selectedVariant.initial,
157
+ animate: selectedVariant.animate,
158
+ exit: selectedVariant.exit,
159
+ style: {
160
+ translateX: animation === "reveal" ? 0 : translateX,
161
+ rotate: animation === "reveal" ? 0 : rotate,
162
+ whiteSpace: "nowrap",
163
+ transformOrigin: "center"
164
+ },
165
+ className: cn(
166
+ "absolute -top-16 left-1/2 z-50 flex -translate-x-1/2 flex-col items-center justify-center rounded-md bg-black px-4 py-2 font-medium text-white text-xs shadow-xl",
167
+ finalTooltipClassName
168
+ ),
169
+ children: [
170
+ /* @__PURE__ */ jsx(
171
+ motion.div,
172
+ {
173
+ className: "absolute inset-x-10 -bottom-px z-30 h-px w-[20%] bg-linear-to-r from-transparent via-emerald-500 to-transparent dark:via-emerald-900",
174
+ initial: { opacity: 0 },
175
+ animate: { opacity: 1 },
176
+ exit: { opacity: 0 },
177
+ transition: { duration: 0.15 }
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx(
181
+ motion.div,
182
+ {
183
+ className: "absolute -bottom-px left-10 z-30 h-px w-[40%] bg-linear-to-r from-transparent via-sky-500 to-transparent dark:via-sky-900",
184
+ initial: { opacity: 0 },
185
+ animate: { opacity: 1 },
186
+ exit: { opacity: 0 },
187
+ transition: { duration: 0.15 }
188
+ }
189
+ ),
190
+ animation === "reveal" ? /* @__PURE__ */ jsx(StaggeredContent, { content: tooltipContent }) : tooltipContent
191
+ ]
192
+ }
193
+ ) }),
194
+ /* @__PURE__ */ jsx(
195
+ motion.div,
196
+ {
197
+ className: "relative cursor-pointer",
198
+ whileHover: {
199
+ zIndex: 30
200
+ },
201
+ whileTap: { scale: 0.95 },
202
+ transition: {
203
+ duration: 0.5
204
+ },
205
+ onMouseMove: handleMouseMove,
206
+ children: otherChildren
207
+ }
208
+ )
209
+ ]
210
+ }
211
+ );
212
+ }
213
+ function AvatarGroupTooltip({ children, className }) {
214
+ return /* @__PURE__ */ jsx(
215
+ motion.div,
216
+ {
217
+ initial: { opacity: 0, y: 20, scale: 0.6 },
218
+ animate: { opacity: 1 },
219
+ exit: { opacity: 0 },
220
+ transition: { duration: 0.15 },
221
+ className: cn("relative z-30 hidden", className),
222
+ children
223
+ }
224
+ );
225
+ }
226
+
227
+ // src/ui/animation/counting-number.tsx
228
+ import { useEffect, useRef, useState as useState2 } from "react";
229
+ import { animate, motion as motion2, useInView, useMotionValue as useMotionValue2 } from "motion/react";
230
+ import { jsx as jsx2 } from "react/jsx-runtime";
231
+ function CountingNumber({
232
+ from = 0,
233
+ to = 100,
234
+ duration = 2,
235
+ delay = 0,
236
+ className,
237
+ startOnView = true,
238
+ once = false,
239
+ inViewMargin,
240
+ onComplete,
241
+ format,
242
+ ...props
243
+ }) {
244
+ const ref = useRef(null);
245
+ const isInView = useInView(ref, { once, margin: inViewMargin });
246
+ const [hasAnimated, setHasAnimated] = useState2(false);
247
+ const [display, setDisplay] = useState2(from);
248
+ const motionValue = useMotionValue2(from);
249
+ const shouldStart = !startOnView || isInView && (!once || !hasAnimated);
250
+ useEffect(() => {
251
+ if (!shouldStart) return;
252
+ setHasAnimated(true);
253
+ const timeout = setTimeout(() => {
254
+ const controls = animate(motionValue, to, {
255
+ duration,
256
+ onUpdate: (v) => setDisplay(v),
257
+ onComplete
258
+ });
259
+ return () => controls.stop();
260
+ }, delay);
261
+ return () => clearTimeout(timeout);
262
+ }, [shouldStart, to, duration, delay, motionValue, onComplete]);
263
+ return /* @__PURE__ */ jsx2(motion2.span, { ref, className: cn("inline-block", className), ...props, children: format ? format(display) : Math.round(display) });
264
+ }
265
+
266
+ // src/ui/animation/github-button.tsx
267
+ import React2, { useCallback, useEffect as useEffect2, useState as useState3 } from "react";
268
+ import { cva } from "class-variance-authority";
269
+ import { StarIcon } from "lucide-react";
270
+ import { motion as motion3, useInView as useInView2 } from "motion/react";
271
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
272
+ var githubButtonVariants = cva(
273
+ "backface-visibility-hidden group relative inline-flex transform-gpu cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap font-medium ring-offset-background transition-transform duration-200 ease-out will-change-transform hover:scale-105 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-60 [&_svg]:shrink-0",
274
+ {
275
+ variants: {
276
+ variant: {
277
+ default: "border-gray-700 bg-zinc-950 text-white hover:bg-zinc-900 dark:border-gray-300 dark:bg-zinc-50 dark:text-zinc-950 dark:hover:bg-zinc-50",
278
+ outline: "border border-input bg-background text-accent-foreground hover:bg-accent"
279
+ },
280
+ size: {
281
+ default: "h-8.5 gap-2 rounded-md px-3 text-[0.8125rem] leading-none [&_svg]:size-4",
282
+ sm: "h-7 gap-1.5 rounded-md px-2.5 text-xs leading-none [&_svg]:size-3.5",
283
+ lg: "h-10 gap-2.5 rounded-md px-4 text-sm leading-none [&_svg]:size-5"
284
+ }
285
+ },
286
+ defaultVariants: {
287
+ variant: "default",
288
+ size: "default"
289
+ }
290
+ }
291
+ );
292
+ function GithubButton({
293
+ initialStars = 0,
294
+ targetStars = 0,
295
+ starsClass = "",
296
+ fixedWidth = true,
297
+ animationDuration = 2,
298
+ animationDelay = 0,
299
+ autoAnimate = true,
300
+ className,
301
+ variant = "default",
302
+ size = "default",
303
+ showGithubIcon = true,
304
+ showStarIcon = true,
305
+ roundStars = false,
306
+ separator = false,
307
+ filled = false,
308
+ repoUrl,
309
+ onClick,
310
+ label = "",
311
+ useInViewTrigger = false,
312
+ inViewOptions = { once: true },
313
+ transition,
314
+ ...props
315
+ }) {
316
+ const [currentStars, setCurrentStars] = useState3(initialStars);
317
+ const [isAnimating, setIsAnimating] = useState3(false);
318
+ const [starProgress, setStarProgress] = useState3(filled ? 100 : 0);
319
+ const [hasAnimated, setHasAnimated] = useState3(false);
320
+ const formatNumber = (num) => {
321
+ const units = ["k", "M", "B", "T"];
322
+ if (roundStars && num >= 1e3) {
323
+ let unitIndex = -1;
324
+ let value = num;
325
+ while (value >= 1e3 && unitIndex < units.length - 1) {
326
+ value /= 1e3;
327
+ unitIndex++;
328
+ }
329
+ const formatted = value % 1 === 0 ? value.toString() : value.toFixed(1);
330
+ return `${formatted}${units[unitIndex]}`;
331
+ }
332
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
333
+ };
334
+ const startAnimation = useCallback(() => {
335
+ if (isAnimating || hasAnimated) return;
336
+ setIsAnimating(true);
337
+ const startTime = Date.now();
338
+ const startValue = 0;
339
+ const endValue = targetStars;
340
+ const duration = animationDuration * 1e3;
341
+ const animate2 = () => {
342
+ const elapsed = Date.now() - startTime;
343
+ const progress = Math.min(elapsed / duration, 1);
344
+ const easeOutQuart = 1 - (1 - progress) ** 4;
345
+ const newStars = Math.round(startValue + (endValue - startValue) * easeOutQuart);
346
+ setCurrentStars(newStars);
347
+ setStarProgress(progress * 100);
348
+ if (progress < 1) {
349
+ requestAnimationFrame(animate2);
350
+ } else {
351
+ setCurrentStars(endValue);
352
+ setStarProgress(100);
353
+ setIsAnimating(false);
354
+ setHasAnimated(true);
355
+ }
356
+ };
357
+ setTimeout(() => {
358
+ requestAnimationFrame(animate2);
359
+ }, animationDelay * 1e3);
360
+ }, [isAnimating, hasAnimated, targetStars, animationDuration, animationDelay]);
361
+ const ref = React2.useRef(null);
362
+ const isInView = useInView2(ref, inViewOptions);
363
+ useEffect2(() => {
364
+ setHasAnimated(false);
365
+ setCurrentStars(initialStars);
366
+ }, [initialStars]);
367
+ useEffect2(() => {
368
+ if (useInViewTrigger) {
369
+ if (isInView && !hasAnimated) {
370
+ startAnimation();
371
+ }
372
+ } else if (autoAnimate && !hasAnimated) {
373
+ startAnimation();
374
+ }
375
+ }, [autoAnimate, useInViewTrigger, isInView, hasAnimated, startAnimation]);
376
+ const navigateToRepo = () => {
377
+ if (!repoUrl) {
378
+ return;
379
+ }
380
+ try {
381
+ const link = document.createElement("a");
382
+ link.href = repoUrl;
383
+ link.target = "_blank";
384
+ link.rel = "noopener noreferrer";
385
+ document.body.appendChild(link);
386
+ link.click();
387
+ document.body.removeChild(link);
388
+ } catch {
389
+ try {
390
+ window.open(repoUrl, "_blank", "noopener,noreferrer");
391
+ } catch {
392
+ window.location.href = repoUrl;
393
+ }
394
+ }
395
+ };
396
+ const handleClick = (event) => {
397
+ if (onClick) {
398
+ onClick(event);
399
+ return;
400
+ }
401
+ if (repoUrl) {
402
+ navigateToRepo();
403
+ } else if (!hasAnimated) {
404
+ startAnimation();
405
+ }
406
+ };
407
+ const handleKeyDown = (event) => {
408
+ if (event.key === "Enter" || event.key === " ") {
409
+ event.preventDefault();
410
+ if (repoUrl) {
411
+ navigateToRepo();
412
+ } else if (!hasAnimated) {
413
+ startAnimation();
414
+ }
415
+ }
416
+ };
417
+ return /* @__PURE__ */ jsxs2(
418
+ "button",
419
+ {
420
+ ref,
421
+ className: cn(githubButtonVariants({ variant, size, className }), separator && "ps-0"),
422
+ onClick: handleClick,
423
+ onKeyDown: handleKeyDown,
424
+ tabIndex: 0,
425
+ "aria-label": repoUrl ? `Star ${label} on GitHub` : label,
426
+ ...props,
427
+ children: [
428
+ showGithubIcon && /* @__PURE__ */ jsx3(
429
+ "div",
430
+ {
431
+ className: cn(
432
+ "relative flex h-full items-center justify-center",
433
+ separator && "w-9 border-input border-e bg-muted/60"
434
+ ),
435
+ children: /* @__PURE__ */ jsx3("svg", { role: "img", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx3("path", { d: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" }) })
436
+ }
437
+ ),
438
+ label && /* @__PURE__ */ jsx3("span", { children: label }),
439
+ showStarIcon && /* @__PURE__ */ jsxs2("div", { className: "relative inline-flex shrink-0", children: [
440
+ /* @__PURE__ */ jsx3(StarIcon, { className: "fill-muted-foreground text-muted-foreground", "aria-hidden": "true" }),
441
+ /* @__PURE__ */ jsx3(
442
+ StarIcon,
443
+ {
444
+ className: "absolute start-0 top-0 fill-yellow-400 text-yellow-400",
445
+ size: 18,
446
+ "aria-hidden": "true",
447
+ style: {
448
+ clipPath: `inset(${100 - starProgress}% 0 0 0)`
449
+ }
450
+ }
451
+ )
452
+ ] }),
453
+ /* @__PURE__ */ jsxs2("div", { className: cn("relative flex flex-col overflow-hidden font-semibold", starsClass), children: [
454
+ /* @__PURE__ */ jsx3(
455
+ motion3.div,
456
+ {
457
+ animate: { opacity: 1 },
458
+ transition: {
459
+ type: "spring",
460
+ stiffness: 300,
461
+ damping: 30,
462
+ ...transition
463
+ },
464
+ className: "tabular-nums",
465
+ children: /* @__PURE__ */ jsx3("span", { children: currentStars > 0 && formatNumber(currentStars) })
466
+ }
467
+ ),
468
+ fixedWidth && /* @__PURE__ */ jsx3("span", { className: "h-0 overflow-hidden tabular-nums opacity-0", children: formatNumber(targetStars) })
469
+ ] })
470
+ ]
471
+ }
472
+ );
473
+ }
474
+
475
+ // src/ui/animation/gradient-background.tsx
476
+ import { motion as motion4 } from "motion/react";
477
+ import { jsx as jsx4 } from "react/jsx-runtime";
478
+ function GradientBackground({
479
+ className,
480
+ transition = { duration: 10, ease: "easeInOut", repeat: Infinity },
481
+ ...props
482
+ }) {
483
+ return /* @__PURE__ */ jsx4(
484
+ motion4.div,
485
+ {
486
+ "data-slot": "gradient-background",
487
+ className: cn(
488
+ "size-full bg-linear-to-br bg-size-[300%_300%] from-0% from-fuchsia-400 via-50% via-violet-500 to-100% to-fuchsia-600",
489
+ className
490
+ ),
491
+ animate: {
492
+ backgroundPosition: [
493
+ "0% 0%",
494
+ "50% 50%",
495
+ "100% 0%",
496
+ "50% 100%",
497
+ "0% 50%",
498
+ "100% 100%",
499
+ "0% 0%"
500
+ ]
501
+ },
502
+ whileTap: {
503
+ scale: 0.98
504
+ },
505
+ transition,
506
+ ...props
507
+ }
508
+ );
509
+ }
510
+
511
+ // src/ui/animation/grid-background.tsx
512
+ import * as React3 from "react";
513
+ import { motion as motion5 } from "motion/react";
514
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
515
+ function GridBackground({
516
+ className,
517
+ children,
518
+ gridSize = "8:8",
519
+ colors = {},
520
+ beams = {},
521
+ ...props
522
+ }) {
523
+ const {
524
+ background = "bg-slate-900",
525
+ borderColor = "border-slate-700/50",
526
+ borderSize = "1px",
527
+ borderStyle = "solid"
528
+ } = colors;
529
+ const {
530
+ count = 12,
531
+ colors: beamColors = [
532
+ "bg-cyan-400",
533
+ "bg-purple-400",
534
+ "bg-fuchsia-400",
535
+ "bg-violet-400",
536
+ "bg-blue-400",
537
+ "bg-indigo-400",
538
+ "bg-green-400",
539
+ "bg-yellow-400",
540
+ "bg-orange-400",
541
+ "bg-red-400",
542
+ "bg-pink-400",
543
+ "bg-rose-400"
544
+ ],
545
+ shadow = "shadow-lg shadow-cyan-400/50 rounded-full",
546
+ speed = 4
547
+ } = beams;
548
+ const [cols, rows] = gridSize.split(":").map(Number);
549
+ const animatedBeams = React3.useMemo(
550
+ () => Array.from({ length: Math.min(count, 12) }, (_, i) => {
551
+ const direction = Math.random() > 0.5 ? "horizontal" : "vertical";
552
+ const startPosition = Math.random() > 0.5 ? "start" : "end";
553
+ return {
554
+ id: i,
555
+ color: beamColors[i % beamColors.length],
556
+ direction,
557
+ startPosition,
558
+ // For horizontal beams: choose a row index (1 to rows-1) - exclude edges
559
+ // For vertical beams: choose a column index (1 to cols-1) - exclude edges
560
+ gridLine: direction === "horizontal" ? Math.floor(Math.random() * (rows - 1)) + 1 : Math.floor(Math.random() * (cols - 1)) + 1,
561
+ delay: Math.random() * 2,
562
+ duration: speed + Math.random() * 2
563
+ };
564
+ }),
565
+ [count, beamColors, speed, cols, rows]
566
+ );
567
+ const gridStyle = {
568
+ "--border-style": borderStyle
569
+ };
570
+ return /* @__PURE__ */ jsxs3(
571
+ motion5.div,
572
+ {
573
+ "data-slot": "grid-background",
574
+ className: cn("relative size-full overflow-hidden", background, className),
575
+ style: gridStyle,
576
+ ...props,
577
+ children: [
578
+ /* @__PURE__ */ jsx5(
579
+ "div",
580
+ {
581
+ className: cn("absolute inset-0 size-full", borderColor),
582
+ style: {
583
+ display: "grid",
584
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
585
+ gridTemplateRows: `repeat(${rows}, 1fr)`,
586
+ borderRightWidth: borderSize,
587
+ borderBottomWidth: borderSize,
588
+ borderRightStyle: borderStyle,
589
+ borderBottomStyle: borderStyle
590
+ },
591
+ children: Array.from({ length: cols * rows }).map((_, index) => /* @__PURE__ */ jsx5(
592
+ "div",
593
+ {
594
+ className: cn("relative", borderColor),
595
+ style: {
596
+ borderTopWidth: borderSize,
597
+ borderLeftWidth: borderSize,
598
+ borderTopStyle: borderStyle,
599
+ borderLeftStyle: borderStyle
600
+ }
601
+ },
602
+ index
603
+ ))
604
+ }
605
+ ),
606
+ animatedBeams.map((beam) => {
607
+ const horizontalPosition = beam.gridLine / rows * 100;
608
+ const verticalPosition = beam.gridLine / cols * 100;
609
+ return /* @__PURE__ */ jsx5(
610
+ motion5.div,
611
+ {
612
+ className: cn(
613
+ "absolute z-20 rounded-full backdrop-blur-sm",
614
+ beam.color,
615
+ beam.direction === "horizontal" ? "h-0.5 w-6" : "h-6 w-0.5",
616
+ shadow
617
+ ),
618
+ style: {
619
+ ...beam.direction === "horizontal" ? {
620
+ // Position exactly on the horizontal grid line
621
+ top: `${horizontalPosition}%`,
622
+ left: beam.startPosition === "start" ? "-12px" : "calc(100% + 12px)",
623
+ transform: "translateY(-50%)"
624
+ // Center on the line
625
+ } : {
626
+ // Position exactly on the vertical grid line
627
+ left: `${verticalPosition}%`,
628
+ top: beam.startPosition === "start" ? "-12px" : "calc(100% + 12px)",
629
+ transform: "translateX(-50%)"
630
+ // Center on the line
631
+ }
632
+ },
633
+ initial: {
634
+ opacity: 0
635
+ },
636
+ animate: {
637
+ opacity: [0, 1, 1, 0],
638
+ ...beam.direction === "horizontal" ? {
639
+ // Move across the full width of the container
640
+ x: beam.startPosition === "start" ? [0, "calc(100vw + 24px)"] : [0, "calc(-100vw - 24px)"]
641
+ } : {
642
+ // Move across the full height of the container
643
+ y: beam.startPosition === "start" ? [0, "calc(100vh + 24px)"] : [0, "calc(-100vh - 24px)"]
644
+ }
645
+ },
646
+ transition: {
647
+ duration: beam.duration,
648
+ delay: beam.delay,
649
+ repeat: Infinity,
650
+ repeatDelay: Math.random() * 3 + 2,
651
+ // 2-5s pause between repeats
652
+ ease: "linear",
653
+ times: [0, 0.1, 0.9, 1]
654
+ // Quick fade in, maintain, quick fade out
655
+ }
656
+ },
657
+ beam.id
658
+ );
659
+ }),
660
+ /* @__PURE__ */ jsx5("div", { className: "relative z-10 size-full", children })
661
+ ]
662
+ }
663
+ );
664
+ }
665
+
666
+ // src/ui/animation/hover-background.tsx
667
+ import * as React4 from "react";
668
+ import { motion as motion6, useMotionValue as useMotionValue3, useSpring as useSpring2 } from "motion/react";
669
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
670
+ function HoverBackground({
671
+ className,
672
+ objectCount = 12,
673
+ children,
674
+ colors = {},
675
+ ...props
676
+ }) {
677
+ const {
678
+ background = "bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900",
679
+ objects = [
680
+ "bg-cyan-400/20",
681
+ "bg-purple-400/20",
682
+ "bg-fuchsia-400/20",
683
+ "bg-violet-400/20",
684
+ "bg-blue-400/20",
685
+ "bg-indigo-400/20"
686
+ ],
687
+ glow = "shadow-cyan-400/50"
688
+ } = colors;
689
+ const [isHovered, setIsHovered] = React4.useState(false);
690
+ const mouseX = useMotionValue3(0);
691
+ const mouseY = useMotionValue3(0);
692
+ const springX = useSpring2(mouseX, {
693
+ stiffness: 300,
694
+ damping: 30,
695
+ // Slower return to center when hover ends
696
+ restSpeed: 0.1,
697
+ restDelta: 0.1
698
+ });
699
+ const springY = useSpring2(mouseY, {
700
+ stiffness: 300,
701
+ damping: 30,
702
+ restSpeed: 0.1,
703
+ restDelta: 0.1
704
+ });
705
+ const animatedObjects = React4.useMemo(
706
+ () => Array.from({ length: objectCount }, (_, i) => {
707
+ const shape = Math.random() > 0.5 ? "circle" : "square";
708
+ return {
709
+ id: i,
710
+ x: Math.random() * 90 + 5,
711
+ // 5-95% to avoid edges
712
+ y: Math.random() * 90 + 5,
713
+ size: Math.random() * 60 + 20,
714
+ // 20-80px
715
+ color: objects[i % objects.length],
716
+ delay: Math.random() * 2,
717
+ shape,
718
+ floatDirection: Math.random() > 0.5 ? 1 : -1,
719
+ breathDuration: Math.random() * 3 + 3,
720
+ // 3-6 seconds
721
+ parallaxStrength: Math.random() * 0.5 + 0.3,
722
+ // 0.3-0.8 for more varied parallax depth
723
+ baseRotation: Math.random() * 360
724
+ // Random starting rotation offset
725
+ };
726
+ }),
727
+ [objectCount, objects]
728
+ );
729
+ const handleMouseMove = (event) => {
730
+ if (!isHovered) return;
731
+ const rect = event.currentTarget.getBoundingClientRect();
732
+ const centerX = rect.width / 2;
733
+ const centerY = rect.height / 2;
734
+ const x = (event.clientX - rect.left - centerX) / centerX;
735
+ const y = (event.clientY - rect.top - centerY) / centerY;
736
+ mouseX.set(x * 15);
737
+ mouseY.set(y * 15);
738
+ };
739
+ const handleHoverStart = () => {
740
+ setIsHovered(true);
741
+ };
742
+ const handleHoverEnd = () => {
743
+ setIsHovered(false);
744
+ mouseX.set(0);
745
+ mouseY.set(0);
746
+ };
747
+ return /* @__PURE__ */ jsxs4(
748
+ motion6.div,
749
+ {
750
+ "data-slot": "hover-background",
751
+ className: cn("relative size-full overflow-hidden", background, className),
752
+ onHoverStart: handleHoverStart,
753
+ onHoverEnd: handleHoverEnd,
754
+ onMouseMove: handleMouseMove,
755
+ whileHover: { scale: 1.02 },
756
+ transition: { duration: 0.3, ease: "easeOut" },
757
+ animate: {
758
+ backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"]
759
+ },
760
+ style: {
761
+ backgroundSize: "200% 200%"
762
+ },
763
+ ...props,
764
+ children: [
765
+ /* @__PURE__ */ jsx6(
766
+ motion6.div,
767
+ {
768
+ className: "absolute inset-0 bg-gradient-radial from-white/5 via-transparent to-transparent",
769
+ animate: {
770
+ opacity: [0.3, 0.6, 0.3],
771
+ scale: [1, 1.1, 1]
772
+ },
773
+ transition: {
774
+ duration: 4,
775
+ repeat: Infinity,
776
+ ease: "easeInOut"
777
+ }
778
+ }
779
+ ),
780
+ animatedObjects.map((obj) => /* @__PURE__ */ jsx6(
781
+ motion6.div,
782
+ {
783
+ className: cn(
784
+ "absolute border border-white/10 backdrop-blur-sm",
785
+ obj.color,
786
+ obj.shape === "circle" ? "rounded-full" : "rotate-45 rounded-lg"
787
+ ),
788
+ style: {
789
+ left: `${obj.x}%`,
790
+ top: `${obj.y}%`,
791
+ width: obj.size,
792
+ height: obj.size,
793
+ // Apply parallax with individual object strength
794
+ x: springX.get() * obj.parallaxStrength,
795
+ y: springY.get() * obj.parallaxStrength
796
+ },
797
+ initial: {
798
+ scale: 0.6,
799
+ opacity: 0.4,
800
+ rotate: obj.baseRotation
801
+ },
802
+ animate: {
803
+ // Default state animations - breathing with base rotation offset
804
+ scale: [0.6, 0.8, 0.6],
805
+ opacity: [0.4, 0.6, 0.4],
806
+ rotate: obj.shape === "circle" ? [obj.baseRotation, obj.baseRotation + 10, obj.baseRotation] : [obj.baseRotation, obj.baseRotation + 5, obj.baseRotation],
807
+ y: [0, obj.floatDirection * 15, 0],
808
+ x: [0, obj.floatDirection * 8, 0]
809
+ },
810
+ transition: {
811
+ duration: obj.breathDuration,
812
+ delay: obj.delay,
813
+ ease: "easeInOut",
814
+ repeat: Infinity,
815
+ repeatType: "reverse"
816
+ },
817
+ whileHover: {
818
+ scale: 1.5,
819
+ boxShadow: `0 0 30px ${glow.replace("shadow-", "").replace("/50", "")}`
820
+ }
821
+ },
822
+ obj.id
823
+ )),
824
+ isHovered && /* @__PURE__ */ jsx6("div", { className: "pointer-events-none absolute inset-0", children: Array.from({ length: 20 }).map((_, i) => /* @__PURE__ */ jsx6(
825
+ motion6.div,
826
+ {
827
+ className: "absolute h-1 w-1 rounded-full bg-white/60",
828
+ style: {
829
+ left: `${Math.random() * 100}%`,
830
+ top: `${Math.random() * 100}%`
831
+ },
832
+ initial: { opacity: 0, scale: 0 },
833
+ animate: {
834
+ opacity: [0, 1, 0],
835
+ scale: [0, 1, 0],
836
+ y: [0, -50, -100]
837
+ },
838
+ transition: {
839
+ duration: 3,
840
+ delay: Math.random() * 2,
841
+ repeat: Infinity,
842
+ ease: "easeOut"
843
+ }
844
+ },
845
+ `particle-${i}`
846
+ )) }),
847
+ /* @__PURE__ */ jsx6("div", { className: "relative z-10 size-full", children })
848
+ ]
849
+ }
850
+ );
851
+ }
852
+
853
+ // src/ui/animation/marquee.tsx
854
+ import React5, { useRef as useRef2 } from "react";
855
+ import { Fragment, jsx as jsx7 } from "react/jsx-runtime";
856
+ function Marquee({
857
+ className,
858
+ reverse = false,
859
+ pauseOnHover = false,
860
+ children,
861
+ vertical = false,
862
+ repeat = 4,
863
+ ariaLabel,
864
+ ariaLive = "off",
865
+ ariaRole = "marquee",
866
+ ...props
867
+ }) {
868
+ const marqueeRef = useRef2(null);
869
+ return /* @__PURE__ */ jsx7(
870
+ "div",
871
+ {
872
+ ...props,
873
+ ref: marqueeRef,
874
+ "data-slot": "marquee",
875
+ className: cn(
876
+ "group flex gap-(--gap) overflow-hidden p-2 [--duration:40s] [--gap:1rem]",
877
+ {
878
+ "flex-row": !vertical,
879
+ "flex-col": vertical
880
+ },
881
+ className
882
+ ),
883
+ "aria-label": ariaLabel,
884
+ "aria-live": ariaLive,
885
+ role: ariaRole,
886
+ tabIndex: 0,
887
+ children: React5.useMemo(
888
+ () => /* @__PURE__ */ jsx7(Fragment, { children: Array.from({ length: repeat }, (_, i) => /* @__PURE__ */ jsx7(
889
+ "div",
890
+ {
891
+ className: cn(
892
+ !vertical ? "flex-row gap-(--gap)" : "flex-col gap-(--gap)",
893
+ "flex shrink-0 justify-around",
894
+ !vertical && "animate-marquee flex-row",
895
+ vertical && "animate-marquee-vertical flex-col",
896
+ pauseOnHover && "group-hover:paused",
897
+ reverse && "direction-[reverse]"
898
+ ),
899
+ children
900
+ },
901
+ i
902
+ )) }),
903
+ [repeat, children, vertical, pauseOnHover, reverse]
904
+ )
905
+ }
906
+ );
907
+ }
908
+
909
+ // src/ui/animation/shimmering-text.tsx
910
+ import { useMemo as useMemo3, useRef as useRef3 } from "react";
911
+ import { motion as motion7, useInView as useInView3 } from "motion/react";
912
+ import { jsx as jsx8 } from "react/jsx-runtime";
913
+ function ShimmeringText({
914
+ text,
915
+ duration = 2,
916
+ delay = 0,
917
+ repeat = true,
918
+ repeatDelay = 0.5,
919
+ className,
920
+ startOnView = true,
921
+ once = false,
922
+ inViewMargin,
923
+ spread = 2,
924
+ color,
925
+ shimmerColor
926
+ }) {
927
+ const ref = useRef3(null);
928
+ const isInView = useInView3(ref, { once, margin: inViewMargin });
929
+ const dynamicSpread = useMemo3(() => {
930
+ return text.length * spread;
931
+ }, [text, spread]);
932
+ const shouldAnimate = !startOnView || isInView;
933
+ return /* @__PURE__ */ jsx8(
934
+ motion7.span,
935
+ {
936
+ ref,
937
+ className: cn(
938
+ "relative inline-block bg-size-[250%_100%,auto] bg-clip-text text-transparent",
939
+ "[--base-color:var(--color-zinc-400)] [--shimmer-color:var(--color-zinc-950)]",
940
+ "[background-repeat:no-repeat,padding-box]",
941
+ "[--shimmer-bg:linear-gradient(90deg,transparent_calc(50%-var(--spread)),var(--shimmer-color),transparent_calc(50%+var(--spread)))]",
942
+ "dark:[--base-color:var(--color-zinc-600)] dark:[--shimmer-color:var(--color-white)]",
943
+ className
944
+ ),
945
+ style: {
946
+ "--spread": `${dynamicSpread}px`,
947
+ ...color && { "--base-color": color },
948
+ ...shimmerColor && { "--shimmer-color": shimmerColor },
949
+ backgroundImage: `var(--shimmer-bg), linear-gradient(var(--base-color), var(--base-color))`
950
+ },
951
+ initial: {
952
+ backgroundPosition: "100% center",
953
+ opacity: 0
954
+ },
955
+ animate: shouldAnimate ? {
956
+ backgroundPosition: "0% center",
957
+ opacity: 1
958
+ } : {},
959
+ transition: {
960
+ backgroundPosition: {
961
+ repeat: repeat ? Infinity : 0,
962
+ duration,
963
+ delay,
964
+ repeatDelay,
965
+ ease: "linear"
966
+ },
967
+ opacity: {
968
+ duration: 0.3,
969
+ delay
970
+ }
971
+ },
972
+ children: text
973
+ }
974
+ );
975
+ }
976
+
977
+ // src/ui/animation/sliding-number.tsx
978
+ import { useEffect as useEffect3, useLayoutEffect, useMemo as useMemo4, useRef as useRef4, useState as useState5 } from "react";
979
+ import { motion as motion8, useInView as useInView4, useSpring as useSpring3, useTransform as useTransform2 } from "motion/react";
980
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
981
+ function Digit({
982
+ place,
983
+ value,
984
+ digitHeight,
985
+ duration
986
+ }) {
987
+ const valueRoundedToPlace = Math.floor(value / place);
988
+ const animatedValue = useSpring3(valueRoundedToPlace, {
989
+ duration: duration * 1e3
990
+ // Convert to milliseconds
991
+ });
992
+ useEffect3(() => {
993
+ animatedValue.set(valueRoundedToPlace);
994
+ }, [animatedValue, valueRoundedToPlace]);
995
+ return /* @__PURE__ */ jsx9("div", { style: { height: digitHeight }, className: "relative w-[1ch] overflow-hidden tabular-nums", children: Array.from({ length: 10 }, (_, i) => /* @__PURE__ */ jsx9(MyNumber, { mv: animatedValue, number: i, digitHeight }, i)) });
996
+ }
997
+ function MyNumber({
998
+ mv,
999
+ number,
1000
+ digitHeight
1001
+ }) {
1002
+ const y = useTransform2(mv, (latest) => {
1003
+ const placeValue = latest % 10;
1004
+ const offset = (10 + number - placeValue) % 10;
1005
+ let memo = offset * digitHeight;
1006
+ if (offset > 5) {
1007
+ memo -= 10 * digitHeight;
1008
+ }
1009
+ return memo;
1010
+ });
1011
+ return /* @__PURE__ */ jsx9(motion8.span, { style: { y }, className: "absolute inset-0 flex items-center justify-center", children: number });
1012
+ }
1013
+ function SlidingNumber({
1014
+ from,
1015
+ to,
1016
+ duration = 2,
1017
+ delay = 0,
1018
+ startOnView = true,
1019
+ once = false,
1020
+ className = "",
1021
+ onComplete,
1022
+ digitHeight = 40
1023
+ }) {
1024
+ const ref = useRef4(null);
1025
+ const isInView = useInView4(ref, { once: false });
1026
+ const [currentValue, setCurrentValue] = useState5(from);
1027
+ const [hasAnimated, setHasAnimated] = useState5(false);
1028
+ const [animationKey, setAnimationKey] = useState5(0);
1029
+ const animationFrameRef = useRef4(null);
1030
+ const timeoutRef = useRef4(null);
1031
+ const onCompleteRef = useRef4(onComplete);
1032
+ useLayoutEffect(() => {
1033
+ onCompleteRef.current = onComplete;
1034
+ }, [onComplete]);
1035
+ useEffect3(() => {
1036
+ setCurrentValue(from);
1037
+ setHasAnimated(false);
1038
+ setAnimationKey((prev) => prev + 1);
1039
+ if (animationFrameRef.current) {
1040
+ cancelAnimationFrame(animationFrameRef.current);
1041
+ animationFrameRef.current = null;
1042
+ }
1043
+ if (timeoutRef.current) {
1044
+ clearTimeout(timeoutRef.current);
1045
+ timeoutRef.current = null;
1046
+ }
1047
+ }, [from]);
1048
+ const shouldStart = useMemo4(() => {
1049
+ if (!startOnView) return true;
1050
+ if (!isInView) return false;
1051
+ if (once && hasAnimated) return false;
1052
+ return true;
1053
+ }, [startOnView, isInView, once, hasAnimated]);
1054
+ useEffect3(() => {
1055
+ if (!shouldStart) return;
1056
+ if (animationFrameRef.current) {
1057
+ cancelAnimationFrame(animationFrameRef.current);
1058
+ animationFrameRef.current = null;
1059
+ }
1060
+ if (timeoutRef.current) {
1061
+ clearTimeout(timeoutRef.current);
1062
+ timeoutRef.current = null;
1063
+ }
1064
+ setHasAnimated(true);
1065
+ timeoutRef.current = setTimeout(() => {
1066
+ const startTime = Date.now();
1067
+ const difference = to - from;
1068
+ const animate2 = () => {
1069
+ const elapsed = Date.now() - startTime;
1070
+ const progress = Math.min(elapsed / (duration * 1e3), 1);
1071
+ const easeOutCubic = 1 - (1 - progress) ** 3;
1072
+ const newValue = from + difference * easeOutCubic;
1073
+ setCurrentValue(newValue);
1074
+ if (progress < 1) {
1075
+ animationFrameRef.current = requestAnimationFrame(animate2);
1076
+ } else {
1077
+ setCurrentValue(to);
1078
+ animationFrameRef.current = null;
1079
+ onCompleteRef.current?.();
1080
+ }
1081
+ };
1082
+ animationFrameRef.current = requestAnimationFrame(animate2);
1083
+ }, delay * 1e3);
1084
+ return () => {
1085
+ if (timeoutRef.current) {
1086
+ clearTimeout(timeoutRef.current);
1087
+ timeoutRef.current = null;
1088
+ }
1089
+ if (animationFrameRef.current) {
1090
+ cancelAnimationFrame(animationFrameRef.current);
1091
+ animationFrameRef.current = null;
1092
+ }
1093
+ };
1094
+ }, [shouldStart, from, to, duration, delay]);
1095
+ const roundedValue = Math.round(currentValue);
1096
+ const absValue = Math.abs(roundedValue);
1097
+ const maxDigits = Math.max(Math.abs(from).toString().length, Math.abs(to).toString().length);
1098
+ const places = useMemo4(
1099
+ () => Array.from({ length: maxDigits }, (_, i) => 10 ** (maxDigits - i - 1)),
1100
+ [maxDigits]
1101
+ );
1102
+ return /* @__PURE__ */ jsxs5("div", { ref, className: `flex items-center ${className}`, children: [
1103
+ roundedValue < 0 && "-",
1104
+ places.map((place) => /* @__PURE__ */ jsx9(
1105
+ Digit,
1106
+ {
1107
+ place,
1108
+ value: absValue,
1109
+ digitHeight,
1110
+ duration
1111
+ },
1112
+ `${place}-${animationKey}`
1113
+ ))
1114
+ ] });
1115
+ }
1116
+
1117
+ // src/ui/animation/svg-text.tsx
1118
+ import * as React6 from "react";
1119
+ import { useEffect as useEffect4, useRef as useRef5, useState as useState6 } from "react";
1120
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1121
+ function SvgText({
1122
+ svg,
1123
+ children,
1124
+ className = "",
1125
+ fontSize = "20vw",
1126
+ fontWeight = "bold",
1127
+ as: Component = "div"
1128
+ }) {
1129
+ const textRef = useRef5(null);
1130
+ const [textDimensions, setTextDimensions] = useState6({ width: 0, height: 0 });
1131
+ const content = React6.Children.toArray(children).join("");
1132
+ const maskId = React6.useId();
1133
+ useEffect4(() => {
1134
+ if (!textRef.current) return;
1135
+ const updateDimensions = () => {
1136
+ const rect = textRef.current?.getBoundingClientRect();
1137
+ if (rect) {
1138
+ setTextDimensions({
1139
+ width: Math.max(rect.width, 200),
1140
+ height: Math.max(rect.height, 100)
1141
+ });
1142
+ }
1143
+ };
1144
+ updateDimensions();
1145
+ const resizeObserver = new ResizeObserver(updateDimensions);
1146
+ resizeObserver.observe(textRef.current);
1147
+ return () => resizeObserver.disconnect();
1148
+ }, []);
1149
+ return /* @__PURE__ */ jsxs6(Component, { className: cn("relative inline-block", className), children: [
1150
+ /* @__PURE__ */ jsx10(
1151
+ "div",
1152
+ {
1153
+ ref: textRef,
1154
+ className: "pointer-events-none absolute whitespace-nowrap font-bold opacity-0",
1155
+ style: {
1156
+ fontSize: typeof fontSize === "number" ? `${fontSize}px` : fontSize,
1157
+ fontWeight,
1158
+ fontFamily: "system-ui, -apple-system, sans-serif"
1159
+ },
1160
+ children: content
1161
+ }
1162
+ ),
1163
+ /* @__PURE__ */ jsxs6(
1164
+ "svg",
1165
+ {
1166
+ className: "block",
1167
+ width: textDimensions.width,
1168
+ height: textDimensions.height,
1169
+ viewBox: `0 0 ${textDimensions.width} ${textDimensions.height}`,
1170
+ style: {
1171
+ fontSize: typeof fontSize === "number" ? `${fontSize}px` : fontSize,
1172
+ fontWeight,
1173
+ fontFamily: "system-ui, -apple-system, sans-serif"
1174
+ },
1175
+ children: [
1176
+ /* @__PURE__ */ jsx10("defs", { children: /* @__PURE__ */ jsxs6("mask", { id: maskId, children: [
1177
+ /* @__PURE__ */ jsx10("rect", { width: "100%", height: "100%", fill: "black" }),
1178
+ /* @__PURE__ */ jsx10(
1179
+ "text",
1180
+ {
1181
+ x: "50%",
1182
+ y: "50%",
1183
+ textAnchor: "middle",
1184
+ dominantBaseline: "central",
1185
+ fill: "white",
1186
+ style: {
1187
+ fontSize: typeof fontSize === "number" ? `${fontSize}px` : fontSize,
1188
+ fontWeight,
1189
+ fontFamily: "system-ui, -apple-system, sans-serif"
1190
+ },
1191
+ children: content
1192
+ }
1193
+ )
1194
+ ] }) }),
1195
+ /* @__PURE__ */ jsx10("g", { mask: `url(#${maskId})`, children: /* @__PURE__ */ jsx10(
1196
+ "foreignObject",
1197
+ {
1198
+ width: "100%",
1199
+ height: "100%",
1200
+ style: {
1201
+ overflow: "visible"
1202
+ },
1203
+ children: /* @__PURE__ */ jsx10(
1204
+ "div",
1205
+ {
1206
+ style: {
1207
+ width: `${textDimensions.width}px`,
1208
+ height: `${textDimensions.height}px`,
1209
+ display: "flex",
1210
+ alignItems: "center",
1211
+ justifyContent: "center"
1212
+ },
1213
+ children: /* @__PURE__ */ jsx10(
1214
+ "div",
1215
+ {
1216
+ style: {
1217
+ width: "400px",
1218
+ height: "200px",
1219
+ transform: `scale(${Math.max(textDimensions.width / 400, textDimensions.height / 200)})`,
1220
+ transformOrigin: "center"
1221
+ },
1222
+ children: svg
1223
+ }
1224
+ )
1225
+ }
1226
+ )
1227
+ }
1228
+ ) })
1229
+ ]
1230
+ }
1231
+ ),
1232
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: content })
1233
+ ] });
1234
+ }
1235
+
1236
+ // src/ui/animation/text-reveal.tsx
1237
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState7 } from "react";
1238
+ import { motion as motion9, useInView as useInView5 } from "motion/react";
1239
+ import { jsx as jsx11 } from "react/jsx-runtime";
1240
+ var containerVariants = {
1241
+ fade: {
1242
+ hidden: {},
1243
+ visible: {
1244
+ transition: { staggerChildren: 0.02 }
1245
+ }
1246
+ },
1247
+ slideUp: {
1248
+ hidden: {},
1249
+ visible: {
1250
+ transition: { staggerChildren: 0.04 }
1251
+ }
1252
+ },
1253
+ slideDown: {
1254
+ hidden: {},
1255
+ visible: {
1256
+ transition: { staggerChildren: 0.04 }
1257
+ }
1258
+ },
1259
+ slideLeft: {
1260
+ hidden: {},
1261
+ visible: {
1262
+ transition: { staggerChildren: 0.04 }
1263
+ }
1264
+ },
1265
+ slideRight: {
1266
+ hidden: {},
1267
+ visible: {
1268
+ transition: { staggerChildren: 0.04 }
1269
+ }
1270
+ },
1271
+ scale: {
1272
+ hidden: {},
1273
+ visible: {
1274
+ transition: { staggerChildren: 0.06 }
1275
+ }
1276
+ },
1277
+ blur: {
1278
+ hidden: {},
1279
+ visible: {
1280
+ transition: { staggerChildren: 0.03 }
1281
+ }
1282
+ },
1283
+ typewriter: {
1284
+ hidden: {},
1285
+ visible: {
1286
+ transition: { staggerChildren: 0.15 }
1287
+ }
1288
+ },
1289
+ wave: {
1290
+ hidden: {},
1291
+ visible: {
1292
+ transition: { staggerChildren: 0.12 }
1293
+ }
1294
+ },
1295
+ stagger: {
1296
+ hidden: {},
1297
+ visible: {
1298
+ transition: { staggerChildren: 0.08 }
1299
+ }
1300
+ },
1301
+ rotate: {
1302
+ hidden: {},
1303
+ visible: {
1304
+ transition: { staggerChildren: 0.05 }
1305
+ }
1306
+ },
1307
+ elastic: {
1308
+ hidden: {},
1309
+ visible: {
1310
+ transition: { staggerChildren: 0.07 }
1311
+ }
1312
+ }
1313
+ };
1314
+ var itemVariants = {
1315
+ fade: {
1316
+ hidden: { opacity: 0 },
1317
+ visible: {
1318
+ opacity: 1,
1319
+ transition: { duration: 0.6, ease: "easeOut" }
1320
+ }
1321
+ },
1322
+ slideUp: {
1323
+ hidden: { opacity: 0, y: 50, scale: 0.95 },
1324
+ visible: {
1325
+ opacity: 1,
1326
+ y: 0,
1327
+ scale: 1,
1328
+ transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] }
1329
+ }
1330
+ },
1331
+ slideDown: {
1332
+ hidden: { opacity: 0, y: -30, scale: 0.98 },
1333
+ visible: {
1334
+ opacity: 1,
1335
+ y: 0,
1336
+ scale: 1,
1337
+ transition: { duration: 0.5, ease: [0.25, 0.46, 0.45, 0.94] }
1338
+ }
1339
+ },
1340
+ slideLeft: {
1341
+ hidden: { opacity: 0, x: 60, rotateY: 15 },
1342
+ visible: {
1343
+ opacity: 1,
1344
+ x: 0,
1345
+ rotateY: 0,
1346
+ transition: { duration: 0.65, ease: [0.16, 1, 0.3, 1] }
1347
+ }
1348
+ },
1349
+ slideRight: {
1350
+ hidden: { opacity: 0, x: -60, rotateY: -15 },
1351
+ visible: {
1352
+ opacity: 1,
1353
+ x: 0,
1354
+ rotateY: 0,
1355
+ transition: { duration: 0.65, ease: [0.16, 1, 0.3, 1] }
1356
+ }
1357
+ },
1358
+ scale: {
1359
+ hidden: { opacity: 0, scale: 0.8 },
1360
+ visible: {
1361
+ opacity: 1,
1362
+ scale: 1,
1363
+ transition: { duration: 0.4, ease: [0.34, 1.56, 0.64, 1] }
1364
+ }
1365
+ },
1366
+ blur: {
1367
+ hidden: { opacity: 0, filter: "blur(4px)" },
1368
+ visible: {
1369
+ opacity: 1,
1370
+ filter: "blur(0px)",
1371
+ transition: { duration: 0.6, ease: "easeOut" }
1372
+ }
1373
+ },
1374
+ typewriter: {
1375
+ hidden: { width: 0 },
1376
+ visible: {
1377
+ width: "auto",
1378
+ transition: { duration: 0.3, ease: "easeInOut" }
1379
+ }
1380
+ },
1381
+ wave: {
1382
+ hidden: { opacity: 0, y: 20, rotateZ: -5 },
1383
+ visible: {
1384
+ opacity: 1,
1385
+ y: [20, -10, 0],
1386
+ rotateZ: [-5, 5, 0],
1387
+ transition: {
1388
+ duration: 0.8,
1389
+ ease: [0.34, 1.56, 0.64, 1],
1390
+ times: [0, 0.5, 1]
1391
+ }
1392
+ }
1393
+ },
1394
+ stagger: {
1395
+ hidden: { opacity: 0, y: 30, scale: 0.9 },
1396
+ visible: {
1397
+ opacity: 1,
1398
+ y: 0,
1399
+ scale: 1,
1400
+ transition: { duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }
1401
+ }
1402
+ },
1403
+ rotate: {
1404
+ hidden: { opacity: 0, rotateY: -90 },
1405
+ visible: {
1406
+ opacity: 1,
1407
+ rotateY: 0,
1408
+ transition: { duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }
1409
+ }
1410
+ },
1411
+ elastic: {
1412
+ hidden: { opacity: 0, scale: 0 },
1413
+ visible: {
1414
+ opacity: 1,
1415
+ scale: [0, 1.2, 1],
1416
+ transition: {
1417
+ duration: 0.8,
1418
+ ease: [0.68, -0.55, 0.265, 1.55],
1419
+ times: [0, 0.6, 1]
1420
+ }
1421
+ }
1422
+ }
1423
+ };
1424
+ function TextReveal({
1425
+ children,
1426
+ variant = "fade",
1427
+ className,
1428
+ style,
1429
+ delay = 0,
1430
+ duration = 0.6,
1431
+ staggerDelay = 0.03,
1432
+ once = true,
1433
+ startOnView = true,
1434
+ wordLevel = false
1435
+ }) {
1436
+ const ref = useRef6(null);
1437
+ const isInView = useInView5(ref, { once, margin: "-10%" });
1438
+ const [hasAnimated, setHasAnimated] = useState7(false);
1439
+ const shouldAnimate = startOnView ? isInView : true;
1440
+ const elements = wordLevel ? children.split(" ").map((word, i, arr) => i < arr.length - 1 ? `${word} ` : word) : children.split("");
1441
+ const customContainerVariants = {
1442
+ ...containerVariants[variant],
1443
+ visible: {
1444
+ transition: {
1445
+ staggerChildren: staggerDelay,
1446
+ delayChildren: delay
1447
+ }
1448
+ }
1449
+ };
1450
+ const originalVariant = itemVariants[variant];
1451
+ const customItemVariants = duration === 0.6 ? originalVariant : {
1452
+ hidden: originalVariant.hidden,
1453
+ visible: {
1454
+ ...originalVariant.visible,
1455
+ transition: {
1456
+ ...originalVariant.visible.transition,
1457
+ duration
1458
+ }
1459
+ }
1460
+ };
1461
+ useEffect5(() => {
1462
+ if (shouldAnimate && !hasAnimated) {
1463
+ setHasAnimated(true);
1464
+ }
1465
+ }, [shouldAnimate, hasAnimated]);
1466
+ const MotionComponent = variant === "typewriter" ? motion9.div : motion9.span;
1467
+ return /* @__PURE__ */ jsx11(
1468
+ motion9.div,
1469
+ {
1470
+ ref,
1471
+ className: cn("inline-block", className),
1472
+ variants: customContainerVariants,
1473
+ initial: "hidden",
1474
+ animate: shouldAnimate ? "visible" : "hidden",
1475
+ style: {
1476
+ willChange: "transform, opacity",
1477
+ WebkitBackfaceVisibility: "hidden",
1478
+ backfaceVisibility: "hidden",
1479
+ WebkitTransform: "translate3d(0,0,0)",
1480
+ transform: "translate3d(0,0,0)",
1481
+ isolation: "isolate",
1482
+ contain: "layout style paint",
1483
+ ...style
1484
+ },
1485
+ children: variant === "typewriter" ? /* @__PURE__ */ jsx11(
1486
+ motion9.span,
1487
+ {
1488
+ className: "inline-block overflow-hidden whitespace-nowrap",
1489
+ variants: customItemVariants,
1490
+ style: {
1491
+ display: "inline-block",
1492
+ whiteSpace: "nowrap"
1493
+ },
1494
+ children
1495
+ }
1496
+ ) : elements.map((element, index) => /* @__PURE__ */ jsx11(
1497
+ MotionComponent,
1498
+ {
1499
+ className: cn("inline-block", {
1500
+ "whitespace-pre": !wordLevel
1501
+ }),
1502
+ variants: customItemVariants,
1503
+ style: {
1504
+ display: "inline-block",
1505
+ transformOrigin: variant === "rotate" ? "center center" : void 0,
1506
+ willChange: "transform, opacity",
1507
+ WebkitBackfaceVisibility: "hidden",
1508
+ backfaceVisibility: "hidden",
1509
+ WebkitTransform: "translate3d(0,0,0)",
1510
+ transform: "translate3d(0,0,0)",
1511
+ isolation: "isolate"
1512
+ },
1513
+ children: element === " " ? "\xA0" : element
1514
+ },
1515
+ index
1516
+ ))
1517
+ }
1518
+ );
1519
+ }
1520
+
1521
+ // src/ui/animation/typing-text.tsx
1522
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState8 } from "react";
1523
+ import {
1524
+ motion as motion10,
1525
+ useInView as useInView6
1526
+ } from "motion/react";
1527
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1528
+ var cursorVariants = {
1529
+ blinking: {
1530
+ opacity: [0, 0, 1, 1],
1531
+ transition: {
1532
+ duration: 1,
1533
+ repeat: Infinity,
1534
+ repeatDelay: 0,
1535
+ ease: "linear",
1536
+ times: [0, 0.5, 0.5, 1]
1537
+ }
1538
+ }
1539
+ };
1540
+ function TypingText({
1541
+ text,
1542
+ texts,
1543
+ speed = 100,
1544
+ delay = 0,
1545
+ showCursor = true,
1546
+ cursorClassName = "",
1547
+ cursor = "|",
1548
+ loop = false,
1549
+ pauseDuration = 2e3,
1550
+ className,
1551
+ onComplete,
1552
+ startOnView = true,
1553
+ once = false,
1554
+ inViewMargin,
1555
+ ...props
1556
+ }) {
1557
+ const ref = useRef7(null);
1558
+ const isInView = useInView6(ref, {
1559
+ once,
1560
+ margin: inViewMargin
1561
+ });
1562
+ const [hasAnimated, setHasAnimated] = useState8(false);
1563
+ const [displayText, setDisplayText] = useState8("");
1564
+ const [currentIndex, setCurrentIndex] = useState8(0);
1565
+ const [isTyping, setIsTyping] = useState8(false);
1566
+ const [currentTextIndex, setCurrentTextIndex] = useState8(0);
1567
+ const shouldStart = !startOnView || isInView && (!once || !hasAnimated);
1568
+ const textArray = texts && texts.length > 0 ? texts : [text];
1569
+ const currentText = textArray[currentTextIndex] ?? "";
1570
+ useEffect6(() => {
1571
+ if (!shouldStart) return;
1572
+ const timeout = setTimeout(() => {
1573
+ setIsTyping(true);
1574
+ setHasAnimated(true);
1575
+ }, delay);
1576
+ return () => clearTimeout(timeout);
1577
+ }, [delay, shouldStart]);
1578
+ useEffect6(() => {
1579
+ if (!isTyping) return;
1580
+ if (currentIndex < currentText.length) {
1581
+ const timeout = setTimeout(() => {
1582
+ setDisplayText(currentText.slice(0, currentIndex + 1));
1583
+ setCurrentIndex(currentIndex + 1);
1584
+ }, speed);
1585
+ return () => clearTimeout(timeout);
1586
+ } else {
1587
+ onComplete?.();
1588
+ if (loop && texts && texts.length > 1) {
1589
+ const timeout = setTimeout(() => {
1590
+ setDisplayText("");
1591
+ setCurrentIndex(0);
1592
+ setCurrentTextIndex((prev) => (prev + 1) % texts.length);
1593
+ }, pauseDuration);
1594
+ return () => clearTimeout(timeout);
1595
+ }
1596
+ }
1597
+ }, [currentIndex, currentText, isTyping, speed, loop, texts, pauseDuration, onComplete]);
1598
+ const finalVariants = {
1599
+ container: {
1600
+ hidden: { opacity: 0, y: 10 },
1601
+ show: { opacity: 1, y: 0, transition: { staggerChildren: 0.02 } },
1602
+ exit: { opacity: 0 }
1603
+ }
1604
+ };
1605
+ const MotionComponent = motion10.span;
1606
+ return /* @__PURE__ */ jsx12(
1607
+ MotionComponent,
1608
+ {
1609
+ ref,
1610
+ variants: finalVariants.container,
1611
+ initial: "hidden",
1612
+ whileInView: startOnView ? "show" : void 0,
1613
+ animate: startOnView ? void 0 : "show",
1614
+ exit: "exit",
1615
+ className: cn("whitespace-pre-wrap", className),
1616
+ viewport: { once },
1617
+ ...props,
1618
+ children: /* @__PURE__ */ jsxs7("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
1619
+ displayText,
1620
+ showCursor && /* @__PURE__ */ jsx12(
1621
+ motion10.span,
1622
+ {
1623
+ variants: cursorVariants,
1624
+ animate: "blinking",
1625
+ className: cn(
1626
+ "ms-1 inline-block w-px select-none font-normal text-foreground",
1627
+ cursorClassName
1628
+ ),
1629
+ children: cursor
1630
+ }
1631
+ )
1632
+ ] })
1633
+ }
1634
+ );
1635
+ }
1636
+
1637
+ // src/ui/animation/video-text.tsx
1638
+ import * as React7 from "react";
1639
+ import { useEffect as useEffect7, useRef as useRef8 } from "react";
1640
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1641
+ function VideoText({
1642
+ src,
1643
+ children,
1644
+ className = "",
1645
+ autoPlay = true,
1646
+ muted = true,
1647
+ loop = true,
1648
+ preload = "auto",
1649
+ fontSize = "20vw",
1650
+ fontWeight = "bold",
1651
+ as: Component = "div",
1652
+ onPlay,
1653
+ onPause,
1654
+ onEnded
1655
+ }) {
1656
+ const videoRef = useRef8(null);
1657
+ const canvasRef = useRef8(null);
1658
+ const textRef = useRef8(null);
1659
+ const containerRef = useRef8(null);
1660
+ useEffect7(() => {
1661
+ const video = videoRef.current;
1662
+ const canvas = canvasRef.current;
1663
+ const textElement = textRef.current;
1664
+ const container = containerRef.current;
1665
+ if (!video || !canvas || !textElement || !container) return;
1666
+ const ctx = canvas.getContext("2d");
1667
+ if (!ctx) return;
1668
+ let animationId;
1669
+ const updateCanvas = () => {
1670
+ const text = textElement.textContent || "";
1671
+ ctx.font = `${fontWeight} ${typeof fontSize === "number" ? `${fontSize}px` : fontSize} system-ui, -apple-system, sans-serif`;
1672
+ const textMetrics = ctx.measureText(text);
1673
+ const textWidth = textMetrics.width;
1674
+ const textHeight = typeof fontSize === "number" ? fontSize : parseFloat(fontSize.replace(/[^\d.]/g, "")) || 100;
1675
+ const padding = 40;
1676
+ canvas.width = Math.max(textWidth + padding * 2, 400);
1677
+ canvas.height = Math.max(textHeight + padding * 2, 200);
1678
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1679
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
1680
+ ctx.globalCompositeOperation = "destination-in";
1681
+ ctx.fillStyle = "white";
1682
+ ctx.font = `${fontWeight} ${typeof fontSize === "number" ? `${fontSize}px` : fontSize} system-ui, -apple-system, sans-serif`;
1683
+ ctx.textAlign = "center";
1684
+ ctx.textBaseline = "middle";
1685
+ ctx.fillText(text, canvas.width / 2, canvas.height / 2);
1686
+ ctx.globalCompositeOperation = "source-over";
1687
+ animationId = requestAnimationFrame(updateCanvas);
1688
+ };
1689
+ const handleVideoLoad = () => {
1690
+ updateCanvas();
1691
+ };
1692
+ const handleResize = () => {
1693
+ updateCanvas();
1694
+ };
1695
+ video.addEventListener("loadeddata", handleVideoLoad);
1696
+ video.addEventListener("play", updateCanvas);
1697
+ window.addEventListener("resize", handleResize);
1698
+ return () => {
1699
+ video.removeEventListener("loadeddata", handleVideoLoad);
1700
+ video.removeEventListener("play", updateCanvas);
1701
+ window.removeEventListener("resize", handleResize);
1702
+ if (animationId) {
1703
+ cancelAnimationFrame(animationId);
1704
+ }
1705
+ };
1706
+ }, [fontSize, fontWeight]);
1707
+ const sources = Array.isArray(src) ? src : [src];
1708
+ const content = React7.Children.toArray(children).join("");
1709
+ return /* @__PURE__ */ jsxs8(
1710
+ Component,
1711
+ {
1712
+ ref: containerRef,
1713
+ className: cn("relative inline-block overflow-hidden", className),
1714
+ children: [
1715
+ /* @__PURE__ */ jsxs8(
1716
+ "video",
1717
+ {
1718
+ ref: videoRef,
1719
+ className: "pointer-events-none absolute opacity-0",
1720
+ autoPlay,
1721
+ muted,
1722
+ loop,
1723
+ preload,
1724
+ playsInline: true,
1725
+ onPlay,
1726
+ onPause,
1727
+ onEnded,
1728
+ crossOrigin: "anonymous",
1729
+ children: [
1730
+ sources.map((source, index) => /* @__PURE__ */ jsx13("source", { src: source }, index)),
1731
+ "Your browser does not support the video tag."
1732
+ ]
1733
+ }
1734
+ ),
1735
+ /* @__PURE__ */ jsx13(
1736
+ "canvas",
1737
+ {
1738
+ ref: canvasRef,
1739
+ className: "block",
1740
+ style: {
1741
+ width: "100%",
1742
+ height: "auto"
1743
+ }
1744
+ }
1745
+ ),
1746
+ /* @__PURE__ */ jsx13(
1747
+ "div",
1748
+ {
1749
+ ref: textRef,
1750
+ className: "pointer-events-none absolute font-bold opacity-0",
1751
+ style: {
1752
+ fontSize: typeof fontSize === "number" ? `${fontSize}px` : fontSize,
1753
+ fontWeight
1754
+ },
1755
+ "aria-label": content,
1756
+ children
1757
+ }
1758
+ ),
1759
+ /* @__PURE__ */ jsx13("span", { className: "sr-only", children: content })
1760
+ ]
1761
+ }
1762
+ );
1763
+ }
1764
+
1765
+ // src/ui/animation/word-rotate.tsx
1766
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState9 } from "react";
1767
+ import {
1768
+ motion as motion11,
1769
+ useInView as useInView7
1770
+ } from "motion/react";
1771
+ import { jsx as jsx14 } from "react/jsx-runtime";
1772
+ function WordRotate({
1773
+ words,
1774
+ duration = 1500,
1775
+ animationStyle = "fade",
1776
+ loop = true,
1777
+ className,
1778
+ containerClassName,
1779
+ pauseDuration = 300,
1780
+ startOnView = true,
1781
+ once = false,
1782
+ inViewMargin,
1783
+ ...props
1784
+ }) {
1785
+ const ref = useRef9(null);
1786
+ const isInView = useInView7(ref, {
1787
+ once,
1788
+ margin: inViewMargin
1789
+ });
1790
+ const [hasAnimated, setHasAnimated] = useState9(false);
1791
+ const [currentWord, setCurrentWord] = useState9(0);
1792
+ const [show, setShow] = useState9(true);
1793
+ const variants = {
1794
+ fade: {
1795
+ initial: { opacity: 0 },
1796
+ animate: {
1797
+ opacity: 1,
1798
+ transition: {
1799
+ duration: 0.4,
1800
+ ease: [0.4, 0, 0.2, 1]
1801
+ // Custom cubic-bezier for smooth fade
1802
+ }
1803
+ },
1804
+ exit: {
1805
+ opacity: 0,
1806
+ transition: {
1807
+ duration: 0.3,
1808
+ ease: [0.4, 0, 1, 1]
1809
+ // Faster exit
1810
+ }
1811
+ }
1812
+ },
1813
+ "slide-up": {
1814
+ initial: { opacity: 0, y: 24 },
1815
+ animate: {
1816
+ opacity: 1,
1817
+ y: 0,
1818
+ transition: {
1819
+ type: "spring",
1820
+ stiffness: 300,
1821
+ damping: 25,
1822
+ mass: 0.8
1823
+ }
1824
+ },
1825
+ exit: {
1826
+ opacity: 0,
1827
+ y: -24,
1828
+ transition: {
1829
+ duration: 0.25,
1830
+ ease: [0.4, 0, 1, 1]
1831
+ }
1832
+ }
1833
+ },
1834
+ "slide-down": {
1835
+ initial: { opacity: 0, y: -24 },
1836
+ animate: {
1837
+ opacity: 1,
1838
+ y: 0,
1839
+ transition: {
1840
+ type: "spring",
1841
+ stiffness: 300,
1842
+ damping: 25,
1843
+ mass: 0.8
1844
+ }
1845
+ },
1846
+ exit: {
1847
+ opacity: 0,
1848
+ y: 24,
1849
+ transition: {
1850
+ duration: 0.25,
1851
+ ease: [0.4, 0, 1, 1]
1852
+ }
1853
+ }
1854
+ },
1855
+ scale: {
1856
+ initial: { opacity: 0, scale: 0.8 },
1857
+ animate: {
1858
+ opacity: 1,
1859
+ scale: 1,
1860
+ transition: {
1861
+ type: "spring",
1862
+ stiffness: 400,
1863
+ damping: 30,
1864
+ mass: 0.6
1865
+ }
1866
+ },
1867
+ exit: {
1868
+ opacity: 0,
1869
+ scale: 0.9,
1870
+ transition: {
1871
+ duration: 0.2,
1872
+ ease: [0.4, 0, 1, 1]
1873
+ }
1874
+ }
1875
+ },
1876
+ flip: {
1877
+ initial: { opacity: 0, rotateX: 90 },
1878
+ animate: {
1879
+ opacity: 1,
1880
+ rotateX: 0,
1881
+ transition: {
1882
+ type: "spring",
1883
+ stiffness: 200,
1884
+ damping: 20,
1885
+ mass: 1
1886
+ }
1887
+ },
1888
+ exit: {
1889
+ opacity: 0,
1890
+ rotateX: -90,
1891
+ transition: {
1892
+ duration: 0.3,
1893
+ ease: [0.4, 0, 1, 1]
1894
+ }
1895
+ }
1896
+ }
1897
+ };
1898
+ const shouldStart = !startOnView || isInView && (!once || !hasAnimated);
1899
+ useEffect8(() => {
1900
+ if (!shouldStart) return;
1901
+ setHasAnimated(true);
1902
+ const interval = setInterval(() => {
1903
+ setShow(false);
1904
+ setTimeout(() => {
1905
+ setCurrentWord((prev) => {
1906
+ if (loop) {
1907
+ return (prev + 1) % words.length;
1908
+ } else {
1909
+ return prev < words.length - 1 ? prev + 1 : prev;
1910
+ }
1911
+ });
1912
+ setShow(true);
1913
+ }, pauseDuration);
1914
+ }, duration + pauseDuration);
1915
+ return () => clearInterval(interval);
1916
+ }, [shouldStart, duration, pauseDuration, words.length, loop]);
1917
+ return /* @__PURE__ */ jsx14(
1918
+ motion11.span,
1919
+ {
1920
+ ref,
1921
+ className: cn("inline-block overflow-hidden", containerClassName),
1922
+ ...props,
1923
+ children: /* @__PURE__ */ jsx14(
1924
+ motion11.span,
1925
+ {
1926
+ initial: "initial",
1927
+ animate: show ? "animate" : "exit",
1928
+ exit: "exit",
1929
+ variants: variants[animationStyle],
1930
+ transition: { duration: 0.5 },
1931
+ style: {
1932
+ perspective: animationStyle === "flip" ? 1e3 : void 0
1933
+ },
1934
+ className: cn("inline-block overflow-hidden", className),
1935
+ children: words[currentWord]
1936
+ },
1937
+ currentWord
1938
+ )
1939
+ }
1940
+ );
1941
+ }
1942
+ export {
1943
+ AvatarGroup,
1944
+ AvatarGroupItem,
1945
+ AvatarGroupTooltip,
1946
+ CountingNumber,
1947
+ GithubButton,
1948
+ GradientBackground,
1949
+ GridBackground,
1950
+ HoverBackground,
1951
+ Marquee,
1952
+ ShimmeringText,
1953
+ SlidingNumber,
1954
+ SvgText,
1955
+ TextReveal,
1956
+ TypingText,
1957
+ VideoText,
1958
+ WordRotate,
1959
+ githubButtonVariants
1960
+ };