展示类

滚动列表

滚动列表。

代码演示

如何引入

import { ScrollList, ScrollItem } from '@fibrous/semi-ui-react';

基本使用

滚动列表提供了一个类似于 iOS 操作系统的滚动选择模式,同时支持滚动至指定窗口位置选择与点击选择。

无限滚动列表
  • 上午
  • 下午
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
代码
import { useState } from 'react';
import { ScrollList, ScrollItem, Button, type ScrollListItem as Item } from '@fibrous/semi-ui-react';

const minutes = Array.from({ length: 60 }).map((_itm, index) => {
    return {
        value: index,
        disabled: index % 3 === 2,
    };
});

export default function ScrollListDemo() {
    const [selectIndex1, setSelectIndex1] = useState(1);
    const [selectIndex2, setSelectIndex2] = useState(1);
    const [selectIndex3, setSelectIndex3] = useState(1);

    const ampms = [
        {
            value: '上午',
        },
        {
            value: '下午',
        },
    ];

    const hours = Array.from({ length: 12 }).map((_itm, index) => {
        return {
            value: index + 1,
        };
    });

    const onSelectAP = (data: Item) => {
        setSelectIndex1(data.index);
    };

    const onSelectHour = (data: Item) => {
        console.log('You have choose the hour for: ', data.value);
        setSelectIndex2(data.index);
    };

    const onSelectMinute = (data: Item) => {
        console.log('You have choose the minute for: ', data.value);
        setSelectIndex3(data.index);
    };

    const handleClose = () => {
        console.log('close');
    };

    const renderFooter = () => {
        return (
            <Button size="small" type="primary" onClick={handleClose}>
                Ok
            </Button>
        );
    };

    const scrollStyle = {
        border: 'unset',
        boxShadow: 'unset',
    };

    return (
        <ScrollList style={scrollStyle} header="无限滚动列表" footer={renderFooter()}>
            <ScrollItem aria-label="上午或下午" mode="wheel" cycled={false} list={ampms} type={1} selectedIndex={selectIndex1} onSelect={onSelectAP} />
            <ScrollItem aria-label="小时" mode="wheel" cycled list={hours} type={2} selectedIndex={selectIndex2} onSelect={onSelectHour} />
            <ScrollItem aria-label="分钟" mode="wheel" cycled list={minutes} type={3} selectedIndex={selectIndex3} onSelect={onSelectMinute} />
        </ScrollList>
    );
}

API 参考

ScrollList

Props

属性说明类型默认值
bodyHeight列表主体的高度,数字单位为 px;未设置时由样式表决定。number | string-
children列表中的 ScrollItem。React.ReactNode-
className根元素的类名。string-
footer底部内容,可传任意 ReactNode。React.ReactNode-
header头部内容,可传任意 ReactNode。React.ReactNode-
prefixCls样式类名前缀,默认为 semi-scrolllist。string-
ref根元素(div)的引用。TypeReact.Ref<HTMLDivElement>-
style根元素的样式。CSSProperties-

aria-* 等其他属性落在根元素上。

ScrollItem

Props

属性说明类型默认值
className根元素的类名。string-
cycled是否为无限循环。wheel 模式下列表首尾相接地无限滚动;两种模式下方向键都在首尾循环。booleanfalse
list列表内容。T[][]
mode模式,可选 normal 或 wheel。string"wheel"
motion是否开启滚动动画。Motiontrue
onSelect选中某项时的回调,参数是该项数据加上列的 type 和该项在 list 中的 index。组件不保存选中状态,需在回调中用它更新 selectedIndex。Type(data: T & { type?: string | number; index: number }) => void-
ref接收 ScrollItemHandle 句柄,其上的 scrollToIndex 方法见下方 Methods。TypeReact.Ref<ScrollItemHandle>-
selectedIndex选中项的索引,没有非受控模式,需配合 onSelect 更新。number0
style根元素的样式。React.CSSProperties-
transform对选中项文案的变换,返回值作为文案显示;未传时显示 text,没有 text 时显示 value。数据项自带的 transform 优先。Type(value: any, text?: string) => string-
type标识该列,供 TimePicker、DatePicker 等组件使用,不面向使用方。string | number-

Methods

方法说明类型
scrollToIndex将列表滚动到当前选中项。selectedIndex 暂不生效,目标始终是最近一次选中的项;duration 为滚动动画时长,单位毫秒,默认 120。Type(selectedIndex?: number, duration?: number) => void

aria-label、aria-labelledby、aria-describedby 落在列表(role="listbox")上,其余属性落在根元素上。

ItemData

属性说明类型默认值
disabled该项是否被禁止选择boolean
text每一项的文案string
transform该项处于选中状态时的变换,返回值会作为文案进行显示,ScrollItem 组件如果同时传入会优先选择 ItemData 中的 transform 方法;未传时显示 text,没有 text 时显示 value(value: any, text: string | undefined) => string
value每一项的值any

Methods

通过 ScrollItem 的 ref 调用,用于在容器尺寸变化等场景下重新对齐选中项

Accessibility

ARIA

  • 传入 aria-label 或 aria-labelledby 时,ScrollList 的根元素带 role="group"
  • ScrollItem 是 role="listbox",支持传入 aria-label, 指定该列标签
  • ScrollItem 使用 aria-disabled 表示该项目是否被禁用
  • ScrollItem 使用 aria-selected 表示该项目是否被选中
  • cycled 的 wheel 列里用来填满滚轮的副本带 aria-hidden="true",读屏只读到本体选项

键盘和焦点

  • 每列是一个 listbox,用 Tab 聚焦,焦点留在列表上,选中项通过 aria-activedescendant 指示;选中跟随焦点,每次选中都会触发选中回调
  • 上箭头 选中上一项,下箭头 选中下一项,被禁用的项会被跳过;Home 选中第一项,End 选中最后一项
  • 设置 cycled 后,两种模式下方向键都在首尾循环;未设置时到首尾即停
  • 输入字符按文案开头匹配并选中对应项,连续输入同一字符时依次选中下一个匹配项

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$transition_duration-scrollList_selected_item-bgvar(--semi-transition_duration-none)滚动列表选中选项-背景颜色-动画持续时间
$transition_function-scrollList_selected_item-bgvar(--semi-transition_function-easeOut)滚动列表选中选项-背景颜色-过渡曲线
$transition_delay-scrollList_selected_item-bg0ms滚动列表选中选项-背景颜色-延迟时间
$color-scrollList-bgvar(--semi-color-bg-3)滚动列表背景色
$color-scrollList-bordervar(--semi-color-border)滚动列表描边颜色
$color-scrollList_header-bgtransparent滚动列表 header 背景色
$color-scrollList_header-titlevar(--semi-color-text-0)滚动列表标题颜色
$color-scrollList_item-bgtransparent滚动列表选项背景色
$color-scrollList_item-textvar(--semi-color-text-0)滚动列表选项文字颜色
$color-scrollList_disabled_item-textvar(--semi-color-disabled-text)滚动列表选项背景色 - 禁用
$color-scrollList_item-text-hovervar(--semi-color-fill-1)滚动列表选项文字颜色 - 悬浮
$color-scrollList_item-bg-activevar(--semi-color-fill-2)滚动列表选项文字颜色 - 选中
$color-scrollList_selected_item-bgvar(--semi-color-primary-light-default)滚动列表选中选项背景颜色
$color-scrollList_selected_item-textvar(--semi-color-text-0)滚动列表选中选项文字颜色
$color-scrollList_selected_item-iconvar(--semi-color-text-2)滚动列表选中选项图标颜色
$color-scrollList_footer-bordervar(--semi-color-border)滚动列表底部描边颜色
$height-scrollList300px
$height-scrollList_item36px
$width-scrollList_item-border$border-thickness-control
$width-scrollList_item_sel_svg16px
$width-scrollList_item_wheel_selector-border$border-thickness-control
$height-scrollList_line1px
$width-scrollList_footer-border$border-thickness-control
$spacing-scrollList_header-paddingY0
$spacing-scrollList_header-paddingX16px
$spacing-scrollList_header_title-paddingY$spacing-base
$spacing-scrollList_header_title-paddingX0
$spacing-scrollList_body-paddingY0
$spacing-scrollList_body-paddingX16px
$spacing-scrollList_item_ul-padding0
$spacing-scrollList_item_ul-margin0
$spacing-scrollList_item_ul-paddingBottom($height-scrollList - $height-scrollList_item) * 0.5
$spacing-scrollList_item_sel_svg-marginRight12px
$spacing-scrollList_item_wheel_list_outer-paddingRight$height-scrollList_item * 0.5
$spacing-scrollList_item_wheel_list_shade_pre-marginTop-1 * ($height-scrollList_item * 0.5 + $border-thickness-control)
$spacing-scrollList_item_wheel_list_shade_post-marginTop$height-scrollList_item * 0.5 + $border-thickness-control
$spacing-scrollList_footer-padding10px
$radius-scrollListvar(--semi-border-radius-medium)
$font-scrollList_header_title-fontWeight$font-weight-bold
$font-scrollList_item_wheel_item_selected-fontWeight$font-weight-bold
$shadow-scrollListvar(--semi-shadow-elevated)