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

<script lang="ts">
    import { OverflowList } from '@fibrae/semi-ui-svelte';
</script>

Collapse Mode - Simple

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

items holds data, not nodes. Each visible item is rendered by the item snippet, with the parameter { item, index }. The overflowing items are rendered by the overflow snippet, with the parameter { items, position }; collapse mode renders it once, only when there are overflowing items, and items are the ones that do not fit.

0 100


Code
<script lang="ts">
    import type { Component } from 'svelte';
    import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrae/semi-icons-svelte';
    import { OverflowList, Slider, Tag } from '@fibrae/semi-ui-svelte';

    interface Item {
        key: string;
    }

    const icons: Record<string, Component> = {
        alarm: IconAlarm,
        bookmark: IconBookmark,
        camera: IconCamera,
        duration: IconDuration,
        edit: IconEdit,
        folder: IconFolder,
    };
    const items: Item[] = Object.keys(icons).map((key) => ({ key }));
    let width = $state<number | number[]>(100);
</script>

<div>
    <Slider aria-label="Container width" bind:value={width} step={1} />
    <br />
    <br />
    <div style="width: {width}%">
        <OverflowList {items}>
            {#snippet item({ item })}
                {@const Icon = icons[item.key]}
                <Tag color="blue" style="margin-right: 8px; flex: 0 0 auto">
                    <Icon style="margin-right: 4px" />
                    <span>{item.key}</span>
                </Tag>
            {/snippet}
            {#snippet overflow({ items: hidden })}
                {#if hidden.length}
                    <Tag style="flex: 0 0 auto; font-variant-numeric: tabular-nums">+{hidden.length}</Tag>
                {/if}
            {/snippet}
        </OverflowList>
    </div>
</div>

Collapse Mode - Direction

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

0 100


Code
<script lang="ts">
    import type { Component } from 'svelte';
    import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrae/semi-icons-svelte';
    import { OverflowList, Slider, Tag } from '@fibrae/semi-ui-svelte';

    interface Item {
        key: string;
    }

    const icons: Record<string, Component> = {
        alarm: IconAlarm,
        bookmark: IconBookmark,
        camera: IconCamera,
        duration: IconDuration,
        edit: IconEdit,
        folder: IconFolder,
    };
    const items: Item[] = Object.keys(icons).map((key) => ({ key }));
    let width = $state<number | number[]>(100);
</script>

<div>
    <Slider aria-label="Container width" bind:value={width} step={1} />
    <br />
    <br />
    <div style="width: {width}%">
        <OverflowList collapseFrom="start" {items}>
            {#snippet item({ item })}
                {@const Icon = icons[item.key]}
                <Tag color="blue" style="margin-right: 8px; flex: 0 0 auto">
                    <Icon style="margin-right: 4px" />
                    <span>{item.key}</span>
                </Tag>
            {/snippet}
            {#snippet overflow({ items: hidden })}
                {#if hidden.length}
                    <Tag style="margin-right: 8px; flex: 0 0 auto; font-variant-numeric: tabular-nums">+{hidden.length}</Tag>
                {/if}
            {/snippet}
        </OverflowList>
    </div>
</div>

Collapse Mode - Visible

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

0 100


Code
<script lang="ts">
    import type { Component } from 'svelte';
    import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrae/semi-icons-svelte';
    import { OverflowList, Slider, Tag } from '@fibrae/semi-ui-svelte';

    interface Item {
        key: string;
    }

    const icons: Record<string, Component> = {
        alarm: IconAlarm,
        bookmark: IconBookmark,
        camera: IconCamera,
        duration: IconDuration,
        edit: IconEdit,
        folder: IconFolder,
    };
    const items: Item[] = Object.keys(icons).map((key) => ({ key }));
    let width = $state<number | number[]>(100);
</script>

<div>
    <Slider aria-label="Container width" bind:value={width} step={1} />
    <br />
    <br />
    <div style="width: {width}%">
        <OverflowList {items} minVisibleItems={3}>
            {#snippet item({ item })}
                {@const Icon = icons[item.key]}
                <Tag color="blue" style="margin-right: 8px; flex: 0 0 auto">
                    <Icon style="margin-right: 4px" />
                    <span>{item.key}</span>
                </Tag>
            {/snippet}
            {#snippet overflow({ items: hidden })}
                {#if hidden.length}
                    <Tag style="flex: 0 0 auto; font-variant-numeric: tabular-nums">+{hidden.length}</Tag>
                {/if}
            {/snippet}
        </OverflowList>
    </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 overflow is rendered once per side, with position set to start or end and items the items scrolled out on that side. Each item is wrapped in a display: contents span, and data-scrollkey is written on, and observed from, the first element the item snippet renders, so item must output an element or the item is skipped.

0 100


+0
alarm
bookmark
camera
duration
edit
folder
+0
Code
<script lang="ts">
    import type { Component } from 'svelte';
    import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder } from '@fibrae/semi-icons-svelte';
    import { OverflowList, Slider, Tag } from '@fibrae/semi-ui-svelte';

    interface Item {
        key: string;
    }

    const icons: Record<string, Component> = {
        alarm: IconAlarm,
        bookmark: IconBookmark,
        camera: IconCamera,
        duration: IconDuration,
        edit: IconEdit,
        folder: IconFolder,
    };
    const items: Item[] = Object.keys(icons).map((key) => ({ key }));
    let width = $state<number | number[]>(100);
</script>

<div>
    <Slider aria-label="Container width" bind:value={width} step={1} />
    <br />
    <br />
    <div style="width: {width}%">
        <OverflowList {items} renderMode="scroll">
            {#snippet item({ item })}
                {@const Icon = icons[item.key]}
                <Tag color="blue" style="margin-right: 8px; flex: 0 0 auto">
                    <Icon style="margin-right: 4px" />
                    <span>{item.key}</span>
                </Tag>
            {/snippet}
            {#snippet overflow({ items: hidden })}
                <Tag style="margin-right: 8px; margin-left: 8px; flex: 0 0 auto; font-variant-numeric: tabular-nums">+{hidden.length}</Tag>
            {/snippet}
        </OverflowList>
    </div>
</div>

API Reference

Props

PropertyDescriptionTypeDefault
collapseFromCollapse direction in collapse mode: start or end.'start' | 'end'"end"
itemSnippet for the content of one visible item, with the parameter { item, index }: the item and its position in items. Scroll mode needs the snippet to render an element, otherwise the item is skipped.TypeSnippet<[OverflowListItemSnippetProps<T>]>-
itemKeyProperty name of each item's key, or a function returning it. Defaults to the key property, then the item's position in items.TypeOverflowListKey | ((item: T) => OverflowListKey)-
itemsThe items to render. They are data, not nodes; scroll mode requires a key on each item.T[]-
minVisibleItemsMinimum number of visible items in collapse mode.number0
onIntersectCalled in scroll mode when the intersection state of the items changes.Type(res: Record<string, IntersectionObserverEntry>) => void-
onOverflowCalled in collapse mode when the overflowing items change; the argument is an empty array when everything fits.Type(overflowItems: T[]) => void-
onVisibleStateChangeCalled in scroll mode when the visible or hidden state of the items changes.Type(visibleState: Map<string, boolean>) => void-
overflowSnippet for the overflowing items, with the parameter { items, position }. Collapse mode renders it once, only when there are overflowing items, with the items that do not fit; scroll mode renders it once per side, with position set to start or end and the items scrolled out on that side.TypeSnippet<[OverflowListOverflowSnippetProps<T>]>-
overflowRenderDirectionWhere scroll mode renders the overflow nodes: both, start or end. Used by Tabs, not meant for end users.'both' | 'start' | 'end'"both"
renderModeRender mode: collapse or scroll.'collapse' | 'scroll'"collapse"
thresholdVisible ratio at which scroll mode reports an item as overflowing.number0.75
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.TypeHTMLAttributes<HTMLDivElement>-
wrapperStyleStyle string of the scroll wrapper in scroll mode.string-

class, style (a string) and native attributes such as aria-* land on the root element; pass the class, style and other attributes of the scroll wrapper 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