@waveso/ui 0.0.10 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion.d.ts +24 -8
- package/dist/accordion.d.ts.map +1 -0
- package/dist/accordion.js +50 -73
- package/dist/accordion.js.map +1 -1
- package/dist/action-bar.d.ts +83 -0
- package/dist/action-bar.d.ts.map +1 -0
- package/dist/action-bar.js +264 -0
- package/dist/action-bar.js.map +1 -0
- package/dist/alert-dialog.d.ts +56 -21
- package/dist/alert-dialog.d.ts.map +1 -0
- package/dist/alert-dialog.js +75 -127
- package/dist/alert-dialog.js.map +1 -1
- package/dist/alert.d.ts +26 -11
- package/dist/alert.d.ts.map +1 -0
- package/dist/alert.js +37 -68
- package/dist/alert.js.map +1 -1
- package/dist/animate.d.ts +117 -75
- package/dist/animate.d.ts.map +1 -0
- package/dist/animate.js +259 -223
- package/dist/animate.js.map +1 -1
- package/dist/aspect-ratio.d.ts +11 -6
- package/dist/aspect-ratio.d.ts.map +1 -0
- package/dist/aspect-ratio.js +12 -14
- package/dist/aspect-ratio.js.map +1 -1
- package/dist/autocomplete.d.ts +91 -25
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +119 -181
- package/dist/autocomplete.js.map +1 -1
- package/dist/avatar.d.ts +32 -11
- package/dist/avatar.d.ts.map +1 -0
- package/dist/avatar.js +42 -89
- package/dist/avatar.js.map +1 -1
- package/dist/badge.d.ts +15 -8
- package/dist/badge.d.ts.map +1 -0
- package/dist/badge.js +34 -48
- package/dist/badge.js.map +1 -1
- package/dist/breadcrumb.d.ts +35 -11
- package/dist/breadcrumb.d.ts.map +1 -0
- package/dist/breadcrumb.js +60 -110
- package/dist/breadcrumb.js.map +1 -1
- package/dist/button-group.d.ts +26 -13
- package/dist/button-group.d.ts.map +1 -0
- package/dist/button-group.js +38 -76
- package/dist/button-group.js.map +1 -1
- package/dist/button.d.ts +17 -10
- package/dist/button.d.ts.map +1 -0
- package/dist/button.js +50 -3
- package/dist/button.js.map +1 -1
- package/dist/card.d.ts +35 -11
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +43 -82
- package/dist/card.js.map +1 -1
- package/dist/checkbox.d.ts +6 -4
- package/dist/checkbox.d.ts.map +1 -0
- package/dist/checkbox.js +21 -29
- package/dist/checkbox.js.map +1 -1
- package/dist/collapsible.d.ts +15 -7
- package/dist/collapsible.d.ts.map +1 -0
- package/dist/collapsible.js +19 -8
- package/dist/collapsible.js.map +1 -1
- package/dist/combobox.d.ts +83 -23
- package/dist/combobox.d.ts.map +1 -0
- package/dist/combobox.js +149 -247
- package/dist/combobox.js.map +1 -1
- package/dist/context-menu.d.ts +80 -26
- package/dist/context-menu.d.ts.map +1 -0
- package/dist/context-menu.js +108 -164
- package/dist/context-menu.js.map +1 -1
- package/dist/count.d.ts +45 -31
- package/dist/count.d.ts.map +1 -0
- package/dist/count.js +170 -165
- package/dist/count.js.map +1 -1
- package/dist/dialog.d.ts +61 -28
- package/dist/dialog.d.ts.map +1 -0
- package/dist/dialog.js +77 -120
- package/dist/dialog.js.map +1 -1
- package/dist/direction.d.ts +2 -1
- package/dist/direction.js +3 -3
- package/dist/drawer.d.ts +45 -15
- package/dist/drawer.d.ts.map +1 -0
- package/dist/drawer.js +93 -5
- package/dist/drawer.js.map +1 -1
- package/dist/encrypted-text.d.ts +25 -12
- package/dist/encrypted-text.d.ts.map +1 -0
- package/dist/encrypted-text.js +102 -134
- package/dist/encrypted-text.js.map +1 -1
- package/dist/field.d.ts +37 -21
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +52 -3
- package/dist/field.js.map +1 -1
- package/dist/film-grain-shader.d.ts +6 -0
- package/dist/film-grain-shader.d.ts.map +1 -0
- package/dist/film-grain-shader.js +88 -0
- package/dist/film-grain-shader.js.map +1 -0
- package/dist/film-grain-webgl.d.ts +20 -0
- package/dist/film-grain-webgl.d.ts.map +1 -0
- package/dist/film-grain-webgl.js +306 -0
- package/dist/film-grain-webgl.js.map +1 -0
- package/dist/film-grain.d.ts +21 -11
- package/dist/film-grain.d.ts.map +1 -0
- package/dist/film-grain.js +28 -420
- package/dist/film-grain.js.map +1 -1
- package/dist/form.d.ts +64 -49
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +112 -91
- package/dist/form.js.map +1 -1
- package/dist/gradient-reveal-text.d.ts +35 -22
- package/dist/gradient-reveal-text.d.ts.map +1 -0
- package/dist/gradient-reveal-text.js +238 -205
- package/dist/gradient-reveal-text.js.map +1 -1
- package/dist/hooks/use-mobile.d.ts +3 -1
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-mobile.js +28 -2
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/infinite-scroll.d.ts +29 -15
- package/dist/infinite-scroll.d.ts.map +1 -0
- package/dist/infinite-scroll.js +69 -99
- package/dist/infinite-scroll.js.map +1 -1
- package/dist/input-group.d.ts +41 -18
- package/dist/input-group.d.ts.map +1 -0
- package/dist/input-group.js +80 -6
- package/dist/input-group.js.map +1 -1
- package/dist/input-otp.d.ts +26 -10
- package/dist/input-otp.d.ts.map +1 -0
- package/dist/input-otp.js +40 -70
- package/dist/input-otp.js.map +1 -1
- package/dist/input.d.ts +10 -4
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +16 -3
- package/dist/input.js.map +1 -1
- package/dist/item.d.ts +58 -23
- package/dist/item.d.ts.map +1 -0
- package/dist/item.js +102 -160
- package/dist/item.js.map +1 -1
- package/dist/kbd.d.ts +12 -4
- package/dist/kbd.d.ts.map +1 -0
- package/dist/kbd.js +15 -24
- package/dist/kbd.js.map +1 -1
- package/dist/label.d.ts +9 -4
- package/dist/label.d.ts.map +1 -0
- package/dist/label.js +12 -16
- package/dist/label.js.map +1 -1
- package/dist/lib/focus.d.ts +42 -0
- package/dist/lib/focus.d.ts.map +1 -0
- package/dist/lib/focus.js +21 -0
- package/dist/lib/focus.js.map +1 -0
- package/dist/lib/internal-icons.d.ts +32 -0
- package/dist/lib/internal-icons.d.ts.map +1 -0
- package/dist/lib/internal-icons.js +222 -0
- package/dist/lib/internal-icons.js.map +1 -0
- package/dist/lib/utils.d.ts +4 -2
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +12 -2
- package/dist/lib/utils.js.map +1 -1
- package/dist/masonry.d.ts +25 -11
- package/dist/masonry.d.ts.map +1 -0
- package/dist/masonry.js +188 -229
- package/dist/masonry.js.map +1 -1
- package/dist/menu.d.ts +84 -26
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +141 -4
- package/dist/menu.js.map +1 -1
- package/dist/menubar.d.ts +60 -22
- package/dist/menubar.d.ts.map +1 -0
- package/dist/menubar.js +80 -52
- package/dist/menubar.js.map +1 -1
- package/dist/pagination.d.ts +38 -17
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +68 -107
- package/dist/pagination.js.map +1 -1
- package/dist/popover.d.ts +56 -14
- package/dist/popover.d.ts.map +1 -0
- package/dist/popover.js +62 -87
- package/dist/popover.js.map +1 -1
- package/dist/preview-card.d.ts +28 -9
- package/dist/preview-card.d.ts.map +1 -0
- package/dist/preview-card.js +40 -60
- package/dist/preview-card.js.map +1 -1
- package/dist/progress.d.ts +28 -9
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +35 -60
- package/dist/progress.js.map +1 -1
- package/dist/radio-group.d.ts +14 -8
- package/dist/radio-group.d.ts.map +1 -0
- package/dist/radio-group.js +18 -22
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.d.ts +14 -6
- package/dist/radio.d.ts.map +1 -0
- package/dist/radio.js +24 -3
- package/dist/radio.js.map +1 -1
- package/dist/scroll-area.d.ts +16 -6
- package/dist/scroll-area.d.ts.map +1 -0
- package/dist/scroll-area.js +34 -55
- package/dist/scroll-area.js.map +1 -1
- package/dist/select.d.ts +66 -18
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +105 -185
- package/dist/select.js.map +1 -1
- package/dist/separator.d.ts +11 -5
- package/dist/separator.d.ts.map +1 -0
- package/dist/separator.js +17 -3
- package/dist/separator.js.map +1 -1
- package/dist/sidebar.d.ts +172 -79
- package/dist/sidebar.d.ts.map +1 -0
- package/dist/sidebar.js +363 -585
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.d.ts +8 -3
- package/dist/skeleton.d.ts.map +1 -0
- package/dist/skeleton.js +13 -3
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.d.ts +16 -6
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +40 -67
- package/dist/slider.js.map +1 -1
- package/dist/spinner.d.ts +8 -3
- package/dist/spinner.d.ts.map +1 -0
- package/dist/spinner.js +15 -4
- package/dist/spinner.js.map +1 -1
- package/dist/switch.d.ts +12 -6
- package/dist/switch.d.ts.map +1 -0
- package/dist/switch.js +18 -25
- package/dist/switch.js.map +1 -1
- package/dist/table.d.ts +37 -11
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +51 -88
- package/dist/table.js.map +1 -1
- package/dist/tabs.d.ts +28 -12
- package/dist/tabs.d.ts.map +1 -0
- package/dist/tabs.js +40 -74
- package/dist/tabs.js.map +1 -1
- package/dist/textarea.d.ts +13 -6
- package/dist/textarea.d.ts.map +1 -0
- package/dist/textarea.js +19 -3
- package/dist/textarea.js.map +1 -1
- package/dist/toast.d.ts +63 -39
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +177 -215
- package/dist/toast.js.map +1 -1
- package/dist/toggle-group.d.ts +26 -12
- package/dist/toggle-group.d.ts.map +1 -0
- package/dist/toggle-group.js +49 -73
- package/dist/toggle-group.js.map +1 -1
- package/dist/toggle.d.ts +17 -10
- package/dist/toggle.d.ts.map +1 -0
- package/dist/toggle.js +38 -3
- package/dist/toggle.js.map +1 -1
- package/dist/tooltip.d.ts +35 -14
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +52 -3
- package/dist/tooltip.js.map +1 -1
- package/dist/typewriter.d.ts +44 -31
- package/dist/typewriter.d.ts.map +1 -0
- package/dist/typewriter.js +185 -185
- package/dist/typewriter.js.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-45VQAWIM.js +0 -228
- package/dist/chunk-45VQAWIM.js.map +0 -1
- package/dist/chunk-6Y7LPQMO.js +0 -11
- package/dist/chunk-6Y7LPQMO.js.map +0 -1
- package/dist/chunk-76UQO56T.js +0 -19
- package/dist/chunk-76UQO56T.js.map +0 -1
- package/dist/chunk-7F4MPMLJ.js +0 -17
- package/dist/chunk-7F4MPMLJ.js.map +0 -1
- package/dist/chunk-BKTJYX4M.js +0 -143
- package/dist/chunk-BKTJYX4M.js.map +0 -1
- package/dist/chunk-D5XPEJ6T.js +0 -36
- package/dist/chunk-D5XPEJ6T.js.map +0 -1
- package/dist/chunk-DIGOLJIR.js +0 -105
- package/dist/chunk-DIGOLJIR.js.map +0 -1
- package/dist/chunk-IQ7YQ5XA.js +0 -141
- package/dist/chunk-IQ7YQ5XA.js.map +0 -1
- package/dist/chunk-NCHHHWTB.js +0 -85
- package/dist/chunk-NCHHHWTB.js.map +0 -1
- package/dist/chunk-OUFYQLVN.js +0 -56
- package/dist/chunk-OUFYQLVN.js.map +0 -1
- package/dist/chunk-QFSEK4M6.js +0 -22
- package/dist/chunk-QFSEK4M6.js.map +0 -1
- package/dist/chunk-QRW37LRP.js +0 -25
- package/dist/chunk-QRW37LRP.js.map +0 -1
- package/dist/chunk-RPQHL6C5.js +0 -26
- package/dist/chunk-RPQHL6C5.js.map +0 -1
- package/dist/chunk-V4ZX4YCP.js +0 -66
- package/dist/chunk-V4ZX4YCP.js.map +0 -1
- package/dist/chunk-YTSQQTSF.js +0 -44
- package/dist/chunk-YTSQQTSF.js.map +0 -1
- package/dist/chunk-ZZZH3JGW.js +0 -23
- package/dist/chunk-ZZZH3JGW.js.map +0 -1
- package/dist/direction.js.map +0 -1
package/dist/encrypted-text.js
CHANGED
|
@@ -1,141 +1,109 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
+
return charset.charAt(Math.floor(Math.random() * charset.length));
|
|
8
9
|
}
|
|
9
10
|
function scramblePreservingSpaces(original, charset) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
108
|
+
|
|
141
109
|
//# sourceMappingURL=encrypted-text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/encrypted-text.tsx"],"names":["timeSinceLastFlip"],"mappings":";;;;AAiBA,IAAM,eAAA,GACJ,0FAAA;AAEF,SAAS,WAAW,OAAA,EAAyB;AAC3C,EAAA,OAAO,OAAA,CAAQ,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,OAAA,CAAQ,MAAM,CAAC,CAAA;AAClE;AAEA,SAAS,wBAAA,CAAyB,UAAkB,OAAA,EAAyB;AAC3E,EAAA,IAAI,CAAC,UAAU,OAAO,EAAA;AACtB,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,MAAA,EAAQ,KAAK,CAAA,EAAG;AAC3C,IAAA,MAAA,IAAU,SAAS,CAAC,CAAA,KAAM,GAAA,GAAM,GAAA,GAAM,WAAW,OAAO,CAAA;AAAA,EAC1D;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,aAAA,CAAc;AAAA,EACrB,IAAA;AAAA,EACA,SAAA;AAAA,EACA,aAAA,GAAgB,EAAA;AAAA,EAChB,OAAA,GAAU,eAAA;AAAA,EACV,WAAA,GAAc,EAAA;AAAA,EACd,kBAAA;AAAA,EACA,iBAAA;AAAA,EACA,YAAA,GAAe,KAAA;AAAA,EACf,eAAA,GAAkB,KAAA;AAAA,EAClB,GAAG;AACL,CAAA,EAAuB;AACrB,EAAA,MAAM,GAAA,GAAY,aAAwB,IAAI,CAAA;AAC9C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,eAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAU,eAAS,CAAC,CAAA;AACtD,EAAA,MAAM,GAAG,WAAW,CAAA,GAAU,eAAS,CAAC,CAAA;AAExC,EAAA,MAAM,iBAAA,GAA0B,aAAsB,IAAI,CAAA;AAC1D,EAAA,MAAM,YAAA,GAAqB,aAAO,CAAC,CAAA;AACnC,EAAA,MAAM,eAAA,GAAwB,aAAO,CAAC,CAAA;AACtC,EAAA,MAAM,gBAAA,GAAyB,KAAA,CAAA,MAAA;AAAA,IAC7B,IAAA,GAAO,yBAAyB,IAAA,EAAM,OAAO,EAAE,KAAA,CAAM,EAAE,IAAI;AAAC,GAC9D;AAEA,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AAET,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACnB,CAAC,CAAC,KAAK,CAAA,KAAM;AACX,QAAA,IAAI,OAAO,cAAA,EAAgB;AACzB,UAAA,WAAA,CAAY,IAAI,CAAA;AAChB,UAAA,QAAA,CAAS,UAAA,EAAW;AAAA,QACtB;AAAA,MACF,CAAA;AAAA,MACA,EAAE,WAAW,CAAA;AAAE,KACjB;AAEA,IAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AACnB,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,QAAA,EAAU;AAEf,IAAA,MAAM,OAAA,GAAU,IAAA,GACZ,wBAAA,CAAyB,IAAA,EAAM,OAAO,CAAA,GACtC,EAAA;AACJ,IAAA,gBAAA,CAAiB,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,EAAE,CAAA;AAC3C,IAAA,YAAA,CAAa,OAAA,GAAU,YAAY,GAAA,EAAI;AACvC,IAAA,eAAA,CAAgB,UAAU,YAAA,CAAa,OAAA;AACvC,IAAA,cAAA,CAAe,CAAC,CAAA;AAEhB,IAAA,IAAI,WAAA,GAAc,KAAA;AAElB,IAAA,MAAM,MAAA,GAAS,CAAC,GAAA,KAAgB;AAC9B,MAAA,IAAI,WAAA,EAAa;AAEjB,MAAA,MAAM,cAAc,IAAA,CAAK,MAAA;AAEzB,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,MAAMA,kBAAAA,GAAoB,MAAM,eAAA,CAAgB,OAAA;AAChD,QAAA,IAAIA,kBAAAA,IAAqB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,WAAW,CAAA,EAAG;AACjD,UAAA,IAAI,eAAA,EAAiB;AACnB,YAAA,MAAM,UAAoB,EAAC;AAC3B,YAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,EAAa,CAAA,EAAA,EAAK;AACpC,cAAA,IAAI,KAAK,CAAC,CAAA,KAAM,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,YACrC;AACA,YAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,cAAA,MAAM,GAAA,GAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,OAAA,CAAQ,MAAM,CAAC,CAAA;AAC9D,cAAA,gBAAA,CAAiB,OAAA,CAAQ,GAAG,CAAA,GAAI,UAAA,CAAW,OAAO,CAAA;AAAA,YACpD;AAAA,UACF,CAAA,MAAO;AACL,YAAA,KAAA,IAAS,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAQ,WAAA,EAAa,SAAS,CAAA,EAAG;AACnD,cAAA,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAA,GAC5B,IAAA,CAAK,KAAK,CAAA,KAAM,GAAA,GAAM,GAAA,GAAM,UAAA,CAAW,OAAO,CAAA;AAAA,YAClD;AAAA,UACF;AACA,UAAA,eAAA,CAAgB,OAAA,GAAU,GAAA;AAC1B,UAAA,WAAA,CAAY,CAAC,CAAA,KAAO,CAAA,GAAI,CAAA,GAAK,KAAM,CAAA;AAAA,QACrC;AACA,QAAA,iBAAA,CAAkB,OAAA,GAAU,sBAAsB,MAAM,CAAA;AACxD,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,OAAA;AACrC,MAAA,MAAM,qBAAqB,IAAA,CAAK,GAAA;AAAA,QAC9B,WAAA;AAAA,QACA,KAAK,KAAA,CAAM,SAAA,GAAY,KAAK,GAAA,CAAI,CAAA,EAAG,aAAa,CAAC;AAAA,OACnD;AAEA,MAAA,cAAA,CAAe,kBAAkB,CAAA;AAEjC,MAAA,IAAI,sBAAsB,WAAA,EAAa;AAEvC,MAAA,MAAM,iBAAA,GAAoB,MAAM,eAAA,CAAgB,OAAA;AAChD,MAAA,IAAI,iBAAA,IAAqB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,WAAW,CAAA,EAAG;AACjD,QAAA,KAAA,IAAS,KAAA,GAAQ,kBAAA,EAAoB,KAAA,GAAQ,WAAA,EAAa,SAAS,CAAA,EAAG;AACpE,UAAA,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAA,GAC5B,IAAA,CAAK,KAAK,CAAA,KAAM,GAAA,GAAM,GAAA,GAAM,UAAA,CAAW,OAAO,CAAA;AAAA,QAClD;AACA,QAAA,eAAA,CAAgB,OAAA,GAAU,GAAA;AAAA,MAC5B;AAEA,MAAA,iBAAA,CAAkB,OAAA,GAAU,sBAAsB,MAAM,CAAA;AAAA,IAC1D,CAAA;AAEA,IAAA,iBAAA,CAAkB,OAAA,GAAU,sBAAsB,MAAM,CAAA;AAExD,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,IAAI,iBAAA,CAAkB,YAAY,IAAA,EAAM;AACtC,QAAA,oBAAA,CAAqB,kBAAkB,OAAO,CAAA;AAAA,MAChD;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,IAAA,EAAM,eAAe,OAAA,EAAS,WAAA,EAAa,YAAA,EAAc,eAAe,CAAC,CAAA;AAEvF,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,WAAA,EAAU,gBAAA;AAAA,MACV,SAAA;AAAA,MACA,YAAA,EAAY,IAAA;AAAA,MACX,GAAG,KAAA;AAAA,MAEH,eAAK,KAAA,CAAM,EAAE,EAAE,GAAA,CAAI,CAAC,MAAM,KAAA,KAAU;AACnC,QAAA,MAAM,UAAA,GAAa,CAAC,YAAA,IAAgB,KAAA,GAAQ,WAAA;AAC5C,QAAA,MAAM,WAAA,GAAc,UAAA,GAChB,IAAA,GACA,IAAA,KAAS,GAAA,GACP,GAAA,GACC,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAA,IAAK,UAAA,CAAW,OAAO,CAAA;AAE5D,QAAA,uBACE,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YAEC,WAAA,EAAU,qBAAA;AAAA,YACV,iBAAe,UAAA,IAAc,MAAA;AAAA,YAC7B,SAAA,EAAW,EAAA,CAAG,UAAA,GAAa,iBAAA,GAAoB,kBAAkB,CAAA;AAAA,YAEhE,QAAA,EAAA;AAAA,WAAA;AAAA,UALI;AAAA,SAMP;AAAA,MAEJ,CAAC;AAAA;AAAA,GACH;AAEJ","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,38 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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
|
-
|
|
8
|
-
type
|
|
9
|
-
type
|
|
10
|
-
type
|
|
11
|
-
type
|
|
12
|
-
type
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
declare function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
declare function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.ComponentPropsWithoutRef<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 const FieldControl: React.ForwardRefExoticComponent<Omit<Omit<_$_base_ui_react0.FieldControlProps, "ref"> & React.RefAttributes<HTMLElement>, "ref"> & React.RefAttributes<HTMLElement>>;
|
|
23
|
+
declare function FieldDescription({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: FieldDescriptionProps): _$react_jsx_runtime0.JSX.Element;
|
|
27
|
+
declare function FieldItem({
|
|
28
|
+
className,
|
|
29
|
+
...props
|
|
30
|
+
}: FieldItemProps): _$react_jsx_runtime0.JSX.Element;
|
|
31
|
+
declare function FieldError({
|
|
32
|
+
className,
|
|
33
|
+
...props
|
|
34
|
+
}: FieldErrorProps): _$react_jsx_runtime0.JSX.Element;
|
|
35
|
+
declare const FieldValidity: React.FC<_$_base_ui_react0.FieldValidityProps>;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { Field, FieldControl, FieldControlProps, FieldDescription, FieldDescriptionProps, FieldError, FieldErrorProps, FieldItem, FieldItemProps, FieldLabel, FieldLabelProps, FieldPrimitive, FieldProps, FieldValidity, FieldValidityProps };
|
|
38
|
+
//# 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,KAGhE,iBAAA,GAAoB,KAAA,CAAM,wBAAA,QAAgC,cAAA,CAAe,OAAA;AAAA,iBAErE,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,cAatD,YAAA,EAAY,KAAA,CAAA,yBAAA,CAAA,IAAA,CAAA,IAAA,CAavB,iBAAA,CAbuB,iBAAA,WAAA,KAAA,CAAA,aAAA,CAAA,WAAA,YAAA,KAAA,CAAA,aAAA,CAAA,WAAA;AAAA,iBAgBT,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
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./lib/utils.js";
|
|
3
|
+
import * as React from "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
|
+
const FieldControl = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(FieldPrimitive.Control, {
|
|
22
|
+
ref,
|
|
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
|
+
FieldControl.displayName = "FieldControl";
|
|
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":[],"
|
|
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` is important for React Hook Form / focus management, so we forward it only on the control.\nexport type FieldControlProps = React.ComponentPropsWithoutRef<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 const FieldControl = React.forwardRef<\n React.ComponentRef<typeof FieldPrimitive.Control>,\n FieldControlProps\n>(({ className, ...props }, ref) => (\n <FieldPrimitive.Control\n ref={ref}\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))\nFieldControl.displayName = \"FieldControl\"\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":";;;;;;AAiBA,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,MAAa,eAAe,MAAM,YAG/B,EAAE,WAAW,GAAG,SAAS,QAC1B,oBAAC,eAAe,SAAhB;CACO;CACL,aAAU;CACV,WAAW,GACT,kKACA,UACD;CACD,GAAI;CACJ,CAAA,CACF;AACF,aAAa,cAAc;AAE3B,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"}
|