pelatform-ui 1.2.9 → 1.3.1

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