UNPKG

flowtoken

Version:

![flow token demo](https://nextjs-omega-five-46.vercel.app/demo.gif)

133 lines (132 loc) 5.96 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const TokenizedText = ({ input, sep, animation, animationDuration, animationTimingFunction, animationIterationCount }) => { // Early return for no animation case if (animation === 'none' || !animation) { return react_1.default.createElement(react_1.default.Fragment, null, input); } // Track previous input to detect changes const prevInputRef = (0, react_1.useRef)(''); // Track tokens with their source for proper keying in diff mode const tokensWithSources = (0, react_1.useRef)([]); // For detecting and handling duplicated content const fullTextRef = (0, react_1.useRef)(''); const tokens = react_1.default.useMemo(() => { if (react_1.default.isValidElement(input)) return [input]; if (typeof input !== 'string') return null; // For diff mode, we need to handle things differently if (sep === 'diff') { // If this is the first render or we've gone backward, reset everything if (!prevInputRef.current || input.length < prevInputRef.current.length) { tokensWithSources.current = []; fullTextRef.current = ''; } // Only process input if it's different from previous if (input !== prevInputRef.current) { // Find the true unique content by comparing with our tracked full text // This handles cases where the input contains duplicates // First check if we're just seeing the same content repeated if (input.includes(fullTextRef.current)) { const uniqueNewContent = input.slice(fullTextRef.current.length); // Only add if there's actual new content if (uniqueNewContent.length > 0) { tokensWithSources.current.push({ text: uniqueNewContent, source: tokensWithSources.current.length }); // Update our full text tracking fullTextRef.current = input; } } else { // Handle case when input completely changes // Just take the whole thing as a new token tokensWithSources.current = [{ text: input, source: 0 }]; fullTextRef.current = input; } } // Return the tokensWithSources directly return tokensWithSources.current; } // Original word/char splitting logic let splitRegex; if (sep === 'word') { splitRegex = /(\s+)/; } else if (sep === 'char') { splitRegex = /(.)/; } else { throw new Error('Invalid separator: must be "word", "char", or "diff"'); } return input.split(splitRegex).filter(token => token.length > 0); }, [input, sep]); // Update previous input after processing (0, react_1.useEffect)(() => { if (typeof input === 'string') { prevInputRef.current = input; } }, [input]); // Helper function to check if token is a TokenWithSource type const isTokenWithSource = (token) => { return token !== null && typeof token === 'object' && 'text' in token && 'source' in token; }; // Apply animation style only once instead of creating it repeatedly const animationStyle = react_1.default.useMemo(() => ({ animationName: animation, animationDuration, animationTimingFunction, animationIterationCount, whiteSpace: 'pre-wrap', display: 'inline-block', }), [animation, animationDuration, animationTimingFunction, animationIterationCount]); return (react_1.default.createElement(react_1.default.Fragment, null, tokens === null || tokens === void 0 ? void 0 : tokens.map((token, index) => { // Determine the key and text based on token type let key = index; let text = ''; if (isTokenWithSource(token)) { key = token.source; text = token.text; } else if (typeof token === 'string') { key = index; text = token; } else if (react_1.default.isValidElement(token)) { key = index; text = ''; return react_1.default.cloneElement(token, { key }); } return (react_1.default.createElement("span", { key: key, style: animationStyle }, text)); }))); }; exports.default = react_1.default.memo(TokenizedText); // Memoize the entire component to prevent unnecessary rerenders