@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,141 +1,109 @@
1
- import { cn } from './chunk-76UQO56T.js';
2
- import * as React from 'react';
3
- import { jsx } from 'react/jsx-runtime';
4
-
5
- var DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[];:,.<>/?";
1
+ "use client";
2
+ import { cn } from "./lib/utils.js";
3
+ import * as React from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/encrypted-text.tsx
6
+ const DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[];:,.<>/?";
6
7
  function randomChar(charset) {
7
- return charset.charAt(Math.floor(Math.random() * charset.length));
8
+ return charset.charAt(Math.floor(Math.random() * charset.length));
8
9
  }
9
10
  function scramblePreservingSpaces(original, charset) {
10
- if (!original) return "";
11
- let result = "";
12
- for (let i = 0; i < original.length; i += 1) {
13
- result += original[i] === " " ? " " : randomChar(charset);
14
- }
15
- return result;
11
+ if (!original) return "";
12
+ let result = "";
13
+ for (let i = 0; i < original.length; i += 1) result += original[i] === " " ? " " : randomChar(charset);
14
+ return result;
16
15
  }
17
- function EncryptedText({
18
- text,
19
- className,
20
- revealDelayMs = 50,
21
- charset = DEFAULT_CHARSET,
22
- flipDelayMs = 50,
23
- encryptedClassName,
24
- revealedClassName,
25
- scrambleOnly = false,
26
- scrambleOneChar = false,
27
- ...props
28
- }) {
29
- const ref = React.useRef(null);
30
- const [isInView, setIsInView] = React.useState(false);
31
- const [revealCount, setRevealCount] = React.useState(0);
32
- const [, setFlipTick] = React.useState(0);
33
- const animationFrameRef = React.useRef(null);
34
- const startTimeRef = React.useRef(0);
35
- const lastFlipTimeRef = React.useRef(0);
36
- const scrambleCharsRef = React.useRef(
37
- text ? scramblePreservingSpaces(text, charset).split("") : []
38
- );
39
- React.useEffect(() => {
40
- const el = ref.current;
41
- if (!el) return;
42
- const observer = new IntersectionObserver(
43
- ([entry]) => {
44
- if (entry?.isIntersecting) {
45
- setIsInView(true);
46
- observer.disconnect();
47
- }
48
- },
49
- { threshold: 0 }
50
- );
51
- observer.observe(el);
52
- return () => observer.disconnect();
53
- }, []);
54
- React.useEffect(() => {
55
- if (!isInView) return;
56
- const initial = text ? scramblePreservingSpaces(text, charset) : "";
57
- scrambleCharsRef.current = initial.split("");
58
- startTimeRef.current = performance.now();
59
- lastFlipTimeRef.current = startTimeRef.current;
60
- setRevealCount(0);
61
- let isCancelled = false;
62
- const update = (now) => {
63
- if (isCancelled) return;
64
- const totalLength = text.length;
65
- if (scrambleOnly) {
66
- const timeSinceLastFlip2 = now - lastFlipTimeRef.current;
67
- if (timeSinceLastFlip2 >= Math.max(0, flipDelayMs)) {
68
- if (scrambleOneChar) {
69
- const indices = [];
70
- for (let i = 0; i < totalLength; i++) {
71
- if (text[i] !== " ") indices.push(i);
72
- }
73
- if (indices.length > 0) {
74
- const idx = indices[Math.floor(Math.random() * indices.length)];
75
- scrambleCharsRef.current[idx] = randomChar(charset);
76
- }
77
- } else {
78
- for (let index = 0; index < totalLength; index += 1) {
79
- scrambleCharsRef.current[index] = text[index] === " " ? " " : randomChar(charset);
80
- }
81
- }
82
- lastFlipTimeRef.current = now;
83
- setFlipTick((t) => t + 1 & 65535);
84
- }
85
- animationFrameRef.current = requestAnimationFrame(update);
86
- return;
87
- }
88
- const elapsedMs = now - startTimeRef.current;
89
- const currentRevealCount = Math.min(
90
- totalLength,
91
- Math.floor(elapsedMs / Math.max(1, revealDelayMs))
92
- );
93
- setRevealCount(currentRevealCount);
94
- if (currentRevealCount >= totalLength) return;
95
- const timeSinceLastFlip = now - lastFlipTimeRef.current;
96
- if (timeSinceLastFlip >= Math.max(0, flipDelayMs)) {
97
- for (let index = currentRevealCount; index < totalLength; index += 1) {
98
- scrambleCharsRef.current[index] = text[index] === " " ? " " : randomChar(charset);
99
- }
100
- lastFlipTimeRef.current = now;
101
- }
102
- animationFrameRef.current = requestAnimationFrame(update);
103
- };
104
- animationFrameRef.current = requestAnimationFrame(update);
105
- return () => {
106
- isCancelled = true;
107
- if (animationFrameRef.current !== null) {
108
- cancelAnimationFrame(animationFrameRef.current);
109
- }
110
- };
111
- }, [isInView, text, revealDelayMs, charset, flipDelayMs, scrambleOnly, scrambleOneChar]);
112
- if (!text) return null;
113
- return /* @__PURE__ */ jsx(
114
- "span",
115
- {
116
- ref,
117
- "data-slot": "encrypted-text",
118
- className,
119
- "aria-label": text,
120
- ...props,
121
- children: text.split("").map((char, index) => {
122
- const isRevealed = !scrambleOnly && index < revealCount;
123
- const displayChar = isRevealed ? char : char === " " ? " " : scrambleCharsRef.current[index] ?? randomChar(charset);
124
- return /* @__PURE__ */ jsx(
125
- "span",
126
- {
127
- "data-slot": "encrypted-text-char",
128
- "data-revealed": isRevealed || void 0,
129
- className: cn(isRevealed ? revealedClassName : encryptedClassName),
130
- children: displayChar
131
- },
132
- index
133
- );
134
- })
135
- }
136
- );
16
+ function EncryptedText({ text, className, revealDelayMs = 50, charset = DEFAULT_CHARSET, flipDelayMs = 50, encryptedClassName, revealedClassName, scrambleOnly = false, scrambleOneChar = false, ...props }) {
17
+ const ref = React.useRef(null);
18
+ const [isInView, setIsInView] = React.useState(false);
19
+ const [revealCount, setRevealCount] = React.useState(0);
20
+ const [, setFlipTick] = React.useState(0);
21
+ const animationFrameRef = React.useRef(null);
22
+ const startTimeRef = React.useRef(0);
23
+ const lastFlipTimeRef = React.useRef(0);
24
+ const scrambleCharsRef = React.useRef(text ? scramblePreservingSpaces(text, charset).split("") : []);
25
+ React.useEffect(() => {
26
+ const el = ref.current;
27
+ if (!el) return;
28
+ const observer = new IntersectionObserver(([entry]) => {
29
+ if (entry?.isIntersecting) {
30
+ setIsInView(true);
31
+ observer.disconnect();
32
+ }
33
+ }, { threshold: 0 });
34
+ observer.observe(el);
35
+ return () => observer.disconnect();
36
+ }, []);
37
+ React.useEffect(() => {
38
+ if (!isInView) return;
39
+ scrambleCharsRef.current = (text ? scramblePreservingSpaces(text, charset) : "").split("");
40
+ startTimeRef.current = performance.now();
41
+ lastFlipTimeRef.current = startTimeRef.current;
42
+ setRevealCount(0);
43
+ let isCancelled = false;
44
+ const update = (now) => {
45
+ if (isCancelled) return;
46
+ const totalLength = text.length;
47
+ if (scrambleOnly) {
48
+ if (now - lastFlipTimeRef.current >= Math.max(0, flipDelayMs)) {
49
+ if (scrambleOneChar) {
50
+ const indices = [];
51
+ for (let i = 0; i < totalLength; i++) if (text[i] !== " ") indices.push(i);
52
+ if (indices.length > 0) {
53
+ const idx = indices[Math.floor(Math.random() * indices.length)];
54
+ scrambleCharsRef.current[idx] = randomChar(charset);
55
+ }
56
+ } else for (let index = 0; index < totalLength; index += 1) scrambleCharsRef.current[index] = text[index] === " " ? " " : randomChar(charset);
57
+ lastFlipTimeRef.current = now;
58
+ setFlipTick((t) => t + 1 & 65535);
59
+ }
60
+ animationFrameRef.current = requestAnimationFrame(update);
61
+ return;
62
+ }
63
+ const elapsedMs = now - startTimeRef.current;
64
+ const currentRevealCount = Math.min(totalLength, Math.floor(elapsedMs / Math.max(1, revealDelayMs)));
65
+ setRevealCount(currentRevealCount);
66
+ if (currentRevealCount >= totalLength) return;
67
+ if (now - lastFlipTimeRef.current >= Math.max(0, flipDelayMs)) {
68
+ for (let index = currentRevealCount; index < totalLength; index += 1) scrambleCharsRef.current[index] = text[index] === " " ? " " : randomChar(charset);
69
+ lastFlipTimeRef.current = now;
70
+ }
71
+ animationFrameRef.current = requestAnimationFrame(update);
72
+ };
73
+ animationFrameRef.current = requestAnimationFrame(update);
74
+ return () => {
75
+ isCancelled = true;
76
+ if (animationFrameRef.current !== null) cancelAnimationFrame(animationFrameRef.current);
77
+ };
78
+ }, [
79
+ isInView,
80
+ text,
81
+ revealDelayMs,
82
+ charset,
83
+ flipDelayMs,
84
+ scrambleOnly,
85
+ scrambleOneChar
86
+ ]);
87
+ if (!text) return null;
88
+ return /* @__PURE__ */ jsx("span", {
89
+ ref,
90
+ "data-slot": "encrypted-text",
91
+ className,
92
+ "aria-label": text,
93
+ ...props,
94
+ children: text.split("").map((char, index) => {
95
+ const isRevealed = !scrambleOnly && index < revealCount;
96
+ const displayChar = isRevealed ? char : char === " " ? " " : scrambleCharsRef.current[index] ?? randomChar(charset);
97
+ return /* @__PURE__ */ jsx("span", {
98
+ "data-slot": "encrypted-text-char",
99
+ "data-revealed": isRevealed || void 0,
100
+ className: cn(isRevealed ? revealedClassName : encryptedClassName),
101
+ children: displayChar
102
+ }, index);
103
+ })
104
+ });
137
105
  }
138
-
106
+ //#endregion
139
107
  export { EncryptedText };
140
- //# sourceMappingURL=encrypted-text.js.map
108
+
141
109
  //# sourceMappingURL=encrypted-text.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/encrypted-text.tsx"],"names":["timeSinceLastFlip"],"mappings":"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","file":"encrypted-text.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\ntype EncryptedTextProps = React.ComponentProps<\"span\"> & {\n text: string\n revealDelayMs?: number\n charset?: string\n flipDelayMs?: number\n encryptedClassName?: string\n revealedClassName?: string\n scrambleOnly?: boolean\n scrambleOneChar?: boolean\n}\n\nconst DEFAULT_CHARSET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[];:,.<>/?\"\n\nfunction randomChar(charset: string): string {\n return charset.charAt(Math.floor(Math.random() * charset.length))\n}\n\nfunction scramblePreservingSpaces(original: string, charset: string): string {\n if (!original) return \"\"\n let result = \"\"\n for (let i = 0; i < original.length; i += 1) {\n result += original[i] === \" \" ? \" \" : randomChar(charset)\n }\n return result\n}\n\nfunction EncryptedText({\n text,\n className,\n revealDelayMs = 50,\n charset = DEFAULT_CHARSET,\n flipDelayMs = 50,\n encryptedClassName,\n revealedClassName,\n scrambleOnly = false,\n scrambleOneChar = false,\n ...props\n}: EncryptedTextProps) {\n const ref = React.useRef<HTMLSpanElement>(null)\n const [isInView, setIsInView] = React.useState(false)\n const [revealCount, setRevealCount] = React.useState(0)\n const [, setFlipTick] = React.useState(0)\n\n const animationFrameRef = React.useRef<number | null>(null)\n const startTimeRef = React.useRef(0)\n const lastFlipTimeRef = React.useRef(0)\n const scrambleCharsRef = React.useRef<string[]>(\n text ? scramblePreservingSpaces(text, charset).split(\"\") : []\n )\n\n React.useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry?.isIntersecting) {\n setIsInView(true)\n observer.disconnect()\n }\n },\n { threshold: 0 }\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [])\n\n React.useEffect(() => {\n if (!isInView) return\n\n const initial = text\n ? scramblePreservingSpaces(text, charset)\n : \"\"\n scrambleCharsRef.current = initial.split(\"\")\n startTimeRef.current = performance.now()\n lastFlipTimeRef.current = startTimeRef.current\n setRevealCount(0)\n\n let isCancelled = false\n\n const update = (now: number) => {\n if (isCancelled) return\n\n const totalLength = text.length\n\n if (scrambleOnly) {\n const timeSinceLastFlip = now - lastFlipTimeRef.current\n if (timeSinceLastFlip >= Math.max(0, flipDelayMs)) {\n if (scrambleOneChar) {\n const indices: number[] = []\n for (let i = 0; i < totalLength; i++) {\n if (text[i] !== \" \") indices.push(i)\n }\n if (indices.length > 0) {\n const idx = indices[Math.floor(Math.random() * indices.length)]!\n scrambleCharsRef.current[idx] = randomChar(charset)\n }\n } else {\n for (let index = 0; index < totalLength; index += 1) {\n scrambleCharsRef.current[index] =\n text[index] === \" \" ? \" \" : randomChar(charset)\n }\n }\n lastFlipTimeRef.current = now\n setFlipTick((t) => (t + 1) & 0xffff)\n }\n animationFrameRef.current = requestAnimationFrame(update)\n return\n }\n\n const elapsedMs = now - startTimeRef.current\n const currentRevealCount = Math.min(\n totalLength,\n Math.floor(elapsedMs / Math.max(1, revealDelayMs))\n )\n\n setRevealCount(currentRevealCount)\n\n if (currentRevealCount >= totalLength) return\n\n const timeSinceLastFlip = now - lastFlipTimeRef.current\n if (timeSinceLastFlip >= Math.max(0, flipDelayMs)) {\n for (let index = currentRevealCount; index < totalLength; index += 1) {\n scrambleCharsRef.current[index] =\n text[index] === \" \" ? \" \" : randomChar(charset)\n }\n lastFlipTimeRef.current = now\n }\n\n animationFrameRef.current = requestAnimationFrame(update)\n }\n\n animationFrameRef.current = requestAnimationFrame(update)\n\n return () => {\n isCancelled = true\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n }\n }\n }, [isInView, text, revealDelayMs, charset, flipDelayMs, scrambleOnly, scrambleOneChar])\n\n if (!text) return null\n\n return (\n <span\n ref={ref}\n data-slot=\"encrypted-text\"\n className={className}\n aria-label={text}\n {...props}\n >\n {text.split(\"\").map((char, index) => {\n const isRevealed = !scrambleOnly && index < revealCount\n const displayChar = isRevealed\n ? char\n : char === \" \"\n ? \" \"\n : (scrambleCharsRef.current[index] ?? randomChar(charset))\n\n return (\n <span\n key={index}\n data-slot=\"encrypted-text-char\"\n data-revealed={isRevealed || undefined}\n className={cn(isRevealed ? revealedClassName : encryptedClassName)}\n >\n {displayChar}\n </span>\n )\n })}\n </span>\n )\n}\n\nexport { EncryptedText }\n"]}
1
+ {"version":3,"file":"encrypted-text.js","names":[],"sources":["../src/encrypted-text.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\ntype EncryptedTextProps = React.ComponentProps<\"span\"> & {\n text: string\n revealDelayMs?: number\n charset?: string\n flipDelayMs?: number\n encryptedClassName?: string\n revealedClassName?: string\n scrambleOnly?: boolean\n scrambleOneChar?: boolean\n}\n\nconst DEFAULT_CHARSET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[];:,.<>/?\"\n\nfunction randomChar(charset: string): string {\n return charset.charAt(Math.floor(Math.random() * charset.length))\n}\n\nfunction scramblePreservingSpaces(original: string, charset: string): string {\n if (!original) return \"\"\n let result = \"\"\n for (let i = 0; i < original.length; i += 1) {\n result += original[i] === \" \" ? \" \" : randomChar(charset)\n }\n return result\n}\n\nfunction EncryptedText({\n text,\n className,\n revealDelayMs = 50,\n charset = DEFAULT_CHARSET,\n flipDelayMs = 50,\n encryptedClassName,\n revealedClassName,\n scrambleOnly = false,\n scrambleOneChar = false,\n ...props\n}: EncryptedTextProps) {\n const ref = React.useRef<HTMLSpanElement>(null)\n const [isInView, setIsInView] = React.useState(false)\n const [revealCount, setRevealCount] = React.useState(0)\n const [, setFlipTick] = React.useState(0)\n\n const animationFrameRef = React.useRef<number | null>(null)\n const startTimeRef = React.useRef(0)\n const lastFlipTimeRef = React.useRef(0)\n const scrambleCharsRef = React.useRef<string[]>(\n text ? scramblePreservingSpaces(text, charset).split(\"\") : []\n )\n\n React.useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry?.isIntersecting) {\n setIsInView(true)\n observer.disconnect()\n }\n },\n { threshold: 0 }\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [])\n\n React.useEffect(() => {\n if (!isInView) return\n\n const initial = text\n ? scramblePreservingSpaces(text, charset)\n : \"\"\n scrambleCharsRef.current = initial.split(\"\")\n startTimeRef.current = performance.now()\n lastFlipTimeRef.current = startTimeRef.current\n setRevealCount(0)\n\n let isCancelled = false\n\n const update = (now: number) => {\n if (isCancelled) return\n\n const totalLength = text.length\n\n if (scrambleOnly) {\n const timeSinceLastFlip = now - lastFlipTimeRef.current\n if (timeSinceLastFlip >= Math.max(0, flipDelayMs)) {\n if (scrambleOneChar) {\n const indices: number[] = []\n for (let i = 0; i < totalLength; i++) {\n if (text[i] !== \" \") indices.push(i)\n }\n if (indices.length > 0) {\n const idx = indices[Math.floor(Math.random() * indices.length)]!\n scrambleCharsRef.current[idx] = randomChar(charset)\n }\n } else {\n for (let index = 0; index < totalLength; index += 1) {\n scrambleCharsRef.current[index] =\n text[index] === \" \" ? \" \" : randomChar(charset)\n }\n }\n lastFlipTimeRef.current = now\n setFlipTick((t) => (t + 1) & 0xffff)\n }\n animationFrameRef.current = requestAnimationFrame(update)\n return\n }\n\n const elapsedMs = now - startTimeRef.current\n const currentRevealCount = Math.min(\n totalLength,\n Math.floor(elapsedMs / Math.max(1, revealDelayMs))\n )\n\n setRevealCount(currentRevealCount)\n\n if (currentRevealCount >= totalLength) return\n\n const timeSinceLastFlip = now - lastFlipTimeRef.current\n if (timeSinceLastFlip >= Math.max(0, flipDelayMs)) {\n for (let index = currentRevealCount; index < totalLength; index += 1) {\n scrambleCharsRef.current[index] =\n text[index] === \" \" ? \" \" : randomChar(charset)\n }\n lastFlipTimeRef.current = now\n }\n\n animationFrameRef.current = requestAnimationFrame(update)\n }\n\n animationFrameRef.current = requestAnimationFrame(update)\n\n return () => {\n isCancelled = true\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n }\n }\n }, [isInView, text, revealDelayMs, charset, flipDelayMs, scrambleOnly, scrambleOneChar])\n\n if (!text) return null\n\n return (\n <span\n ref={ref}\n data-slot=\"encrypted-text\"\n className={className}\n aria-label={text}\n {...props}\n >\n {text.split(\"\").map((char, index) => {\n const isRevealed = !scrambleOnly && index < revealCount\n const displayChar = isRevealed\n ? char\n : char === \" \"\n ? \" \"\n : (scrambleCharsRef.current[index] ?? randomChar(charset))\n\n return (\n <span\n key={index}\n data-slot=\"encrypted-text-char\"\n data-revealed={isRevealed || undefined}\n className={cn(isRevealed ? revealedClassName : encryptedClassName)}\n >\n {displayChar}\n </span>\n )\n })}\n </span>\n )\n}\n\nexport { EncryptedText }\n"],"mappings":"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"}
package/dist/field.d.ts CHANGED
@@ -1,22 +1,41 @@
1
- import * as _base_ui_react from '@base-ui/react';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import * as React from 'react';
4
- import { Field as Field$1 } from '@base-ui/react/field';
5
- export { Field as FieldPrimitive } from '@base-ui/react/field';
1
+ import * as React from "react";
2
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { Field as FieldPrimitive } from "@base-ui/react/field";
4
+ import * as _$_base_ui_react0 from "@base-ui/react";
6
5
 
7
- type FieldProps = React.ComponentProps<typeof Field$1.Root>;
8
- type FieldLabelProps = React.ComponentProps<typeof Field$1.Label>;
9
- type FieldDescriptionProps = React.ComponentProps<typeof Field$1.Description>;
10
- type FieldItemProps = React.ComponentProps<typeof Field$1.Item>;
11
- type FieldErrorProps = React.ComponentProps<typeof Field$1.Error>;
12
- type FieldValidityProps = React.ComponentProps<typeof Field$1.Validity>;
13
- type FieldControlProps = React.ComponentPropsWithoutRef<typeof Field$1.Control>;
14
- declare function Field({ className, ...props }: FieldProps): react_jsx_runtime.JSX.Element;
15
- declare function FieldLabel({ className, ...props }: FieldLabelProps): react_jsx_runtime.JSX.Element;
16
- declare const FieldControl: React.ForwardRefExoticComponent<Omit<Omit<_base_ui_react.FieldControlProps, "ref"> & React.RefAttributes<HTMLElement>, "ref"> & React.RefAttributes<HTMLElement>>;
17
- declare function FieldDescription({ className, ...props }: FieldDescriptionProps): react_jsx_runtime.JSX.Element;
18
- declare function FieldItem({ className, ...props }: FieldItemProps): react_jsx_runtime.JSX.Element;
19
- declare function FieldError({ className, ...props }: FieldErrorProps): react_jsx_runtime.JSX.Element;
20
- declare const FieldValidity: React.FC<_base_ui_react.FieldValidityProps>;
21
-
22
- export { Field, FieldControl, type FieldControlProps, FieldDescription, type FieldDescriptionProps, FieldError, type FieldErrorProps, FieldItem, type FieldItemProps, FieldLabel, type FieldLabelProps, type FieldProps, FieldValidity, type FieldValidityProps };
6
+ //#region src/field.d.ts
7
+ type FieldProps = React.ComponentProps<typeof FieldPrimitive.Root>;
8
+ type FieldLabelProps = React.ComponentProps<typeof FieldPrimitive.Label>;
9
+ type FieldDescriptionProps = React.ComponentProps<typeof FieldPrimitive.Description>;
10
+ type FieldItemProps = React.ComponentProps<typeof FieldPrimitive.Item>;
11
+ type FieldErrorProps = React.ComponentProps<typeof FieldPrimitive.Error>;
12
+ type FieldValidityProps = React.ComponentProps<typeof FieldPrimitive.Validity>;
13
+ type FieldControlProps = React.ComponentProps<typeof FieldPrimitive.Control>;
14
+ declare function Field({
15
+ className,
16
+ ...props
17
+ }: FieldProps): _$react_jsx_runtime0.JSX.Element;
18
+ declare function FieldLabel({
19
+ className,
20
+ ...props
21
+ }: FieldLabelProps): _$react_jsx_runtime0.JSX.Element;
22
+ declare function FieldControl({
23
+ className,
24
+ ...props
25
+ }: FieldControlProps): _$react_jsx_runtime0.JSX.Element;
26
+ declare function FieldDescription({
27
+ className,
28
+ ...props
29
+ }: FieldDescriptionProps): _$react_jsx_runtime0.JSX.Element;
30
+ declare function FieldItem({
31
+ className,
32
+ ...props
33
+ }: FieldItemProps): _$react_jsx_runtime0.JSX.Element;
34
+ declare function FieldError({
35
+ className,
36
+ ...props
37
+ }: FieldErrorProps): _$react_jsx_runtime0.JSX.Element;
38
+ declare const FieldValidity: React.FC<_$_base_ui_react0.FieldValidityProps>;
39
+ //#endregion
40
+ export { Field, FieldControl, FieldControlProps, FieldDescription, FieldDescriptionProps, FieldError, FieldErrorProps, FieldItem, FieldItemProps, FieldLabel, FieldLabelProps, FieldPrimitive, FieldProps, FieldValidity, FieldValidityProps };
41
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","names":[],"sources":["../src/field.tsx"],"mappings":";;;;;;KAOY,UAAA,GAAa,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,IAAA;AAAA,KACxD,eAAA,GAAkB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,KAAA;AAAA,KAC7D,qBAAA,GAAwB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,WAAA;AAAA,KACnE,cAAA,GAAiB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,IAAA;AAAA,KAC5D,eAAA,GAAkB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,KAAA;AAAA,KAC7D,kBAAA,GAAqB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,QAAA;AAAA,KAIhE,iBAAA,GAAoB,KAAA,CAAM,cAAA,QAAsB,cAAA,CAAe,OAAA;AAAA,iBAE3D,KAAA,CAAA;EAAQ,SAAA;EAAA,GAAc;AAAA,GAAS,UAAA,GAAU,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAazC,UAAA,CAAA;EAAa,SAAA;EAAA,GAAc;AAAA,GAAS,eAAA,GAAe,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAanD,YAAA,CAAA;EAAe,SAAA;EAAA,GAAc;AAAA,GAAS,iBAAA,GAAiB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAavD,gBAAA,CAAA;EAAmB,SAAA;EAAA,GAAc;AAAA,GAAS,qBAAA,GAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAe/D,SAAA,CAAA;EAAY,SAAA;EAAA,GAAc;AAAA,GAAS,cAAA,GAAc,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAUjD,UAAA,CAAA;EAAa,SAAA;EAAA,GAAc;AAAA,GAAS,eAAA,GAAe,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,cAWtD,aAAA,EAAa,KAAA,CAAA,EAAA,CAA0B,iBAAA,CAA1B,kBAAA"}
package/dist/field.js CHANGED
@@ -1,4 +1,53 @@
1
- export { Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldPrimitive, FieldValidity } from './chunk-NCHHHWTB.js';
2
- import './chunk-76UQO56T.js';
3
- //# sourceMappingURL=field.js.map
1
+ "use client";
2
+ import { cn } from "./lib/utils.js";
3
+ import "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { Field as FieldPrimitive } from "@base-ui/react/field";
6
+ //#region src/field.tsx
7
+ function Field({ className, ...props }) {
8
+ return /* @__PURE__ */ jsx(FieldPrimitive.Root, {
9
+ "data-slot": "field",
10
+ className: cn("grid w-full gap-2 data-[invalid]:text-destructive", className),
11
+ ...props
12
+ });
13
+ }
14
+ function FieldLabel({ className, ...props }) {
15
+ return /* @__PURE__ */ jsx(FieldPrimitive.Label, {
16
+ "data-slot": "field-label",
17
+ className: cn("text-sm font-medium leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70", className),
18
+ ...props
19
+ });
20
+ }
21
+ function FieldControl({ className, ...props }) {
22
+ return /* @__PURE__ */ jsx(FieldPrimitive.Control, {
23
+ "data-slot": "field-control",
24
+ className: cn("rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50", className),
25
+ ...props
26
+ });
27
+ }
28
+ function FieldDescription({ className, ...props }) {
29
+ return /* @__PURE__ */ jsx(FieldPrimitive.Description, {
30
+ "data-slot": "field-description",
31
+ className: cn("text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5", "last:mt-0 nth-last-2:-mt-1", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className),
32
+ ...props
33
+ });
34
+ }
35
+ function FieldItem({ className, ...props }) {
36
+ return /* @__PURE__ */ jsx(FieldPrimitive.Item, {
37
+ "data-slot": "field-item",
38
+ className: cn("flex items-center gap-2", className),
39
+ ...props
40
+ });
41
+ }
42
+ function FieldError({ className, ...props }) {
43
+ return /* @__PURE__ */ jsx(FieldPrimitive.Error, {
44
+ "data-slot": "field-error",
45
+ className: cn("text-destructive text-sm font-normal", className),
46
+ ...props
47
+ });
48
+ }
49
+ const FieldValidity = FieldPrimitive.Validity;
50
+ //#endregion
51
+ export { Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldPrimitive, FieldValidity };
52
+
4
53
  //# sourceMappingURL=field.js.map
package/dist/field.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"field.js"}
1
+ {"version":3,"file":"field.js","names":[],"sources":["../src/field.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Field as FieldPrimitive } from \"@base-ui/react/field\"\n\nimport { cn } from \"./lib/utils\"\n\nexport type FieldProps = React.ComponentProps<typeof FieldPrimitive.Root>\nexport type FieldLabelProps = React.ComponentProps<typeof FieldPrimitive.Label>\nexport type FieldDescriptionProps = React.ComponentProps<typeof FieldPrimitive.Description>\nexport type FieldItemProps = React.ComponentProps<typeof FieldPrimitive.Item>\nexport type FieldErrorProps = React.ComponentProps<typeof FieldPrimitive.Error>\nexport type FieldValidityProps = React.ComponentProps<typeof FieldPrimitive.Validity>\n\n// `ref` matters here for React Hook Form / focus management; it flows\n// through to the control via standard prop forwarding (React 19).\nexport type FieldControlProps = React.ComponentProps<typeof FieldPrimitive.Control>\n\nexport function Field({ className, ...props }: FieldProps) {\n return (\n <FieldPrimitive.Root\n data-slot=\"field\"\n className={cn(\n \"grid w-full gap-2 data-[invalid]:text-destructive\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport function FieldLabel({ className, ...props }: FieldLabelProps) {\n return (\n <FieldPrimitive.Label\n data-slot=\"field-label\"\n className={cn(\n \"text-sm font-medium leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport function FieldControl({ className, ...props }: FieldControlProps) {\n return (\n <FieldPrimitive.Control\n data-slot=\"field-control\"\n className={cn(\n \"rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport function FieldDescription({ className, ...props }: FieldDescriptionProps) {\n return (\n <FieldPrimitive.Description\n data-slot=\"field-description\"\n className={cn(\n \"text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5\",\n \"last:mt-0 nth-last-2:-mt-1\",\n \"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport function FieldItem({ className, ...props }: FieldItemProps) {\n return (\n <FieldPrimitive.Item\n data-slot=\"field-item\"\n className={cn(\"flex items-center gap-2\", className)}\n {...props}\n />\n )\n}\n\nexport function FieldError({ className, ...props }: FieldErrorProps) {\n return (\n <FieldPrimitive.Error\n data-slot=\"field-error\"\n className={cn(\"text-destructive text-sm font-normal\", className)}\n {...props}\n />\n )\n}\n\n// Base UI's Validity does not accept `className`/`ref` (and we don't need to style it yet), so we export it as-is.\nexport const FieldValidity = FieldPrimitive.Validity\n\nexport { FieldPrimitive }"],"mappings":";;;;;;AAkBA,SAAgB,MAAM,EAAE,WAAW,GAAG,SAAqB;AACzD,QACE,oBAAC,eAAe,MAAhB;EACE,aAAU;EACV,WAAW,GACT,qDACA,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAgB,WAAW,EAAE,WAAW,GAAG,SAA0B;AACnE,QACE,oBAAC,eAAe,OAAhB;EACE,aAAU;EACV,WAAW,GACT,kGACA,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAgB,aAAa,EAAE,WAAW,GAAG,SAA4B;AACvE,QACE,oBAAC,eAAe,SAAhB;EACE,aAAU;EACV,WAAW,GACT,kKACA,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAgB,iBAAiB,EAAE,WAAW,GAAG,SAAgC;AAC/E,QACE,oBAAC,eAAe,aAAhB;EACE,aAAU;EACV,WAAW,GACT,qJACA,8BACA,qEACA,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAyB;AACjE,QACE,oBAAC,eAAe,MAAhB;EACE,aAAU;EACV,WAAW,GAAG,2BAA2B,UAAU;EACnD,GAAI;EACJ,CAAA;;AAIN,SAAgB,WAAW,EAAE,WAAW,GAAG,SAA0B;AACnE,QACE,oBAAC,eAAe,OAAhB;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,UAAU;EAChE,GAAI;EACJ,CAAA;;AAKN,MAAa,gBAAgB,eAAe"}
@@ -0,0 +1,6 @@
1
+ //#region src/film-grain-shader.d.ts
2
+ declare const vertexShader = "\nattribute vec2 position;\nvarying vec2 vUv;\n\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n";
3
+ declare const fragmentShader = "\nprecision highp float;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform float uDensity;\nuniform float uOpacity;\nuniform float uFps;\nuniform vec3 uColor;\n\nvarying vec2 vUv;\n\n// Stable hash\nfloat hash(vec2 p, float seed) {\n return fract(sin(dot(p + seed, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\n// Film response curve (soft toe + shoulder rolloff)\nfloat filmCurve(float x) {\n return smoothstep(0.0, 0.2, x) * (1.0 - smoothstep(0.8, 1.0, x));\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy;\n vec2 p = floor(uv);\n\n // Temporal coherence: blend between current and next frame seed\n float frame = floor(uTime * uFps);\n float t = fract(uTime * uFps);\n float seed = frame * 0.1731;\n\n // Multi-scale grain with inter-frame blending on fine layer\n float fineA = hash(p, seed);\n float fineB = hash(p, seed + 0.1731);\n float fine = mix(fineA, fineB, t);\n\n float medium = hash(floor(uv * 0.5), seed + 3.1);\n float coarse = hash(floor(uv * 0.25), seed + 7.93);\n\n float grain = fine * 0.65 + medium * 0.25 + coarse * 0.10;\n\n // Emulsion clumping (organic structure)\n float cluster = hash(floor(uv * 0.18), seed + 9.2);\n grain *= mix(0.75, 1.35, cluster);\n\n // Density threshold\n grain = smoothstep(1.0 - uDensity, 1.0, grain);\n\n // Film response curve (replaces raw pow gamma)\n grain = filmCurve(grain);\n\n // Micro-blur: sample neighbors for optical diffusion\n float n1 = hash(p + vec2(1.0, 0.0), seed);\n float n2 = hash(p - vec2(1.0, 0.0), seed);\n float n3 = hash(p + vec2(0.0, 1.0), seed);\n float n4 = hash(p - vec2(0.0, 1.0), seed);\n float neighbors = (n1 + n2 + n3 + n4) * 0.25;\n grain = mix(grain, neighbors, 0.15);\n\n // Chromatic aberration (reuse neighbor hashes)\n float rShift = n1 * 0.015;\n float bShift = n2 * 0.015;\n\n vec3 color = uColor * vec3(\n grain + rShift,\n grain,\n grain + bShift\n );\n\n color = clamp(color, 0.0, 1.0);\n\n gl_FragColor = vec4(color, grain * uOpacity);\n}\n";
4
+ //#endregion
5
+ export { fragmentShader, vertexShader };
6
+ //# sourceMappingURL=film-grain-shader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"film-grain-shader.d.ts","names":[],"sources":["../src/film-grain-shader.ts"],"mappings":";cAAa,YAAA;AAAA,cAUA,cAAA"}
@@ -0,0 +1,88 @@
1
+ //#region src/film-grain-shader.ts
2
+ const vertexShader = `
3
+ attribute vec2 position;
4
+ varying vec2 vUv;
5
+
6
+ void main() {
7
+ vUv = position * 0.5 + 0.5;
8
+ gl_Position = vec4(position, 0.0, 1.0);
9
+ }
10
+ `;
11
+ const fragmentShader = `
12
+ precision highp float;
13
+
14
+ uniform float uTime;
15
+ uniform vec2 uResolution;
16
+ uniform float uDensity;
17
+ uniform float uOpacity;
18
+ uniform float uFps;
19
+ uniform vec3 uColor;
20
+
21
+ varying vec2 vUv;
22
+
23
+ // Stable hash
24
+ float hash(vec2 p, float seed) {
25
+ return fract(sin(dot(p + seed, vec2(127.1, 311.7))) * 43758.5453123);
26
+ }
27
+
28
+ // Film response curve (soft toe + shoulder rolloff)
29
+ float filmCurve(float x) {
30
+ return smoothstep(0.0, 0.2, x) * (1.0 - smoothstep(0.8, 1.0, x));
31
+ }
32
+
33
+ void main() {
34
+ vec2 uv = gl_FragCoord.xy;
35
+ vec2 p = floor(uv);
36
+
37
+ // Temporal coherence: blend between current and next frame seed
38
+ float frame = floor(uTime * uFps);
39
+ float t = fract(uTime * uFps);
40
+ float seed = frame * 0.1731;
41
+
42
+ // Multi-scale grain with inter-frame blending on fine layer
43
+ float fineA = hash(p, seed);
44
+ float fineB = hash(p, seed + 0.1731);
45
+ float fine = mix(fineA, fineB, t);
46
+
47
+ float medium = hash(floor(uv * 0.5), seed + 3.1);
48
+ float coarse = hash(floor(uv * 0.25), seed + 7.93);
49
+
50
+ float grain = fine * 0.65 + medium * 0.25 + coarse * 0.10;
51
+
52
+ // Emulsion clumping (organic structure)
53
+ float cluster = hash(floor(uv * 0.18), seed + 9.2);
54
+ grain *= mix(0.75, 1.35, cluster);
55
+
56
+ // Density threshold
57
+ grain = smoothstep(1.0 - uDensity, 1.0, grain);
58
+
59
+ // Film response curve (replaces raw pow gamma)
60
+ grain = filmCurve(grain);
61
+
62
+ // Micro-blur: sample neighbors for optical diffusion
63
+ float n1 = hash(p + vec2(1.0, 0.0), seed);
64
+ float n2 = hash(p - vec2(1.0, 0.0), seed);
65
+ float n3 = hash(p + vec2(0.0, 1.0), seed);
66
+ float n4 = hash(p - vec2(0.0, 1.0), seed);
67
+ float neighbors = (n1 + n2 + n3 + n4) * 0.25;
68
+ grain = mix(grain, neighbors, 0.15);
69
+
70
+ // Chromatic aberration (reuse neighbor hashes)
71
+ float rShift = n1 * 0.015;
72
+ float bShift = n2 * 0.015;
73
+
74
+ vec3 color = uColor * vec3(
75
+ grain + rShift,
76
+ grain,
77
+ grain + bShift
78
+ );
79
+
80
+ color = clamp(color, 0.0, 1.0);
81
+
82
+ gl_FragColor = vec4(color, grain * uOpacity);
83
+ }
84
+ `;
85
+ //#endregion
86
+ export { fragmentShader, vertexShader };
87
+
88
+ //# sourceMappingURL=film-grain-shader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"film-grain-shader.js","names":[],"sources":["../src/film-grain-shader.ts"],"sourcesContent":["export const vertexShader = `\nattribute vec2 position;\nvarying vec2 vUv;\n\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`\n\nexport const fragmentShader = `\nprecision highp float;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform float uDensity;\nuniform float uOpacity;\nuniform float uFps;\nuniform vec3 uColor;\n\nvarying vec2 vUv;\n\n// Stable hash\nfloat hash(vec2 p, float seed) {\n return fract(sin(dot(p + seed, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\n// Film response curve (soft toe + shoulder rolloff)\nfloat filmCurve(float x) {\n return smoothstep(0.0, 0.2, x) * (1.0 - smoothstep(0.8, 1.0, x));\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy;\n vec2 p = floor(uv);\n\n // Temporal coherence: blend between current and next frame seed\n float frame = floor(uTime * uFps);\n float t = fract(uTime * uFps);\n float seed = frame * 0.1731;\n\n // Multi-scale grain with inter-frame blending on fine layer\n float fineA = hash(p, seed);\n float fineB = hash(p, seed + 0.1731);\n float fine = mix(fineA, fineB, t);\n\n float medium = hash(floor(uv * 0.5), seed + 3.1);\n float coarse = hash(floor(uv * 0.25), seed + 7.93);\n\n float grain = fine * 0.65 + medium * 0.25 + coarse * 0.10;\n\n // Emulsion clumping (organic structure)\n float cluster = hash(floor(uv * 0.18), seed + 9.2);\n grain *= mix(0.75, 1.35, cluster);\n\n // Density threshold\n grain = smoothstep(1.0 - uDensity, 1.0, grain);\n\n // Film response curve (replaces raw pow gamma)\n grain = filmCurve(grain);\n\n // Micro-blur: sample neighbors for optical diffusion\n float n1 = hash(p + vec2(1.0, 0.0), seed);\n float n2 = hash(p - vec2(1.0, 0.0), seed);\n float n3 = hash(p + vec2(0.0, 1.0), seed);\n float n4 = hash(p - vec2(0.0, 1.0), seed);\n float neighbors = (n1 + n2 + n3 + n4) * 0.25;\n grain = mix(grain, neighbors, 0.15);\n\n // Chromatic aberration (reuse neighbor hashes)\n float rShift = n1 * 0.015;\n float bShift = n2 * 0.015;\n\n vec3 color = uColor * vec3(\n grain + rShift,\n grain,\n grain + bShift\n );\n\n color = clamp(color, 0.0, 1.0);\n\n gl_FragColor = vec4(color, grain * uOpacity);\n}\n`\n"],"mappings":";AAAA,MAAa,eAAe;;;;;;;;;AAU5B,MAAa,iBAAiB"}
@@ -0,0 +1,20 @@
1
+ import * as _$react from "react";
2
+
3
+ //#region src/film-grain-webgl.d.ts
4
+ interface UseFilmGrainOptions {
5
+ density: number;
6
+ opacity: number;
7
+ /** Target FPS for both WebGL and canvas fallback. Default 18 */
8
+ fps?: number;
9
+ /** Hex color for canvas fallback grain. Default '#ffffff' */
10
+ color?: string;
11
+ }
12
+ declare function useFilmGrain({
13
+ density,
14
+ opacity,
15
+ fps,
16
+ color
17
+ }: UseFilmGrainOptions): _$react.RefObject<HTMLCanvasElement | null>;
18
+ //#endregion
19
+ export { useFilmGrain };
20
+ //# sourceMappingURL=film-grain-webgl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"film-grain-webgl.d.ts","names":[],"sources":["../src/film-grain-webgl.ts"],"mappings":";;;UAGU,mBAAA;EACR,OAAA;EACA,OAAA;EAFQ;EAIR,GAAA;;EAEA,KAAA;AAAA;AAAA,iBAgJc,YAAA,CAAA;EACd,OAAA;EACA,OAAA;EACA,GAAA;EACA;AAAA,GACC,mBAAA,GAAmB,OAAA,CAAA,SAAA,CAAA,iBAAA"}