@waveso/ui 0.0.10 → 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.
Files changed (288) hide show
  1. package/dist/accordion.d.ts +24 -8
  2. package/dist/accordion.d.ts.map +1 -0
  3. package/dist/accordion.js +50 -73
  4. package/dist/accordion.js.map +1 -1
  5. package/dist/action-bar.d.ts +83 -0
  6. package/dist/action-bar.d.ts.map +1 -0
  7. package/dist/action-bar.js +264 -0
  8. package/dist/action-bar.js.map +1 -0
  9. package/dist/alert-dialog.d.ts +56 -21
  10. package/dist/alert-dialog.d.ts.map +1 -0
  11. package/dist/alert-dialog.js +75 -127
  12. package/dist/alert-dialog.js.map +1 -1
  13. package/dist/alert.d.ts +26 -11
  14. package/dist/alert.d.ts.map +1 -0
  15. package/dist/alert.js +37 -68
  16. package/dist/alert.js.map +1 -1
  17. package/dist/animate.d.ts +117 -75
  18. package/dist/animate.d.ts.map +1 -0
  19. package/dist/animate.js +259 -223
  20. package/dist/animate.js.map +1 -1
  21. package/dist/aspect-ratio.d.ts +11 -6
  22. package/dist/aspect-ratio.d.ts.map +1 -0
  23. package/dist/aspect-ratio.js +12 -14
  24. package/dist/aspect-ratio.js.map +1 -1
  25. package/dist/autocomplete.d.ts +91 -25
  26. package/dist/autocomplete.d.ts.map +1 -0
  27. package/dist/autocomplete.js +119 -181
  28. package/dist/autocomplete.js.map +1 -1
  29. package/dist/avatar.d.ts +32 -11
  30. package/dist/avatar.d.ts.map +1 -0
  31. package/dist/avatar.js +42 -89
  32. package/dist/avatar.js.map +1 -1
  33. package/dist/badge.d.ts +15 -8
  34. package/dist/badge.d.ts.map +1 -0
  35. package/dist/badge.js +34 -48
  36. package/dist/badge.js.map +1 -1
  37. package/dist/breadcrumb.d.ts +35 -11
  38. package/dist/breadcrumb.d.ts.map +1 -0
  39. package/dist/breadcrumb.js +60 -110
  40. package/dist/breadcrumb.js.map +1 -1
  41. package/dist/button-group.d.ts +26 -13
  42. package/dist/button-group.d.ts.map +1 -0
  43. package/dist/button-group.js +38 -76
  44. package/dist/button-group.js.map +1 -1
  45. package/dist/button.d.ts +17 -10
  46. package/dist/button.d.ts.map +1 -0
  47. package/dist/button.js +50 -3
  48. package/dist/button.js.map +1 -1
  49. package/dist/card.d.ts +35 -11
  50. package/dist/card.d.ts.map +1 -0
  51. package/dist/card.js +43 -82
  52. package/dist/card.js.map +1 -1
  53. package/dist/checkbox.d.ts +6 -4
  54. package/dist/checkbox.d.ts.map +1 -0
  55. package/dist/checkbox.js +21 -29
  56. package/dist/checkbox.js.map +1 -1
  57. package/dist/collapsible.d.ts +15 -7
  58. package/dist/collapsible.d.ts.map +1 -0
  59. package/dist/collapsible.js +19 -8
  60. package/dist/collapsible.js.map +1 -1
  61. package/dist/combobox.d.ts +83 -23
  62. package/dist/combobox.d.ts.map +1 -0
  63. package/dist/combobox.js +149 -247
  64. package/dist/combobox.js.map +1 -1
  65. package/dist/context-menu.d.ts +80 -26
  66. package/dist/context-menu.d.ts.map +1 -0
  67. package/dist/context-menu.js +108 -164
  68. package/dist/context-menu.js.map +1 -1
  69. package/dist/count.d.ts +45 -31
  70. package/dist/count.d.ts.map +1 -0
  71. package/dist/count.js +170 -165
  72. package/dist/count.js.map +1 -1
  73. package/dist/dialog.d.ts +61 -28
  74. package/dist/dialog.d.ts.map +1 -0
  75. package/dist/dialog.js +77 -120
  76. package/dist/dialog.js.map +1 -1
  77. package/dist/direction.d.ts +2 -1
  78. package/dist/direction.js +3 -3
  79. package/dist/drawer.d.ts +45 -15
  80. package/dist/drawer.d.ts.map +1 -0
  81. package/dist/drawer.js +93 -5
  82. package/dist/drawer.js.map +1 -1
  83. package/dist/encrypted-text.d.ts +25 -12
  84. package/dist/encrypted-text.d.ts.map +1 -0
  85. package/dist/encrypted-text.js +102 -134
  86. package/dist/encrypted-text.js.map +1 -1
  87. package/dist/field.d.ts +37 -21
  88. package/dist/field.d.ts.map +1 -0
  89. package/dist/field.js +52 -3
  90. package/dist/field.js.map +1 -1
  91. package/dist/film-grain-shader.d.ts +6 -0
  92. package/dist/film-grain-shader.d.ts.map +1 -0
  93. package/dist/film-grain-shader.js +88 -0
  94. package/dist/film-grain-shader.js.map +1 -0
  95. package/dist/film-grain-webgl.d.ts +20 -0
  96. package/dist/film-grain-webgl.d.ts.map +1 -0
  97. package/dist/film-grain-webgl.js +306 -0
  98. package/dist/film-grain-webgl.js.map +1 -0
  99. package/dist/film-grain.d.ts +21 -11
  100. package/dist/film-grain.d.ts.map +1 -0
  101. package/dist/film-grain.js +28 -420
  102. package/dist/film-grain.js.map +1 -1
  103. package/dist/form.d.ts +64 -49
  104. package/dist/form.d.ts.map +1 -0
  105. package/dist/form.js +112 -91
  106. package/dist/form.js.map +1 -1
  107. package/dist/gradient-reveal-text.d.ts +35 -22
  108. package/dist/gradient-reveal-text.d.ts.map +1 -0
  109. package/dist/gradient-reveal-text.js +238 -205
  110. package/dist/gradient-reveal-text.js.map +1 -1
  111. package/dist/hooks/use-mobile.d.ts +3 -1
  112. package/dist/hooks/use-mobile.d.ts.map +1 -0
  113. package/dist/hooks/use-mobile.js +28 -2
  114. package/dist/hooks/use-mobile.js.map +1 -1
  115. package/dist/infinite-scroll.d.ts +29 -15
  116. package/dist/infinite-scroll.d.ts.map +1 -0
  117. package/dist/infinite-scroll.js +69 -99
  118. package/dist/infinite-scroll.js.map +1 -1
  119. package/dist/input-group.d.ts +41 -18
  120. package/dist/input-group.d.ts.map +1 -0
  121. package/dist/input-group.js +80 -6
  122. package/dist/input-group.js.map +1 -1
  123. package/dist/input-otp.d.ts +26 -10
  124. package/dist/input-otp.d.ts.map +1 -0
  125. package/dist/input-otp.js +40 -70
  126. package/dist/input-otp.js.map +1 -1
  127. package/dist/input.d.ts +10 -4
  128. package/dist/input.d.ts.map +1 -0
  129. package/dist/input.js +16 -3
  130. package/dist/input.js.map +1 -1
  131. package/dist/item.d.ts +58 -23
  132. package/dist/item.d.ts.map +1 -0
  133. package/dist/item.js +102 -160
  134. package/dist/item.js.map +1 -1
  135. package/dist/kbd.d.ts +12 -4
  136. package/dist/kbd.d.ts.map +1 -0
  137. package/dist/kbd.js +15 -24
  138. package/dist/kbd.js.map +1 -1
  139. package/dist/label.d.ts +9 -4
  140. package/dist/label.d.ts.map +1 -0
  141. package/dist/label.js +12 -16
  142. package/dist/label.js.map +1 -1
  143. package/dist/lib/focus.d.ts +42 -0
  144. package/dist/lib/focus.d.ts.map +1 -0
  145. package/dist/lib/focus.js +21 -0
  146. package/dist/lib/focus.js.map +1 -0
  147. package/dist/lib/internal-icons.d.ts +32 -0
  148. package/dist/lib/internal-icons.d.ts.map +1 -0
  149. package/dist/lib/internal-icons.js +222 -0
  150. package/dist/lib/internal-icons.js.map +1 -0
  151. package/dist/lib/utils.d.ts +4 -2
  152. package/dist/lib/utils.d.ts.map +1 -0
  153. package/dist/lib/utils.js +12 -2
  154. package/dist/lib/utils.js.map +1 -1
  155. package/dist/masonry.d.ts +25 -11
  156. package/dist/masonry.d.ts.map +1 -0
  157. package/dist/masonry.js +188 -229
  158. package/dist/masonry.js.map +1 -1
  159. package/dist/menu.d.ts +84 -26
  160. package/dist/menu.d.ts.map +1 -0
  161. package/dist/menu.js +141 -4
  162. package/dist/menu.js.map +1 -1
  163. package/dist/menubar.d.ts +60 -22
  164. package/dist/menubar.d.ts.map +1 -0
  165. package/dist/menubar.js +80 -52
  166. package/dist/menubar.js.map +1 -1
  167. package/dist/pagination.d.ts +38 -17
  168. package/dist/pagination.d.ts.map +1 -0
  169. package/dist/pagination.js +68 -107
  170. package/dist/pagination.js.map +1 -1
  171. package/dist/popover.d.ts +56 -14
  172. package/dist/popover.d.ts.map +1 -0
  173. package/dist/popover.js +62 -87
  174. package/dist/popover.js.map +1 -1
  175. package/dist/preview-card.d.ts +28 -9
  176. package/dist/preview-card.d.ts.map +1 -0
  177. package/dist/preview-card.js +40 -60
  178. package/dist/preview-card.js.map +1 -1
  179. package/dist/progress.d.ts +28 -9
  180. package/dist/progress.d.ts.map +1 -0
  181. package/dist/progress.js +35 -60
  182. package/dist/progress.js.map +1 -1
  183. package/dist/radio-group.d.ts +14 -8
  184. package/dist/radio-group.d.ts.map +1 -0
  185. package/dist/radio-group.js +18 -22
  186. package/dist/radio-group.js.map +1 -1
  187. package/dist/radio.d.ts +14 -6
  188. package/dist/radio.d.ts.map +1 -0
  189. package/dist/radio.js +24 -3
  190. package/dist/radio.js.map +1 -1
  191. package/dist/scroll-area.d.ts +16 -6
  192. package/dist/scroll-area.d.ts.map +1 -0
  193. package/dist/scroll-area.js +34 -55
  194. package/dist/scroll-area.js.map +1 -1
  195. package/dist/select.d.ts +66 -18
  196. package/dist/select.d.ts.map +1 -0
  197. package/dist/select.js +105 -185
  198. package/dist/select.js.map +1 -1
  199. package/dist/separator.d.ts +11 -5
  200. package/dist/separator.d.ts.map +1 -0
  201. package/dist/separator.js +17 -3
  202. package/dist/separator.js.map +1 -1
  203. package/dist/sidebar.d.ts +172 -79
  204. package/dist/sidebar.d.ts.map +1 -0
  205. package/dist/sidebar.js +363 -585
  206. package/dist/sidebar.js.map +1 -1
  207. package/dist/skeleton.d.ts +8 -3
  208. package/dist/skeleton.d.ts.map +1 -0
  209. package/dist/skeleton.js +13 -3
  210. package/dist/skeleton.js.map +1 -1
  211. package/dist/slider.d.ts +16 -6
  212. package/dist/slider.d.ts.map +1 -0
  213. package/dist/slider.js +40 -67
  214. package/dist/slider.js.map +1 -1
  215. package/dist/spinner.d.ts +8 -3
  216. package/dist/spinner.d.ts.map +1 -0
  217. package/dist/spinner.js +15 -4
  218. package/dist/spinner.js.map +1 -1
  219. package/dist/switch.d.ts +12 -6
  220. package/dist/switch.d.ts.map +1 -0
  221. package/dist/switch.js +18 -25
  222. package/dist/switch.js.map +1 -1
  223. package/dist/table.d.ts +37 -11
  224. package/dist/table.d.ts.map +1 -0
  225. package/dist/table.js +51 -88
  226. package/dist/table.js.map +1 -1
  227. package/dist/tabs.d.ts +28 -12
  228. package/dist/tabs.d.ts.map +1 -0
  229. package/dist/tabs.js +40 -74
  230. package/dist/tabs.js.map +1 -1
  231. package/dist/textarea.d.ts +13 -6
  232. package/dist/textarea.d.ts.map +1 -0
  233. package/dist/textarea.js +19 -3
  234. package/dist/textarea.js.map +1 -1
  235. package/dist/toast.d.ts +63 -39
  236. package/dist/toast.d.ts.map +1 -0
  237. package/dist/toast.js +177 -215
  238. package/dist/toast.js.map +1 -1
  239. package/dist/toggle-group.d.ts +26 -12
  240. package/dist/toggle-group.d.ts.map +1 -0
  241. package/dist/toggle-group.js +49 -73
  242. package/dist/toggle-group.js.map +1 -1
  243. package/dist/toggle.d.ts +17 -10
  244. package/dist/toggle.d.ts.map +1 -0
  245. package/dist/toggle.js +38 -3
  246. package/dist/toggle.js.map +1 -1
  247. package/dist/tooltip.d.ts +35 -14
  248. package/dist/tooltip.d.ts.map +1 -0
  249. package/dist/tooltip.js +52 -3
  250. package/dist/tooltip.js.map +1 -1
  251. package/dist/typewriter.d.ts +44 -31
  252. package/dist/typewriter.d.ts.map +1 -0
  253. package/dist/typewriter.js +185 -185
  254. package/dist/typewriter.js.map +1 -1
  255. package/package.json +6 -6
  256. package/dist/chunk-45VQAWIM.js +0 -228
  257. package/dist/chunk-45VQAWIM.js.map +0 -1
  258. package/dist/chunk-6Y7LPQMO.js +0 -11
  259. package/dist/chunk-6Y7LPQMO.js.map +0 -1
  260. package/dist/chunk-76UQO56T.js +0 -19
  261. package/dist/chunk-76UQO56T.js.map +0 -1
  262. package/dist/chunk-7F4MPMLJ.js +0 -17
  263. package/dist/chunk-7F4MPMLJ.js.map +0 -1
  264. package/dist/chunk-BKTJYX4M.js +0 -143
  265. package/dist/chunk-BKTJYX4M.js.map +0 -1
  266. package/dist/chunk-D5XPEJ6T.js +0 -36
  267. package/dist/chunk-D5XPEJ6T.js.map +0 -1
  268. package/dist/chunk-DIGOLJIR.js +0 -105
  269. package/dist/chunk-DIGOLJIR.js.map +0 -1
  270. package/dist/chunk-IQ7YQ5XA.js +0 -141
  271. package/dist/chunk-IQ7YQ5XA.js.map +0 -1
  272. package/dist/chunk-NCHHHWTB.js +0 -85
  273. package/dist/chunk-NCHHHWTB.js.map +0 -1
  274. package/dist/chunk-OUFYQLVN.js +0 -56
  275. package/dist/chunk-OUFYQLVN.js.map +0 -1
  276. package/dist/chunk-QFSEK4M6.js +0 -22
  277. package/dist/chunk-QFSEK4M6.js.map +0 -1
  278. package/dist/chunk-QRW37LRP.js +0 -25
  279. package/dist/chunk-QRW37LRP.js.map +0 -1
  280. package/dist/chunk-RPQHL6C5.js +0 -26
  281. package/dist/chunk-RPQHL6C5.js.map +0 -1
  282. package/dist/chunk-V4ZX4YCP.js +0 -66
  283. package/dist/chunk-V4ZX4YCP.js.map +0 -1
  284. package/dist/chunk-YTSQQTSF.js +0 -44
  285. package/dist/chunk-YTSQQTSF.js.map +0 -1
  286. package/dist/chunk-ZZZH3JGW.js +0 -23
  287. package/dist/chunk-ZZZH3JGW.js.map +0 -1
  288. package/dist/direction.js.map +0 -1
@@ -1,209 +1,242 @@
1
- import { cn } from './chunk-76UQO56T.js';
2
- import { useId, useRef, useState, useCallback, useEffect } from 'react';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
4
-
5
- var DEFAULT_COLORS = [
6
- "#eab308",
7
- "#ef4444",
8
- "#3b82f6",
9
- "#06b6d4",
10
- "#8b5cf6"
1
+ "use client";
2
+ import { cn } from "./lib/utils.js";
3
+ import { useCallback, useEffect, useId, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/gradient-reveal-text.tsx
6
+ const DEFAULT_COLORS = [
7
+ "#eab308",
8
+ "#ef4444",
9
+ "#3b82f6",
10
+ "#06b6d4",
11
+ "#8b5cf6"
11
12
  ];
12
- function GradientRevealText({
13
- text,
14
- duration = 0,
15
- colors = DEFAULT_COLORS,
16
- baseOpacity = 0.3,
17
- hoverOpacity = 0.7,
18
- fontFamily = "Helvetica Neue, Helvetica, Arial, sans-serif",
19
- spotlightSize = 0.6,
20
- strokeWidth: strokeWidthPx,
21
- baseColor,
22
- className
23
- }) {
24
- const uid = useId();
25
- const svgRef = useRef(null);
26
- const textRef = useRef(null);
27
- const gradientRef = useRef(null);
28
- const [vb, setVb] = useState({ x: 0, y: 0, w: 100, h: 20 });
29
- const [measured, setMeasured] = useState(false);
30
- const [hovered, setHovered] = useState(false);
31
- const targetPos = useRef({ cx: 0.5, cy: 0.5 });
32
- const currentPos = useRef({ cx: 0.5, cy: 0.5 });
33
- const rafId = useRef(0);
34
- const measure = useCallback(() => {
35
- const el = textRef.current;
36
- if (!el) return;
37
- const bbox = el.getBBox();
38
- if (bbox.width === 0) return;
39
- setVb({ x: bbox.x, y: bbox.y, w: bbox.width, h: bbox.height });
40
- setMeasured(true);
41
- }, []);
42
- useEffect(() => {
43
- measure();
44
- document.fonts?.ready?.then(measure);
45
- }, [text, measure]);
46
- const applyGradientPos = useCallback((cx, cy) => {
47
- const el = gradientRef.current;
48
- if (!el) return;
49
- const svgCx = vb.x + cx * vb.w;
50
- const svgCy = vb.y + cy * vb.h;
51
- el.setAttribute("cx", String(svgCx));
52
- el.setAttribute("cy", String(svgCy));
53
- }, [vb]);
54
- useEffect(() => {
55
- if (duration <= 0) return;
56
- const speed = 1 - Math.pow(1e-3, 1 / (duration * 60));
57
- const tick = () => {
58
- const cur = currentPos.current;
59
- const tgt = targetPos.current;
60
- cur.cx += (tgt.cx - cur.cx) * speed;
61
- cur.cy += (tgt.cy - cur.cy) * speed;
62
- applyGradientPos(cur.cx, cur.cy);
63
- rafId.current = requestAnimationFrame(tick);
64
- };
65
- rafId.current = requestAnimationFrame(tick);
66
- return () => cancelAnimationFrame(rafId.current);
67
- }, [duration, applyGradientPos]);
68
- const updatePos = (e) => {
69
- const svg = svgRef.current;
70
- if (!svg) return;
71
- const rect = svg.getBoundingClientRect();
72
- const cx = (e.clientX - rect.left) / rect.width;
73
- const cy = (e.clientY - rect.top) / rect.height;
74
- targetPos.current = { cx, cy };
75
- if (duration <= 0) {
76
- currentPos.current = { cx, cy };
77
- applyGradientPos(cx, cy);
78
- }
79
- };
80
- const handleMouseEnter = (e) => {
81
- const svg = svgRef.current;
82
- if (svg) {
83
- const rect = svg.getBoundingClientRect();
84
- const cx = (e.clientX - rect.left) / rect.width;
85
- const cy = (e.clientY - rect.top) / rect.height;
86
- targetPos.current = { cx, cy };
87
- currentPos.current = { cx, cy };
88
- applyGradientPos(cx, cy);
89
- }
90
- setHovered(true);
91
- };
92
- const spotlightR = vb.h * spotlightSize;
93
- const strokeW = strokeWidthPx ?? vb.h * 0.015;
94
- const initCx = vb.x + 0.5 * vb.w;
95
- const initCy = vb.y + 0.5 * vb.h;
96
- const gradientId = `grad-${uid}`;
97
- const maskId = `mask-${uid}`;
98
- const revealId = `reveal-${uid}`;
99
- const stops = colors.map((color, i) => ({
100
- offset: `${i / Math.max(colors.length - 1, 1) * 100}%`,
101
- color
102
- }));
103
- const textStyle = {
104
- fontSize: "1em",
105
- fontFamily,
106
- fill: "none",
107
- strokeWidth: strokeW,
108
- strokeLinejoin: "round",
109
- strokeLinecap: "round",
110
- paintOrder: "stroke fill"
111
- };
112
- return /* @__PURE__ */ jsxs(
113
- "svg",
114
- {
115
- ref: svgRef,
116
- "data-slot": "gradient-reveal-text",
117
- width: "100%",
118
- viewBox: `${vb.x} ${vb.y} ${vb.w} ${vb.h}`,
119
- preserveAspectRatio: "xMidYMid meet",
120
- xmlns: "http://www.w3.org/2000/svg",
121
- onMouseEnter: handleMouseEnter,
122
- onMouseLeave: () => setHovered(false),
123
- onMouseMove: updatePos,
124
- className: cn("select-none", className),
125
- style: { opacity: measured ? 1 : 0 },
126
- "aria-hidden": true,
127
- children: [
128
- /* @__PURE__ */ jsxs("defs", { children: [
129
- /* @__PURE__ */ jsx("linearGradient", { id: gradientId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: hovered && stops.map((s) => /* @__PURE__ */ jsx("stop", { offset: s.offset, stopColor: s.color }, s.offset)) }),
130
- /* @__PURE__ */ jsxs(
131
- "radialGradient",
132
- {
133
- ref: gradientRef,
134
- id: revealId,
135
- gradientUnits: "userSpaceOnUse",
136
- r: spotlightR,
137
- cx: initCx,
138
- cy: initCy,
139
- children: [
140
- /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "white" }),
141
- /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "black" })
142
- ]
143
- }
144
- ),
145
- /* @__PURE__ */ jsx("mask", { id: maskId, children: /* @__PURE__ */ jsx(
146
- "rect",
147
- {
148
- x: vb.x - vb.w,
149
- y: vb.y - vb.h,
150
- width: vb.w * 3,
151
- height: vb.h * 3,
152
- fill: `url(#${revealId})`
153
- }
154
- ) })
155
- ] }),
156
- /* @__PURE__ */ jsx(
157
- "text",
158
- {
159
- ref: textRef,
160
- x: "50%",
161
- y: "50%",
162
- textAnchor: "middle",
163
- dominantBaseline: "central",
164
- className: "font-bold",
165
- style: { fontSize: "1em", fontFamily, visibility: "hidden" },
166
- children: text
167
- }
168
- ),
169
- /* @__PURE__ */ jsx(
170
- "text",
171
- {
172
- x: "50%",
173
- y: "50%",
174
- textAnchor: "middle",
175
- dominantBaseline: "central",
176
- className: baseColor ? "font-bold" : "font-bold stroke-neutral-200 dark:stroke-neutral-800",
177
- style: {
178
- ...textStyle,
179
- ...baseColor ? { stroke: baseColor } : {},
180
- opacity: hovered ? hoverOpacity : baseOpacity,
181
- transition: "opacity 0.3s ease"
182
- },
183
- children: text
184
- }
185
- ),
186
- /* @__PURE__ */ jsx(
187
- "text",
188
- {
189
- x: "50%",
190
- y: "50%",
191
- textAnchor: "middle",
192
- dominantBaseline: "central",
193
- mask: `url(#${maskId})`,
194
- className: "font-bold",
195
- style: {
196
- ...textStyle,
197
- stroke: `url(#${gradientId})`
198
- },
199
- children: text
200
- }
201
- )
202
- ]
203
- }
204
- );
13
+ /**
14
+ * Large decorative text with a gradient spotlight that follows the cursor.
15
+ *
16
+ * Renders as an SVG that auto-sizes to fit the text with zero padding.
17
+ * The gradient reveal effect activates on hover — a circular spotlight
18
+ * follows the mouse, revealing rainbow-colored strokes beneath.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <GradientRevealText text="HELLO" />
23
+ * <GradientRevealText text="BRAND" colors={["#ff0000", "#00ff00"]} />
24
+ * ```
25
+ */
26
+ function GradientRevealText({ text, duration = 0, colors = DEFAULT_COLORS, baseOpacity = .3, hoverOpacity = .7, fontFamily = "Helvetica Neue, Helvetica, Arial, sans-serif", spotlightSize = .6, strokeWidth: strokeWidthPx, baseColor, className }) {
27
+ const uid = useId();
28
+ const svgRef = useRef(null);
29
+ const textRef = useRef(null);
30
+ const gradientRef = useRef(null);
31
+ const [vb, setVb] = useState({
32
+ x: 0,
33
+ y: 0,
34
+ w: 100,
35
+ h: 20
36
+ });
37
+ const [measured, setMeasured] = useState(false);
38
+ const [hovered, setHovered] = useState(false);
39
+ const targetPos = useRef({
40
+ cx: .5,
41
+ cy: .5
42
+ });
43
+ const currentPos = useRef({
44
+ cx: .5,
45
+ cy: .5
46
+ });
47
+ const rafId = useRef(0);
48
+ const measure = useCallback(() => {
49
+ const el = textRef.current;
50
+ if (!el) return;
51
+ const bbox = el.getBBox();
52
+ if (bbox.width === 0) return;
53
+ setVb({
54
+ x: bbox.x,
55
+ y: bbox.y,
56
+ w: bbox.width,
57
+ h: bbox.height
58
+ });
59
+ setMeasured(true);
60
+ }, []);
61
+ useEffect(() => {
62
+ measure();
63
+ document.fonts?.ready?.then(measure);
64
+ }, [text, measure]);
65
+ const applyGradientPos = useCallback((cx, cy) => {
66
+ const el = gradientRef.current;
67
+ if (!el) return;
68
+ const svgCx = vb.x + cx * vb.w;
69
+ const svgCy = vb.y + cy * vb.h;
70
+ el.setAttribute("cx", String(svgCx));
71
+ el.setAttribute("cy", String(svgCy));
72
+ }, [vb]);
73
+ useEffect(() => {
74
+ if (duration <= 0) return;
75
+ const speed = 1 - Math.pow(.001, 1 / (duration * 60));
76
+ const tick = () => {
77
+ const cur = currentPos.current;
78
+ const tgt = targetPos.current;
79
+ cur.cx += (tgt.cx - cur.cx) * speed;
80
+ cur.cy += (tgt.cy - cur.cy) * speed;
81
+ applyGradientPos(cur.cx, cur.cy);
82
+ rafId.current = requestAnimationFrame(tick);
83
+ };
84
+ rafId.current = requestAnimationFrame(tick);
85
+ return () => cancelAnimationFrame(rafId.current);
86
+ }, [duration, applyGradientPos]);
87
+ const updatePos = (e) => {
88
+ const svg = svgRef.current;
89
+ if (!svg) return;
90
+ const rect = svg.getBoundingClientRect();
91
+ const cx = (e.clientX - rect.left) / rect.width;
92
+ const cy = (e.clientY - rect.top) / rect.height;
93
+ targetPos.current = {
94
+ cx,
95
+ cy
96
+ };
97
+ if (duration <= 0) {
98
+ currentPos.current = {
99
+ cx,
100
+ cy
101
+ };
102
+ applyGradientPos(cx, cy);
103
+ }
104
+ };
105
+ const handleMouseEnter = (e) => {
106
+ const svg = svgRef.current;
107
+ if (svg) {
108
+ const rect = svg.getBoundingClientRect();
109
+ const cx = (e.clientX - rect.left) / rect.width;
110
+ const cy = (e.clientY - rect.top) / rect.height;
111
+ targetPos.current = {
112
+ cx,
113
+ cy
114
+ };
115
+ currentPos.current = {
116
+ cx,
117
+ cy
118
+ };
119
+ applyGradientPos(cx, cy);
120
+ }
121
+ setHovered(true);
122
+ };
123
+ const spotlightR = vb.h * spotlightSize;
124
+ const strokeW = strokeWidthPx ?? vb.h * .015;
125
+ const initCx = vb.x + .5 * vb.w;
126
+ const initCy = vb.y + .5 * vb.h;
127
+ const gradientId = `grad-${uid}`;
128
+ const maskId = `mask-${uid}`;
129
+ const revealId = `reveal-${uid}`;
130
+ const stops = colors.map((color, i) => ({
131
+ offset: `${i / Math.max(colors.length - 1, 1) * 100}%`,
132
+ color
133
+ }));
134
+ const textStyle = {
135
+ fontSize: "1em",
136
+ fontFamily,
137
+ fill: "none",
138
+ strokeWidth: strokeW,
139
+ strokeLinejoin: "round",
140
+ strokeLinecap: "round",
141
+ paintOrder: "stroke fill"
142
+ };
143
+ return /* @__PURE__ */ jsxs("svg", {
144
+ ref: svgRef,
145
+ "data-slot": "gradient-reveal-text",
146
+ width: "100%",
147
+ viewBox: `${vb.x} ${vb.y} ${vb.w} ${vb.h}`,
148
+ preserveAspectRatio: "xMidYMid meet",
149
+ xmlns: "http://www.w3.org/2000/svg",
150
+ onMouseEnter: handleMouseEnter,
151
+ onMouseLeave: () => setHovered(false),
152
+ onMouseMove: updatePos,
153
+ className: cn("select-none", className),
154
+ style: { opacity: measured ? 1 : 0 },
155
+ "aria-hidden": true,
156
+ children: [
157
+ /* @__PURE__ */ jsxs("defs", { children: [
158
+ /* @__PURE__ */ jsx("linearGradient", {
159
+ id: gradientId,
160
+ x1: "0%",
161
+ y1: "0%",
162
+ x2: "100%",
163
+ y2: "0%",
164
+ children: hovered && stops.map((s) => /* @__PURE__ */ jsx("stop", {
165
+ offset: s.offset,
166
+ stopColor: s.color
167
+ }, s.offset))
168
+ }),
169
+ /* @__PURE__ */ jsxs("radialGradient", {
170
+ ref: gradientRef,
171
+ id: revealId,
172
+ gradientUnits: "userSpaceOnUse",
173
+ r: spotlightR,
174
+ cx: initCx,
175
+ cy: initCy,
176
+ children: [/* @__PURE__ */ jsx("stop", {
177
+ offset: "0%",
178
+ stopColor: "white"
179
+ }), /* @__PURE__ */ jsx("stop", {
180
+ offset: "100%",
181
+ stopColor: "black"
182
+ })]
183
+ }),
184
+ /* @__PURE__ */ jsx("mask", {
185
+ id: maskId,
186
+ children: /* @__PURE__ */ jsx("rect", {
187
+ x: vb.x - vb.w,
188
+ y: vb.y - vb.h,
189
+ width: vb.w * 3,
190
+ height: vb.h * 3,
191
+ fill: `url(#${revealId})`
192
+ })
193
+ })
194
+ ] }),
195
+ /* @__PURE__ */ jsx("text", {
196
+ ref: textRef,
197
+ x: "50%",
198
+ y: "50%",
199
+ textAnchor: "middle",
200
+ dominantBaseline: "central",
201
+ className: "font-bold",
202
+ style: {
203
+ fontSize: "1em",
204
+ fontFamily,
205
+ visibility: "hidden"
206
+ },
207
+ children: text
208
+ }),
209
+ /* @__PURE__ */ jsx("text", {
210
+ x: "50%",
211
+ y: "50%",
212
+ textAnchor: "middle",
213
+ dominantBaseline: "central",
214
+ className: baseColor ? "font-bold" : "font-bold stroke-neutral-200 dark:stroke-neutral-800",
215
+ style: {
216
+ ...textStyle,
217
+ ...baseColor ? { stroke: baseColor } : {},
218
+ opacity: hovered ? hoverOpacity : baseOpacity,
219
+ transition: "opacity 0.3s ease"
220
+ },
221
+ children: text
222
+ }),
223
+ /* @__PURE__ */ jsx("text", {
224
+ x: "50%",
225
+ y: "50%",
226
+ textAnchor: "middle",
227
+ dominantBaseline: "central",
228
+ mask: `url(#${maskId})`,
229
+ className: "font-bold",
230
+ style: {
231
+ ...textStyle,
232
+ stroke: `url(#${gradientId})`
233
+ },
234
+ children: text
235
+ })
236
+ ]
237
+ });
205
238
  }
206
-
239
+ //#endregion
207
240
  export { GradientRevealText };
208
- //# sourceMappingURL=gradient-reveal-text.js.map
241
+
209
242
  //# sourceMappingURL=gradient-reveal-text.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/gradient-reveal-text.tsx"],"names":[],"mappings":"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","file":"gradient-reveal-text.js","sourcesContent":["\"use client\"\n\nimport { useRef, useEffect, useState, useId, useCallback } from \"react\"\nimport { cn } from \"./lib/utils\"\n\ninterface GradientRevealTextProps {\n /** The text to display */\n text: string\n /** Spotlight follow speed in seconds. Default: 0 (instant) */\n duration?: number\n /** Gradient colors for the reveal effect. Default: rainbow */\n colors?: string[]\n /** Base stroke opacity when not hovered. Default: 0.3 */\n baseOpacity?: number\n /** Hovered stroke opacity. Default: 0.7 */\n hoverOpacity?: number\n /** Font family for SVG text. Default: Helvetica Neue */\n fontFamily?: string\n /** Spotlight radius multiplier relative to text height. Default: 0.6 */\n spotlightSize?: number\n /** Stroke width in px. Default: auto (1.5% of text height) */\n strokeWidth?: number\n /** Base stroke color. Default: neutral-200 (light) / neutral-800 (dark) via Tailwind */\n baseColor?: string\n className?: string\n}\n\nconst DEFAULT_COLORS = [\n \"#eab308\",\n \"#ef4444\",\n \"#3b82f6\",\n \"#06b6d4\",\n \"#8b5cf6\",\n]\n\n/**\n * Large decorative text with a gradient spotlight that follows the cursor.\n *\n * Renders as an SVG that auto-sizes to fit the text with zero padding.\n * The gradient reveal effect activates on hover — a circular spotlight\n * follows the mouse, revealing rainbow-colored strokes beneath.\n *\n * @example\n * ```tsx\n * <GradientRevealText text=\"HELLO\" />\n * <GradientRevealText text=\"BRAND\" colors={[\"#ff0000\", \"#00ff00\"]} />\n * ```\n */\nfunction GradientRevealText({\n text,\n duration = 0,\n colors = DEFAULT_COLORS,\n baseOpacity = 0.3,\n hoverOpacity = 0.7,\n fontFamily = \"Helvetica Neue, Helvetica, Arial, sans-serif\",\n spotlightSize = 0.6,\n strokeWidth: strokeWidthPx,\n baseColor,\n className,\n}: GradientRevealTextProps) {\n const uid = useId()\n const svgRef = useRef<SVGSVGElement>(null)\n const textRef = useRef<SVGTextElement>(null)\n const gradientRef = useRef<SVGRadialGradientElement>(null)\n\n const [vb, setVb] = useState({ x: 0, y: 0, w: 100, h: 20 })\n const [measured, setMeasured] = useState(false)\n const [hovered, setHovered] = useState(false)\n\n // Target position (where cursor is) and current animated position\n const targetPos = useRef({ cx: 0.5, cy: 0.5 })\n const currentPos = useRef({ cx: 0.5, cy: 0.5 })\n const rafId = useRef<number>(0)\n\n // Measure text bbox → set viewBox to fit exactly\n const measure = useCallback(() => {\n const el = textRef.current\n if (!el) return\n const bbox = el.getBBox()\n if (bbox.width === 0) return\n\n setVb({ x: bbox.x, y: bbox.y, w: bbox.width, h: bbox.height })\n setMeasured(true)\n }, [])\n\n useEffect(() => {\n measure()\n document.fonts?.ready?.then(measure)\n }, [text, measure])\n\n // Update the SVG gradient attributes directly (no React re-render)\n const applyGradientPos = useCallback((cx: number, cy: number) => {\n const el = gradientRef.current\n if (!el) return\n const svgCx = vb.x + cx * vb.w\n const svgCy = vb.y + cy * vb.h\n el.setAttribute(\"cx\", String(svgCx))\n el.setAttribute(\"cy\", String(svgCy))\n }, [vb])\n\n // RAF loop for smooth follow\n useEffect(() => {\n if (duration <= 0) return\n\n // Lerp factor: higher = faster catch-up. Derived from duration.\n const speed = 1 - Math.pow(0.001, 1 / (duration * 60))\n\n const tick = () => {\n const cur = currentPos.current\n const tgt = targetPos.current\n cur.cx += (tgt.cx - cur.cx) * speed\n cur.cy += (tgt.cy - cur.cy) * speed\n applyGradientPos(cur.cx, cur.cy)\n rafId.current = requestAnimationFrame(tick)\n }\n\n rafId.current = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(rafId.current)\n }, [duration, applyGradientPos])\n\n const updatePos = (e: React.MouseEvent<SVGSVGElement>) => {\n const svg = svgRef.current\n if (!svg) return\n const rect = svg.getBoundingClientRect()\n const cx = (e.clientX - rect.left) / rect.width\n const cy = (e.clientY - rect.top) / rect.height\n targetPos.current = { cx, cy }\n\n // If no smooth follow, apply instantly\n if (duration <= 0) {\n currentPos.current = { cx, cy }\n applyGradientPos(cx, cy)\n }\n }\n\n const handleMouseEnter = (e: React.MouseEvent<SVGSVGElement>) => {\n // Snap to entry point — no lerp on first frame\n const svg = svgRef.current\n if (svg) {\n const rect = svg.getBoundingClientRect()\n const cx = (e.clientX - rect.left) / rect.width\n const cy = (e.clientY - rect.top) / rect.height\n targetPos.current = { cx, cy }\n currentPos.current = { cx, cy }\n applyGradientPos(cx, cy)\n }\n setHovered(true)\n }\n\n // Derived values\n const spotlightR = vb.h * spotlightSize\n const strokeW = strokeWidthPx ?? vb.h * 0.015\n const initCx = vb.x + 0.5 * vb.w\n const initCy = vb.y + 0.5 * vb.h\n\n // Unique SVG IDs\n const gradientId = `grad-${uid}`\n const maskId = `mask-${uid}`\n const revealId = `reveal-${uid}`\n\n // Evenly distribute color stops\n const stops = colors.map((color, i) => ({\n offset: `${(i / Math.max(colors.length - 1, 1)) * 100}%`,\n color,\n }))\n\n const textStyle = {\n fontSize: \"1em\",\n fontFamily,\n fill: \"none\",\n strokeWidth: strokeW,\n strokeLinejoin: \"round\" as const,\n strokeLinecap: \"round\" as const,\n paintOrder: \"stroke fill\" as const,\n }\n\n return (\n <svg\n ref={svgRef}\n data-slot=\"gradient-reveal-text\"\n width=\"100%\"\n viewBox={`${vb.x} ${vb.y} ${vb.w} ${vb.h}`}\n preserveAspectRatio=\"xMidYMid meet\"\n xmlns=\"http://www.w3.org/2000/svg\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={() => setHovered(false)}\n onMouseMove={updatePos}\n className={cn(\"select-none\", className)}\n style={{ opacity: measured ? 1 : 0 }}\n aria-hidden\n >\n <defs>\n <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n {hovered &&\n stops.map((s) => (\n <stop key={s.offset} offset={s.offset} stopColor={s.color} />\n ))}\n </linearGradient>\n\n <radialGradient\n ref={gradientRef}\n id={revealId}\n gradientUnits=\"userSpaceOnUse\"\n r={spotlightR}\n cx={initCx}\n cy={initCy}\n >\n <stop offset=\"0%\" stopColor=\"white\" />\n <stop offset=\"100%\" stopColor=\"black\" />\n </radialGradient>\n\n <mask id={maskId}>\n <rect\n x={vb.x - vb.w}\n y={vb.y - vb.h}\n width={vb.w * 3}\n height={vb.h * 3}\n fill={`url(#${revealId})`}\n />\n </mask>\n </defs>\n\n {/* Hidden text for measurement */}\n <text\n ref={textRef}\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"font-bold\"\n style={{ fontSize: \"1em\", fontFamily, visibility: \"hidden\" }}\n >\n {text}\n </text>\n\n {/* Base stroke — subtle outline */}\n <text\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className={baseColor ? \"font-bold\" : \"font-bold stroke-neutral-200 dark:stroke-neutral-800\"}\n style={{\n ...textStyle,\n ...(baseColor ? { stroke: baseColor } : {}),\n opacity: hovered ? hoverOpacity : baseOpacity,\n transition: \"opacity 0.3s ease\",\n }}\n >\n {text}\n </text>\n\n {/* Gradient reveal on hover */}\n <text\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n mask={`url(#${maskId})`}\n className=\"font-bold\"\n style={{\n ...textStyle,\n stroke: `url(#${gradientId})`,\n }}\n >\n {text}\n </text>\n </svg>\n )\n}\n\nexport { GradientRevealText }\nexport type { GradientRevealTextProps }\n"]}
1
+ {"version":3,"file":"gradient-reveal-text.js","names":[],"sources":["../src/gradient-reveal-text.tsx"],"sourcesContent":["\"use client\"\n\nimport { useRef, useEffect, useState, useId, useCallback } from \"react\"\nimport { cn } from \"./lib/utils\"\n\ninterface GradientRevealTextProps {\n /** The text to display */\n text: string\n /** Spotlight follow speed in seconds. Default: 0 (instant) */\n duration?: number\n /** Gradient colors for the reveal effect. Default: rainbow */\n colors?: string[]\n /** Base stroke opacity when not hovered. Default: 0.3 */\n baseOpacity?: number\n /** Hovered stroke opacity. Default: 0.7 */\n hoverOpacity?: number\n /** Font family for SVG text. Default: Helvetica Neue */\n fontFamily?: string\n /** Spotlight radius multiplier relative to text height. Default: 0.6 */\n spotlightSize?: number\n /** Stroke width in px. Default: auto (1.5% of text height) */\n strokeWidth?: number\n /** Base stroke color. Default: neutral-200 (light) / neutral-800 (dark) via Tailwind */\n baseColor?: string\n className?: string\n}\n\nconst DEFAULT_COLORS = [\n \"#eab308\",\n \"#ef4444\",\n \"#3b82f6\",\n \"#06b6d4\",\n \"#8b5cf6\",\n]\n\n/**\n * Large decorative text with a gradient spotlight that follows the cursor.\n *\n * Renders as an SVG that auto-sizes to fit the text with zero padding.\n * The gradient reveal effect activates on hover — a circular spotlight\n * follows the mouse, revealing rainbow-colored strokes beneath.\n *\n * @example\n * ```tsx\n * <GradientRevealText text=\"HELLO\" />\n * <GradientRevealText text=\"BRAND\" colors={[\"#ff0000\", \"#00ff00\"]} />\n * ```\n */\nfunction GradientRevealText({\n text,\n duration = 0,\n colors = DEFAULT_COLORS,\n baseOpacity = 0.3,\n hoverOpacity = 0.7,\n fontFamily = \"Helvetica Neue, Helvetica, Arial, sans-serif\",\n spotlightSize = 0.6,\n strokeWidth: strokeWidthPx,\n baseColor,\n className,\n}: GradientRevealTextProps) {\n const uid = useId()\n const svgRef = useRef<SVGSVGElement>(null)\n const textRef = useRef<SVGTextElement>(null)\n const gradientRef = useRef<SVGRadialGradientElement>(null)\n\n const [vb, setVb] = useState({ x: 0, y: 0, w: 100, h: 20 })\n const [measured, setMeasured] = useState(false)\n const [hovered, setHovered] = useState(false)\n\n // Target position (where cursor is) and current animated position\n const targetPos = useRef({ cx: 0.5, cy: 0.5 })\n const currentPos = useRef({ cx: 0.5, cy: 0.5 })\n const rafId = useRef<number>(0)\n\n // Measure text bbox → set viewBox to fit exactly\n const measure = useCallback(() => {\n const el = textRef.current\n if (!el) return\n const bbox = el.getBBox()\n if (bbox.width === 0) return\n\n setVb({ x: bbox.x, y: bbox.y, w: bbox.width, h: bbox.height })\n setMeasured(true)\n }, [])\n\n useEffect(() => {\n measure()\n document.fonts?.ready?.then(measure)\n }, [text, measure])\n\n // Update the SVG gradient attributes directly (no React re-render)\n const applyGradientPos = useCallback((cx: number, cy: number) => {\n const el = gradientRef.current\n if (!el) return\n const svgCx = vb.x + cx * vb.w\n const svgCy = vb.y + cy * vb.h\n el.setAttribute(\"cx\", String(svgCx))\n el.setAttribute(\"cy\", String(svgCy))\n }, [vb])\n\n // RAF loop for smooth follow\n useEffect(() => {\n if (duration <= 0) return\n\n // Lerp factor: higher = faster catch-up. Derived from duration.\n const speed = 1 - Math.pow(0.001, 1 / (duration * 60))\n\n const tick = () => {\n const cur = currentPos.current\n const tgt = targetPos.current\n cur.cx += (tgt.cx - cur.cx) * speed\n cur.cy += (tgt.cy - cur.cy) * speed\n applyGradientPos(cur.cx, cur.cy)\n rafId.current = requestAnimationFrame(tick)\n }\n\n rafId.current = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(rafId.current)\n }, [duration, applyGradientPos])\n\n const updatePos = (e: React.MouseEvent<SVGSVGElement>) => {\n const svg = svgRef.current\n if (!svg) return\n const rect = svg.getBoundingClientRect()\n const cx = (e.clientX - rect.left) / rect.width\n const cy = (e.clientY - rect.top) / rect.height\n targetPos.current = { cx, cy }\n\n // If no smooth follow, apply instantly\n if (duration <= 0) {\n currentPos.current = { cx, cy }\n applyGradientPos(cx, cy)\n }\n }\n\n const handleMouseEnter = (e: React.MouseEvent<SVGSVGElement>) => {\n // Snap to entry point — no lerp on first frame\n const svg = svgRef.current\n if (svg) {\n const rect = svg.getBoundingClientRect()\n const cx = (e.clientX - rect.left) / rect.width\n const cy = (e.clientY - rect.top) / rect.height\n targetPos.current = { cx, cy }\n currentPos.current = { cx, cy }\n applyGradientPos(cx, cy)\n }\n setHovered(true)\n }\n\n // Derived values\n const spotlightR = vb.h * spotlightSize\n const strokeW = strokeWidthPx ?? vb.h * 0.015\n const initCx = vb.x + 0.5 * vb.w\n const initCy = vb.y + 0.5 * vb.h\n\n // Unique SVG IDs\n const gradientId = `grad-${uid}`\n const maskId = `mask-${uid}`\n const revealId = `reveal-${uid}`\n\n // Evenly distribute color stops\n const stops = colors.map((color, i) => ({\n offset: `${(i / Math.max(colors.length - 1, 1)) * 100}%`,\n color,\n }))\n\n const textStyle = {\n fontSize: \"1em\",\n fontFamily,\n fill: \"none\",\n strokeWidth: strokeW,\n strokeLinejoin: \"round\" as const,\n strokeLinecap: \"round\" as const,\n paintOrder: \"stroke fill\" as const,\n }\n\n return (\n <svg\n ref={svgRef}\n data-slot=\"gradient-reveal-text\"\n width=\"100%\"\n viewBox={`${vb.x} ${vb.y} ${vb.w} ${vb.h}`}\n preserveAspectRatio=\"xMidYMid meet\"\n xmlns=\"http://www.w3.org/2000/svg\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={() => setHovered(false)}\n onMouseMove={updatePos}\n className={cn(\"select-none\", className)}\n style={{ opacity: measured ? 1 : 0 }}\n aria-hidden\n >\n <defs>\n <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n {hovered &&\n stops.map((s) => (\n <stop key={s.offset} offset={s.offset} stopColor={s.color} />\n ))}\n </linearGradient>\n\n <radialGradient\n ref={gradientRef}\n id={revealId}\n gradientUnits=\"userSpaceOnUse\"\n r={spotlightR}\n cx={initCx}\n cy={initCy}\n >\n <stop offset=\"0%\" stopColor=\"white\" />\n <stop offset=\"100%\" stopColor=\"black\" />\n </radialGradient>\n\n <mask id={maskId}>\n <rect\n x={vb.x - vb.w}\n y={vb.y - vb.h}\n width={vb.w * 3}\n height={vb.h * 3}\n fill={`url(#${revealId})`}\n />\n </mask>\n </defs>\n\n {/* Hidden text for measurement */}\n <text\n ref={textRef}\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"font-bold\"\n style={{ fontSize: \"1em\", fontFamily, visibility: \"hidden\" }}\n >\n {text}\n </text>\n\n {/* Base stroke — subtle outline */}\n <text\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className={baseColor ? \"font-bold\" : \"font-bold stroke-neutral-200 dark:stroke-neutral-800\"}\n style={{\n ...textStyle,\n ...(baseColor ? { stroke: baseColor } : {}),\n opacity: hovered ? hoverOpacity : baseOpacity,\n transition: \"opacity 0.3s ease\",\n }}\n >\n {text}\n </text>\n\n {/* Gradient reveal on hover */}\n <text\n x=\"50%\"\n y=\"50%\"\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n mask={`url(#${maskId})`}\n className=\"font-bold\"\n style={{\n ...textStyle,\n stroke: `url(#${gradientId})`,\n }}\n >\n {text}\n </text>\n </svg>\n )\n}\n\nexport { GradientRevealText }\nexport type { GradientRevealTextProps }\n"],"mappings":"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"}
@@ -1,8 +1,10 @@
1
+ //#region src/hooks/use-mobile.d.ts
1
2
  /**
2
3
  * Returns `true` when the viewport is below the given breakpoint.
3
4
  *
4
5
  * @param breakpoint - Width in pixels. Defaults to 768.
5
6
  */
6
7
  declare function useIsMobile(breakpoint?: number): boolean;
7
-
8
+ //#endregion
8
9
  export { useIsMobile };
10
+ //# sourceMappingURL=use-mobile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-mobile.d.ts","names":[],"sources":["../../src/hooks/use-mobile.ts"],"mappings":";;AAWA;;;;iBAAgB,WAAA,CAAY,UAAA"}
@@ -1,3 +1,29 @@
1
- export { useIsMobile } from '../chunk-ZZZH3JGW.js';
2
- //# sourceMappingURL=use-mobile.js.map
1
+ "use client";
2
+ import * as React from "react";
3
+ //#region src/hooks/use-mobile.ts
4
+ const DEFAULT_MOBILE_BREAKPOINT = 768;
5
+ /**
6
+ * Returns `true` when the viewport is below the given breakpoint.
7
+ *
8
+ * @param breakpoint - Width in pixels. Defaults to 768.
9
+ */
10
+ function useIsMobile(breakpoint = DEFAULT_MOBILE_BREAKPOINT) {
11
+ const [isMobile, setIsMobile] = React.useState(() => {
12
+ if (typeof window === "undefined") return false;
13
+ return window.innerWidth < breakpoint;
14
+ });
15
+ React.useEffect(() => {
16
+ const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
17
+ const onChange = () => {
18
+ setIsMobile(window.innerWidth < breakpoint);
19
+ };
20
+ mql.addEventListener("change", onChange);
21
+ setIsMobile(window.innerWidth < breakpoint);
22
+ return () => mql.removeEventListener("change", onChange);
23
+ }, [breakpoint]);
24
+ return isMobile;
25
+ }
26
+ //#endregion
27
+ export { useIsMobile };
28
+
3
29
  //# sourceMappingURL=use-mobile.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"use-mobile.js"}
1
+ {"version":3,"file":"use-mobile.js","names":[],"sources":["../../src/hooks/use-mobile.ts"],"sourcesContent":["'use client';\n\nimport * as React from \"react\";\n\nconst DEFAULT_MOBILE_BREAKPOINT = 768;\n\n/**\n * Returns `true` when the viewport is below the given breakpoint.\n *\n * @param breakpoint - Width in pixels. Defaults to 768.\n */\nexport function useIsMobile(breakpoint: number = DEFAULT_MOBILE_BREAKPOINT) {\n const [isMobile, setIsMobile] = React.useState(() => {\n if (typeof window === 'undefined') return false;\n return window.innerWidth < breakpoint;\n });\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < breakpoint);\n };\n mql.addEventListener(\"change\", onChange);\n // Sync in case breakpoint prop changed\n setIsMobile(window.innerWidth < breakpoint);\n return () => mql.removeEventListener(\"change\", onChange);\n }, [breakpoint]);\n\n return isMobile;\n}\n"],"mappings":";;;AAIA,MAAM,4BAA4B;;;;;;AAOlC,SAAgB,YAAY,aAAqB,2BAA2B;CAC1E,MAAM,CAAC,UAAU,eAAe,MAAM,eAAe;AACnD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,aAAa;GAC3B;AAEF,OAAM,gBAAgB;EACpB,MAAM,MAAM,OAAO,WAAW,eAAe,aAAa,EAAE,KAAK;EACjE,MAAM,iBAAiB;AACrB,eAAY,OAAO,aAAa,WAAW;;AAE7C,MAAI,iBAAiB,UAAU,SAAS;AAExC,cAAY,OAAO,aAAa,WAAW;AAC3C,eAAa,IAAI,oBAAoB,UAAU,SAAS;IACvD,CAAC,WAAW,CAAC;AAEhB,QAAO"}
@@ -1,19 +1,33 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as React from 'react';
1
+ import * as React from "react";
2
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
3
3
 
4
+ //#region src/infinite-scroll.d.ts
4
5
  type InfiniteScrollProps = {
5
- onLoadMore: () => void;
6
- hasMore: boolean;
7
- isLoading?: boolean;
8
- direction?: "down" | "up";
9
- root?: React.RefObject<Element | null>;
10
- rootMargin?: string;
11
- threshold?: number;
12
- loader?: React.ReactNode;
13
- endMessage?: React.ReactNode;
14
- className?: string;
15
- children?: React.ReactNode;
6
+ onLoadMore: () => void;
7
+ hasMore: boolean;
8
+ isLoading?: boolean;
9
+ direction?: "down" | "up";
10
+ root?: React.RefObject<Element | null>;
11
+ rootMargin?: string;
12
+ threshold?: number;
13
+ loader?: React.ReactNode;
14
+ endMessage?: React.ReactNode;
15
+ className?: string;
16
+ children?: React.ReactNode;
16
17
  };
17
- declare function InfiniteScroll({ onLoadMore, hasMore, isLoading, direction, root, rootMargin, threshold, loader, endMessage, className, children, }: InfiniteScrollProps): react_jsx_runtime.JSX.Element;
18
-
18
+ declare function InfiniteScroll({
19
+ onLoadMore,
20
+ hasMore,
21
+ isLoading,
22
+ direction,
23
+ root,
24
+ rootMargin,
25
+ threshold,
26
+ loader,
27
+ endMessage,
28
+ className,
29
+ children
30
+ }: InfiniteScrollProps): _$react_jsx_runtime0.JSX.Element;
31
+ //#endregion
19
32
  export { InfiniteScroll, type InfiniteScrollProps };
33
+ //# sourceMappingURL=infinite-scroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infinite-scroll.d.ts","names":[],"sources":["../src/infinite-scroll.tsx"],"mappings":";;;;KAOK,mBAAA;EACH,UAAA;EACA,OAAA;EACA,SAAA;EACA,SAAA;EACA,IAAA,GAAO,KAAA,CAAM,SAAA,CAAU,OAAA;EACvB,UAAA;EACA,SAAA;EACA,MAAA,GAAS,KAAA,CAAM,SAAA;EACf,UAAA,GAAa,KAAA,CAAM,SAAA;EACnB,SAAA;EACA,QAAA,GAAW,KAAA,CAAM,SAAA;AAAA;AAAA,iBAGV,cAAA,CAAA;EACP,UAAA;EACA,OAAA;EACA,SAAA;EACA,SAAA;EACA,IAAA;EACA,UAAA;EACA,SAAA;EACA,MAAA;EACA,UAAA;EACA,SAAA;EACA;AAAA,GACC,mBAAA,GAAmB,oBAAA,CAAA,GAAA,CAAA,OAAA"}