@waveso/ui 0.0.10 → 0.2.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 (289) hide show
  1. package/README.md +1 -1
  2. package/dist/accordion.d.ts +24 -8
  3. package/dist/accordion.d.ts.map +1 -0
  4. package/dist/accordion.js +51 -73
  5. package/dist/accordion.js.map +1 -1
  6. package/dist/action-bar.d.ts +83 -0
  7. package/dist/action-bar.d.ts.map +1 -0
  8. package/dist/action-bar.js +264 -0
  9. package/dist/action-bar.js.map +1 -0
  10. package/dist/alert-dialog.d.ts +56 -21
  11. package/dist/alert-dialog.d.ts.map +1 -0
  12. package/dist/alert-dialog.js +75 -127
  13. package/dist/alert-dialog.js.map +1 -1
  14. package/dist/alert.d.ts +26 -11
  15. package/dist/alert.d.ts.map +1 -0
  16. package/dist/alert.js +37 -68
  17. package/dist/alert.js.map +1 -1
  18. package/dist/animate.d.ts +117 -75
  19. package/dist/animate.d.ts.map +1 -0
  20. package/dist/animate.js +259 -223
  21. package/dist/animate.js.map +1 -1
  22. package/dist/aspect-ratio.d.ts +11 -6
  23. package/dist/aspect-ratio.d.ts.map +1 -0
  24. package/dist/aspect-ratio.js +12 -14
  25. package/dist/aspect-ratio.js.map +1 -1
  26. package/dist/autocomplete.d.ts +91 -25
  27. package/dist/autocomplete.d.ts.map +1 -0
  28. package/dist/autocomplete.js +119 -181
  29. package/dist/autocomplete.js.map +1 -1
  30. package/dist/avatar.d.ts +32 -11
  31. package/dist/avatar.d.ts.map +1 -0
  32. package/dist/avatar.js +42 -89
  33. package/dist/avatar.js.map +1 -1
  34. package/dist/badge.d.ts +15 -8
  35. package/dist/badge.d.ts.map +1 -0
  36. package/dist/badge.js +35 -48
  37. package/dist/badge.js.map +1 -1
  38. package/dist/breadcrumb.d.ts +35 -11
  39. package/dist/breadcrumb.d.ts.map +1 -0
  40. package/dist/breadcrumb.js +61 -110
  41. package/dist/breadcrumb.js.map +1 -1
  42. package/dist/button-group.d.ts +26 -13
  43. package/dist/button-group.d.ts.map +1 -0
  44. package/dist/button-group.js +39 -76
  45. package/dist/button-group.js.map +1 -1
  46. package/dist/button.d.ts +17 -10
  47. package/dist/button.d.ts.map +1 -0
  48. package/dist/button.js +50 -3
  49. package/dist/button.js.map +1 -1
  50. package/dist/card.d.ts +35 -11
  51. package/dist/card.d.ts.map +1 -0
  52. package/dist/card.js +43 -82
  53. package/dist/card.js.map +1 -1
  54. package/dist/checkbox.d.ts +11 -4
  55. package/dist/checkbox.d.ts.map +1 -0
  56. package/dist/checkbox.js +21 -31
  57. package/dist/checkbox.js.map +1 -1
  58. package/dist/collapsible.d.ts +15 -7
  59. package/dist/collapsible.d.ts.map +1 -0
  60. package/dist/collapsible.js +19 -8
  61. package/dist/collapsible.js.map +1 -1
  62. package/dist/combobox.d.ts +83 -23
  63. package/dist/combobox.d.ts.map +1 -0
  64. package/dist/combobox.js +151 -247
  65. package/dist/combobox.js.map +1 -1
  66. package/dist/context-menu.d.ts +87 -26
  67. package/dist/context-menu.d.ts.map +1 -0
  68. package/dist/context-menu.js +115 -165
  69. package/dist/context-menu.js.map +1 -1
  70. package/dist/count.d.ts +45 -31
  71. package/dist/count.d.ts.map +1 -0
  72. package/dist/count.js +170 -165
  73. package/dist/count.js.map +1 -1
  74. package/dist/dialog.d.ts +60 -28
  75. package/dist/dialog.d.ts.map +1 -0
  76. package/dist/dialog.js +77 -120
  77. package/dist/dialog.js.map +1 -1
  78. package/dist/direction.d.ts +2 -1
  79. package/dist/direction.js +3 -3
  80. package/dist/drawer.d.ts +45 -15
  81. package/dist/drawer.d.ts.map +1 -0
  82. package/dist/drawer.js +100 -5
  83. package/dist/drawer.js.map +1 -1
  84. package/dist/encrypted-text.d.ts +25 -12
  85. package/dist/encrypted-text.d.ts.map +1 -0
  86. package/dist/encrypted-text.js +102 -134
  87. package/dist/encrypted-text.js.map +1 -1
  88. package/dist/field.d.ts +40 -21
  89. package/dist/field.d.ts.map +1 -0
  90. package/dist/field.js +52 -3
  91. package/dist/field.js.map +1 -1
  92. package/dist/film-grain-shader.d.ts +6 -0
  93. package/dist/film-grain-shader.d.ts.map +1 -0
  94. package/dist/film-grain-shader.js +88 -0
  95. package/dist/film-grain-shader.js.map +1 -0
  96. package/dist/film-grain-webgl.d.ts +20 -0
  97. package/dist/film-grain-webgl.d.ts.map +1 -0
  98. package/dist/film-grain-webgl.js +306 -0
  99. package/dist/film-grain-webgl.js.map +1 -0
  100. package/dist/film-grain.d.ts +21 -11
  101. package/dist/film-grain.d.ts.map +1 -0
  102. package/dist/film-grain.js +28 -420
  103. package/dist/film-grain.js.map +1 -1
  104. package/dist/form.d.ts +64 -49
  105. package/dist/form.d.ts.map +1 -0
  106. package/dist/form.js +112 -91
  107. package/dist/form.js.map +1 -1
  108. package/dist/gradient-reveal-text.d.ts +35 -22
  109. package/dist/gradient-reveal-text.d.ts.map +1 -0
  110. package/dist/gradient-reveal-text.js +238 -205
  111. package/dist/gradient-reveal-text.js.map +1 -1
  112. package/dist/hooks/use-mobile.d.ts +3 -1
  113. package/dist/hooks/use-mobile.d.ts.map +1 -0
  114. package/dist/hooks/use-mobile.js +28 -2
  115. package/dist/hooks/use-mobile.js.map +1 -1
  116. package/dist/infinite-scroll.d.ts +29 -15
  117. package/dist/infinite-scroll.d.ts.map +1 -0
  118. package/dist/infinite-scroll.js +69 -99
  119. package/dist/infinite-scroll.js.map +1 -1
  120. package/dist/input-group.d.ts +41 -18
  121. package/dist/input-group.d.ts.map +1 -0
  122. package/dist/input-group.js +80 -6
  123. package/dist/input-group.js.map +1 -1
  124. package/dist/input-otp.d.ts +26 -10
  125. package/dist/input-otp.d.ts.map +1 -0
  126. package/dist/input-otp.js +40 -70
  127. package/dist/input-otp.js.map +1 -1
  128. package/dist/input.d.ts +10 -4
  129. package/dist/input.d.ts.map +1 -0
  130. package/dist/input.js +16 -3
  131. package/dist/input.js.map +1 -1
  132. package/dist/item.d.ts +58 -23
  133. package/dist/item.d.ts.map +1 -0
  134. package/dist/item.js +103 -160
  135. package/dist/item.js.map +1 -1
  136. package/dist/kbd.d.ts +12 -4
  137. package/dist/kbd.d.ts.map +1 -0
  138. package/dist/kbd.js +15 -24
  139. package/dist/kbd.js.map +1 -1
  140. package/dist/label.d.ts +9 -4
  141. package/dist/label.d.ts.map +1 -0
  142. package/dist/label.js +12 -16
  143. package/dist/label.js.map +1 -1
  144. package/dist/lib/focus.d.ts +42 -0
  145. package/dist/lib/focus.d.ts.map +1 -0
  146. package/dist/lib/focus.js +21 -0
  147. package/dist/lib/focus.js.map +1 -0
  148. package/dist/lib/internal-icons.d.ts +32 -0
  149. package/dist/lib/internal-icons.d.ts.map +1 -0
  150. package/dist/lib/internal-icons.js +222 -0
  151. package/dist/lib/internal-icons.js.map +1 -0
  152. package/dist/lib/utils.d.ts +4 -2
  153. package/dist/lib/utils.d.ts.map +1 -0
  154. package/dist/lib/utils.js +12 -2
  155. package/dist/lib/utils.js.map +1 -1
  156. package/dist/masonry.d.ts +25 -11
  157. package/dist/masonry.d.ts.map +1 -0
  158. package/dist/masonry.js +188 -229
  159. package/dist/masonry.js.map +1 -1
  160. package/dist/menu.d.ts +84 -26
  161. package/dist/menu.d.ts.map +1 -0
  162. package/dist/menu.js +141 -4
  163. package/dist/menu.js.map +1 -1
  164. package/dist/menubar.d.ts +60 -22
  165. package/dist/menubar.d.ts.map +1 -0
  166. package/dist/menubar.js +80 -52
  167. package/dist/menubar.js.map +1 -1
  168. package/dist/pagination.d.ts +38 -17
  169. package/dist/pagination.d.ts.map +1 -0
  170. package/dist/pagination.js +68 -107
  171. package/dist/pagination.js.map +1 -1
  172. package/dist/popover.d.ts +56 -14
  173. package/dist/popover.d.ts.map +1 -0
  174. package/dist/popover.js +62 -87
  175. package/dist/popover.js.map +1 -1
  176. package/dist/preview-card.d.ts +28 -9
  177. package/dist/preview-card.d.ts.map +1 -0
  178. package/dist/preview-card.js +40 -60
  179. package/dist/preview-card.js.map +1 -1
  180. package/dist/progress.d.ts +28 -9
  181. package/dist/progress.d.ts.map +1 -0
  182. package/dist/progress.js +35 -60
  183. package/dist/progress.js.map +1 -1
  184. package/dist/radio-group.d.ts +14 -8
  185. package/dist/radio-group.d.ts.map +1 -0
  186. package/dist/radio-group.js +18 -22
  187. package/dist/radio-group.js.map +1 -1
  188. package/dist/radio.d.ts +14 -6
  189. package/dist/radio.d.ts.map +1 -0
  190. package/dist/radio.js +24 -3
  191. package/dist/radio.js.map +1 -1
  192. package/dist/scroll-area.d.ts +16 -6
  193. package/dist/scroll-area.d.ts.map +1 -0
  194. package/dist/scroll-area.js +34 -55
  195. package/dist/scroll-area.js.map +1 -1
  196. package/dist/select.d.ts +66 -18
  197. package/dist/select.d.ts.map +1 -0
  198. package/dist/select.js +109 -185
  199. package/dist/select.js.map +1 -1
  200. package/dist/separator.d.ts +11 -5
  201. package/dist/separator.d.ts.map +1 -0
  202. package/dist/separator.js +17 -3
  203. package/dist/separator.js.map +1 -1
  204. package/dist/sidebar.d.ts +172 -79
  205. package/dist/sidebar.d.ts.map +1 -0
  206. package/dist/sidebar.js +362 -585
  207. package/dist/sidebar.js.map +1 -1
  208. package/dist/skeleton.d.ts +8 -3
  209. package/dist/skeleton.d.ts.map +1 -0
  210. package/dist/skeleton.js +13 -3
  211. package/dist/skeleton.js.map +1 -1
  212. package/dist/slider.d.ts +16 -6
  213. package/dist/slider.d.ts.map +1 -0
  214. package/dist/slider.js +40 -67
  215. package/dist/slider.js.map +1 -1
  216. package/dist/spinner.d.ts +8 -3
  217. package/dist/spinner.d.ts.map +1 -0
  218. package/dist/spinner.js +15 -4
  219. package/dist/spinner.js.map +1 -1
  220. package/dist/switch.d.ts +12 -6
  221. package/dist/switch.d.ts.map +1 -0
  222. package/dist/switch.js +18 -25
  223. package/dist/switch.js.map +1 -1
  224. package/dist/table.d.ts +37 -11
  225. package/dist/table.d.ts.map +1 -0
  226. package/dist/table.js +51 -88
  227. package/dist/table.js.map +1 -1
  228. package/dist/tabs.d.ts +28 -12
  229. package/dist/tabs.d.ts.map +1 -0
  230. package/dist/tabs.js +40 -74
  231. package/dist/tabs.js.map +1 -1
  232. package/dist/textarea.d.ts +13 -6
  233. package/dist/textarea.d.ts.map +1 -0
  234. package/dist/textarea.js +19 -3
  235. package/dist/textarea.js.map +1 -1
  236. package/dist/toast.d.ts +63 -39
  237. package/dist/toast.d.ts.map +1 -0
  238. package/dist/toast.js +177 -215
  239. package/dist/toast.js.map +1 -1
  240. package/dist/toggle-group.d.ts +26 -12
  241. package/dist/toggle-group.d.ts.map +1 -0
  242. package/dist/toggle-group.js +49 -73
  243. package/dist/toggle-group.js.map +1 -1
  244. package/dist/toggle.d.ts +17 -10
  245. package/dist/toggle.d.ts.map +1 -0
  246. package/dist/toggle.js +38 -3
  247. package/dist/toggle.js.map +1 -1
  248. package/dist/tooltip.d.ts +45 -14
  249. package/dist/tooltip.d.ts.map +1 -0
  250. package/dist/tooltip.js +63 -3
  251. package/dist/tooltip.js.map +1 -1
  252. package/dist/typewriter.d.ts +44 -31
  253. package/dist/typewriter.d.ts.map +1 -0
  254. package/dist/typewriter.js +185 -185
  255. package/dist/typewriter.js.map +1 -1
  256. package/package.json +6 -6
  257. package/dist/chunk-45VQAWIM.js +0 -228
  258. package/dist/chunk-45VQAWIM.js.map +0 -1
  259. package/dist/chunk-6Y7LPQMO.js +0 -11
  260. package/dist/chunk-6Y7LPQMO.js.map +0 -1
  261. package/dist/chunk-76UQO56T.js +0 -19
  262. package/dist/chunk-76UQO56T.js.map +0 -1
  263. package/dist/chunk-7F4MPMLJ.js +0 -17
  264. package/dist/chunk-7F4MPMLJ.js.map +0 -1
  265. package/dist/chunk-BKTJYX4M.js +0 -143
  266. package/dist/chunk-BKTJYX4M.js.map +0 -1
  267. package/dist/chunk-D5XPEJ6T.js +0 -36
  268. package/dist/chunk-D5XPEJ6T.js.map +0 -1
  269. package/dist/chunk-DIGOLJIR.js +0 -105
  270. package/dist/chunk-DIGOLJIR.js.map +0 -1
  271. package/dist/chunk-IQ7YQ5XA.js +0 -141
  272. package/dist/chunk-IQ7YQ5XA.js.map +0 -1
  273. package/dist/chunk-NCHHHWTB.js +0 -85
  274. package/dist/chunk-NCHHHWTB.js.map +0 -1
  275. package/dist/chunk-OUFYQLVN.js +0 -56
  276. package/dist/chunk-OUFYQLVN.js.map +0 -1
  277. package/dist/chunk-QFSEK4M6.js +0 -22
  278. package/dist/chunk-QFSEK4M6.js.map +0 -1
  279. package/dist/chunk-QRW37LRP.js +0 -25
  280. package/dist/chunk-QRW37LRP.js.map +0 -1
  281. package/dist/chunk-RPQHL6C5.js +0 -26
  282. package/dist/chunk-RPQHL6C5.js.map +0 -1
  283. package/dist/chunk-V4ZX4YCP.js +0 -66
  284. package/dist/chunk-V4ZX4YCP.js.map +0 -1
  285. package/dist/chunk-YTSQQTSF.js +0 -44
  286. package/dist/chunk-YTSQQTSF.js.map +0 -1
  287. package/dist/chunk-ZZZH3JGW.js +0 -23
  288. package/dist/chunk-ZZZH3JGW.js.map +0 -1
  289. package/dist/direction.js.map +0 -1
@@ -1,198 +1,198 @@
1
- import { useRef, useId, useState, useEffect, isValidElement, cloneElement } from 'react';
2
- import { useInView } from 'motion/react';
3
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
-
1
+ "use client";
2
+ import { cloneElement, isValidElement, useEffect, useId, useRef, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useInView } from "motion/react";
5
+ //#region src/typewriter.tsx
5
6
  function mergeRef(internalRef, externalRef) {
6
- return (el) => {
7
- internalRef.current = el;
8
- if (typeof externalRef === "function") externalRef(el);
9
- else if (externalRef && typeof externalRef === "object") {
10
- externalRef.current = el;
11
- }
12
- };
7
+ return (el) => {
8
+ internalRef.current = el;
9
+ if (typeof externalRef === "function") externalRef(el);
10
+ else if (externalRef && typeof externalRef === "object") externalRef.current = el;
11
+ };
13
12
  }
13
+ /** Flatten text prop into a single string for character counting */
14
14
  function flattenText(text) {
15
- if (typeof text === "string") return text;
16
- return text.map((s) => s.text).join("");
15
+ if (typeof text === "string") return text;
16
+ return text.map((s) => s.text).join("");
17
17
  }
18
+ /** Build rendered content from segments up to a character index */
18
19
  function buildSegmentContent(segments, charIndex) {
19
- const nodes = [];
20
- let remaining = charIndex;
21
- for (let i = 0; i < segments.length; i++) {
22
- const seg = segments[i];
23
- if (remaining <= 0) break;
24
- const visibleChars = seg.text.slice(0, remaining);
25
- remaining -= visibleChars.length;
26
- if (seg.className) {
27
- nodes.push(
28
- /* @__PURE__ */ jsx("span", { className: seg.className, children: visibleChars }, i)
29
- );
30
- } else {
31
- nodes.push(visibleChars);
32
- }
33
- }
34
- return nodes;
20
+ const nodes = [];
21
+ let remaining = charIndex;
22
+ for (let i = 0; i < segments.length; i++) {
23
+ const seg = segments[i];
24
+ if (remaining <= 0) break;
25
+ const visibleChars = seg.text.slice(0, remaining);
26
+ remaining -= visibleChars.length;
27
+ if (seg.className) nodes.push(/* @__PURE__ */ jsx("span", {
28
+ className: seg.className,
29
+ children: visibleChars
30
+ }, i));
31
+ else nodes.push(visibleChars);
32
+ }
33
+ return nodes;
35
34
  }
36
35
  function BlinkingCursor({ char, id }) {
37
- const name = `tw-blink-${id}`;
38
- return /* @__PURE__ */ jsxs(Fragment, { children: [
39
- /* @__PURE__ */ jsx(
40
- "span",
41
- {
42
- "aria-hidden": true,
43
- style: {
44
- animation: `${name} 0.8s step-end infinite`,
45
- fontWeight: "normal"
46
- },
47
- children: char
48
- }
49
- ),
50
- /* @__PURE__ */ jsx("style", { children: `@keyframes ${name} { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }` })
51
- ] });
36
+ const name = `tw-blink-${id}`;
37
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
38
+ "aria-hidden": true,
39
+ style: {
40
+ animation: `${name} 0.8s step-end infinite`,
41
+ fontWeight: "normal"
42
+ },
43
+ children: char
44
+ }), /* @__PURE__ */ jsx("style", { children: `@keyframes ${name} { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }` })] });
52
45
  }
53
- function Typewriter({
54
- text,
55
- children,
56
- speed = 0.04,
57
- delay = 0,
58
- cursor = true,
59
- cursorChar = "|",
60
- onView = false,
61
- once = true,
62
- variant = "typing",
63
- smoothDuration = 2
64
- }) {
65
- const ref = useRef(null);
66
- const id = useId().replace(/:/g, "");
67
- const isInView = useInView(ref, { once, margin: "-50px" });
68
- const shouldAnimate = onView ? isInView : true;
69
- if (variant === "smooth") {
70
- return /* @__PURE__ */ jsx(
71
- SmoothReveal,
72
- {
73
- text,
74
- elementRef: ref,
75
- id,
76
- delay,
77
- duration: smoothDuration,
78
- cursor,
79
- cursorChar,
80
- shouldAnimate,
81
- children
82
- }
83
- );
84
- }
85
- return /* @__PURE__ */ jsx(
86
- TypingReveal,
87
- {
88
- text,
89
- elementRef: ref,
90
- id,
91
- speed,
92
- delay,
93
- cursor,
94
- cursorChar,
95
- shouldAnimate,
96
- children
97
- }
98
- );
46
+ /**
47
+ * Text reveal animation with two modes:
48
+ *
49
+ * **typing** (default) — character-by-character like someone typing.
50
+ * **smooth** — cinematic sliding mask reveal.
51
+ *
52
+ * Supports plain strings or styled segments for per-word coloring.
53
+ * Zero wrapper divs — renders into the child element via cloneElement.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * // Simple string
58
+ * <Typewriter text="Welcome to the future.">
59
+ * <h1 className="text-4xl font-bold" />
60
+ * </Typewriter>
61
+ *
62
+ * // Per-word styling
63
+ * <Typewriter text={[
64
+ * { text: "Build on " },
65
+ * { text: "Intuition", className: "text-primary font-bold" },
66
+ * ]}>
67
+ * <h1 className="text-4xl" />
68
+ * </Typewriter>
69
+ *
70
+ * // Smooth reveal
71
+ * <Typewriter text="Cinematic reveal." variant="smooth" smoothDuration={1.5}>
72
+ * <h1 className="text-4xl font-bold" />
73
+ * </Typewriter>
74
+ * ```
75
+ */
76
+ function Typewriter({ text, children, speed = .04, delay = 0, cursor = true, cursorChar = "|", onView = false, once = true, variant = "typing", smoothDuration = 2 }) {
77
+ const ref = useRef(null);
78
+ const id = useId().replace(/:/g, "");
79
+ const isInView = useInView(ref, {
80
+ once,
81
+ margin: "-50px"
82
+ });
83
+ const shouldAnimate = onView ? isInView : true;
84
+ if (variant === "smooth") return /* @__PURE__ */ jsx(SmoothReveal, {
85
+ text,
86
+ elementRef: ref,
87
+ id,
88
+ delay,
89
+ duration: smoothDuration,
90
+ cursor,
91
+ cursorChar,
92
+ shouldAnimate,
93
+ children
94
+ });
95
+ return /* @__PURE__ */ jsx(TypingReveal, {
96
+ text,
97
+ elementRef: ref,
98
+ id,
99
+ speed,
100
+ delay,
101
+ cursor,
102
+ cursorChar,
103
+ shouldAnimate,
104
+ children
105
+ });
99
106
  }
100
- function TypingReveal({
101
- text,
102
- children,
103
- elementRef,
104
- id,
105
- speed,
106
- delay,
107
- cursor,
108
- cursorChar,
109
- shouldAnimate
110
- }) {
111
- const flat = flattenText(text);
112
- const segments = typeof text === "string" ? [{ text }] : text;
113
- const [charIndex, setCharIndex] = useState(0);
114
- const [started, setStarted] = useState(false);
115
- const [done, setDone] = useState(false);
116
- useEffect(() => {
117
- if (!shouldAnimate || started) return;
118
- const timer = setTimeout(() => setStarted(true), delay * 1e3);
119
- return () => clearTimeout(timer);
120
- }, [shouldAnimate, delay, started]);
121
- useEffect(() => {
122
- if (!started || done) return;
123
- if (charIndex >= flat.length) {
124
- setDone(true);
125
- return;
126
- }
127
- const timer = setTimeout(() => setCharIndex((prev) => prev + 1), speed * 1e3);
128
- return () => clearTimeout(timer);
129
- }, [started, charIndex, flat.length, speed, done]);
130
- if (!isValidElement(children)) return children;
131
- const childProps = children.props;
132
- const existingRef = childProps.ref;
133
- const content = started ? buildSegmentContent(segments, charIndex) : null;
134
- const showCursor = cursor && started && !done;
135
- return cloneElement(children, {
136
- ref: mergeRef(elementRef, existingRef),
137
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
138
- content,
139
- showCursor && /* @__PURE__ */ jsx(BlinkingCursor, { char: cursorChar, id })
140
- ] })
141
- });
107
+ function TypingReveal({ text, children, elementRef, id, speed, delay, cursor, cursorChar, shouldAnimate }) {
108
+ const flat = flattenText(text);
109
+ const segments = typeof text === "string" ? [{ text }] : text;
110
+ const [charIndex, setCharIndex] = useState(0);
111
+ const [started, setStarted] = useState(false);
112
+ const [done, setDone] = useState(false);
113
+ useEffect(() => {
114
+ if (!shouldAnimate || started) return;
115
+ const timer = setTimeout(() => setStarted(true), delay * 1e3);
116
+ return () => clearTimeout(timer);
117
+ }, [
118
+ shouldAnimate,
119
+ delay,
120
+ started
121
+ ]);
122
+ useEffect(() => {
123
+ if (!started || done) return;
124
+ if (charIndex >= flat.length) {
125
+ setDone(true);
126
+ return;
127
+ }
128
+ const timer = setTimeout(() => setCharIndex((prev) => prev + 1), speed * 1e3);
129
+ return () => clearTimeout(timer);
130
+ }, [
131
+ started,
132
+ charIndex,
133
+ flat.length,
134
+ speed,
135
+ done
136
+ ]);
137
+ if (!isValidElement(children)) return children;
138
+ const existingRef = children.props.ref;
139
+ const content = started ? buildSegmentContent(segments, charIndex) : null;
140
+ const showCursor = cursor && started && !done;
141
+ return cloneElement(children, {
142
+ ref: mergeRef(elementRef, existingRef),
143
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [content, showCursor && /* @__PURE__ */ jsx(BlinkingCursor, {
144
+ char: cursorChar,
145
+ id
146
+ })] })
147
+ });
142
148
  }
143
- function SmoothReveal({
144
- text,
145
- children,
146
- elementRef,
147
- id,
148
- delay,
149
- duration,
150
- cursor,
151
- cursorChar,
152
- shouldAnimate
153
- }) {
154
- const segments = typeof text === "string" ? [{ text }] : text;
155
- const [revealed, setRevealed] = useState(false);
156
- const [done, setDone] = useState(false);
157
- useEffect(() => {
158
- if (!shouldAnimate || revealed) return;
159
- const timer = setTimeout(() => setRevealed(true), delay * 1e3);
160
- return () => clearTimeout(timer);
161
- }, [shouldAnimate, delay, revealed]);
162
- useEffect(() => {
163
- if (!revealed) return;
164
- const timer = setTimeout(() => setDone(true), duration * 1e3);
165
- return () => clearTimeout(timer);
166
- }, [revealed, duration]);
167
- if (!isValidElement(children)) return children;
168
- const childProps = children.props;
169
- const existingRef = childProps.ref;
170
- const existingStyle = childProps.style ?? {};
171
- const animName = `tw-smooth-${id}`;
172
- const showCursor = cursor && revealed && !done;
173
- const fullContent = segments.map(
174
- (seg, i) => seg.className ? /* @__PURE__ */ jsx("span", { className: seg.className, children: seg.text }, i) : seg.text
175
- );
176
- return cloneElement(children, {
177
- ref: mergeRef(elementRef, existingRef),
178
- style: {
179
- ...existingStyle,
180
- whiteSpace: "nowrap",
181
- overflow: "hidden",
182
- ...revealed ? {
183
- animation: `${animName} ${duration}s linear forwards`
184
- } : {
185
- maxWidth: 0
186
- }
187
- },
188
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
189
- /* @__PURE__ */ jsx("style", { children: `@keyframes ${animName} { from { max-width: 0; } to { max-width: 100%; } }` }),
190
- fullContent,
191
- showCursor && /* @__PURE__ */ jsx(BlinkingCursor, { char: cursorChar, id })
192
- ] })
193
- });
149
+ function SmoothReveal({ text, children, elementRef, id, delay, duration, cursor, cursorChar, shouldAnimate }) {
150
+ const segments = typeof text === "string" ? [{ text }] : text;
151
+ const [revealed, setRevealed] = useState(false);
152
+ const [done, setDone] = useState(false);
153
+ useEffect(() => {
154
+ if (!shouldAnimate || revealed) return;
155
+ const timer = setTimeout(() => setRevealed(true), delay * 1e3);
156
+ return () => clearTimeout(timer);
157
+ }, [
158
+ shouldAnimate,
159
+ delay,
160
+ revealed
161
+ ]);
162
+ useEffect(() => {
163
+ if (!revealed) return;
164
+ const timer = setTimeout(() => setDone(true), duration * 1e3);
165
+ return () => clearTimeout(timer);
166
+ }, [revealed, duration]);
167
+ if (!isValidElement(children)) return children;
168
+ const childProps = children.props;
169
+ const existingRef = childProps.ref;
170
+ const existingStyle = childProps.style ?? {};
171
+ const animName = `tw-smooth-${id}`;
172
+ const showCursor = cursor && revealed && !done;
173
+ const fullContent = segments.map((seg, i) => seg.className ? /* @__PURE__ */ jsx("span", {
174
+ className: seg.className,
175
+ children: seg.text
176
+ }, i) : seg.text);
177
+ return cloneElement(children, {
178
+ ref: mergeRef(elementRef, existingRef),
179
+ style: {
180
+ ...existingStyle,
181
+ whiteSpace: "nowrap",
182
+ overflow: "hidden",
183
+ ...revealed ? { animation: `${animName} ${duration}s linear forwards` } : { maxWidth: 0 }
184
+ },
185
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
186
+ /* @__PURE__ */ jsx("style", { children: `@keyframes ${animName} { from { max-width: 0; } to { max-width: 100%; } }` }),
187
+ fullContent,
188
+ showCursor && /* @__PURE__ */ jsx(BlinkingCursor, {
189
+ char: cursorChar,
190
+ id
191
+ })
192
+ ] })
193
+ });
194
194
  }
195
-
195
+ //#endregion
196
196
  export { Typewriter };
197
- //# sourceMappingURL=typewriter.js.map
197
+
198
198
  //# sourceMappingURL=typewriter.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/typewriter.tsx"],"names":[],"mappings":"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","file":"typewriter.js","sourcesContent":["\"use client\"\n\nimport {\n type CSSProperties,\n type ReactElement,\n type Ref,\n cloneElement,\n isValidElement,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\"\nimport { useInView } from \"motion/react\"\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface TextSegment {\n text: string\n /** Optional className applied to this segment (e.g., bold, colored) */\n className?: string\n}\n\ninterface TypewriterProps {\n /** Simple string or styled segments for per-word/phrase styling */\n text: string | TextSegment[]\n /** Element to render into. Receives the animated text as children. */\n children: ReactElement\n /** Time per character in seconds. Default: 0.04 */\n speed?: number\n /** Delay before typing starts in seconds. Default: 0 */\n delay?: number\n /** Show a blinking cursor during typing. Default: true */\n cursor?: boolean\n /** Cursor character. Default: '|' */\n cursorChar?: string\n /** Trigger when scrolled into view instead of on mount. Default: false */\n onView?: boolean\n /** Trigger once. Default: true */\n once?: boolean\n /**\n * Reveal mode. Default: 'typing'\n * - 'typing': character-by-character like someone typing\n * - 'smooth': sliding mask reveal (cinematic feel)\n */\n variant?: \"typing\" | \"smooth\"\n /** Duration of the smooth reveal in seconds. Default: 2 */\n smoothDuration?: number\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction mergeRef(\n internalRef: React.RefObject<HTMLElement | null>,\n externalRef?: Ref<HTMLElement>,\n) {\n return (el: HTMLElement | null) => {\n ;(internalRef as { current: HTMLElement | null }).current = el\n if (typeof externalRef === \"function\") externalRef(el)\n else if (externalRef && typeof externalRef === \"object\") {\n ;(externalRef as { current: HTMLElement | null }).current = el\n }\n }\n}\n\n/** Flatten text prop into a single string for character counting */\nfunction flattenText(text: string | TextSegment[]): string {\n if (typeof text === \"string\") return text\n return text.map((s) => s.text).join(\"\")\n}\n\n/** Build rendered content from segments up to a character index */\nfunction buildSegmentContent(\n segments: TextSegment[],\n charIndex: number,\n): React.ReactNode[] {\n const nodes: React.ReactNode[] = []\n let remaining = charIndex\n\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]!\n if (remaining <= 0) break\n\n const visibleChars = seg.text.slice(0, remaining)\n remaining -= visibleChars.length\n\n if (seg.className) {\n nodes.push(\n <span key={i} className={seg.className}>\n {visibleChars}\n </span>,\n )\n } else {\n nodes.push(visibleChars)\n }\n }\n\n return nodes\n}\n\n// ── Cursor ───────────────────────────────────────────────────────────\n\nfunction BlinkingCursor({ char, id }: { char: string; id: string }) {\n const name = `tw-blink-${id}`\n return (\n <>\n <span\n aria-hidden\n style={{\n animation: `${name} 0.8s step-end infinite`,\n fontWeight: \"normal\",\n }}\n >\n {char}\n </span>\n <style>{`@keyframes ${name} { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }`}</style>\n </>\n )\n}\n\n// ── Typewriter ───────────────────────────────────────────────────────\n\n/**\n * Text reveal animation with two modes:\n *\n * **typing** (default) — character-by-character like someone typing.\n * **smooth** — cinematic sliding mask reveal.\n *\n * Supports plain strings or styled segments for per-word coloring.\n * Zero wrapper divs — renders into the child element via cloneElement.\n *\n * @example\n * ```tsx\n * // Simple string\n * <Typewriter text=\"Welcome to the future.\">\n * <h1 className=\"text-4xl font-bold\" />\n * </Typewriter>\n *\n * // Per-word styling\n * <Typewriter text={[\n * { text: \"Build on \" },\n * { text: \"Intuition\", className: \"text-primary font-bold\" },\n * ]}>\n * <h1 className=\"text-4xl\" />\n * </Typewriter>\n *\n * // Smooth reveal\n * <Typewriter text=\"Cinematic reveal.\" variant=\"smooth\" smoothDuration={1.5}>\n * <h1 className=\"text-4xl font-bold\" />\n * </Typewriter>\n * ```\n */\nfunction Typewriter({\n text,\n children,\n speed = 0.04,\n delay = 0,\n cursor = true,\n cursorChar = \"|\",\n onView = false,\n once = true,\n variant = \"typing\",\n smoothDuration = 2,\n}: TypewriterProps) {\n const ref = useRef<HTMLElement>(null)\n const id = useId().replace(/:/g, \"\")\n const isInView = useInView(ref, { once, margin: \"-50px\" })\n\n const shouldAnimate = onView ? isInView : true\n\n if (variant === \"smooth\") {\n return (\n <SmoothReveal\n text={text}\n elementRef={ref}\n id={id}\n delay={delay}\n duration={smoothDuration}\n cursor={cursor}\n cursorChar={cursorChar}\n shouldAnimate={shouldAnimate}\n >\n {children}\n </SmoothReveal>\n )\n }\n\n return (\n <TypingReveal\n text={text}\n elementRef={ref}\n id={id}\n speed={speed}\n delay={delay}\n cursor={cursor}\n cursorChar={cursorChar}\n shouldAnimate={shouldAnimate}\n >\n {children}\n </TypingReveal>\n )\n}\n\n// ── Typing Reveal ────────────────────────────────────────────────────\n\nfunction TypingReveal({\n text,\n children,\n elementRef,\n id,\n speed,\n delay,\n cursor,\n cursorChar,\n shouldAnimate,\n}: {\n text: string | TextSegment[]\n children: ReactElement\n elementRef: React.RefObject<HTMLElement | null>\n id: string\n speed: number\n delay: number\n cursor: boolean\n cursorChar: string\n shouldAnimate: boolean\n}) {\n const flat = flattenText(text)\n const segments = typeof text === \"string\" ? [{ text }] : text\n const [charIndex, setCharIndex] = useState(0)\n const [started, setStarted] = useState(false)\n const [done, setDone] = useState(false)\n\n useEffect(() => {\n if (!shouldAnimate || started) return\n const timer = setTimeout(() => setStarted(true), delay * 1000)\n return () => clearTimeout(timer)\n }, [shouldAnimate, delay, started])\n\n useEffect(() => {\n if (!started || done) return\n if (charIndex >= flat.length) {\n setDone(true)\n return\n }\n const timer = setTimeout(() => setCharIndex((prev) => prev + 1), speed * 1000)\n return () => clearTimeout(timer)\n }, [started, charIndex, flat.length, speed, done])\n\n if (!isValidElement(children)) return children\n\n const childProps = children.props as Record<string, unknown>\n const existingRef = (childProps as { ref?: Ref<HTMLElement> }).ref\n\n const content = started ? buildSegmentContent(segments, charIndex) : null\n const showCursor = cursor && started && !done\n\n return cloneElement(children, {\n ref: mergeRef(elementRef, existingRef),\n children: (\n <>\n {content}\n {showCursor && <BlinkingCursor char={cursorChar} id={id} />}\n </>\n ),\n } as Record<string, unknown>)\n}\n\n// ── Smooth Reveal ────────────────────────────────────────────────────\n\nfunction SmoothReveal({\n text,\n children,\n elementRef,\n id,\n delay,\n duration,\n cursor,\n cursorChar,\n shouldAnimate,\n}: {\n text: string | TextSegment[]\n children: ReactElement\n elementRef: React.RefObject<HTMLElement | null>\n id: string\n delay: number\n duration: number\n cursor: boolean\n cursorChar: string\n shouldAnimate: boolean\n}) {\n const segments = typeof text === \"string\" ? [{ text }] : text\n const [revealed, setRevealed] = useState(false)\n const [done, setDone] = useState(false)\n\n useEffect(() => {\n if (!shouldAnimate || revealed) return\n const timer = setTimeout(() => setRevealed(true), delay * 1000)\n return () => clearTimeout(timer)\n }, [shouldAnimate, delay, revealed])\n\n useEffect(() => {\n if (!revealed) return\n const timer = setTimeout(() => setDone(true), duration * 1000)\n return () => clearTimeout(timer)\n }, [revealed, duration])\n\n if (!isValidElement(children)) return children\n\n const childProps = children.props as Record<string, unknown>\n const existingRef = (childProps as { ref?: Ref<HTMLElement> }).ref\n const existingStyle = (childProps.style ?? {}) as CSSProperties\n\n const animName = `tw-smooth-${id}`\n const showCursor = cursor && revealed && !done\n\n // Full text is always in the DOM — the mask clip animates to reveal it\n const fullContent = segments.map((seg, i) =>\n seg.className ? (\n <span key={i} className={seg.className}>\n {seg.text}\n </span>\n ) : (\n seg.text\n ),\n )\n\n return cloneElement(children, {\n ref: mergeRef(elementRef, existingRef),\n style: {\n ...existingStyle,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n ...(revealed\n ? {\n animation: `${animName} ${duration}s linear forwards`,\n }\n : {\n maxWidth: 0,\n }),\n },\n children: (\n <>\n <style>{`@keyframes ${animName} { from { max-width: 0; } to { max-width: 100%; } }`}</style>\n {fullContent}\n {showCursor && <BlinkingCursor char={cursorChar} id={id} />}\n </>\n ),\n } as Record<string, unknown>)\n}\n\n// ── Exports ──────────────────────────────────────────────────────────\n\nexport { Typewriter }\nexport type { TypewriterProps, TextSegment }\n"]}
1
+ {"version":3,"file":"typewriter.js","names":[],"sources":["../src/typewriter.tsx"],"sourcesContent":["\"use client\"\n\nimport {\n type CSSProperties,\n type ReactElement,\n type Ref,\n cloneElement,\n isValidElement,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\"\nimport { useInView } from \"motion/react\"\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface TextSegment {\n text: string\n /** Optional className applied to this segment (e.g., bold, colored) */\n className?: string\n}\n\ninterface TypewriterProps {\n /** Simple string or styled segments for per-word/phrase styling */\n text: string | TextSegment[]\n /** Element to render into. Receives the animated text as children. */\n children: ReactElement\n /** Time per character in seconds. Default: 0.04 */\n speed?: number\n /** Delay before typing starts in seconds. Default: 0 */\n delay?: number\n /** Show a blinking cursor during typing. Default: true */\n cursor?: boolean\n /** Cursor character. Default: '|' */\n cursorChar?: string\n /** Trigger when scrolled into view instead of on mount. Default: false */\n onView?: boolean\n /** Trigger once. Default: true */\n once?: boolean\n /**\n * Reveal mode. Default: 'typing'\n * - 'typing': character-by-character like someone typing\n * - 'smooth': sliding mask reveal (cinematic feel)\n */\n variant?: \"typing\" | \"smooth\"\n /** Duration of the smooth reveal in seconds. Default: 2 */\n smoothDuration?: number\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction mergeRef(\n internalRef: React.RefObject<HTMLElement | null>,\n externalRef?: Ref<HTMLElement>,\n) {\n return (el: HTMLElement | null) => {\n ;(internalRef as { current: HTMLElement | null }).current = el\n if (typeof externalRef === \"function\") externalRef(el)\n else if (externalRef && typeof externalRef === \"object\") {\n ;(externalRef as { current: HTMLElement | null }).current = el\n }\n }\n}\n\n/** Flatten text prop into a single string for character counting */\nfunction flattenText(text: string | TextSegment[]): string {\n if (typeof text === \"string\") return text\n return text.map((s) => s.text).join(\"\")\n}\n\n/** Build rendered content from segments up to a character index */\nfunction buildSegmentContent(\n segments: TextSegment[],\n charIndex: number,\n): React.ReactNode[] {\n const nodes: React.ReactNode[] = []\n let remaining = charIndex\n\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]!\n if (remaining <= 0) break\n\n const visibleChars = seg.text.slice(0, remaining)\n remaining -= visibleChars.length\n\n if (seg.className) {\n nodes.push(\n <span key={i} className={seg.className}>\n {visibleChars}\n </span>,\n )\n } else {\n nodes.push(visibleChars)\n }\n }\n\n return nodes\n}\n\n// ── Cursor ───────────────────────────────────────────────────────────\n\nfunction BlinkingCursor({ char, id }: { char: string; id: string }) {\n const name = `tw-blink-${id}`\n return (\n <>\n <span\n aria-hidden\n style={{\n animation: `${name} 0.8s step-end infinite`,\n fontWeight: \"normal\",\n }}\n >\n {char}\n </span>\n <style>{`@keyframes ${name} { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }`}</style>\n </>\n )\n}\n\n// ── Typewriter ───────────────────────────────────────────────────────\n\n/**\n * Text reveal animation with two modes:\n *\n * **typing** (default) — character-by-character like someone typing.\n * **smooth** — cinematic sliding mask reveal.\n *\n * Supports plain strings or styled segments for per-word coloring.\n * Zero wrapper divs — renders into the child element via cloneElement.\n *\n * @example\n * ```tsx\n * // Simple string\n * <Typewriter text=\"Welcome to the future.\">\n * <h1 className=\"text-4xl font-bold\" />\n * </Typewriter>\n *\n * // Per-word styling\n * <Typewriter text={[\n * { text: \"Build on \" },\n * { text: \"Intuition\", className: \"text-primary font-bold\" },\n * ]}>\n * <h1 className=\"text-4xl\" />\n * </Typewriter>\n *\n * // Smooth reveal\n * <Typewriter text=\"Cinematic reveal.\" variant=\"smooth\" smoothDuration={1.5}>\n * <h1 className=\"text-4xl font-bold\" />\n * </Typewriter>\n * ```\n */\nfunction Typewriter({\n text,\n children,\n speed = 0.04,\n delay = 0,\n cursor = true,\n cursorChar = \"|\",\n onView = false,\n once = true,\n variant = \"typing\",\n smoothDuration = 2,\n}: TypewriterProps) {\n const ref = useRef<HTMLElement>(null)\n const id = useId().replace(/:/g, \"\")\n const isInView = useInView(ref, { once, margin: \"-50px\" })\n\n const shouldAnimate = onView ? isInView : true\n\n if (variant === \"smooth\") {\n return (\n <SmoothReveal\n text={text}\n elementRef={ref}\n id={id}\n delay={delay}\n duration={smoothDuration}\n cursor={cursor}\n cursorChar={cursorChar}\n shouldAnimate={shouldAnimate}\n >\n {children}\n </SmoothReveal>\n )\n }\n\n return (\n <TypingReveal\n text={text}\n elementRef={ref}\n id={id}\n speed={speed}\n delay={delay}\n cursor={cursor}\n cursorChar={cursorChar}\n shouldAnimate={shouldAnimate}\n >\n {children}\n </TypingReveal>\n )\n}\n\n// ── Typing Reveal ────────────────────────────────────────────────────\n\nfunction TypingReveal({\n text,\n children,\n elementRef,\n id,\n speed,\n delay,\n cursor,\n cursorChar,\n shouldAnimate,\n}: {\n text: string | TextSegment[]\n children: ReactElement\n elementRef: React.RefObject<HTMLElement | null>\n id: string\n speed: number\n delay: number\n cursor: boolean\n cursorChar: string\n shouldAnimate: boolean\n}) {\n const flat = flattenText(text)\n const segments = typeof text === \"string\" ? [{ text }] : text\n const [charIndex, setCharIndex] = useState(0)\n const [started, setStarted] = useState(false)\n const [done, setDone] = useState(false)\n\n useEffect(() => {\n if (!shouldAnimate || started) return\n const timer = setTimeout(() => setStarted(true), delay * 1000)\n return () => clearTimeout(timer)\n }, [shouldAnimate, delay, started])\n\n useEffect(() => {\n if (!started || done) return\n if (charIndex >= flat.length) {\n setDone(true)\n return\n }\n const timer = setTimeout(() => setCharIndex((prev) => prev + 1), speed * 1000)\n return () => clearTimeout(timer)\n }, [started, charIndex, flat.length, speed, done])\n\n if (!isValidElement(children)) return children\n\n const childProps = children.props as Record<string, unknown>\n const existingRef = (childProps as { ref?: Ref<HTMLElement> }).ref\n\n const content = started ? buildSegmentContent(segments, charIndex) : null\n const showCursor = cursor && started && !done\n\n return cloneElement(children, {\n ref: mergeRef(elementRef, existingRef),\n children: (\n <>\n {content}\n {showCursor && <BlinkingCursor char={cursorChar} id={id} />}\n </>\n ),\n } as Record<string, unknown>)\n}\n\n// ── Smooth Reveal ────────────────────────────────────────────────────\n\nfunction SmoothReveal({\n text,\n children,\n elementRef,\n id,\n delay,\n duration,\n cursor,\n cursorChar,\n shouldAnimate,\n}: {\n text: string | TextSegment[]\n children: ReactElement\n elementRef: React.RefObject<HTMLElement | null>\n id: string\n delay: number\n duration: number\n cursor: boolean\n cursorChar: string\n shouldAnimate: boolean\n}) {\n const segments = typeof text === \"string\" ? [{ text }] : text\n const [revealed, setRevealed] = useState(false)\n const [done, setDone] = useState(false)\n\n useEffect(() => {\n if (!shouldAnimate || revealed) return\n const timer = setTimeout(() => setRevealed(true), delay * 1000)\n return () => clearTimeout(timer)\n }, [shouldAnimate, delay, revealed])\n\n useEffect(() => {\n if (!revealed) return\n const timer = setTimeout(() => setDone(true), duration * 1000)\n return () => clearTimeout(timer)\n }, [revealed, duration])\n\n if (!isValidElement(children)) return children\n\n const childProps = children.props as Record<string, unknown>\n const existingRef = (childProps as { ref?: Ref<HTMLElement> }).ref\n const existingStyle = (childProps.style ?? {}) as CSSProperties\n\n const animName = `tw-smooth-${id}`\n const showCursor = cursor && revealed && !done\n\n // Full text is always in the DOM — the mask clip animates to reveal it\n const fullContent = segments.map((seg, i) =>\n seg.className ? (\n <span key={i} className={seg.className}>\n {seg.text}\n </span>\n ) : (\n seg.text\n ),\n )\n\n return cloneElement(children, {\n ref: mergeRef(elementRef, existingRef),\n style: {\n ...existingStyle,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n ...(revealed\n ? {\n animation: `${animName} ${duration}s linear forwards`,\n }\n : {\n maxWidth: 0,\n }),\n },\n children: (\n <>\n <style>{`@keyframes ${animName} { from { max-width: 0; } to { max-width: 100%; } }`}</style>\n {fullContent}\n {showCursor && <BlinkingCursor char={cursorChar} id={id} />}\n </>\n ),\n } as Record<string, unknown>)\n}\n\n// ── Exports ──────────────────────────────────────────────────────────\n\nexport { Typewriter }\nexport type { TypewriterProps, TextSegment }\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waveso/ui",
3
- "version": "0.0.10",
3
+ "version": "0.2.0",
4
4
  "description": "Wave UI component library built on Base UI and Tailwind CSS",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -26,8 +26,8 @@
26
26
  "package.json"
27
27
  ],
28
28
  "scripts": {
29
- "build": "tsup && cp src/styles.css dist/styles.css",
30
- "dev": "tsup --watch",
29
+ "build": "tsdown && cp src/styles.css dist/styles.css",
30
+ "dev": "tsdown --watch",
31
31
  "typecheck": "tsc --noEmit",
32
32
  "clean": "rm -rf dist",
33
33
  "storybook": "storybook dev -p 6006",
@@ -38,7 +38,7 @@
38
38
  "release": "npm run build && changeset publish"
39
39
  },
40
40
  "peerDependencies": {
41
- "@base-ui/react": "^1.3.0",
41
+ "@base-ui/react": "^1.4.1",
42
42
  "class-variance-authority": "^0.7.0",
43
43
  "clsx": "^2.0.0",
44
44
  "react": "^19.0.0",
@@ -66,7 +66,7 @@
66
66
  }
67
67
  },
68
68
  "devDependencies": {
69
- "@base-ui/react": "^1.3.0",
69
+ "@base-ui/react": "^1.4.1",
70
70
  "@changesets/cli": "^2.27.0",
71
71
  "@storybook/addon-a11y": "^10.2.13",
72
72
  "@storybook/addon-docs": "^10.2.13",
@@ -87,7 +87,7 @@
87
87
  "storybook": "^10.2.13",
88
88
  "tailwind-merge": "^3.4.0",
89
89
  "tailwindcss": "^4.2.1",
90
- "tsup": "^8.4.0",
90
+ "tsdown": "0.21.10",
91
91
  "tw-animate-css": "^1.4.0",
92
92
  "typescript": "^5.7.0",
93
93
  "usehooks-ts": "^3.1.1",