UNPKG

@azure/communication-react

Version:

React library for building modern communication user experiences utilizing Azure Communication Services

86 lines 4.13 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import React, { useLayoutEffect, useRef, useState } from 'react'; /** * An SVG element component that wraps inner text to fit the width of the SVG. * @private */ export const SvgWithWordWrapping = (props) => { const { width, text, lineHeightPx, bufferHeightPx, role } = props; const svgRef = useRef(null); const calculationTextElement = useRef(null); const visibleTextElement = useRef(null); const [height, setHeight] = useState(0); // useLayoutEffect ensures that the calculationTextElement is rendered before being used for calculations. // Using useLayoutEffect over useEffect ensures we do not get a layout shift when the visibleTextElement is rendered // and the height is updated. This is because useLayoutEffect runs synchronously after DOM mutations but // before the browser has a chance to paint. See https://reactjs.org/docs/hooks-reference.html#uselayouteffect // for more details. useLayoutEffect(() => { if (text && calculationTextElement.current && visibleTextElement.current) { const numLines = convertTextToWrappedText(calculationTextElement.current, visibleTextElement.current, width, lineHeightPx); setHeight(numLines * lineHeightPx); } }, [width, lineHeightPx, text]); return React.createElement("svg", { "aria-level": 1, role: role, width: width, height: height + bufferHeightPx, ref: svgRef, xmlns: "http://www.w3.org/2000/svg" }, React.createElement("text", { height: 0, ref: calculationTextElement, style: { visibility: 'hidden' } }, text), React.createElement("text", { ref: visibleTextElement, x: "0", y: bufferHeightPx / 4, role: "heading", "aria-level": 1 })); }; /** * Wrap text in tspan elements to fit the width of the SVG * @param baseTextElement The text element to create the wrapped text from. * @param outputTextElement The text element to insert the wrapped text into. * @param maxWidth The maximum width of the text element. * @param lineHeightPx The height of each line in pixels. * @returns The number of lines of text. */ const convertTextToWrappedText = (inputTextElement, outputTextElement, maxWidth, lineHeightPx) => { var _a, _b; const words = (_b = (_a = inputTextElement.textContent) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : []; if (words.length === 0 || words[0] === '') { throw new Error('Text element must contain text'); } // The current line being built. let line = ''; // Running total of the number of lines. let numLines = 0; // First, clear the output text element. outputTextElement.textContent = ''; // Iterate through each word and create a tspan element for each line. for (let i = 0; i < words.length; i++) { const testLine = line + words[i] + ' '; const testWidth = inputTextElement.getSubStringLength(0, testLine.length); if (testWidth > maxWidth && i > 0) { const newLine = constructTSpanLine(line, lineHeightPx); outputTextElement.appendChild(newLine); line = words[i] + ' '; numLines++; } else { line = testLine; } } // Add the last line. const newLine = constructTSpanLine(line, lineHeightPx); outputTextElement.appendChild(newLine); numLines++; // Return the number of lines to calculate the height of the SVG. return numLines; }; /** * Create a tspan element for a line of text, with text set to be centered. * @param line The line of text. * @param lineHeightPx The height of each line in pixels. * @returns The tspan element. */ const constructTSpanLine = (line, lineHeightPx) => { const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); tspan.textContent = line; tspan.setAttribute('x', '50%'); tspan.setAttribute('dy', `${lineHeightPx}px`); tspan.setAttribute('text-anchor', 'middle'); return tspan; }; //# sourceMappingURL=SvgWithWordWrapping.js.map