This commit is contained in:
Dsaquel
2025-05-25 23:44:02 +02:00
parent c0a82c4d56
commit 4f15391dd2
8 changed files with 95 additions and 37 deletions

View File

@@ -39,9 +39,9 @@ scrollbar/manual-scroll
:::
## Infinite scroll ^(2.9.11)
## Infinite scroll ^(2.9.12)
:::demo When the end of the scroll is reached `endReached` is called. It can be used for infinite scroll.
:::demo When the end of the scroll is reached `bottom-reached` is called. It can be used for infinite scroll.
scrollbar/infinite-scroll
@@ -51,25 +51,26 @@ scrollbar/infinite-scroll
### Attributes
| Name | Description | Type | Default |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------- |
| height | height of scrollbar | ^[string] / ^[number] | — |
| max-height | max height of scrollbar | ^[string] / ^[number] | — |
| native | whether to use the native scrollbar style | ^[boolean] | false |
| wrap-style | style of wrap container | ^[string] / ^[object]`CSSProperties \| CSSProperties[] \| string[]` | — |
| wrap-class | class of wrap container | ^[string] | — |
| view-style | style of view | ^[string] / ^[object]`CSSProperties \| CSSProperties[] \| string[]` | — |
| view-class | class of view | ^[string] | — |
| noresize | do not respond to container size changes, if the container size does not change, it is better to set it to optimize performance | ^[boolean] | false |
| tag | element tag of the view | ^[string] | div |
| always | always show scrollbar | ^[boolean] | false |
| min-size | minimum size of scrollbar | ^[number] | 20 |
| id ^(2.4.0) | id of view | ^[string] | — |
| role ^(2.4.0) ^(a11y) | role of view | ^[string] | — |
| aria-label ^(2.4.0) ^(a11y) | aria-label of view | ^[string] | — |
| aria-orientation ^(2.4.0) ^(a11y) | aria-orientation of view | ^[enum]`'horizontal' \| 'vertical'` | — |
| tabindex ^(2.8.3) | tabindex of wrap container | ^[number] / ^[string] | — |
| end-reached ^(2.9.11) | Invoked when the end of the scroll is reached. Useful for infinite scroll. | (options: [InfiniteLoadScrollOptions](#typings)) => Awaitable<void\> | — |
| Name | Description | Type | Default |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------- |
| height | height of scrollbar | ^[string] / ^[number] | — |
| max-height | max height of scrollbar | ^[string] / ^[number] | — |
| native | whether to use the native scrollbar style | ^[boolean] | false |
| wrap-style | style of wrap container | ^[string] / ^[object]`CSSProperties \| CSSProperties[] \| string[]` | — |
| wrap-class | class of wrap container | ^[string] | — |
| view-style | style of view | ^[string] / ^[object]`CSSProperties \| CSSProperties[] \| string[]` | — |
| view-class | class of view | ^[string] | — |
| noresize | do not respond to container size changes, if the container size does not change, it is better to set it to optimize performance | ^[boolean] | false |
| tag | element tag of the view | ^[string] | div |
| always | always show scrollbar | ^[boolean] | false |
| min-size | minimum size of scrollbar | ^[number] | 20 |
| id ^(2.4.0) | id of view | ^[string] | — |
| role ^(2.4.0) ^(a11y) | role of view | ^[string] | — |
| aria-label ^(2.4.0) ^(a11y) | aria-label of view | ^[string] | — |
| aria-orientation ^(2.4.0) ^(a11y) | aria-orientation of view | ^[enum]`'horizontal' \| 'vertical'` | — |
| tabindex ^(2.8.3) | tabindex of wrap container | ^[number] / ^[string] | — |
| bottom-reached ^(2.9.12) | invoked when the end of the scroll is reached. Useful for infinite scroll. | (options: [ScrollOptions](#typings)) => Awaitable<void\> | — |
| direction ^(2.9.12) | direction to use the infinite scroll scroll. | ^[enum]`'top'\| 'bottom' \| 'left'\| 'right'` | bottom |
### Events
@@ -104,8 +105,7 @@ import { useScroll } from '@vueuse/core'
import type { UnwrapNestedRefs } from 'vue'
type Awaitable<T> = Promise<T> | T
type InfiniteLoadScrollOptions = UnwrapNestedRefs<ReturnType<typeof useScroll>>
type ScrollOptions = UnwrapNestedRefs<ReturnType<typeof useScroll>>
```
</details>

View File

@@ -1,5 +1,5 @@
<template>
<el-scrollbar height="400px" :end-reached="loadMore">
<el-scrollbar height="400px" :bottom-reached="loadMore">
<p v-for="item in num" :key="item" class="scrollbar-demo-item">
{{ item }}
</p>

View File

@@ -1,8 +1,10 @@
import type { InjectionKey } from 'vue'
import type { ScrollbarProps } from './scrollbar'
export interface ScrollbarContext {
scrollbarElement: HTMLDivElement
wrapElement: HTMLDivElement
direction: ScrollbarProps['direction']
}
export const scrollbarContextKey: InjectionKey<ScrollbarContext> = Symbol(

View File

@@ -100,18 +100,22 @@ export const scrollbarProps = buildProps({
/**
* @description hook function when reach the end of scroll.
*/
endReached: {
type: definePropType<
(options: InfiniteLoadScrollOptions) => Awaitable<void>
>(Function),
bottomReached: {
type: definePropType<(options: ScrollOptions) => Awaitable<void>>(Function),
default: NOOP,
},
/**
* @description direction to use the infinite scroll
*/
direction: {
type: String,
values: ['top', 'bottom', 'left', 'right'] as const,
default: 'bottom',
},
...useAriaProps(['ariaLabel', 'ariaOrientation']),
} as const)
export type ScrollbarProps = ExtractPropTypes<typeof scrollbarProps>
export type InfiniteLoadScrollOptions = UnwrapNestedRefs<
ReturnType<typeof useScroll>
>
export type ScrollOptions = UnwrapNestedRefs<ReturnType<typeof useScroll>>
export const scrollbarEmits = {
scroll: ({

View File

@@ -1,5 +1,5 @@
<template>
<div ref="scrollbarRef" :class="ns.b()">
<div ref="scrollbarRef" :class="[ns.b(), ns.is(direction)]">
<div ref="wrapRef" :class="wrapKls" :style="wrapStyle" :tabindex="tabindex">
<component
:is="tag"
@@ -67,7 +67,10 @@ const resizeRef = ref<HTMLElement>()
const barRef = ref<BarInstance>()
onMounted(() => {
useInfiniteScroll(wrapRef, props.endReached, { onScroll: handleScroll })
useInfiniteScroll(wrapRef, props.bottomReached, {
onScroll: handleScroll,
direction: props.direction,
})
})
const wrapStyle = computed<StyleValue>(() => {
@@ -166,6 +169,7 @@ provide(
reactive({
scrollbarElement: scrollbarRef,
wrapElement: wrapRef,
direction: props.direction,
})
)

View File

@@ -47,12 +47,16 @@ let originalOnSelectStart:
| null = isClient ? document.onselectstart : null
const bar = computed(() => BAR_MAP[props.vertical ? 'vertical' : 'horizontal'])
const reverseScroll = computed(() =>
scrollbar.direction.includes(bar.value.direction)
)
const thumbStyle = computed(() =>
renderThumbStyle({
size: props.size,
move: props.move,
bar: bar.value,
reverse: reverseScroll.value,
})
)
@@ -94,11 +98,17 @@ const clickTrackHandler = (e: MouseEvent) => {
((offset - thumbHalf) * 100 * offsetRatio.value) /
instance.value[bar.value.offset]
scrollbar.wrapElement[bar.value.scroll] =
let scrollOffset =
(thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
100
if (reverseScroll.value) {
const scrollSize =
scrollbar.wrapElement[bar.value.scrollSize] -
scrollbar.wrapElement[bar.value.offset]
scrollOffset = -(scrollSize - scrollOffset)
}
scrollbar.wrapElement[bar.value.scroll] = scrollOffset
}
const startDrag = (e: MouseEvent) => {
e.stopImmediatePropagation()
cursorDown = true
@@ -123,9 +133,16 @@ const mouseMoveDocumentHandler = (e: MouseEvent) => {
const thumbPositionPercentage =
((offset - thumbClickPosition) * 100 * offsetRatio.value) /
instance.value[bar.value.offset]
scrollbar.wrapElement[bar.value.scroll] =
let scrollOffset =
(thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
100
if (reverseScroll.value) {
const scrollSize =
scrollbar.wrapElement[bar.value.scrollSize] -
scrollbar.wrapElement[bar.value.offset]
scrollOffset = -(scrollSize - scrollOffset)
}
scrollbar.wrapElement[bar.value.scroll] = scrollOffset
}
const mouseUpDocumentHandler = () => {

View File

@@ -30,9 +30,13 @@ export const renderThumbStyle = ({
move,
size,
bar,
reverse,
}: Pick<ThumbProps, 'move' | 'size'> & {
bar: typeof BAR_MAP[keyof typeof BAR_MAP]
reverse: boolean
}): CSSProperties => ({
[bar.size]: size,
transform: `translate${bar.axis}(${move}%)`,
transform: `${reverse ? `scale${bar.axis}(-1) ` : ''}translate${
bar.axis
}(${move}%)`,
})

View File

@@ -13,6 +13,33 @@
position: relative;
height: 100%;
@include when(top) {
@include e(wrap) {
display: flex;
flex-direction: column-reverse;
}
@include e(bar) {
transform: scaleY(-1);
}
}
@include when(left) {
@include e(wrap) {
display: flex;
flex-direction: row-reverse;
}
@include e(view) {
display: inline-flex;
}
@include e(bar) {
transform: scaleX(-1);
}
}
@include when(right) {
@include e(view) {
display: inline-flex;
}
}
@include e(wrap) {
overflow: auto;
height: 100%;