代码演示
如何引入
<script lang="ts">
import { ScrollList, ScrollItem } from '@fibrae/semi-ui-svelte';
</script>
基本使用
滚动列表提供了一个类似于 iOS 操作系统的滚动选择模式,同时支持滚动至指定窗口位置选择与点击选择。
选中项用 bind:selectedIndex 绑定,选中时写回新索引,回调为 onSelect;列写成 ScrollList.Item 或具名导出 ScrollItem,两者是同一组件。header、footer 接受字符串或 snippet,传了就渲染:用 {#snippet footer()}...{/snippet} 定义后作为属性传入,或直接写在 ScrollList 标签内。
无限滚动列表
- 上午
- 下午
- 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
代码
<script lang="ts">
import { Button, ScrollItem, ScrollList, type ScrollItemSelectData } from '@fibrae/semi-ui-svelte';
const ampms = [{ value: '上午' }, { value: '下午' }];
const hours = Array.from({ length: 12 }, (_, index) => ({ value: index + 1 }));
const minutes = Array.from({ length: 60 }, (_, index) => ({ value: index, disabled: index % 3 === 0 }));
let selectIndex1 = $state(1);
let selectIndex2 = $state(1);
let selectIndex3 = $state(1);
const onSelectHour = (data: ScrollItemSelectData) => {
console.log('You have choose the hour for: ', data.value);
};
const onSelectMinute = (data: ScrollItemSelectData) => {
console.log('You have choose the minute for: ', data.value);
};
const handleClose = () => {
console.log('close');
};
</script>
<ScrollList header="无限滚动列表" style="border: unset; box-shadow: unset">
<ScrollItem aria-label="上午或下午" bind:selectedIndex={selectIndex1} cycled={false} list={ampms} mode="wheel" type={1} />
<ScrollItem aria-label="小时" bind:selectedIndex={selectIndex2} cycled list={hours} mode="wheel" onSelect={onSelectHour} type={2} />
<ScrollItem aria-label="分钟" bind:selectedIndex={selectIndex3} cycled list={minutes} mode="wheel" onSelect={onSelectMinute} type={3} />
{#snippet footer()}
<Button size="small" type="primary" onclick={handleClose}>Ok</Button>
{/snippet}
</ScrollList>API 参考
ScrollList
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| bodyHeight | 列表主体的高度。 | number | string | - |
| children | 列表中的 ScrollItem,写在标签内。 | Snippet | - |
| footer | 底部内容,字符串或 snippet;传了就渲染。 | Snippet | string | - |
| header | 头部内容,字符串或 snippet;传了就渲染。 | Snippet | string | - |
class、style(字符串)与原生属性(如 aria-*)落在根元素上。
ScrollItem
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| cycled | 是否无限循环。wheel 模式下列表首尾相接地无限滚动;两种模式下方向键都在首尾循环。 | boolean | false |
| list | 列表内容。 | T[] | - |
| mode | 模式,可选 normal 或 wheel。 | 'normal' | 'wheel' | "wheel" |
| motion | 是否开启滚动动画。 | boolean | true |
| onSelect | 选中某项时的回调,参数是该项数据加上列的 type 和该项在 list 中的 index。 | Type(data: ScrollItemSelectData<T>) => void | - |
| selectedIndex | 选中项的索引,可用 bind:selectedIndex 绑定;选中时写回新索引。 | number | - |
| transform | 对选中项文案的变换,没有 text 的项参数为 undefined;未传时显示 text,没有 text 时显示 value。数据项自带的 transform 优先。 | Type(value: T['value'], 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")上,class、style(字符串)等其余属性落在根元素上。
ItemData
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| disabled | 该项是否被禁止选择 | boolean | |
| text | 每一项的文案 | string | |
| transform | 该项处于选中状态时的变换,返回值会作为文案进行显示,ScrollItem 组件如果同时传入会优先选择 ItemData 中的 transform 方法;未传时显示 text,没有 text 时显示 value | (value: any, text: string | undefined) => string | |
| value | 每一项的值 | any |
Methods
通过 ScrollItem 的 bind:this 取实例调用,签名见上方 ScrollItem 的 Methods 表。
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) |