+
+
{{ i }}
+
+ loading...
+
@@ -36,8 +46,10 @@ export default defineComponent({
margin-bottom: 10px;
background-color: rgba(0, 128, 0, 0.16);
}
-
.item:last-child {
margin-bottom: 0;
}
+.text {
+ text-align: center;
+}
diff --git a/src/infinite-scroll/demos/enUS/index.demo-entry.md b/src/infinite-scroll/demos/enUS/index.demo-entry.md
index 00707618358..a9f04254f7b 100644
--- a/src/infinite-scroll/demos/enUS/index.demo-entry.md
+++ b/src/infinite-scroll/demos/enUS/index.demo-entry.md
@@ -8,6 +8,7 @@ Available since `2.38.2`.
```demo
basic.vue
+reverse.vue
chat.vue
```
@@ -18,5 +19,12 @@ chat.vue
| Name | Type | Default | Description | Version |
| --- | --- | --- | --- | --- |
| distance | `number` | `0` | Distance threshold that triggers loading. | 2.38.2 |
+| reverse | `boolean` | `false` | Whether to trigger load more from the top. Defaults to loading from bottom. | NEXT_VERSION |
| scrollbar-props | `Object` | `undefined` | Attribute reference [Scrollbar props](scrollbar#Scrollbar-Props). | 2.38.2 |
| on-load | `() => Promise
\| void` | `undefined` | The callback function when scrolling to the bottom. | 2.38.2 |
+
+### Infinite Slots
+
+| Name | Parameters | Description |
+| ------- | ---------- | ------------------ |
+| default | `()` | Infinite's content |
diff --git a/src/infinite-scroll/demos/enUS/reverse.demo.vue b/src/infinite-scroll/demos/enUS/reverse.demo.vue
new file mode 100644
index 00000000000..5c7475e79a1
--- /dev/null
+++ b/src/infinite-scroll/demos/enUS/reverse.demo.vue
@@ -0,0 +1,61 @@
+
+# Reverse
+
+
+
+
+
+
+
+ loading...
+
+
+ {{ i }}
+
+
+
+
+
diff --git a/src/infinite-scroll/demos/zhCN/basic.demo.vue b/src/infinite-scroll/demos/zhCN/basic.demo.vue
index 3c25f1084bf..e0791588535 100644
--- a/src/infinite-scroll/demos/zhCN/basic.demo.vue
+++ b/src/infinite-scroll/demos/zhCN/basic.demo.vue
@@ -2,28 +2,38 @@
# 基础
-
-
-
+
+
{{ i }}
+
+ loading...
+
@@ -36,8 +46,10 @@ export default defineComponent({
margin-bottom: 10px;
background-color: rgba(0, 128, 0, 0.16);
}
-
.item:last-child {
margin-bottom: 0;
}
+.text {
+ text-align: center;
+}
diff --git a/src/infinite-scroll/demos/zhCN/index.demo-entry.md b/src/infinite-scroll/demos/zhCN/index.demo-entry.md
index de2aa1810b1..14bb9c38630 100644
--- a/src/infinite-scroll/demos/zhCN/index.demo-entry.md
+++ b/src/infinite-scroll/demos/zhCN/index.demo-entry.md
@@ -8,6 +8,7 @@
```demo
basic.vue
+reverse.vue
chat.vue
```
@@ -18,5 +19,12 @@ chat.vue
| 名称 | 类型 | 默认值 | 说明 | 版本 |
| --- | --- | --- | --- | --- |
| distance | `number` | `0` | 触发加载的距离阈值 | 2.38.2 |
+| reverse | `boolean` | `false` | 是否从顶部触发加载。默认从底部触发加载 | NEXT_VERSION |
| scrollbar-props | `Object` | `undefined` | 属性参考 [Scrollbar props](scrollbar#Scrollbar-Props) | 2.38.2 |
| on-load | `() => Promise
\| void` | `undefined` | 滚动到底部时的回调函数 | 2.38.2 |
+
+### Infinite Slots
+
+| 名称 | 参数 | 说明 |
+| ------- | ---- | --------------- |
+| default | `()` | Infinite 的内容 |
diff --git a/src/infinite-scroll/demos/zhCN/reverse.demo.vue b/src/infinite-scroll/demos/zhCN/reverse.demo.vue
new file mode 100644
index 00000000000..1b62da65dee
--- /dev/null
+++ b/src/infinite-scroll/demos/zhCN/reverse.demo.vue
@@ -0,0 +1,61 @@
+
+# 反向
+
+
+
+
+
+
+
+ loading...
+
+
+ {{ i }}
+
+
+
+
+
diff --git a/src/infinite-scroll/src/InfiniteScroll.tsx b/src/infinite-scroll/src/InfiniteScroll.tsx
index fbb3d3e111c..824560caee2 100644
--- a/src/infinite-scroll/src/InfiniteScroll.tsx
+++ b/src/infinite-scroll/src/InfiniteScroll.tsx
@@ -1,6 +1,13 @@
-import { type PropType, defineComponent, h, ref } from 'vue'
+import {
+ type PropType,
+ defineComponent,
+ h,
+ nextTick,
+ onMounted,
+ ref
+} from 'vue'
+import { throttle } from 'lodash'
import type { ExtractPublicPropTypes } from '../../_utils'
-import { resolveSlot } from '../../_utils'
import type { ScrollbarProps } from '../../scrollbar/src/Scrollbar'
import { NxScrollbar, type ScrollbarInst } from '../../_internal'
@@ -9,6 +16,7 @@ export const infiniteScrollProps = {
type: Number,
default: 0
},
+ reverse: Boolean,
onLoad: Function as PropType<() => Promise | void>,
scrollbarProps: Object as PropType
} as const
@@ -24,51 +32,54 @@ export default defineComponent({
const scrollbarInstRef = ref(null)
let loading = false
-
- const handleCheckBottom = async (): Promise => {
- const { value: scrollbarInst } = scrollbarInstRef
- if (scrollbarInst) {
- const { containerRef } = scrollbarInst
- const scrollHeight = containerRef?.scrollHeight
- const clientHeight = containerRef?.clientHeight
- const scrollTop = containerRef?.scrollTop
-
- if (
- containerRef
- && scrollHeight !== undefined
- && clientHeight !== undefined
- && scrollTop !== undefined
- ) {
- if (scrollTop + clientHeight >= scrollHeight - props.distance) {
- loading = true
- try {
- await props.onLoad?.()
- }
- catch {}
- loading = false
- }
- }
+ async function handleLoad() {
+ loading = true
+ try {
+ await props.onLoad?.()
+ }
+ finally {
+ loading = false
}
}
-
- const handleScroll = (): void => {
- if (loading)
- return
- void handleCheckBottom()
+ const handleScroll = throttle(_handleScroll, 200, { trailing: true })
+ function _handleScroll(): void {
+ triggerLoad()
}
-
- const handleWheel = (e: WheelEvent): void => {
- if (e.deltaY <= 0)
- return
+ async function triggerLoad() {
if (loading)
return
- void handleCheckBottom()
+ const { value: scrollbarInst } = scrollbarInstRef
+ const containerRef = scrollbarInst?.containerRef
+ if (!containerRef)
+ return
+ const {
+ scrollHeight: scrollHeightBefore,
+ clientHeight,
+ scrollTop
+ } = containerRef
+ const { reverse, distance } = props
+ if (reverse) {
+ if (scrollTop <= distance) {
+ await handleLoad()
+ nextTick(() => {
+ const scrollHeightAfter = containerRef.scrollHeight
+ const top = scrollHeightAfter - scrollHeightBefore
+ scrollbarInstRef.value?.scrollTo({ top })
+ })
+ }
+ }
+ else if (scrollTop + clientHeight + distance >= scrollHeightBefore) {
+ handleLoad()
+ }
}
+ onMounted(() => {
+ triggerLoad()
+ })
+
return {
scrollbarInstRef,
- handleScroll,
- handleWheel
+ handleScroll
}
},
render() {
@@ -76,14 +87,9 @@ export default defineComponent({
- {{
- default: () => {
- return resolveSlot(this.$slots.default, () => [])
- }
- }}
+ {this.$slots.default}
)
}