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, Slider, Tag } from '@fibrous/semi-ui-solid';

Collapse Mode - Simple

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

items holds data only, never nodes. Each visible item is rendered by the element visibleItemRenderer returns; overflowing items are rendered by overflowRenderer, and collapse mode renders one overflow node whose argument is the items that do not fit.

0100


Code
import { createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-solid';
import { OverflowList, Slider, Tag } from '@fibrous/semi-ui-solid';

interface Item {
    icon: typeof IconAlarm;
    key: string;
}

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

const renderOverflow = (overflowItems: Item[]) => (overflowItems.length ? <Tag style={{ flex: '0 0 auto', 'font-variant-numeric': 'tabular-nums' }}>+{overflowItems.length}</Tag> : null);

const renderItem = (item: Item) => (
    <Tag color="blue" style={{ 'margin-right': '8px', flex: '0 0 auto' }}>
        <Dynamic component={item.icon} style={{ 'margin-right': '4px' }} />
        {item.key}
    </Tag>
);

export default function OverflowListDemo() {
    const [width, setWidth] = createSignal(100);

    return (
        <div>
            <Slider
                aria-label="Container width"
                step={1}
                value={width()}
                onChange={(value) => {
                    if (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 { createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-solid';
import { OverflowList, Slider, Tag } from '@fibrous/semi-ui-solid';

interface Item {
    icon: typeof IconAlarm;
    key: string;
}

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

const renderOverflow = (overflowItems: Item[]) => (overflowItems.length ? <Tag style={{ 'margin-right': '8px', flex: '0 0 auto', 'font-variant-numeric': 'tabular-nums' }}>+{overflowItems.length}</Tag> : null);

const renderItem = (item: Item) => (
    <Tag color="blue" style={{ 'margin-right': '8px', flex: '0 0 auto' }}>
        <Dynamic component={item.icon} style={{ 'margin-right': '4px' }} />
        {item.key}
    </Tag>
);

export default function OverflowListDemo() {
    const [width, setWidth] = createSignal(100);

    return (
        <div>
            <Slider
                aria-label="Container width"
                step={1}
                value={width()}
                onChange={(value) => {
                    if (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 { createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-solid';
import { OverflowList, Slider, Tag } from '@fibrous/semi-ui-solid';

interface Item {
    icon: typeof IconAlarm;
    key: string;
}

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

const renderOverflow = (overflowItems: Item[]) => (overflowItems.length ? <Tag style={{ flex: '0 0 auto', 'font-variant-numeric': 'tabular-nums' }}>+{overflowItems.length}</Tag> : null);

const renderItem = (item: Item) => (
    <Tag color="blue" style={{ 'margin-right': '8px', flex: '0 0 auto' }}>
        <Dynamic component={item.icon} style={{ 'margin-right': '4px' }} />
        {item.key}
    </Tag>
);

export default function OverflowListDemo() {
    const [width, setWidth] = createSignal(100);

    return (
        <div>
            <Slider
                aria-label="Container width"
                step={1}
                value={width()}
                onChange={(value) => {
                    if (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.

In scroll mode overflowRenderer receives [start, end], the items scrolled out before and after the visible ones, and returns one node for each side.

0100


+0
alarm
bookmark
camera
duration
edit
folder
+0
Code
import { createSignal, type JSX } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrous/semi-icons-solid';
import { OverflowList, Slider, Tag } from '@fibrous/semi-ui-solid';

interface Item {
    icon: typeof IconAlarm;
    key: string;
}

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

const renderHiddenCount = (hiddenItems: Item[]) => <Tag style={{ 'margin-right': '8px', 'margin-left': '8px', flex: '0 0 auto', 'font-variant-numeric': 'tabular-nums' }}>+{hiddenItems.length}</Tag>;

const renderOverflow = ([start, end]: [Item[], Item[]]): [JSX.Element, JSX.Element] => [renderHiddenCount(start), renderHiddenCount(end)];

const renderItem = (item: Item) => (
    <Tag color="blue" style={{ 'margin-right': '8px', flex: '0 0 auto' }}>
        <Dynamic component={item.icon} style={{ 'margin-right': '4px' }} />
        {item.key}
    </Tag>
);

export default function OverflowListDemo() {
    const [width, setWidth] = createSignal(100);

    return (
        <div>
            <Slider
                aria-label="Container width"
                step={1}
                value={width()}
                onChange={(value) => {
                    if (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
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>) => void-
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>) => JSX.Element) | ((overflowItems: [Array<T>, Array<T>]) => [JSX.Element, JSX.Element])Type() => null
renderModeRender mode: collapse or scroll.'collapse' | 'scroll'"collapse"
thresholdVisible ratio at which scroll mode reports an item's intersection.number0.75
visibleItemRendererRenders one visible item and returns its 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) => JSX.ElementType() => 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.TypeJSX.HTMLAttributes<HTMLDivElement>-
wrapperStyleStyle of the scroll wrapper in scroll mode, as a style object or a string.JSX.CSSProperties | string-

class, style, ref and native attributes such as aria-* land on the root element; pass the scroll wrapper's class, style and other attributes with wrapperClassName, wrapperStyle and wrapperProps.

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