Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(list): support scrollTo API to scroll to a specified element #4863

Merged
merged 4 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions src/list/_example-ts/virtual-scroll.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
<template>
<t-list style="height: 300px" :scroll="{ type: 'virtual' }"
><t-list-item v-for="(item, index) in listRef" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" /></t-list-item
></t-list>
<t-space direction="vertical">
<t-list
ref="list"
style="height: 300px"
:scroll="{ type: 'virtual', rowHeight: 80, bufferSize: 10, threshold: 10 }"
>
<t-list-item v-for="(item, index) in listData" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" />
</t-list-item>
</t-list>
<t-space>
<t-button @click="handleScroll">滚动到指定节点</t-button>
</t-space>
</t-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ListItemMetaProps } from 'tdesign-vue-next';
const list = [];
import { ref, onMounted } from 'vue';
import { ListItemMetaProps, ListInstanceFunctions } from 'tdesign-vue-next';

const list = ref<ListInstanceFunctions>(); // 用于存储对 t-list 的引用
const listData = ref([]); // 使用 ref 来存储列表数据
const imageUrl: ListItemMetaProps['image'] = 'https://tdesign.gtimg.com/site/avatar.jpg';
for (let i = 0; i < 3000; i++) {
list.push({
content: `列表内容的描述性文字`,

onMounted(() => {
for (let i = 0; i < 3000; i++) {
listData.value.push({ content: `第${i + 1}个列表内容的描述性文字` });
}
});

const handleScroll = () => {
// scroll 属性需要设置 rowHeight 参数
list.value?.scrollTo({
// list 不存在嵌套,key 与 index 相同
index: 30,
behavior: 'smooth',
});
}
const listRef = ref(list);
};
</script>
43 changes: 33 additions & 10 deletions src/list/_example/virtual-scroll.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
<template>
<t-list style="height: 300px" :scroll="{ type: 'virtual' }"
><t-list-item v-for="(item, index) in listRef" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" /></t-list-item
></t-list>
<t-space direction="vertical">
<t-list
ref="list"
style="height: 300px"
:scroll="{ type: 'virtual', rowHeight: 80, bufferSize: 10, threshold: 10 }"
>
<t-list-item v-for="(item, index) in listData" :key="index">
<t-list-item-meta :image="imageUrl" title="列表标题" :description="item.content" />
</t-list-item>
</t-list>
<t-space>
<t-button @click="handleScroll">滚动到指定节点</t-button>
</t-space>
</t-space>
</template>

<script setup>
import { ref } from 'vue';
const list = [];
import { ref, onMounted } from 'vue';

const list = ref(); // 用于存储对 t-list 的引用
const listData = ref([]); // 使用 ref 来存储列表数据
const imageUrl = 'https://tdesign.gtimg.com/site/avatar.jpg';
for (let i = 0; i < 3000; i++) {
list.push({ content: `列表内容的描述性文字` });
}

const listRef = ref(list);
onMounted(() => {
for (let i = 0; i < 3000; i++) {
listData.value.push({ content: `第${i + 1}个列表内容的描述性文字` });
}
});

const handleScroll = () => {
// scroll 属性需要设置 rowHeight 参数
list.value?.scrollTo({
// list 不存在嵌套,key 与 index 相同
index: 30,
behavior: 'smooth',
});
};
</script>
19 changes: 18 additions & 1 deletion src/list/hooks/useListVirtualScroll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Ref, computed } from 'vue';

import log from '../../_common/js/log';
import useVirtualScroll from '../../hooks/useVirtualScrollNew';
import { TdListProps } from '../type';
import { Styles } from '../../common';
import { Styles, type ComponentScrollToElementParams } from '../../common';

export const useListVirtualScroll = (
scroll: TdListProps['scroll'],
Expand Down Expand Up @@ -51,11 +53,26 @@ export const useListVirtualScroll = (
} as Styles),
);

const handleScrollTo = (params: ComponentScrollToElementParams) => {
const { index, key } = params;
const targetIndex = index === 0 ? index : index ?? Number(key);
if (!targetIndex && targetIndex !== 0) {
log.error('List', 'scrollTo: `index` or `key` must exist.');
return;
}
if (targetIndex < 0 || targetIndex >= listItems.value.length) {
log.error('List', `${targetIndex} does not exist in data, check \`index\` or \`key\` please.`);
return;
}
virtualConfig.scrollToElement({ ...params, index: targetIndex - 1 });
};

return {
virtualConfig,
cursorStyle,
listStyle,
isVirtualScroll,
onInnerVirtualScroll,
scrollToElement: handleScrollTo,
};
};
7 changes: 7 additions & 0 deletions src/list/list.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ name | params | description
load-more | `(options: { e: MouseEvent })` | \-
scroll | `(options: { e: Event \| WheelEvent; scrollTop: number; scrollBottom: number })` | \-

### ListInstanceFunctions

name | params | return | description
-- | -- | -- | --
scrollTo | `(scrollToParams: ScrollToElementParams)` | \- | support scrolling to a specific node when virtual scrolling


### ListItem Props

name | type | default | description | required
Expand Down
7 changes: 7 additions & 0 deletions src/list/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ onScroll | Function | | TS 类型:`(options: { e: Event \| WheelEvent; scroll
load-more | `(options: { e: MouseEvent })` | 点击加载更多时触发
scroll | `(options: { e: Event \| WheelEvent; scrollTop: number; scrollBottom: number })` | 列表滚动时触发,scrollTop 表示顶部滚动距离,scrollBottom 表示底部滚动距离

### ListInstanceFunctions 组件实例方法

名称 | 参数 | 返回值 | 描述
-- | -- | -- | --
scrollTo | `(scrollToParams: ScrollToElementParams)` | \- | 虚拟滚动场景下,支持指定滚动到具体的节点


### ListItem Props

名称 | 类型 | 默认值 | 说明 | 必传
Expand Down
8 changes: 3 additions & 5 deletions src/list/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ export default defineComponent({
const renderTNodeJSX = useTNodeJSX();
const { listItems } = useListItems();

const { virtualConfig, cursorStyle, listStyle, isVirtualScroll, onInnerVirtualScroll } = useListVirtualScroll(
props.scroll,
listRef,
listItems,
);
const { virtualConfig, cursorStyle, listStyle, isVirtualScroll, onInnerVirtualScroll, scrollToElement } =
useListVirtualScroll(props.scroll, listRef, listItems);

/** 列表基础逻辑 start */
const listClass = computed(() => {
Expand Down Expand Up @@ -123,6 +120,7 @@ export default defineComponent({
handleLoadMore,
listRef,
isVirtualScroll,
scrollTo: scrollToElement,
};
},

Expand Down
10 changes: 9 additions & 1 deletion src/list/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */

import { TNode, TScroll } from '../common';
import { TNode, TScroll, ComponentScrollToElementParams } from '../common';

export interface TdListProps {
/**
Expand Down Expand Up @@ -53,6 +53,14 @@ export interface TdListProps {
onScroll?: (options: { e: Event | WheelEvent; scrollTop: number; scrollBottom: number }) => void;
}

/** 组件实例方法 */
export interface ListInstanceFunctions {
/**
* 虚拟滚动场景下,支持指定滚动到具体的节点
*/
scrollTo?: (scrollToParams: ComponentScrollToElementParams) => void;
}

export interface TdListItemProps {
/**
* 操作栏
Expand Down
75 changes: 60 additions & 15 deletions test/unit/snap/__snapshots__/csr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -87330,31 +87330,76 @@ exports[`csr snapshot test > csr test ./src/list/_example/stripe.vue 1`] = `

exports[`csr snapshot test > csr test ./src/list/_example/virtual-scroll.vue 1`] = `
<div
class="t-list t-size-m"
style="position: relative; height: 300px;"
class="t-space t-space-vertical"
style="gap: 16px;"
>


<!---->

<div
style="position: absolute; width: 1px; height: 1px; transition: transform 0.2s; transform: translate(0, 0px);"
/>
<ul
class="t-list__inner"
style="transform: translate(0, 150000px);"
class="t-space-item"
>


</ul>

<div
class="t-list t-size-m"
style="height: 300px;"
>


<!---->
<ul
class="t-list__inner"
>




</ul>
<!---->

<div
class="t-list__load"
>
<!---->
</div>

</div>
</div>
<!---->


<div
class="t-list__load"
class="t-space-item"
>
<!---->
<div
class="t-space t-space-horizontal"
style="gap: 16px;"
>


<div
class="t-space-item"
>
<button
class="t-button t-button--variant-base t-button--theme-primary"
href=""
tabindex="0"
type="button"
>
<span
class="t-button__text"
>

滚动到指定节点

</span>
</button>
</div>
<!---->


</div>
</div>
<!---->


</div>
`;
Expand Down
2 changes: 1 addition & 1 deletion test/unit/snap/__snapshots__/ssr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ exports[`ssr snapshot test > ssr test ./src/list/_example/size.vue 1`] = `"<div

exports[`ssr snapshot test > ssr test ./src/list/_example/stripe.vue 1`] = `"<div class="t-list t-size-m t-list--stripe" style=""><!--[--><!--[--><!----><ul class="t-list__inner"><!--[--><!--[--><li class="t-list-item"><div class="t-list-item-main"><!--[-->列表内容的描述性文字<!--]--><!----></div></li><li class="t-list-item"><div class="t-list-item-main"><!--[-->列表内容的描述性文字<!--]--><!----></div></li><li class="t-list-item"><div class="t-list-item-main"><!--[-->列表内容的描述性文字<!--]--><!----></div></li><li class="t-list-item"><div class="t-list-item-main"><!--[-->列表内容的描述性文字<!--]--><!----></div></li><!--]--><!--]--></ul><!----><!--]--><div class="t-list__load"><!----></div><!--]--></div>"`;

exports[`ssr snapshot test > ssr test ./src/list/_example/virtual-scroll.vue 1`] = `"<div class="t-list t-size-m" style="position:relative;height:300px;"><!--[--><!--[--><!----><!--[--><div style="position:absolute;width:1px;height:1px;transition:transform 0.2s;transform:translate(0, 0px);-ms-transform:translate(0, 0px);-moz-transform:translate(0, 0px);-webkit-transform:translate(0, 0px);"></div><ul class="t-list__inner" style="transform:translate(0, 150000px);-ms-transform:translate(0, 150000px);-moz-transform:translate(0, 150000px);-webkit-transform:translate(0, 150000px);"><!--[--><!--]--></ul><!--]--><!----><!--]--><div class="t-list__load"><!----></div><!--]--></div>"`;
exports[`ssr snapshot test > ssr test ./src/list/_example/virtual-scroll.vue 1`] = `"<div class="t-space t-space-vertical" style="gap:16px;"><!--[--><!--[--><div class="t-space-item"><div class="t-list t-size-m" style="height:300px;"><!--[--><!--[--><!----><ul class="t-list__inner"><!--[--><!--[--><!--]--><!--]--></ul><!----><!--]--><div class="t-list__load"><!----></div><!--]--></div></div><!----><!--]--><!--[--><div class="t-space-item"><div class="t-space t-space-horizontal" style="gap:16px;"><!--[--><!--[--><div class="t-space-item"><button class="t-button t-button--variant-base t-button--theme-primary" type="button" href tabindex="0"><span class="t-button__text"><!--[-->滚动到指定节点<!--]--></span></button></div><!----><!--]--><!--]--></div></div><!----><!--]--><!--]--></div>"`;

exports[`ssr snapshot test > ssr test ./src/loading/_example/attach.vue 1`] = `"<div class="t-space t-space-vertical" style="gap:16px;" data-v-5d1ead0d><!--[--><!--[--><div class="t-space-item"><div id="alice" class="loading-attach-demo__title" data-v-5d1ead0d>Hello, I&#39;m Alice. I&#39;m going to be a front-end developer.</div></div><!----><!--]--><!--[--><div class="t-space-item"><!----></div><!----><!--]--><!--[--><div class="t-space-item"><div class="t-switch t-size-m" data-v-5d1ead0d><span class="t-switch__handle"><!----></span><div class="t-switch__content t-size-m">隐藏</div></div></div><!----><!--]--><!--]--></div>"`;

Expand Down
Loading