UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

159 lines 6.21 kB
/** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import React from 'react'; import { ELLIPSIS_CHAR, ESTIMATION_FACTORS } from './constants'; /** * Recursively extracts plain text from React nodes */ export const getTextContent = (node) => { if (typeof node === 'string') { return node; } if (typeof node === 'number') { return node.toString(); } if (React.isValidElement(node)) { // Type assertion to access props safely const element = node; return getTextContent(element.props.children); } if (Array.isArray(node)) { return node.map(getTextContent).join(''); } return ''; }; /** * Truncates React node content by character count while preserving element structure. */ export const truncateReactNodeByCharacters = (node, maxCharacters, shouldAddEllipsis = true) => { // Handle the case where node is an array (React children) const normalizedNode = Array.isArray(node) ? React.createElement(React.Fragment, null, ...node) : node; // Get the total character count first to determine if truncation is needed const totalLength = getTextContent(normalizedNode).length; if (totalLength <= maxCharacters) { return { needsTruncation: false, truncatedContent: null }; } let charCount = 0; let hasReachedLimit = false; const truncateNode = (currentNode) => { // Stop processing if character limit is reached if (hasReachedLimit || charCount >= maxCharacters) { hasReachedLimit = true; return null; } // Handle arrays of children (mixed content case) if (Array.isArray(currentNode)) { const truncatedChildren = []; for (const child of currentNode) { if (hasReachedLimit || charCount >= maxCharacters) { break; } const truncatedChild = truncateNode(child); if (truncatedChild !== null) { truncatedChildren.push(truncatedChild); } } return truncatedChildren.length > 0 ? truncatedChildren : null; } if (typeof currentNode === 'string') { const availableChars = maxCharacters - charCount; if (currentNode.length <= availableChars) { charCount += currentNode.length; return currentNode; } // Truncate at character limit, preferring word boundaries let truncated = currentNode.substring(0, availableChars); const lastSpaceIndex = truncated.lastIndexOf(' '); if (lastSpaceIndex > availableChars * ESTIMATION_FACTORS.WORD_BOUNDARY) { truncated = truncated.substring(0, lastSpaceIndex); } truncated = truncated.trim(); charCount += truncated.length; hasReachedLimit = true; return truncated; } if (typeof currentNode === 'number') { const str = currentNode.toString(); const availableChars = maxCharacters - charCount; if (str.length <= availableChars) { charCount += str.length; return currentNode; } const truncated = str.substring(0, availableChars); charCount += truncated.length; hasReachedLimit = true; return truncated; } if (!React.isValidElement(currentNode)) { return currentNode; } const { children } = currentNode.props; if (children === null || children === undefined) { return currentNode; } if (typeof children === 'string') { const truncatedChild = truncateNode(children); if (truncatedChild === null) { return null; } return React.cloneElement(currentNode, {}, truncatedChild); } if (Array.isArray(children)) { const truncatedChildren = []; for (const child of children) { if (hasReachedLimit || charCount >= maxCharacters) { break; } const truncatedChild = truncateNode(child); if (truncatedChild !== null) { truncatedChildren.push(truncatedChild); } } if (truncatedChildren.length === 0) { return null; } return React.cloneElement(currentNode, {}, ...truncatedChildren); } // Single React element child const truncatedChild = truncateNode(children); if (truncatedChild === null) { return null; } return React.cloneElement(currentNode, {}, truncatedChild); }; const truncatedResult = truncateNode(normalizedNode); // Convert array results to proper React elements const finalResult = Array.isArray(truncatedResult) ? React.createElement(React.Fragment, null, ...truncatedResult) : truncatedResult; // Add ellipsis if needed if (shouldAddEllipsis && finalResult !== null) { const withEllipsis = React.createElement(React.Fragment, null, finalResult, ELLIPSIS_CHAR); return { needsTruncation: true, truncatedContent: withEllipsis }; } return { needsTruncation: true, truncatedContent: finalResult, }; }; //# sourceMappingURL=text-overflow-utils.js.map