Skip to content

Instantly share code, notes, and snippets.

@erickvieira
Last active April 12, 2021 21:17
Show Gist options
  • Save erickvieira/52038ce9ccf8208a55cb5c31506b7fcd to your computer and use it in GitHub Desktop.
Save erickvieira/52038ce9ccf8208a55cb5c31506b7fcd to your computer and use it in GitHub Desktop.
AntD TagsCloud Component with StyledComponents
import React from 'react';
import styled, { css } from 'styled-components';
export interface TagsWrapperProps extends React.HTMLAttributes<HTMLDivElement> {
collapsed?: boolean;
uncollapsedHeight?: number | string;
}
const handleCollapse = (props: TagsWrapperProps) => css`
max-height: ${props.collapsed
? '22px'
: typeof props.uncollapsedHeight === 'string'
? props.uncollapsedHeight
: `${props.uncollapsedHeight}em`};
overflow: ${props.collapsed ? 'hidden' : 'auto'};
`;
export const TagsWrapper = styled.div.attrs((props: TagsWrapperProps) => ({
...props,
collapsed: props.collapsed ?? false
}))`
flex: 1;
${handleCollapse}
transition: all 0.3s ease-in-out;
`;
export interface TagsCloudContentProps
extends React.HTMLAttributes<HTMLDivElement> {
showToggler?: boolean;
}
const handleShowToggle = (props: TagsCloudContentProps) => {
if (props.showToggler) {
return css`
margin-right: 32px;
position: relative;
& > .tags-cloud-toggler {
position: absolute;
bottom: 0;
left: calc(100% - 32px);
}
`;
}
};
export const TagsCloudContent = styled.div.attrs(
(props: TagsCloudContentProps) => ({
...props,
showToggler: props.showToggler ?? false
})
)`
margin: 8px 0;
display: flex;
align-items: center;
width: 100%;
${handleShowToggle}
`;
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { TagsWrapper, TagsCloudContent } from './TagsCloud.styled';
import { Button } from 'antd';
let defaultTogglerOffset: string | number = 'calc(100% - 32px)';
export interface TagsCloudProps extends React.HTMLAttributes<HTMLDivElement> {
wrapperProps?: React.HTMLAttributes<HTMLDivElement>;
children: React.ReactNode;
tagsSelector?: string;
uncollapsedHeight?: string | number;
}
function TagsCloud({
wrapperProps = {},
children,
tagsSelector = '.ant-tag',
uncollapsedHeight = '12em',
...contentProps
}: TagsCloudProps) {
const [collapsed, setCollapsed] = useState(false);
const [shotToggler, setShowToggler] = useState(false);
const [togglerOffset, setTogglerOffset] = useState<string | number>(
defaultTogglerOffset
);
const tagsWrapperRef = useRef() as React.MutableRefObject<HTMLDivElement>;
useLayoutEffect(() => {
if (tagsWrapperRef?.current) {
const wrapperWidth = tagsWrapperRef.current.clientWidth;
const renderedTags = Array.from(
tagsWrapperRef.current.querySelectorAll(tagsSelector)
);
if (renderedTags.length === 0) return;
let childrenTotalWidth = 0;
let childDOMRect: DOMRect;
for (const index in renderedTags) {
childDOMRect = renderedTags[index].getBoundingClientRect();
childrenTotalWidth += childDOMRect.width;
console.log({
childrenTotalWidth,
wrapperWidth,
childDomRect: childDOMRect
});
if (childrenTotalWidth > wrapperWidth) {
setCollapsed(true);
setShowToggler(true);
defaultTogglerOffset = childrenTotalWidth - childDOMRect.width + 8;
setTogglerOffset(defaultTogglerOffset);
break;
}
}
}
}, [tagsWrapperRef]);
const toggle = useCallback(() => {
if (collapsed) {
const lastTag = tagsWrapperRef.current.querySelector(
`${tagsSelector}:last-child`
);
setTogglerOffset(lastTag!.getBoundingClientRect().left + 20);
} else setTogglerOffset(defaultTogglerOffset);
setCollapsed(!collapsed);
}, [collapsed]);
return (
<TagsCloudContent
{...contentProps}
className={`tags-cloud ${contentProps.className ?? ''}`}
showToggler={shotToggler}>
<TagsWrapper
{...wrapperProps}
ref={tagsWrapperRef}
collapsed={collapsed}
uncollapsedHeight={uncollapsedHeight}>
{children}
</TagsWrapper>
{shotToggler && (
<Button
className="tags-cloud-toggler"
size="small"
type="link"
shape="circle"
onClick={toggle}
style={{ left: togglerOffset }}>
{collapsed ? '+' : '-'}
</Button>
)}
</TagsCloudContent>
);
}
export default TagsCloud;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment