UNPKG

tap

Version:

A Test-Anything-Protocol library for JavaScript

165 lines (126 loc) 5.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _widestLine = _interopRequireDefault(require("widest-line")); var _wrapText = _interopRequireDefault(require("./wrap-text")); var _getMaxWidth = _interopRequireDefault(require("./get-max-width")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const isAllTextNodes = node => { if (node.nodeName === '#text') { return true; } if (node.nodeName === 'SPAN') { if (node.textContent) { return true; } if (Array.isArray(node.childNodes)) { return node.childNodes.every(isAllTextNodes); } } return false; }; // Squashing text nodes allows to combine multiple text nodes into one and write // to `Output` instance only once. For example, <Text>hello{' '}world</Text> // is actually 3 text nodes, which would result 3 writes to `Output`. // // Also, this is necessary for libraries like ink-link (https://github.com/sindresorhus/ink-link), // which need to wrap all children at once, instead of wrapping 3 text nodes separately. const squashTextNodes = node => { // If parent container is `<Box>`, text nodes will be treated as separate nodes in // the tree and will have their own coordinates in the layout. // To ensure text nodes are aligned correctly, take X and Y of the first text node // and use them as offset for the rest of the nodes // Only first node is taken into account, because other text nodes can't have margin or padding, // so their coordinates will be relative to the first node anyway const offsetX = node.childNodes[0].yogaNode.getComputedLeft(); const offsetY = node.childNodes[0].yogaNode.getComputedTop(); let text = '\n'.repeat(offsetY) + ' '.repeat(offsetX); for (const childNode of node.childNodes) { let nodeText; if (childNode.nodeName === '#text') { nodeText = childNode.nodeValue; } if (childNode.nodeName === 'SPAN') { nodeText = childNode.textContent || squashTextNodes(childNode); } // Since these text nodes are being concatenated, `Output` instance won't be able to // apply children transform, so we have to do it manually here for each text node if (childNode.unstable__transformChildren) { nodeText = childNode.unstable__transformChildren(nodeText); } text += nodeText; } return text; }; // After nodes are laid out, render each to output object, which later gets rendered to terminal const renderNodeToOutput = (node, output, { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements }) => { if (node.unstable__static && skipStaticElements) { return; } const { yogaNode } = node; // Left and top positions in Yoga are relative to their parent node const x = offsetX + yogaNode.getComputedLeft(); const y = offsetY + yogaNode.getComputedTop(); // Transformers are functions that transform final text output of each component // See Output class for logic that applies transformers let newTransformers = transformers; if (node.unstable__transformChildren) { newTransformers = [node.unstable__transformChildren, ...transformers]; } // Nodes with only text inside if (node.textContent) { let text = node.textContent; // Since text nodes are always wrapped in an additional node, parent node // is where we should look for attributes if (node.parentNode.style.textWrap) { const currentWidth = (0, _widestLine.default)(text); const maxWidth = (0, _getMaxWidth.default)(node.parentNode.yogaNode); if (currentWidth > maxWidth) { text = (0, _wrapText.default)(text, maxWidth, { textWrap: node.parentNode.style.textWrap }); } } output.write(x, y, text, { transformers: newTransformers }); return; } // Text nodes if (node.nodeName === '#text') { output.write(x, y, node.nodeValue, { transformers: newTransformers }); return; } // Nodes that have other nodes as children if (Array.isArray(node.childNodes) && node.childNodes.length > 0) { const isFlexDirectionRow = node.style.flexDirection === 'row'; if (isFlexDirectionRow && node.childNodes.every(isAllTextNodes)) { let text = squashTextNodes(node); if (node.style.textWrap) { const currentWidth = (0, _widestLine.default)(text); const maxWidth = (0, _getMaxWidth.default)(yogaNode); if (currentWidth > maxWidth) { text = (0, _wrapText.default)(text, maxWidth, { textWrap: node.style.textWrap }); } } output.write(x, y, text, { transformers: newTransformers }); return; } for (const childNode of node.childNodes) { renderNodeToOutput(childNode, output, { offsetX: x, offsetY: y, transformers: newTransformers, skipStaticElements }); } } }; var _default = renderNodeToOutput; exports.default = _default;