Created
June 4, 2021 14:47
-
-
Save igorcaldeira/9908aef69e1b51fa2186936fa7ae359e to your computer and use it in GitHub Desktop.
Hide extra text with js+react+resizeobserver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const isElement = (obj) => { | |
try { | |
return obj instanceof HTMLElement; | |
} catch (e) { | |
return ( | |
typeof obj === "object" && | |
obj.nodeType === 1 && | |
typeof obj.style === "object" && | |
typeof obj.ownerDocument === "object" | |
); | |
} | |
} | |
const innerChildrenIsVisibleOnWrapper = (containerBounds, childEl) => { | |
if (!containerBounds || !childEl) return false; | |
const childBounds = childEl.getBoundingClientRect(); | |
return ( | |
containerBounds.top <= childBounds.top && | |
containerBounds.left <= childBounds.left && | |
containerBounds.right >= childBounds.right && | |
containerBounds.bottom >= childBounds.bottom | |
); | |
}; | |
const TagContainer = ({ children }) => { | |
const containerRef = useRef(); | |
const [count, setCount] = useState(0); | |
const [hiddenList, setHiddenList] = useState({}); | |
const [containerBounds, setContainerBounds] = useState({}); | |
const displayCount = children?.length - count; | |
const newHiddenListEntry = (property, value) => { | |
const newObject = Object.assign(hiddenList, { [property]: value }); | |
const newCount = Object.values(newObject)?.filter((isHidden) => !isHidden) | |
?.length; | |
setHiddenList(newObject); | |
setCount(newCount); | |
}; | |
useLayoutEffect(() => { | |
let resizeObserver = {}; | |
if (containerRef.current) { | |
resizeObserver = new ResizeObserver(() => { | |
setContainerBounds(containerRef.current.getBoundingClientRect()); | |
}); | |
resizeObserver.observe(containerRef.current); | |
} | |
return () => { | |
if (resizeObserver?.unobserve && isElement(containerRef?.current)) { | |
resizeObserver.unobserve(containerRef?.current); | |
} | |
}; | |
}, []); | |
return ( | |
<ContainerWrapper> | |
<TagLine ref={containerRef}> | |
{React.Children.map(children, (child, childIndex) => | |
React.cloneElement(child, { | |
...child.props, | |
containerBounds, | |
newHiddenListEntry, | |
hiddenList, | |
displayCount, | |
childIndex, | |
countTagsRendered: children?.length - displayCount, | |
}) | |
)} | |
</TagLine> | |
</ContainerWrapper> | |
); | |
}; | |
const Tag = ({ | |
containerBounds, | |
id, | |
name, | |
newHiddenListEntry, | |
hiddenList, | |
displayCount, | |
countTagsRendered, | |
childIndex, | |
}) => { | |
const childRef = useRef(); | |
const endChar = childIndex < countTagsRendered - 1 ? ', ': ' '; | |
const showLastTagCounter = childIndex === countTagsRendered - 1 && Boolean(displayCount); | |
const shouldRenderTag = hiddenList?.[id] === false; | |
const validateVisibility = useCallback( | |
(bounds) => { | |
const response = innerChildrenIsVisibleOnWrapper( | |
bounds, | |
childRef?.current | |
); | |
if (newHiddenListEntry) { | |
newHiddenListEntry(id, !response); | |
} | |
}, | |
[id, newHiddenListEntry] | |
); | |
useLayoutEffect(() => { | |
validateVisibility(containerBounds); | |
}, [validateVisibility, containerBounds]); | |
return ( | |
<InlineTag | |
ref={childRef} | |
style={{ opacity: shouldRenderTag ? "1" : "0" }} | |
> | |
{name}{endChar}{showLastTagCounter && `e +${displayCount}`} | |
</InlineTag> | |
); | |
}; | |
const InlineTagWithCounter = ({ tagsList = [] }) => <TagContainer> | |
{tagsList.map((obj) => <Tag {...obj} />)} | |
</TagContainer>; | |
export default InlineTagWithCounter; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment