代码演示
如何引入
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 模式下列表首尾相接地无限滚动;两种模式下方向键都在首尾循环。 | boolean | false |
| list | 列表内容。 | T[] | [] |
| mode | 模式,可选 normal 或 wheel。 | string | "wheel" |
| motion | 是否开启滚动动画。 | Motion | true |
| onSelect | 选中某项时的回调,参数是该项数据加上列的 type 和该项在 list 中的 index。组件不保存选中状态,需在回调中用它更新 selectedIndex。 | Type(data: T & { type?: string | number; index: number }) => void | - |
| ref | 接收 ScrollItemHandle 句柄,其上的 scrollToIndex 方法见下方 Methods。 | TypeReact.Ref<ScrollItemHandle> | - |
| selectedIndex | 选中项的索引,没有非受控模式,需配合 onSelect 更新。 | number | 0 |
| 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-bg | var(--semi-transition_duration-none) | 滚动列表选中选项-背景颜色-动画持续时间 |
| $transition_function-scrollList_selected_item-bg | var(--semi-transition_function-easeOut) | 滚动列表选中选项-背景颜色-过渡曲线 |
| $transition_delay-scrollList_selected_item-bg | 0ms | 滚动列表选中选项-背景颜色-延迟时间 |
| $color-scrollList-bg | var(--semi-color-bg-3) | 滚动列表背景色 |
| $color-scrollList-border | var(--semi-color-border) | 滚动列表描边颜色 |
| $color-scrollList_header-bg | transparent | 滚动列表 header 背景色 |
| $color-scrollList_header-title | var(--semi-color-text-0) | 滚动列表标题颜色 |
| $color-scrollList_item-bg | transparent | 滚动列表选项背景色 |
| $color-scrollList_item-text | var(--semi-color-text-0) | 滚动列表选项文字颜色 |
| $color-scrollList_disabled_item-text | var(--semi-color-disabled-text) | 滚动列表选项背景色 - 禁用 |
| $color-scrollList_item-text-hover | var(--semi-color-fill-1) | 滚动列表选项文字颜色 - 悬浮 |
| $color-scrollList_item-bg-active | var(--semi-color-fill-2) | 滚动列表选项文字颜色 - 选中 |
| $color-scrollList_selected_item-bg | var(--semi-color-primary-light-default) | 滚动列表选中选项背景颜色 |
| $color-scrollList_selected_item-text | var(--semi-color-text-0) | 滚动列表选中选项文字颜色 |
| $color-scrollList_selected_item-icon | var(--semi-color-text-2) | 滚动列表选中选项图标颜色 |
| $color-scrollList_footer-border | var(--semi-color-border) | 滚动列表底部描边颜色 |
| $height-scrollList | 300px | |
| $height-scrollList_item | 36px | |
| $width-scrollList_item-border | $border-thickness-control | |
| $width-scrollList_item_sel_svg | 16px | |
| $width-scrollList_item_wheel_selector-border | $border-thickness-control | |
| $height-scrollList_line | 1px | |
| $width-scrollList_footer-border | $border-thickness-control | |
| $spacing-scrollList_header-paddingY | 0 | |
| $spacing-scrollList_header-paddingX | 16px | |
| $spacing-scrollList_header_title-paddingY | $spacing-base | |
| $spacing-scrollList_header_title-paddingX | 0 | |
| $spacing-scrollList_body-paddingY | 0 | |
| $spacing-scrollList_body-paddingX | 16px | |
| $spacing-scrollList_item_ul-padding | 0 | |
| $spacing-scrollList_item_ul-margin | 0 | |
| $spacing-scrollList_item_ul-paddingBottom | ($height-scrollList - $height-scrollList_item) * 0.5 | |
| $spacing-scrollList_item_sel_svg-marginRight | 12px | |
| $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-padding | 10px | |
| $radius-scrollList | var(--semi-border-radius-medium) | |
| $font-scrollList_header_title-fontWeight | $font-weight-bold | |
| $font-scrollList_item_wheel_item_selected-fontWeight | $font-weight-bold | |
| $shadow-scrollList | var(--semi-shadow-elevated) |