Show · OverflowList

OverflowList

OverflowList is a behavior component used to take list of items and display as many items as can fit inside itself. Overflowed items that do not fit are collected and rendered by callback function. The visible items will be recomputed when a resize is detected.

Demos

How to import

import { OverflowList } from '@fibrous/semi-ui-react';

Collapse Mode - Simple

You could use renderMode="collapse" (default) to render items.

0100


Code
import { useState, type ReactNode } from 'react';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-react';
import { OverflowList, Tag, Slider } from '@fibrous/semi-ui-react';

interface Item {
    icon: ReactNode;
    key: string;
}

export default function OverflowListDemo() {
    const [width, setWidth] = useState(100);
    const renderOverflow = (items: Item[]) => {
        return items.length ? <Tag style={{ flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' }}>+{items.length}</Tag> : null;
    };
    const renderItem = (item: Item) => {
        return (
            <Tag color="blue" key={item.key} style={{ marginRight: 8, flex: '0 0 auto' }}>
                {item.icon}
                {item.key}
            </Tag>
        );
    };

    const items: Item[] = [
        { icon: <IconAlarm style={{ marginRight: 4 }} />, key: 'alarm' },
        { icon: <IconBookmark style={{ marginRight: 4 }} />, key: 'bookmark' },
        { icon: <IconCamera style={{ marginRight: 4 }} />, key: 'camera' },
        { icon: <IconDuration style={{ marginRight: 4 }} />, key: 'duration' },
        { icon: <IconEdit style={{ marginRight: 4 }} />, key: 'edit' },
        { icon: <IconFolder style={{ marginRight: 4 }} />, key: 'folder' },
    ];

    return (
        <div>
            <Slider aria-label="Container width" step={1} value={width} onChange={(value) => typeof value === 'number' && setWidth(value)} />
            <br />
            <br />
            <div style={{ width: `${width}%` }}>
                <OverflowList items={items} overflowRenderer={renderOverflow} visibleItemRenderer={renderItem} />
            </div>
        </div>
    );
}

Collapse Mode - Direction

collapse mode supports two collapseFrom directions: start and end (default).

0100


Code
import { useState, type ReactNode } from 'react';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-react';
import { OverflowList, Tag, Slider } from '@fibrous/semi-ui-react';

interface Item {
    icon: ReactNode;
    key: string;
}

export default function OverflowListDemo() {
    const [width, setWidth] = useState(100);
    const renderOverflow = (items: Item[]) => {
        return items.length ? <Tag style={{ marginRight: 8, flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' }}>+{items.length}</Tag> : null;
    };
    const renderItem = (item: Item) => {
        return (
            <Tag color="blue" key={item.key} style={{ marginRight: 8, flex: '0 0 auto' }}>
                {item.icon}
                {item.key}
            </Tag>
        );
    };

    const items: Item[] = [
        { icon: <IconAlarm style={{ marginRight: 4 }} />, key: 'alarm' },
        { icon: <IconBookmark style={{ marginRight: 4 }} />, key: 'bookmark' },
        { icon: <IconCamera style={{ marginRight: 4 }} />, key: 'camera' },
        { icon: <IconDuration style={{ marginRight: 4 }} />, key: 'duration' },
        { icon: <IconEdit style={{ marginRight: 4 }} />, key: 'edit' },
        { icon: <IconFolder style={{ marginRight: 4 }} />, key: 'folder' },
    ];

    return (
        <div>
            <Slider aria-label="Container width" step={1} value={width} onChange={(value) => typeof value === 'number' && setWidth(value)} />
            <br />
            <br />
            <div style={{ width: `${width}%` }}>
                <OverflowList items={items} collapseFrom="start" overflowRenderer={renderOverflow} visibleItemRenderer={renderItem} />
            </div>
        </div>
    );
}

Collapse Mode - Visible

collapse mode supports to set up minimum visible items that will not be collected.

0100


Code
import { useState, type ReactNode } from 'react';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-react';
import { OverflowList, Tag, Slider } from '@fibrous/semi-ui-react';

interface Item {
    icon: ReactNode;
    key: string;
}

export default function OverflowListDemo() {
    const [width, setWidth] = useState(100);
    const renderOverflow = (items: Item[]) => {
        return items.length ? <Tag style={{ flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' }}>+{items.length}</Tag> : null;
    };
    const renderItem = (item: Item) => {
        return (
            <Tag color="blue" key={item.key} style={{ marginRight: 8, flex: '0 0 auto' }}>
                {item.icon}
                {item.key}
            </Tag>
        );
    };

    const items: Item[] = [
        { icon: <IconAlarm style={{ marginRight: 4 }} />, key: 'alarm' },
        { icon: <IconBookmark style={{ marginRight: 4 }} />, key: 'bookmark' },
        { icon: <IconCamera style={{ marginRight: 4 }} />, key: 'camera' },
        { icon: <IconDuration style={{ marginRight: 4 }} />, key: 'duration' },
        { icon: <IconEdit style={{ marginRight: 4 }} />, key: 'edit' },
        { icon: <IconFolder style={{ marginRight: 4 }} />, key: 'folder' },
    ];

    return (
        <div>
            <Slider aria-label="Container width" step={1} value={width} onChange={(value) => typeof value === 'number' && setWidth(value)} />
            <br />
            <br />
            <div style={{ width: `${width}%` }}>
                <OverflowList items={items} minVisibleItems={3} overflowRenderer={renderOverflow} visibleItemRenderer={renderItem} />
            </div>
        </div>
    );
}

Scroll Mode

You could use renderMode="scroll" for a scrollable list.
If you need certain element in the list to scrollIntoView, use document.querySelector(`.item-cls[data-scrollkey="${key}"] to select to corresponding DOM.

0100


+0
alarm
bookmark
camera
duration
edit
folder
+0
Code
import { useState, type ReactNode } from 'react';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-react';
import { OverflowList, Tag, Slider } from '@fibrous/semi-ui-react';

interface Item {
    icon: ReactNode;
    key: string;
}

export default function OverflowListDemo() {
    const [width, setWidth] = useState(100);
    const renderHiddenCount = (hiddenItems: Item[]) => <Tag style={{ marginRight: 8, marginLeft: 8, flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' }}>+{hiddenItems.length}</Tag>;
    const renderOverflow = ([start, end]: [Item[], Item[]]): [ReactNode, ReactNode] => [renderHiddenCount(start), renderHiddenCount(end)];
    const renderItem = (item: Item) => {
        return (
            <Tag key={item.key} color="blue" style={{ marginRight: 8, flex: '0 0 auto' }}>
                {item.icon}
                {item.key}
            </Tag>
        );
    };

    const items: Item[] = [
        { icon: <IconAlarm style={{ marginRight: 4 }} />, key: 'alarm' },
        { icon: <IconBookmark style={{ marginRight: 4 }} />, key: 'bookmark' },
        { icon: <IconCamera style={{ marginRight: 4 }} />, key: 'camera' },
        { icon: <IconDuration style={{ marginRight: 4 }} />, key: 'duration' },
        { icon: <IconEdit style={{ marginRight: 4 }} />, key: 'edit' },
        { icon: <IconFolder style={{ marginRight: 4 }} />, key: 'folder' },
    ];

    return (
        <div>
            <Slider aria-label="Container width" step={1} value={width} onChange={(value) => typeof value === 'number' && setWidth(value)} />
            <br />
            <br />
            <div style={{ width: `${width}%` }}>
                <OverflowList items={items} renderMode="scroll" overflowRenderer={renderOverflow} visibleItemRenderer={renderItem} />
            </div>
        </div>
    );
}

API Reference

Props

PropertyDescriptionTypeDefault
classNameClass name of the root element.string-
collapseFromCollapse direction in collapse mode: start or end.'start' | 'end'"end"
itemKeyProperty name of each item's key, or a function returning it. Defaults to the key property, then the item's position in items.Typestring | number | ((item: T) => string | number)-
itemsItems to render. They are data, not nodes; visibleItemRenderer renders them. Scroll mode requires a key on each item, the field named by itemKey when it is set.TypeArray<T>[]
minVisibleItemsMinimum number of visible items in collapse mode; these items are not collapsed even when they do not fit.number0
onIntersectCalled in scroll mode when the intersection state of the items changes, with each item's IntersectionObserverEntry keyed by its key.Type(res: { [key: string]: IntersectionObserverEntry }) => void-
onOverflowCalled in collapse mode when the overflowing items change, with an empty array once everything fits.Type(overflowItems: Array<T>) => voidType() => null
onVisibleStateChangeCalled in scroll mode when the visible or hidden state of the items changes, with a Map keyed by item key.Type(visibleState: Map<string, boolean>) => void-
overflowRenderDirectionWhere scroll mode renders the two overflow nodes: both, start or end. Used by Tabs, not meant for end users.'both' | 'start' | 'end'"both"
overflowRendererRenders the overflowing items. In collapse mode it receives the items that do not fit and returns one overflow node; in scroll mode it receives [start, end], the items scrolled out before and after the visible ones, and returns one node for each side.Type((overflowItems: Array<T>) => ReactNode) | ((overflowItems: [Array<T>, Array<T>]) => [ReactNode, ReactNode])Type(): ReactElement => null
refReference to the root element.TypeReact.Ref<HTMLDivElement>-
renderModeRender mode: collapse or scroll.'collapse' | 'scroll'"collapse"
styleStyle of the root element.CSSProperties-
thresholdVisible ratio at which scroll mode reports an item's intersection.number0.75
visibleItemRendererRenders one visible item and returns its React element; index is the item's position in items. In scroll mode the element is observed and gets a data-scrollkey attribute.Type(item: T, index: number) => ReactElementType(): ReactElement => null
wrapperClassNameClass name of the scroll wrapper in scroll mode.string-
wrapperPropsOther attributes of the scroll wrapper in scroll mode, such as a role that owns the visible items. Its className and style are overridden by wrapperClassName and wrapperStyle.TypeHTMLAttributes<HTMLDivElement>-
wrapperStyleStyle of the scroll wrapper in scroll mode, as a style object.CSSProperties-

Other native attributes such as aria-*, id, data-* and onClick land on the root element.

Accessibility

ARIA

  • When aria-label or aria-labelledby is passed, the root element gets role="group" so the group of items has a readable name; without them the root has no role