import cn from 'classnames'; import { memo, useEffect, useRef, useState } from 'react'; import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils'; import { TAG_ROW_TEST_IDS } from '../../components/TagRow/constants'; import { Size, TagRowItemInner } from '../../types'; import { TagList } from '../TagList'; import { TagMore } from '../TagMore'; import { useResizeObserver } from './hooks'; import styles from './styles.module.scss'; import { getGapWidth } from './utils'; type TagRowTruncatedProps = WithSupportProps<{ items: TagRowItemInner[]; rowLimit: number; moreButtonLabel?: string; size: Size; className?: string; onItemRemove?(item: string): void; }>; function TagRowTruncatedInner({ items, rowLimit, size, moreButtonLabel = '', className, onItemRemove, ...rest }: TagRowTruncatedProps) { const [visibleTags, setVisibleTags] = useState([]); const [hiddenTags, setHiddenTags] = useState([]); const hiddenRowElementRef = useRef(null); const hiddenMoreButtonRef = useRef(null); const hiddenMoreButtonWrapperRef = useRef(null); const [firstTagElement, setFirstTagElement] = useState(null); const tagsMapRef = useRef(new Map()); const { width: maxWidth } = useResizeObserver(hiddenRowElementRef.current); const { width: firstVisibleTagWidth } = useResizeObserver(firstTagElement); function setTagElement(tag: TagRowItemInner, index: number) { return (tagElement: HTMLElement | null) => { if (index === 0 && tagElement) { setFirstTagElement(tagElement); } if (tagElement === null) { tagsMapRef.current.delete(tag); } else { tagsMapRef.current.set(tag, tagElement); } }; } const gapWidth = getGapWidth(hiddenRowElementRef); useEffect(() => { if (maxWidth < 1) { return; } const newVisibleTags: TagRowItemInner[] = []; const newHiddenTags: TagRowItemInner[] = []; const moreButtonWidth = hiddenMoreButtonRef.current?.offsetWidth || 0; let currentRowWidth = 0; let currentRowCount = 1; let currentRowVisibleTagAmount = 0; tagsMapRef.current.forEach((tagElement, tagRowItem) => { const tagWidth = tagElement?.offsetWidth || 0; const sumRowWidth = currentRowWidth + tagWidth + gapWidth; if (currentRowCount > rowLimit) { newHiddenTags.push(tagRowItem); return; } if (currentRowCount === rowLimit) { if (sumRowWidth + moreButtonWidth > maxWidth) { currentRowCount++; currentRowVisibleTagAmount = 0; newHiddenTags.push(tagRowItem); return; } currentRowWidth = sumRowWidth; newVisibleTags.push(tagRowItem); currentRowVisibleTagAmount++; return; } if (sumRowWidth > maxWidth) { currentRowWidth = currentRowVisibleTagAmount ? tagWidth + gapWidth : 0; currentRowCount++; currentRowVisibleTagAmount = 0; newVisibleTags.push(tagRowItem); return; } currentRowVisibleTagAmount++; currentRowWidth = sumRowWidth; newVisibleTags.push(tagRowItem); }); setVisibleTags(newVisibleTags); setHiddenTags(newHiddenTags); }, [items, rowLimit, maxWidth, onItemRemove, gapWidth, firstVisibleTagWidth]); return (
{hiddenTags.length > 0 && ( )}
); } export const TagRowTruncated = memo(TagRowTruncatedInner);