UNPKG

react-truncate-list

Version:

Truncate a list of elements with a symbol or component of your choice

124 lines (123 loc) 5.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TruncatedList = void 0; const react_1 = require("react"); const rectContainsRect = (parent, child) => { return (child.top >= parent.top && child.bottom <= parent.bottom && child.left >= parent.left && child.right <= parent.right); }; const TruncatedList = ({ renderTruncator, alwaysShowTruncator, onResize, className, style, children, }) => { const containerRef = (0, react_1.useRef)(null); const truncate = (0, react_1.useCallback)(() => { if (!containerRef.current) { return; } const childNodes = Array.from(containerRef.current.children); // // Put the list in its initial state. // // Change from a scrollable container to overflow: hidden during hydration containerRef.current.style.overflow = "hidden"; // Show all items, hide all truncators. for (let i = 0; i < childNodes.length; ++i) { childNodes[i].hidden = i % 2 === 0; } // If there are no items (the last truncator is always included). if (childNodes.length === 1) { return; } // // Test if truncation is necessary. // if (alwaysShowTruncator) { // if the last truncator fits, exit const truncatorEl = childNodes[childNodes.length - 1]; truncatorEl.hidden = false; if (rectContainsRect(containerRef.current.getBoundingClientRect(), truncatorEl.getBoundingClientRect())) { return; } truncatorEl.hidden = true; } else { // if the last item fits, exit const itemEl = childNodes[childNodes.length - 2]; if (rectContainsRect(containerRef.current.getBoundingClientRect(), itemEl.getBoundingClientRect())) { return; } } // // Truncation is necessary - binary search to find the last truncator that can fit. // const numTruncators = Math.floor((childNodes.length - 1) / 2); let left = 0; let right = numTruncators - 1; let truncatorIndex = null; while (left <= right) { const middle = Math.floor((left + right) / 2); // show all items before the truncator for (let i = 0; i < middle; i += 1) { childNodes[i * 2 + 1].hidden = false; } // hide all items after the truncator for (let i = middle; i < numTruncators; i += 1) { childNodes[i * 2 + 1].hidden = true; } const truncatorEl = childNodes[middle * 2]; truncatorEl.hidden = false; // check if this truncator fits if (rectContainsRect(containerRef.current.getBoundingClientRect(), truncatorEl.getBoundingClientRect())) { truncatorIndex = middle; left = middle + 1; } else { right = middle - 1; } truncatorEl.hidden = true; } // If we didn't find a truncator that fits, everything will be hidden at this point and we can exit early if (truncatorIndex === null) { return; } // // Now we have found the last truncator that fits, show it. // // show all items before the truncator for (let i = 0; i < truncatorIndex; i += 1) { childNodes[i * 2 + 1].hidden = false; } // hide all items after truncator for (let i = truncatorIndex; i < numTruncators; i += 1) { childNodes[i * 2 + 1].hidden = true; } const truncatorEl = childNodes[truncatorIndex * 2]; truncatorEl.hidden = false; }, [alwaysShowTruncator]); // Set up a resize observer (0, react_1.useLayoutEffect)(() => { const resizeObserver = new ResizeObserver(() => { if (onResize) { onResize({ truncate }); } else { truncate(); } }); const containerEl = containerRef.current; if (!containerEl) { throw new Error("assert: container is mounted"); } resizeObserver.observe(containerEl); truncate(); return () => { resizeObserver.unobserve(containerEl); }; // trigger if children change also }, [truncate, onResize, children]); const childArray = react_1.default.Children.toArray(children); const items = childArray.map((item, i) => (react_1.default.createElement(react_1.default.Fragment, { key: i }, react_1.default.createElement("li", { hidden: true }, renderTruncator({ hiddenItemsCount: childArray.length - i })), react_1.default.createElement("li", null, item)))); return (react_1.default.createElement("ul", { ref: containerRef, className: `react-truncate-list ${className !== null && className !== void 0 ? className : ""}`, style: style }, items, react_1.default.createElement("li", { hidden: true }, renderTruncator({ hiddenItemsCount: 0 })))); }; exports.TruncatedList = TruncatedList;