@alkimi.org/ui-kit 0.1.11 → 0.1.12
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/components/TextDecoder.d.mts +1 -5
- package/dist/components/TextDecoder.d.ts +1 -5
- package/dist/components/TextDecoder.js +31 -39
- package/dist/components/TextDecoder.js.map +1 -1
- package/dist/components/TextDecoder.mjs +38 -44
- package/dist/components/TextDecoder.mjs.map +1 -1
- package/dist/components/button.d.mts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/index.css +13 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +39 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +42 -36
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +13 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
|
@@ -5,11 +5,7 @@ interface TextDecoderProps {
|
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
className?: string;
|
|
7
7
|
delay?: number;
|
|
8
|
-
symbols?: string[];
|
|
9
|
-
maxIterations?: number;
|
|
10
|
-
speed?: number;
|
|
11
8
|
}
|
|
12
|
-
declare const TextDecoder: ({ children, className, delay, symbols, maxIterations, speed, }: TextDecoderProps) => react_jsx_runtime.JSX.Element;
|
|
13
9
|
declare const SuspenseDecodedText: (props: TextDecoderProps) => react_jsx_runtime.JSX.Element;
|
|
14
10
|
|
|
15
|
-
export {
|
|
11
|
+
export { type TextDecoderProps, SuspenseDecodedText as default };
|
|
@@ -5,11 +5,7 @@ interface TextDecoderProps {
|
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
className?: string;
|
|
7
7
|
delay?: number;
|
|
8
|
-
symbols?: string[];
|
|
9
|
-
maxIterations?: number;
|
|
10
|
-
speed?: number;
|
|
11
8
|
}
|
|
12
|
-
declare const TextDecoder: ({ children, className, delay, symbols, maxIterations, speed, }: TextDecoderProps) => react_jsx_runtime.JSX.Element;
|
|
13
9
|
declare const SuspenseDecodedText: (props: TextDecoderProps) => react_jsx_runtime.JSX.Element;
|
|
14
10
|
|
|
15
|
-
export {
|
|
11
|
+
export { type TextDecoderProps, SuspenseDecodedText as default };
|
|
@@ -32,11 +32,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
// src/components/TextDecoder.tsx
|
|
33
33
|
var TextDecoder_exports = {};
|
|
34
34
|
__export(TextDecoder_exports, {
|
|
35
|
-
SuspenseDecodedText: () => SuspenseDecodedText,
|
|
36
35
|
default: () => TextDecoder_default
|
|
37
36
|
});
|
|
38
37
|
module.exports = __toCommonJS(TextDecoder_exports);
|
|
39
|
-
var import_react = __toESM(require("react"));
|
|
40
38
|
|
|
41
39
|
// src/lib/utils.ts
|
|
42
40
|
var import_clsx = require("clsx");
|
|
@@ -46,15 +44,16 @@ function cn(...inputs) {
|
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
// src/components/TextDecoder.tsx
|
|
47
|
+
var import_react = __toESM(require("react"));
|
|
49
48
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
50
49
|
var SYMBOLS = ["\\", "-", "?", "/", "#", "!", "_", "+", "<", ">"];
|
|
51
50
|
var MAX_ITERATIONS = 3;
|
|
52
51
|
var SPEED = 400;
|
|
53
|
-
var scrambleText = (input
|
|
52
|
+
var scrambleText = (input) => {
|
|
54
53
|
if (!input) return "";
|
|
55
54
|
return input.split(" ").map((word) => {
|
|
56
55
|
if (!word) return "";
|
|
57
|
-
const randomSymbol =
|
|
56
|
+
const randomSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
|
|
58
57
|
return randomSymbol.repeat(word.length);
|
|
59
58
|
}).join(" ");
|
|
60
59
|
};
|
|
@@ -170,22 +169,13 @@ var scrambleReactNode = (node, scrambledText, textStartIndex) => {
|
|
|
170
169
|
}
|
|
171
170
|
return node;
|
|
172
171
|
};
|
|
173
|
-
var TextDecoder = ({
|
|
174
|
-
children,
|
|
175
|
-
className,
|
|
176
|
-
delay = 0,
|
|
177
|
-
symbols = SYMBOLS,
|
|
178
|
-
maxIterations = MAX_ITERATIONS,
|
|
179
|
-
speed = SPEED
|
|
180
|
-
}) => {
|
|
172
|
+
var TextDecoder = ({ children, className, delay = 0 }) => {
|
|
181
173
|
const [displayContent, setDisplayContent] = (0, import_react.useState)(null);
|
|
182
174
|
const [hasAnimated, setHasAnimated] = (0, import_react.useState)(false);
|
|
175
|
+
const [showDecoded, setShowDecoded] = (0, import_react.useState)(true);
|
|
183
176
|
const elementRef = (0, import_react.useRef)(null);
|
|
184
177
|
const fullText = (0, import_react.useMemo)(() => extractText(children), [children]);
|
|
185
|
-
const initialScrambled = (0, import_react.useMemo)(
|
|
186
|
-
() => scrambleText(fullText, symbols),
|
|
187
|
-
[fullText, symbols]
|
|
188
|
-
);
|
|
178
|
+
const initialScrambled = (0, import_react.useMemo)(() => scrambleText(fullText), [fullText]);
|
|
189
179
|
(0, import_react.useEffect)(() => {
|
|
190
180
|
if (!hasAnimated && fullText) {
|
|
191
181
|
const textStartIndex = { current: 0 };
|
|
@@ -195,15 +185,17 @@ var TextDecoder = ({
|
|
|
195
185
|
textStartIndex
|
|
196
186
|
);
|
|
197
187
|
setDisplayContent(scrambledContent);
|
|
188
|
+
setShowDecoded(true);
|
|
198
189
|
}
|
|
199
190
|
}, [children, initialScrambled, hasAnimated, fullText]);
|
|
200
191
|
(0, import_react.useEffect)(() => {
|
|
201
192
|
if (!fullText) {
|
|
202
193
|
setDisplayContent(null);
|
|
194
|
+
setShowDecoded(false);
|
|
203
195
|
return;
|
|
204
196
|
}
|
|
205
197
|
if (hasAnimated) {
|
|
206
|
-
|
|
198
|
+
setShowDecoded(false);
|
|
207
199
|
return;
|
|
208
200
|
}
|
|
209
201
|
const element = elementRef.current;
|
|
@@ -223,14 +215,15 @@ var TextDecoder = ({
|
|
|
223
215
|
textStartIndex
|
|
224
216
|
);
|
|
225
217
|
setDisplayContent(scrambledContent);
|
|
218
|
+
setShowDecoded(true);
|
|
226
219
|
intervalId = setInterval(() => {
|
|
227
220
|
iteration++;
|
|
228
|
-
if (iteration >=
|
|
229
|
-
setDisplayContent(children);
|
|
221
|
+
if (iteration >= MAX_ITERATIONS) {
|
|
230
222
|
setHasAnimated(true);
|
|
223
|
+
setShowDecoded(false);
|
|
231
224
|
if (intervalId) clearInterval(intervalId);
|
|
232
225
|
} else {
|
|
233
|
-
const newScrambled = scrambleText(fullText
|
|
226
|
+
const newScrambled = scrambleText(fullText);
|
|
234
227
|
const textStartIndex2 = { current: 0 };
|
|
235
228
|
const scrambledContent2 = scrambleReactNode(
|
|
236
229
|
children,
|
|
@@ -239,7 +232,7 @@ var TextDecoder = ({
|
|
|
239
232
|
);
|
|
240
233
|
setDisplayContent(scrambledContent2);
|
|
241
234
|
}
|
|
242
|
-
},
|
|
235
|
+
}, SPEED);
|
|
243
236
|
}, delay);
|
|
244
237
|
observer.disconnect();
|
|
245
238
|
}
|
|
@@ -255,24 +248,27 @@ var TextDecoder = ({
|
|
|
255
248
|
if (intervalId) clearInterval(intervalId);
|
|
256
249
|
if (delayTimeoutId) clearTimeout(delayTimeoutId);
|
|
257
250
|
};
|
|
258
|
-
}, [
|
|
259
|
-
children,
|
|
260
|
-
fullText,
|
|
261
|
-
initialScrambled,
|
|
262
|
-
hasAnimated,
|
|
263
|
-
delay,
|
|
264
|
-
symbols,
|
|
265
|
-
maxIterations,
|
|
266
|
-
speed
|
|
267
|
-
]);
|
|
251
|
+
}, [children, fullText, initialScrambled, hasAnimated, delay]);
|
|
268
252
|
const ariaLabel = (0, import_react.useMemo)(() => extractText(children), [children]);
|
|
269
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
270
254
|
"span",
|
|
271
255
|
{
|
|
272
256
|
ref: elementRef,
|
|
273
257
|
"aria-label": ariaLabel,
|
|
274
|
-
className,
|
|
275
|
-
children:
|
|
258
|
+
className: cn("relative block", className),
|
|
259
|
+
children: [
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
261
|
+
"span",
|
|
262
|
+
{
|
|
263
|
+
className: cn(
|
|
264
|
+
"transition-opacity duration-300",
|
|
265
|
+
hasAnimated ? "opacity-100" : "opacity-0"
|
|
266
|
+
),
|
|
267
|
+
children
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
showDecoded && displayContent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute inset-0 transition-opacity duration-300", children: displayContent })
|
|
271
|
+
]
|
|
276
272
|
}
|
|
277
273
|
);
|
|
278
274
|
};
|
|
@@ -285,9 +281,5 @@ var SuspenseDecodedText = (props) => {
|
|
|
285
281
|
}
|
|
286
282
|
);
|
|
287
283
|
};
|
|
288
|
-
var TextDecoder_default =
|
|
289
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
290
|
-
0 && (module.exports = {
|
|
291
|
-
SuspenseDecodedText
|
|
292
|
-
});
|
|
284
|
+
var TextDecoder_default = SuspenseDecodedText;
|
|
293
285
|
//# sourceMappingURL=TextDecoder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TextDecoder.tsx","../../src/lib/utils.ts"],"sourcesContent":["\"use client\"\nimport React, {\n useState,\n useEffect,\n useMemo,\n useRef,\n Suspense,\n ReactNode,\n} from \"react\"\nimport { cn } from \"../lib/utils\"\n\n// The symbols to use for the scrambling effect (moved outside to avoid recreation)\nconst SYMBOLS = [\"\\\\\", \"-\", \"?\", \"/\", \"#\", \"!\", \"_\", \"+\", \"<\", \">\"]\nconst MAX_ITERATIONS = 3 // How many times to scramble\nconst SPEED = 400 // Speed in milliseconds between scrambles\n\n// Helper function moved outside component to avoid recreation\nconst scrambleText = (input: string, symbols: string[]): string => {\n if (!input) return \"\"\n\n return input\n .split(\" \")\n .map((word: string) => {\n if (!word) return \"\"\n const randomSymbol = symbols[Math.floor(Math.random() * symbols.length)]\n return randomSymbol.repeat(word.length)\n })\n .join(\" \")\n}\n\n// Extract text content from React nodes recursively\n// Also checks common text props like 'text', 'label', 'title', etc.\nconst extractText = (node: ReactNode): string => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node)\n }\n\n if (Array.isArray(node)) {\n return node.map(extractText).join(\"\")\n }\n\n if (React.isValidElement(node)) {\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n\n let result = \"\"\n\n // Extract text from props first (in order: text, label, title)\n if (typeof props.text === \"string\") {\n result += props.text\n }\n if (typeof props.label === \"string\") {\n result += props.label\n }\n if (typeof props.title === \"string\") {\n result += props.title\n }\n\n // Then extract from children\n if (props.children) {\n result += extractText(props.children)\n }\n\n return result\n }\n\n return \"\"\n}\n\n// Clone React nodes and replace text content with scrambled text\n// This function maps scrambled text back to the original structure\nconst scrambleReactNode = (\n node: ReactNode,\n scrambledText: string,\n textStartIndex: { current: number }\n): ReactNode => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n const text = String(node)\n // For each character in the original text, take the corresponding scrambled character\n // This preserves the length and structure\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n // Fallback if scrambled text is shorter (shouldn't happen, but safety check)\n scrambled += text[i]\n }\n }\n return scrambled\n }\n\n if (Array.isArray(node)) {\n return node.map((child, index) => {\n const scrambledChild = scrambleReactNode(\n child,\n scrambledText,\n textStartIndex\n )\n // Preserve original key if it exists, otherwise add one for React elements\n if (React.isValidElement(scrambledChild)) {\n const originalKey = React.isValidElement(child) ? child.key : null\n if (scrambledChild.key == null && originalKey == null) {\n return React.cloneElement(scrambledChild, { key: `decoded-${index}` })\n }\n // If original had a key but scrambled doesn't, preserve it\n if (scrambledChild.key == null && originalKey != null) {\n return React.cloneElement(scrambledChild, { key: originalKey })\n }\n }\n return scrambledChild\n })\n }\n\n if (React.isValidElement(node)) {\n // Regular element handling - works for all components\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n const newProps = { ...props }\n\n // Handle text props in the same order as extraction (text, label, title)\n if (typeof props.text === \"string\") {\n const text = props.text\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += text[i]\n }\n }\n newProps.text = scrambled\n }\n\n if (typeof props.label === \"string\") {\n const label = props.label\n let scrambled = \"\"\n for (let i = 0; i < label.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += label[i]\n }\n }\n newProps.label = scrambled\n }\n\n if (typeof props.title === \"string\") {\n const title = props.title\n let scrambled = \"\"\n for (let i = 0; i < title.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += title[i]\n }\n }\n newProps.title = scrambled\n }\n\n // Handle children after props\n if (props.children) {\n newProps.children = scrambleReactNode(\n props.children,\n scrambledText,\n textStartIndex\n )\n }\n return React.cloneElement(node, newProps)\n }\n\n return node\n}\n\nexport interface TextDecoderProps {\n children: ReactNode\n className?: string\n delay?: number // Delay in milliseconds before starting animation\n symbols?: string[] // Symbols to use for scrambling effect\n maxIterations?: number // How many times to scramble\n speed?: number // Speed in milliseconds between scrambles\n}\n\nconst TextDecoder = ({\n children,\n className,\n delay = 0,\n symbols = SYMBOLS,\n maxIterations = MAX_ITERATIONS,\n speed = SPEED,\n}: TextDecoderProps) => {\n const [displayContent, setDisplayContent] = useState<ReactNode>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n const elementRef = useRef<HTMLSpanElement>(null)\n\n // Extract full text for scrambling\n const fullText = useMemo(() => extractText(children), [children])\n\n // Memoize initial scrambled text\n const initialScrambled = useMemo(\n () => scrambleText(fullText, symbols),\n [fullText, symbols]\n )\n\n // Initialize with scrambled content to prevent layout shift\n useEffect(() => {\n if (!hasAnimated && fullText) {\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, [children, initialScrambled, hasAnimated, fullText])\n\n useEffect(() => {\n if (!fullText) {\n setDisplayContent(null)\n return\n }\n\n // If already animated, show the final content\n if (hasAnimated) {\n setDisplayContent(children)\n return\n }\n\n const element = elementRef.current\n if (!element) return\n\n let iteration = 0\n let intervalId: NodeJS.Timeout | null = null\n let delayTimeoutId: NodeJS.Timeout | null = null\n\n // Intersection Observer to detect when element is visible\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry.isIntersecting && !hasAnimated) {\n // Start animation after delay\n delayTimeoutId = setTimeout(() => {\n // 1. Initial scramble\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n\n // 2. Set up the interval\n intervalId = setInterval(() => {\n iteration++\n\n if (iteration >= maxIterations) {\n // Stop scrambling and show the real content\n setDisplayContent(children)\n setHasAnimated(true)\n if (intervalId) clearInterval(intervalId)\n } else {\n // Continue scrambling\n const newScrambled = scrambleText(fullText, symbols)\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n newScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, speed)\n }, delay)\n\n // Disconnect observer once animation starts\n observer.disconnect()\n }\n },\n {\n threshold: 0.1, // Trigger when 10% of the element is visible\n }\n )\n\n observer.observe(element)\n\n // Cleanup function\n return () => {\n observer.disconnect()\n if (intervalId) clearInterval(intervalId)\n if (delayTimeoutId) clearTimeout(delayTimeoutId)\n }\n }, [\n children,\n fullText,\n initialScrambled,\n hasAnimated,\n delay,\n symbols,\n maxIterations,\n speed,\n ])\n\n const ariaLabel = useMemo(() => extractText(children), [children])\n\n return (\n <span\n ref={elementRef}\n aria-label={ariaLabel} // Accessibility: Screen readers read the real text\n className={className}\n >\n {displayContent}\n </span>\n )\n}\n\nconst SuspenseDecodedText = (props: TextDecoderProps) => {\n return (\n <Suspense\n fallback={\n <span className={cn(\"opacity-0\", props.className)}>\n {props.children}\n </span>\n }\n >\n <TextDecoder {...props} />\n </Suspense>\n )\n}\n\nexport { SuspenseDecodedText }\nexport default TextDecoder\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":["React","textStartIndex","scrambledContent"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/TextDecoder.tsx","../../src/lib/utils.ts"],"sourcesContent":["\"use client\"\nimport { cn } from \"@/lib/utils\"\nimport React, {\n useState,\n useEffect,\n useMemo,\n useRef,\n Suspense,\n ReactNode,\n} from \"react\"\n\n// The symbols to use for the scrambling effect (moved outside to avoid recreation)\nconst SYMBOLS = [\"\\\\\", \"-\", \"?\", \"/\", \"#\", \"!\", \"_\", \"+\", \"<\", \">\"]\nconst MAX_ITERATIONS = 3 // How many times to scramble\nconst SPEED = 400 // Speed in milliseconds between scrambles\n\n// Helper function moved outside component to avoid recreation\nconst scrambleText = (input: string): string => {\n if (!input) return \"\"\n\n return input\n .split(\" \")\n .map((word: string) => {\n if (!word) return \"\"\n const randomSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)]\n return randomSymbol.repeat(word.length)\n })\n .join(\" \")\n}\n\n// Extract text content from React nodes recursively\n// Also checks common text props like 'text', 'label', 'title', etc.\nconst extractText = (node: ReactNode): string => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node)\n }\n\n if (Array.isArray(node)) {\n return node.map(extractText).join(\"\")\n }\n\n if (React.isValidElement(node)) {\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n\n let result = \"\"\n\n // Extract text from props first (in order: text, label, title)\n if (typeof props.text === \"string\") {\n result += props.text\n }\n if (typeof props.label === \"string\") {\n result += props.label\n }\n if (typeof props.title === \"string\") {\n result += props.title\n }\n\n // Then extract from children\n if (props.children) {\n result += extractText(props.children)\n }\n\n return result\n }\n\n return \"\"\n}\n\n// Clone React nodes and replace text content with scrambled text\n// This function maps scrambled text back to the original structure\nconst scrambleReactNode = (\n node: ReactNode,\n scrambledText: string,\n textStartIndex: { current: number }\n): ReactNode => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n const text = String(node)\n // For each character in the original text, take the corresponding scrambled character\n // This preserves the length and structure\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n // Fallback if scrambled text is shorter (shouldn't happen, but safety check)\n scrambled += text[i]\n }\n }\n return scrambled\n }\n\n if (Array.isArray(node)) {\n return node.map((child, index) => {\n const scrambledChild = scrambleReactNode(\n child,\n scrambledText,\n textStartIndex\n )\n // Preserve original key if it exists, otherwise add one for React elements\n if (React.isValidElement(scrambledChild)) {\n const originalKey = React.isValidElement(child) ? child.key : null\n if (scrambledChild.key == null && originalKey == null) {\n return React.cloneElement(scrambledChild, { key: `decoded-${index}` })\n }\n // If original had a key but scrambled doesn't, preserve it\n if (scrambledChild.key == null && originalKey != null) {\n return React.cloneElement(scrambledChild, { key: originalKey })\n }\n }\n return scrambledChild\n })\n }\n\n if (React.isValidElement(node)) {\n // Regular element handling - works for all components\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n const newProps = { ...props }\n\n // Handle text props in the same order as extraction (text, label, title)\n if (typeof props.text === \"string\") {\n const text = props.text\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += text[i]\n }\n }\n newProps.text = scrambled\n }\n\n if (typeof props.label === \"string\") {\n const label = props.label\n let scrambled = \"\"\n for (let i = 0; i < label.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += label[i]\n }\n }\n newProps.label = scrambled\n }\n\n if (typeof props.title === \"string\") {\n const title = props.title\n let scrambled = \"\"\n for (let i = 0; i < title.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += title[i]\n }\n }\n newProps.title = scrambled\n }\n\n // Handle children after props\n if (props.children) {\n newProps.children = scrambleReactNode(\n props.children,\n scrambledText,\n textStartIndex\n )\n }\n return React.cloneElement(node, newProps)\n }\n\n return node\n}\n\nexport interface TextDecoderProps {\n children: ReactNode\n className?: string\n delay?: number // Delay in milliseconds before starting animation\n}\n\nconst TextDecoder = ({ children, className, delay = 0 }: TextDecoderProps) => {\n const [displayContent, setDisplayContent] = useState<ReactNode>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n const [showDecoded, setShowDecoded] = useState(true)\n const elementRef = useRef<HTMLSpanElement>(null)\n\n // Extract full text for scrambling\n const fullText = useMemo(() => extractText(children), [children])\n\n // Memoize initial scrambled text\n const initialScrambled = useMemo(() => scrambleText(fullText), [fullText])\n\n // Initialize with scrambled content to prevent layout shift\n useEffect(() => {\n if (!hasAnimated && fullText) {\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n setShowDecoded(true)\n }\n }, [children, initialScrambled, hasAnimated, fullText])\n\n useEffect(() => {\n if (!fullText) {\n setDisplayContent(null)\n setShowDecoded(false)\n return\n }\n\n // If already animated, hide decoded and show children\n if (hasAnimated) {\n setShowDecoded(false)\n return\n }\n\n const element = elementRef.current\n if (!element) return\n\n let iteration = 0\n let intervalId: NodeJS.Timeout | null = null\n let delayTimeoutId: NodeJS.Timeout | null = null\n\n // Intersection Observer to detect when element is visible\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry.isIntersecting && !hasAnimated) {\n // Start animation after delay\n delayTimeoutId = setTimeout(() => {\n // 1. Initial scramble\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n setShowDecoded(true)\n\n // 2. Set up the interval\n intervalId = setInterval(() => {\n iteration++\n\n if (iteration >= MAX_ITERATIONS) {\n // Stop scrambling, fade out decoded and fade in children\n setHasAnimated(true)\n setShowDecoded(false)\n if (intervalId) clearInterval(intervalId)\n } else {\n // Continue scrambling\n const newScrambled = scrambleText(fullText)\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n newScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, SPEED)\n }, delay)\n\n // Disconnect observer once animation starts\n observer.disconnect()\n }\n },\n {\n threshold: 0.1, // Trigger when 10% of the element is visible\n }\n )\n\n observer.observe(element)\n\n // Cleanup function\n return () => {\n observer.disconnect()\n if (intervalId) clearInterval(intervalId)\n if (delayTimeoutId) clearTimeout(delayTimeoutId)\n }\n }, [children, fullText, initialScrambled, hasAnimated, delay])\n\n const ariaLabel = useMemo(() => extractText(children), [children])\n\n return (\n <span\n ref={elementRef}\n aria-label={ariaLabel} // Accessibility: Screen readers read the real text\n className={cn(\"relative block\", className)}\n >\n {/* Children always rendered but with opacity-0 initially */}\n <span\n className={cn(\n \"transition-opacity duration-300\",\n hasAnimated ? \"opacity-100\" : \"opacity-0\"\n )}\n >\n {children}\n </span>\n {/* Decoded animation overlay */}\n {showDecoded && displayContent && (\n <span className=\"absolute inset-0 transition-opacity duration-300\">\n {displayContent}\n </span>\n )}\n </span>\n )\n}\n\nconst SuspenseDecodedText = (props: TextDecoderProps) => {\n return (\n <Suspense\n fallback={\n <span className={cn(\"opacity-0\", props.className)}>\n {props.children}\n </span>\n }\n >\n <TextDecoder {...props} />\n </Suspense>\n )\n}\n\nexport default SuspenseDecodedText\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":["React","textStartIndex","scrambledContent"]}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
|
-
// src/components/TextDecoder.tsx
|
|
5
|
-
import React, {
|
|
6
|
-
useState,
|
|
7
|
-
useEffect,
|
|
8
|
-
useMemo,
|
|
9
|
-
useRef,
|
|
10
|
-
Suspense
|
|
11
|
-
} from "react";
|
|
12
|
-
|
|
13
4
|
// src/lib/utils.ts
|
|
14
5
|
import { clsx } from "clsx";
|
|
15
6
|
import { twMerge } from "tailwind-merge";
|
|
@@ -18,15 +9,22 @@ function cn(...inputs) {
|
|
|
18
9
|
}
|
|
19
10
|
|
|
20
11
|
// src/components/TextDecoder.tsx
|
|
21
|
-
import {
|
|
12
|
+
import React, {
|
|
13
|
+
useState,
|
|
14
|
+
useEffect,
|
|
15
|
+
useMemo,
|
|
16
|
+
useRef,
|
|
17
|
+
Suspense
|
|
18
|
+
} from "react";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
20
|
var SYMBOLS = ["\\", "-", "?", "/", "#", "!", "_", "+", "<", ">"];
|
|
23
21
|
var MAX_ITERATIONS = 3;
|
|
24
22
|
var SPEED = 400;
|
|
25
|
-
var scrambleText = (input
|
|
23
|
+
var scrambleText = (input) => {
|
|
26
24
|
if (!input) return "";
|
|
27
25
|
return input.split(" ").map((word) => {
|
|
28
26
|
if (!word) return "";
|
|
29
|
-
const randomSymbol =
|
|
27
|
+
const randomSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
|
|
30
28
|
return randomSymbol.repeat(word.length);
|
|
31
29
|
}).join(" ");
|
|
32
30
|
};
|
|
@@ -142,22 +140,13 @@ var scrambleReactNode = (node, scrambledText, textStartIndex) => {
|
|
|
142
140
|
}
|
|
143
141
|
return node;
|
|
144
142
|
};
|
|
145
|
-
var TextDecoder = ({
|
|
146
|
-
children,
|
|
147
|
-
className,
|
|
148
|
-
delay = 0,
|
|
149
|
-
symbols = SYMBOLS,
|
|
150
|
-
maxIterations = MAX_ITERATIONS,
|
|
151
|
-
speed = SPEED
|
|
152
|
-
}) => {
|
|
143
|
+
var TextDecoder = ({ children, className, delay = 0 }) => {
|
|
153
144
|
const [displayContent, setDisplayContent] = useState(null);
|
|
154
145
|
const [hasAnimated, setHasAnimated] = useState(false);
|
|
146
|
+
const [showDecoded, setShowDecoded] = useState(true);
|
|
155
147
|
const elementRef = useRef(null);
|
|
156
148
|
const fullText = useMemo(() => extractText(children), [children]);
|
|
157
|
-
const initialScrambled = useMemo(
|
|
158
|
-
() => scrambleText(fullText, symbols),
|
|
159
|
-
[fullText, symbols]
|
|
160
|
-
);
|
|
149
|
+
const initialScrambled = useMemo(() => scrambleText(fullText), [fullText]);
|
|
161
150
|
useEffect(() => {
|
|
162
151
|
if (!hasAnimated && fullText) {
|
|
163
152
|
const textStartIndex = { current: 0 };
|
|
@@ -167,15 +156,17 @@ var TextDecoder = ({
|
|
|
167
156
|
textStartIndex
|
|
168
157
|
);
|
|
169
158
|
setDisplayContent(scrambledContent);
|
|
159
|
+
setShowDecoded(true);
|
|
170
160
|
}
|
|
171
161
|
}, [children, initialScrambled, hasAnimated, fullText]);
|
|
172
162
|
useEffect(() => {
|
|
173
163
|
if (!fullText) {
|
|
174
164
|
setDisplayContent(null);
|
|
165
|
+
setShowDecoded(false);
|
|
175
166
|
return;
|
|
176
167
|
}
|
|
177
168
|
if (hasAnimated) {
|
|
178
|
-
|
|
169
|
+
setShowDecoded(false);
|
|
179
170
|
return;
|
|
180
171
|
}
|
|
181
172
|
const element = elementRef.current;
|
|
@@ -195,14 +186,15 @@ var TextDecoder = ({
|
|
|
195
186
|
textStartIndex
|
|
196
187
|
);
|
|
197
188
|
setDisplayContent(scrambledContent);
|
|
189
|
+
setShowDecoded(true);
|
|
198
190
|
intervalId = setInterval(() => {
|
|
199
191
|
iteration++;
|
|
200
|
-
if (iteration >=
|
|
201
|
-
setDisplayContent(children);
|
|
192
|
+
if (iteration >= MAX_ITERATIONS) {
|
|
202
193
|
setHasAnimated(true);
|
|
194
|
+
setShowDecoded(false);
|
|
203
195
|
if (intervalId) clearInterval(intervalId);
|
|
204
196
|
} else {
|
|
205
|
-
const newScrambled = scrambleText(fullText
|
|
197
|
+
const newScrambled = scrambleText(fullText);
|
|
206
198
|
const textStartIndex2 = { current: 0 };
|
|
207
199
|
const scrambledContent2 = scrambleReactNode(
|
|
208
200
|
children,
|
|
@@ -211,7 +203,7 @@ var TextDecoder = ({
|
|
|
211
203
|
);
|
|
212
204
|
setDisplayContent(scrambledContent2);
|
|
213
205
|
}
|
|
214
|
-
},
|
|
206
|
+
}, SPEED);
|
|
215
207
|
}, delay);
|
|
216
208
|
observer.disconnect();
|
|
217
209
|
}
|
|
@@ -227,24 +219,27 @@ var TextDecoder = ({
|
|
|
227
219
|
if (intervalId) clearInterval(intervalId);
|
|
228
220
|
if (delayTimeoutId) clearTimeout(delayTimeoutId);
|
|
229
221
|
};
|
|
230
|
-
}, [
|
|
231
|
-
children,
|
|
232
|
-
fullText,
|
|
233
|
-
initialScrambled,
|
|
234
|
-
hasAnimated,
|
|
235
|
-
delay,
|
|
236
|
-
symbols,
|
|
237
|
-
maxIterations,
|
|
238
|
-
speed
|
|
239
|
-
]);
|
|
222
|
+
}, [children, fullText, initialScrambled, hasAnimated, delay]);
|
|
240
223
|
const ariaLabel = useMemo(() => extractText(children), [children]);
|
|
241
|
-
return /* @__PURE__ */
|
|
224
|
+
return /* @__PURE__ */ jsxs(
|
|
242
225
|
"span",
|
|
243
226
|
{
|
|
244
227
|
ref: elementRef,
|
|
245
228
|
"aria-label": ariaLabel,
|
|
246
|
-
className,
|
|
247
|
-
children:
|
|
229
|
+
className: cn("relative block", className),
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
"span",
|
|
233
|
+
{
|
|
234
|
+
className: cn(
|
|
235
|
+
"transition-opacity duration-300",
|
|
236
|
+
hasAnimated ? "opacity-100" : "opacity-0"
|
|
237
|
+
),
|
|
238
|
+
children
|
|
239
|
+
}
|
|
240
|
+
),
|
|
241
|
+
showDecoded && displayContent && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 transition-opacity duration-300", children: displayContent })
|
|
242
|
+
]
|
|
248
243
|
}
|
|
249
244
|
);
|
|
250
245
|
};
|
|
@@ -257,9 +252,8 @@ var SuspenseDecodedText = (props) => {
|
|
|
257
252
|
}
|
|
258
253
|
);
|
|
259
254
|
};
|
|
260
|
-
var TextDecoder_default =
|
|
255
|
+
var TextDecoder_default = SuspenseDecodedText;
|
|
261
256
|
export {
|
|
262
|
-
SuspenseDecodedText,
|
|
263
257
|
TextDecoder_default as default
|
|
264
258
|
};
|
|
265
259
|
//# sourceMappingURL=TextDecoder.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TextDecoder.tsx","../../src/lib/utils.ts"],"sourcesContent":["\"use client\"\nimport React, {\n useState,\n useEffect,\n useMemo,\n useRef,\n Suspense,\n ReactNode,\n} from \"react\"\nimport { cn } from \"../lib/utils\"\n\n// The symbols to use for the scrambling effect (moved outside to avoid recreation)\nconst SYMBOLS = [\"\\\\\", \"-\", \"?\", \"/\", \"#\", \"!\", \"_\", \"+\", \"<\", \">\"]\nconst MAX_ITERATIONS = 3 // How many times to scramble\nconst SPEED = 400 // Speed in milliseconds between scrambles\n\n// Helper function moved outside component to avoid recreation\nconst scrambleText = (input: string, symbols: string[]): string => {\n if (!input) return \"\"\n\n return input\n .split(\" \")\n .map((word: string) => {\n if (!word) return \"\"\n const randomSymbol = symbols[Math.floor(Math.random() * symbols.length)]\n return randomSymbol.repeat(word.length)\n })\n .join(\" \")\n}\n\n// Extract text content from React nodes recursively\n// Also checks common text props like 'text', 'label', 'title', etc.\nconst extractText = (node: ReactNode): string => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node)\n }\n\n if (Array.isArray(node)) {\n return node.map(extractText).join(\"\")\n }\n\n if (React.isValidElement(node)) {\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n\n let result = \"\"\n\n // Extract text from props first (in order: text, label, title)\n if (typeof props.text === \"string\") {\n result += props.text\n }\n if (typeof props.label === \"string\") {\n result += props.label\n }\n if (typeof props.title === \"string\") {\n result += props.title\n }\n\n // Then extract from children\n if (props.children) {\n result += extractText(props.children)\n }\n\n return result\n }\n\n return \"\"\n}\n\n// Clone React nodes and replace text content with scrambled text\n// This function maps scrambled text back to the original structure\nconst scrambleReactNode = (\n node: ReactNode,\n scrambledText: string,\n textStartIndex: { current: number }\n): ReactNode => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n const text = String(node)\n // For each character in the original text, take the corresponding scrambled character\n // This preserves the length and structure\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n // Fallback if scrambled text is shorter (shouldn't happen, but safety check)\n scrambled += text[i]\n }\n }\n return scrambled\n }\n\n if (Array.isArray(node)) {\n return node.map((child, index) => {\n const scrambledChild = scrambleReactNode(\n child,\n scrambledText,\n textStartIndex\n )\n // Preserve original key if it exists, otherwise add one for React elements\n if (React.isValidElement(scrambledChild)) {\n const originalKey = React.isValidElement(child) ? child.key : null\n if (scrambledChild.key == null && originalKey == null) {\n return React.cloneElement(scrambledChild, { key: `decoded-${index}` })\n }\n // If original had a key but scrambled doesn't, preserve it\n if (scrambledChild.key == null && originalKey != null) {\n return React.cloneElement(scrambledChild, { key: originalKey })\n }\n }\n return scrambledChild\n })\n }\n\n if (React.isValidElement(node)) {\n // Regular element handling - works for all components\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n const newProps = { ...props }\n\n // Handle text props in the same order as extraction (text, label, title)\n if (typeof props.text === \"string\") {\n const text = props.text\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += text[i]\n }\n }\n newProps.text = scrambled\n }\n\n if (typeof props.label === \"string\") {\n const label = props.label\n let scrambled = \"\"\n for (let i = 0; i < label.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += label[i]\n }\n }\n newProps.label = scrambled\n }\n\n if (typeof props.title === \"string\") {\n const title = props.title\n let scrambled = \"\"\n for (let i = 0; i < title.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += title[i]\n }\n }\n newProps.title = scrambled\n }\n\n // Handle children after props\n if (props.children) {\n newProps.children = scrambleReactNode(\n props.children,\n scrambledText,\n textStartIndex\n )\n }\n return React.cloneElement(node, newProps)\n }\n\n return node\n}\n\nexport interface TextDecoderProps {\n children: ReactNode\n className?: string\n delay?: number // Delay in milliseconds before starting animation\n symbols?: string[] // Symbols to use for scrambling effect\n maxIterations?: number // How many times to scramble\n speed?: number // Speed in milliseconds between scrambles\n}\n\nconst TextDecoder = ({\n children,\n className,\n delay = 0,\n symbols = SYMBOLS,\n maxIterations = MAX_ITERATIONS,\n speed = SPEED,\n}: TextDecoderProps) => {\n const [displayContent, setDisplayContent] = useState<ReactNode>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n const elementRef = useRef<HTMLSpanElement>(null)\n\n // Extract full text for scrambling\n const fullText = useMemo(() => extractText(children), [children])\n\n // Memoize initial scrambled text\n const initialScrambled = useMemo(\n () => scrambleText(fullText, symbols),\n [fullText, symbols]\n )\n\n // Initialize with scrambled content to prevent layout shift\n useEffect(() => {\n if (!hasAnimated && fullText) {\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, [children, initialScrambled, hasAnimated, fullText])\n\n useEffect(() => {\n if (!fullText) {\n setDisplayContent(null)\n return\n }\n\n // If already animated, show the final content\n if (hasAnimated) {\n setDisplayContent(children)\n return\n }\n\n const element = elementRef.current\n if (!element) return\n\n let iteration = 0\n let intervalId: NodeJS.Timeout | null = null\n let delayTimeoutId: NodeJS.Timeout | null = null\n\n // Intersection Observer to detect when element is visible\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry.isIntersecting && !hasAnimated) {\n // Start animation after delay\n delayTimeoutId = setTimeout(() => {\n // 1. Initial scramble\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n\n // 2. Set up the interval\n intervalId = setInterval(() => {\n iteration++\n\n if (iteration >= maxIterations) {\n // Stop scrambling and show the real content\n setDisplayContent(children)\n setHasAnimated(true)\n if (intervalId) clearInterval(intervalId)\n } else {\n // Continue scrambling\n const newScrambled = scrambleText(fullText, symbols)\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n newScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, speed)\n }, delay)\n\n // Disconnect observer once animation starts\n observer.disconnect()\n }\n },\n {\n threshold: 0.1, // Trigger when 10% of the element is visible\n }\n )\n\n observer.observe(element)\n\n // Cleanup function\n return () => {\n observer.disconnect()\n if (intervalId) clearInterval(intervalId)\n if (delayTimeoutId) clearTimeout(delayTimeoutId)\n }\n }, [\n children,\n fullText,\n initialScrambled,\n hasAnimated,\n delay,\n symbols,\n maxIterations,\n speed,\n ])\n\n const ariaLabel = useMemo(() => extractText(children), [children])\n\n return (\n <span\n ref={elementRef}\n aria-label={ariaLabel} // Accessibility: Screen readers read the real text\n className={className}\n >\n {displayContent}\n </span>\n )\n}\n\nconst SuspenseDecodedText = (props: TextDecoderProps) => {\n return (\n <Suspense\n fallback={\n <span className={cn(\"opacity-0\", props.className)}>\n {props.children}\n </span>\n }\n >\n <TextDecoder {...props} />\n </Suspense>\n )\n}\n\nexport { SuspenseDecodedText }\nexport default TextDecoder\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":["textStartIndex","scrambledContent"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/utils.ts","../../src/components/TextDecoder.tsx"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\"\nimport { cn } from \"@/lib/utils\"\nimport React, {\n useState,\n useEffect,\n useMemo,\n useRef,\n Suspense,\n ReactNode,\n} from \"react\"\n\n// The symbols to use for the scrambling effect (moved outside to avoid recreation)\nconst SYMBOLS = [\"\\\\\", \"-\", \"?\", \"/\", \"#\", \"!\", \"_\", \"+\", \"<\", \">\"]\nconst MAX_ITERATIONS = 3 // How many times to scramble\nconst SPEED = 400 // Speed in milliseconds between scrambles\n\n// Helper function moved outside component to avoid recreation\nconst scrambleText = (input: string): string => {\n if (!input) return \"\"\n\n return input\n .split(\" \")\n .map((word: string) => {\n if (!word) return \"\"\n const randomSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)]\n return randomSymbol.repeat(word.length)\n })\n .join(\" \")\n}\n\n// Extract text content from React nodes recursively\n// Also checks common text props like 'text', 'label', 'title', etc.\nconst extractText = (node: ReactNode): string => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node)\n }\n\n if (Array.isArray(node)) {\n return node.map(extractText).join(\"\")\n }\n\n if (React.isValidElement(node)) {\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n\n let result = \"\"\n\n // Extract text from props first (in order: text, label, title)\n if (typeof props.text === \"string\") {\n result += props.text\n }\n if (typeof props.label === \"string\") {\n result += props.label\n }\n if (typeof props.title === \"string\") {\n result += props.title\n }\n\n // Then extract from children\n if (props.children) {\n result += extractText(props.children)\n }\n\n return result\n }\n\n return \"\"\n}\n\n// Clone React nodes and replace text content with scrambled text\n// This function maps scrambled text back to the original structure\nconst scrambleReactNode = (\n node: ReactNode,\n scrambledText: string,\n textStartIndex: { current: number }\n): ReactNode => {\n if (typeof node === \"string\" || typeof node === \"number\") {\n const text = String(node)\n // For each character in the original text, take the corresponding scrambled character\n // This preserves the length and structure\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n // Fallback if scrambled text is shorter (shouldn't happen, but safety check)\n scrambled += text[i]\n }\n }\n return scrambled\n }\n\n if (Array.isArray(node)) {\n return node.map((child, index) => {\n const scrambledChild = scrambleReactNode(\n child,\n scrambledText,\n textStartIndex\n )\n // Preserve original key if it exists, otherwise add one for React elements\n if (React.isValidElement(scrambledChild)) {\n const originalKey = React.isValidElement(child) ? child.key : null\n if (scrambledChild.key == null && originalKey == null) {\n return React.cloneElement(scrambledChild, { key: `decoded-${index}` })\n }\n // If original had a key but scrambled doesn't, preserve it\n if (scrambledChild.key == null && originalKey != null) {\n return React.cloneElement(scrambledChild, { key: originalKey })\n }\n }\n return scrambledChild\n })\n }\n\n if (React.isValidElement(node)) {\n // Regular element handling - works for all components\n const props = node.props as {\n children?: ReactNode\n text?: string\n label?: string\n title?: string\n [key: string]: unknown\n }\n const newProps = { ...props }\n\n // Handle text props in the same order as extraction (text, label, title)\n if (typeof props.text === \"string\") {\n const text = props.text\n let scrambled = \"\"\n for (let i = 0; i < text.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += text[i]\n }\n }\n newProps.text = scrambled\n }\n\n if (typeof props.label === \"string\") {\n const label = props.label\n let scrambled = \"\"\n for (let i = 0; i < label.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += label[i]\n }\n }\n newProps.label = scrambled\n }\n\n if (typeof props.title === \"string\") {\n const title = props.title\n let scrambled = \"\"\n for (let i = 0; i < title.length; i++) {\n if (textStartIndex.current < scrambledText.length) {\n scrambled += scrambledText[textStartIndex.current]\n textStartIndex.current++\n } else {\n scrambled += title[i]\n }\n }\n newProps.title = scrambled\n }\n\n // Handle children after props\n if (props.children) {\n newProps.children = scrambleReactNode(\n props.children,\n scrambledText,\n textStartIndex\n )\n }\n return React.cloneElement(node, newProps)\n }\n\n return node\n}\n\nexport interface TextDecoderProps {\n children: ReactNode\n className?: string\n delay?: number // Delay in milliseconds before starting animation\n}\n\nconst TextDecoder = ({ children, className, delay = 0 }: TextDecoderProps) => {\n const [displayContent, setDisplayContent] = useState<ReactNode>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n const [showDecoded, setShowDecoded] = useState(true)\n const elementRef = useRef<HTMLSpanElement>(null)\n\n // Extract full text for scrambling\n const fullText = useMemo(() => extractText(children), [children])\n\n // Memoize initial scrambled text\n const initialScrambled = useMemo(() => scrambleText(fullText), [fullText])\n\n // Initialize with scrambled content to prevent layout shift\n useEffect(() => {\n if (!hasAnimated && fullText) {\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n setShowDecoded(true)\n }\n }, [children, initialScrambled, hasAnimated, fullText])\n\n useEffect(() => {\n if (!fullText) {\n setDisplayContent(null)\n setShowDecoded(false)\n return\n }\n\n // If already animated, hide decoded and show children\n if (hasAnimated) {\n setShowDecoded(false)\n return\n }\n\n const element = elementRef.current\n if (!element) return\n\n let iteration = 0\n let intervalId: NodeJS.Timeout | null = null\n let delayTimeoutId: NodeJS.Timeout | null = null\n\n // Intersection Observer to detect when element is visible\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry.isIntersecting && !hasAnimated) {\n // Start animation after delay\n delayTimeoutId = setTimeout(() => {\n // 1. Initial scramble\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n initialScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n setShowDecoded(true)\n\n // 2. Set up the interval\n intervalId = setInterval(() => {\n iteration++\n\n if (iteration >= MAX_ITERATIONS) {\n // Stop scrambling, fade out decoded and fade in children\n setHasAnimated(true)\n setShowDecoded(false)\n if (intervalId) clearInterval(intervalId)\n } else {\n // Continue scrambling\n const newScrambled = scrambleText(fullText)\n const textStartIndex = { current: 0 }\n const scrambledContent = scrambleReactNode(\n children,\n newScrambled,\n textStartIndex\n )\n setDisplayContent(scrambledContent)\n }\n }, SPEED)\n }, delay)\n\n // Disconnect observer once animation starts\n observer.disconnect()\n }\n },\n {\n threshold: 0.1, // Trigger when 10% of the element is visible\n }\n )\n\n observer.observe(element)\n\n // Cleanup function\n return () => {\n observer.disconnect()\n if (intervalId) clearInterval(intervalId)\n if (delayTimeoutId) clearTimeout(delayTimeoutId)\n }\n }, [children, fullText, initialScrambled, hasAnimated, delay])\n\n const ariaLabel = useMemo(() => extractText(children), [children])\n\n return (\n <span\n ref={elementRef}\n aria-label={ariaLabel} // Accessibility: Screen readers read the real text\n className={cn(\"relative block\", className)}\n >\n {/* Children always rendered but with opacity-0 initially */}\n <span\n className={cn(\n \"transition-opacity duration-300\",\n hasAnimated ? \"opacity-100\" : \"opacity-0\"\n )}\n >\n {children}\n </span>\n {/* Decoded animation overlay */}\n {showDecoded && displayContent && (\n <span className=\"absolute inset-0 transition-opacity duration-300\">\n {displayContent}\n </span>\n )}\n </span>\n )\n}\n\nconst SuspenseDecodedText = (props: TextDecoderProps) => {\n return (\n <Suspense\n fallback={\n <span className={cn(\"opacity-0\", props.className)}>\n {props.children}\n </span>\n }\n >\n <TextDecoder {...props} />\n </Suspense>\n )\n}\n\nexport default SuspenseDecodedText\n"],"mappings":"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","names":["textStartIndex","scrambledContent"]}
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
package/dist/index.css
CHANGED
|
@@ -352,7 +352,7 @@ video {
|
|
|
352
352
|
--border: 214.3 31.8% 91.4%;
|
|
353
353
|
--input: 214.3 31.8% 91.4%;
|
|
354
354
|
--ring: 222.2 84% 4.9%;
|
|
355
|
-
--radius: 0.
|
|
355
|
+
--radius: 0.625rem;
|
|
356
356
|
}
|
|
357
357
|
* {
|
|
358
358
|
border-color: hsl(var(--border));
|
|
@@ -392,6 +392,9 @@ body {
|
|
|
392
392
|
.relative {
|
|
393
393
|
position: relative;
|
|
394
394
|
}
|
|
395
|
+
.inset-0 {
|
|
396
|
+
inset: 0px;
|
|
397
|
+
}
|
|
395
398
|
.left-0 {
|
|
396
399
|
left: 0px;
|
|
397
400
|
}
|
|
@@ -407,6 +410,9 @@ body {
|
|
|
407
410
|
.mt-2 {
|
|
408
411
|
margin-top: 0.5rem;
|
|
409
412
|
}
|
|
413
|
+
.block {
|
|
414
|
+
display: block;
|
|
415
|
+
}
|
|
410
416
|
.flex {
|
|
411
417
|
display: flex;
|
|
412
418
|
}
|
|
@@ -727,6 +733,9 @@ body {
|
|
|
727
733
|
.duration-150 {
|
|
728
734
|
transition-duration: 150ms;
|
|
729
735
|
}
|
|
736
|
+
.duration-300 {
|
|
737
|
+
transition-duration: 300ms;
|
|
738
|
+
}
|
|
730
739
|
.ease-out {
|
|
731
740
|
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
732
741
|
}
|
|
@@ -745,6 +754,9 @@ body {
|
|
|
745
754
|
.duration-150 {
|
|
746
755
|
animation-duration: 150ms;
|
|
747
756
|
}
|
|
757
|
+
.duration-300 {
|
|
758
|
+
animation-duration: 300ms;
|
|
759
|
+
}
|
|
748
760
|
.ease-out {
|
|
749
761
|
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
750
762
|
}
|