Skip to content

Commit

Permalink
feat(List): support scroll API (#3286)
Browse files Browse the repository at this point in the history
* feat(list): support virtual scroll

* feat(list): support virtual scroll

* chore: fix docs and demo

* chore: fix lint

* chore: fix lint

* chore: fix test

* chore: fix test

* chore: fix test
  • Loading branch information
uyarn authored Aug 28, 2024
1 parent e89ace1 commit 44466c3
Show file tree
Hide file tree
Showing 16 changed files with 1,447 additions and 182 deletions.
58 changes: 30 additions & 28 deletions src/list/__tests__/__snapshots__/index.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,36 @@ exports[`List > :props > :asyncLoading is a string 1`] = `
text
</ul>
<div
class="t-list__load"
class="t-list__load t-list__load--loading"
>
<div
class="t-loading--center t-size-m t-loading t-is-loading"
>
<svg
class="t-loading__gradient t-icon-loading"
height="1em"
size="medium"
version="1.1"
viewBox="0 0 12 12"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<foreignobject
height="12"
width="12"
x="0"
y="0"
>
<div
class="t-loading__gradient-conic"
/>
</foreignobject>
</svg>
<div>
<div
class="t-loading__text"
class="t-loading--center t-size-m t-loading"
>
正在加载中,请稍后
<svg
class="t-loading__gradient t-icon-loading"
height="1em"
size="medium"
version="1.1"
viewBox="0 0 12 12"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<foreignobject
height="12"
width="12"
x="0"
y="0"
>
<div
class="t-loading__gradient-conic"
/>
</foreignobject>
</svg>
</div>
<span>
正在加载中,请稍等
</span>
</div>
</div>
</div>
Expand All @@ -72,9 +72,11 @@ exports[`List > :props > :asyncLoading is load-more 1`] = `
text
</ul>
<div
class="t-list__load"
class="t-list__load t-list__load--load-more"
>
<!---->
<span>
点击加载更多
</span>
</div>
</div>
`;
Expand Down
13 changes: 13 additions & 0 deletions src/list/_example-composition/virtual-scroll.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<t-list style="height: 300px" :scroll="{ type: 'virtual' }"
><t-list-item v-for="(item, index) in list" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" /></t-list-item
></t-list>
</template>
<script setup>
const list = [];
const imageUrl = 'https://tdesign.gtimg.com/site/avatar.jpg';
for (let i = 0; i < 3000; i++) {
list.push({ content: `${i + 1}个列表内容的描述性文字` });
}
</script>
22 changes: 22 additions & 0 deletions src/list/_example/virtual-scroll.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<t-list style="height: 300px" :scroll="{ type: 'virtual' }"
><t-list-item v-for="(item, index) in list" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" /></t-list-item
></t-list>
</template>
<script>
const list = [];
const imageUrl = 'https://tdesign.gtimg.com/site/avatar.jpg';
for (let i = 0; i < 3000; i++) {
list.push({ content: `${i + 1}个列表内容的描述性文字` });
}
export default {
data() {
return {
list,
imageUrl,
};
},
};
</script>
28 changes: 28 additions & 0 deletions src/list/hooks/useListItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { VNode } from 'vue';
import { computed, getCurrentInstance } from '@vue/composition-api';

const useListItems = () => {
const instance = getCurrentInstance();
const currentSlots = instance.proxy.$slots.default || [];

const listItems = computed(() => {
const computedListItems: VNode[] = [];
currentSlots.forEach((child) => {
if (child.componentOptions?.tag === 't-list-item') {
computedListItems.push({
class: child.data.staticClass,
style: child.data.staticStyle,
...child.componentOptions.propsData,
slots: () => child.componentOptions.children,
} as unknown as VNode);
}
});
return computedListItems;
});

return {
listItems,
};
};

export default useListItems;
57 changes: 57 additions & 0 deletions src/list/hooks/useListVirtualScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Ref, computed } from '@vue/composition-api';
import useVirtualScroll from '../../hooks/useVirtualScrollNew';
import { TdListProps } from '../type';
import { Styles } from '../../common';

const useListVirtualScroll = (scroll: TdListProps['scroll'], listRef: Ref<HTMLElement>, listItems: Ref<any[]>) => {
const virtualScrollParams = computed(() => ({
data: listItems.value,
scroll,
}));
const virtualConfig = useVirtualScroll(listRef, virtualScrollParams);
const isVirtualScroll = computed(() => virtualConfig.isVirtualScroll.value);
let lastScrollY = -1;

const onInnerVirtualScroll = (e: WheelEvent) => {
const target = (e.target || e.srcElement) as HTMLElement;
const top = target.scrollTop;
if (lastScrollY !== top) {
virtualConfig.isVirtualScroll.value && virtualConfig.handleScroll();
} else {
lastScrollY = -1;
}
lastScrollY = top;
};

const cursorStyle = computed(
() => ({
position: 'absolute',
width: '1px',
height: '1px',
transition: 'transform 0.2s',
transform: `translate(0, ${virtualConfig.scrollHeight.value}px)`,
'-ms-transform': `translate(0, ${virtualConfig.scrollHeight.value}px)`,
'-moz-transform': `translate(0, ${virtualConfig.scrollHeight.value}px)`,
'-webkit-transform': `translate(0, ${virtualConfig.scrollHeight.value}px)`,
} as Styles),
);

const listStyle = computed(
() => ({
transform: `translate(0, ${virtualConfig.translateY.value}px)`,
'-ms-transform': `translate(0, ${virtualConfig.translateY.value}px)`,
'-moz-transform': `translate(0, ${virtualConfig.translateY.value}px)`,
'-webkit-transform': `translate(0, ${virtualConfig.translateY.value}px)`,
} as Styles),
);

return {
virtualConfig,
cursorStyle,
listStyle,
isVirtualScroll,
onInnerVirtualScroll,
};
};

export default useListVirtualScroll;
1 change: 0 additions & 1 deletion src/list/list-item-meta-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* updated at 2021-11-19 10:44:26
* */

import { TdListItemMetaProps } from '../list/type';
Expand Down
1 change: 0 additions & 1 deletion src/list/list-item-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* updated at 2021-11-19 10:44:26
* */

import { TdListItemProps } from '../list/type';
Expand Down
36 changes: 20 additions & 16 deletions src/list/list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import { VNode } from 'vue';
import { defineComponent } from '@vue/composition-api';
import props from './list-item-props';
import { usePrefixClass } from '../hooks/useConfig';
import { renderTNodeJSX, renderContent } from '../utils/render-tnode';
import { getClassPrefixMixins } from '../config-provider/config-receiver';
import mixins from '../utils/mixins';

const classPrefixMixins = getClassPrefixMixins('list-item');

export default mixins(classPrefixMixins).extend({
export default defineComponent({
name: 'TListItem',
props,
methods: {
handleClick(e: MouseEvent): void {
this.$emit('click', { e });
this.onClick?.({ e });
},
setup(props, { emit }) {
const componentName = usePrefixClass('list-item');

const handleClick = (e: MouseEvent) => {
emit('click', { e });
props.onClick?.({ e });
};
return {
componentName,
handleClick,
};
},
render(): VNode {
render() {
const { componentName, handleClick } = this;
const content = renderContent(this, 'default', 'content');
const propsActionContent = renderTNodeJSX(this, 'action');

return (
<li class={this.componentName} onClick={this.handleClick}>
<div class={`${this.componentName}-main`}>
<div class={`${this.componentName}__content`}>{content}</div>
{propsActionContent && <li class={`${this.componentName}__action`}>{propsActionContent}</li>}
<li class={componentName} onClick={handleClick}>
<div class={`${componentName}-main`}>
<div class={`${componentName}__content`}>{content}</div>
{propsActionContent && <li class={`${componentName}__action`}>{propsActionContent}</li>}
</div>
</li>
);
Expand Down
13 changes: 5 additions & 8 deletions src/list/list.en-US.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
:: BASE_DOC ::

## API

### List Props

name | type | default | description | required
-- | -- | -- | -- | --
asyncLoading | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
footer | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
header | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
layout | String | horizontal | options:horizontal/vertical | N
size | String | medium | options:small/medium/large | N
layout | String | horizontal | options: horizontal/vertical | N
scroll | Object | - | lazy load and virtual scroll。Typescript:`TScroll`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
size | String | medium | options: small/medium/large | N
split | Boolean | false | \- | N
stripe | Boolean | false | \- | N
onLoadMore | Function | | Typescript:`(options: { e: MouseEvent }) => void`<br/> | N
Expand All @@ -22,20 +24,15 @@ name | params | description
load-more | `(options: { e: MouseEvent })` | \-
scroll | `(options: { e: Event \| WheelEvent; scrollTop: number; scrollBottom: number })` | \-


### ListItem Props

name | type | default | description | required
-- | -- | -- | -- | --
action | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
content | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
onClick | Function | | Typescript:`(context: { e: MouseEvent }) => void`<br/> | N

### ListItem Events

name | params | description
-- | -- | --
click | `(context: { e: MouseEvent })` | \-

### ListItemMeta Props

Expand Down
40 changes: 6 additions & 34 deletions src/list/list.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,16 @@
:: BASE_DOC ::

### 斑马纹的列表

当列表内容较多时,可以使用斑马纹样式,便于用户获取信息。

{{ stripe }}

### 异步加载的列表

当数据需要通过二次请求加载展示时,可以通过`asyncLoading`来处理相关的逻辑。

{{ loading }}

### 带头部及尾部的列表

当列表需要展示头部或尾部信息时,可以通过`header``footer`来配置。

{{ header-footer }}

### 带滚动事件的列表

当列表较长时,可以配置滚动条及事件来进行滚动处理

{{ scroll }}

## API

### List Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
asyncLoading | String / Slot / Function | - | 自定义加载中。值为空不显示加载中,值为 'loading' 显示加载中状态,值为 'load-more' 显示加载更多状态。值类型为函数,则表示自定义加载状态呈现内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
footer | String / Slot / Function | - | 底部。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
header | String / Slot / Function | - | 头部。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
layout | String | horizontal | 排列方式(待设计稿输出)。可选项:horizontal/vertical | N
scroll | Object | - | 懒加载和虚拟滚动。为保证组件收益最大化,当数据量小于阈值 `scroll.threshold` 时,无论虚拟滚动的配置是否存在,组件内部都不会开启虚拟滚动,`scroll.threshold` 默认为 `100`。TS 类型:`TScroll`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
size | String | medium | 尺寸。可选项:small/medium/large | N
split | Boolean | false | 是否展示分割线 | N
stripe | Boolean | false | 是否展示斑马纹 | N
Expand All @@ -46,25 +24,19 @@ onScroll | Function | | TS 类型:`(options: { e: Event \| WheelEvent; scroll
load-more | `(options: { e: MouseEvent })` | 点击加载更多时触发
scroll | `(options: { e: Event \| WheelEvent; scrollTop: number; scrollBottom: number })` | 列表滚动时触发,scrollTop 表示顶部滚动距离,scrollBottom 表示底部滚动距离


### ListItem Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
action | String / Slot / Function | - | 操作栏。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
content | String / Slot / Function | - | 内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | 内容,同 content。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>点击时触发 | N

### ListItem Events

名称 | 参数 | 描述
-- | -- | --
click | `(context: { e: MouseEvent })` | 点击时触发


### ListItemMeta Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
avatar | String / Slot / Function | - | 已废弃。列表项图片。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
description | String / Slot / Function | - | 列表项内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
Expand Down
Loading

0 comments on commit 44466c3

Please sign in to comment.