UNPKG

@superset-ui/core

Version:
86 lines 3.76 kB
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import { useLayoutEffect, useRef, useState } from 'react'; /** * This hook encapsulates logic to support truncation of child HTML * elements contained in a fixed-width parent HTML element. Given * a ref to the parent element and optionally a ref to the "+x" * component that shows the number of truncated items, this hook * will return the number of elements that are not fully visible * (including those completely hidden) and whether any elements * are completely hidden. */ const useChildElementTruncation = () => { const [elementsTruncated, setElementsTruncated] = useState(0); const [hasHiddenElements, setHasHiddenElements] = useState(false); const elementRef = useRef(null); const plusRef = useRef(null); useLayoutEffect(() => { const onResize = () => { const currentElement = elementRef.current; if (!currentElement) { return; } const plusRefElement = plusRef.current; const { scrollWidth, clientWidth, childNodes } = currentElement; if (scrollWidth > clientWidth) { // "..." is around 6px wide const truncationWidth = 6; const plusSize = plusRefElement?.offsetWidth || 0; const maxWidth = clientWidth - truncationWidth; const elementsCount = childNodes.length; let width = 0; let hiddenElements = 0; for (let i = 0; i < elementsCount; i += 1) { const itemWidth = childNodes[i].offsetWidth; const remainingWidth = maxWidth - width - plusSize; // assures it shows +{number} only when the item is not visible if (remainingWidth <= 0) { hiddenElements += 1; } width += itemWidth; } if (elementsCount > 1 && hiddenElements) { setHasHiddenElements(true); setElementsTruncated(hiddenElements); } else { setHasHiddenElements(false); setElementsTruncated(1); } } else { setHasHiddenElements(false); setElementsTruncated(0); } }; const obs = new ResizeObserver(onResize); const element = elementRef.current?.parentElement; if (element) { obs.observe(element); } onResize(); return () => { obs.disconnect(); }; }, [plusRef.current]); // plus is rendered dynamically - the component rerenders the hook when plus appears, this makes sure that useLayoutEffect is rerun return [elementRef, plusRef, elementsTruncated, hasHiddenElements]; }; export default useChildElementTruncation; //# sourceMappingURL=useChildElementTruncation.js.map