Skip to content

Instantly share code, notes, and snippets.

@igorcaldeira
Created June 4, 2021 14:47
Show Gist options
  • Save igorcaldeira/9908aef69e1b51fa2186936fa7ae359e to your computer and use it in GitHub Desktop.
Save igorcaldeira/9908aef69e1b51fa2186936fa7ae359e to your computer and use it in GitHub Desktop.
Hide extra text with js+react+resizeobserver
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