@v-c/virtual-list 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/bump.config.ts +6 -0
  2. package/dist/Filler.cjs +51 -1
  3. package/dist/Filler.js +47 -56
  4. package/dist/Item.cjs +26 -1
  5. package/dist/Item.js +23 -26
  6. package/dist/List.cjs +408 -1
  7. package/dist/List.d.ts +45 -9
  8. package/dist/List.js +403 -274
  9. package/dist/ScrollBar.cjs +259 -1
  10. package/dist/ScrollBar.d.ts +3 -97
  11. package/dist/ScrollBar.js +254 -191
  12. package/dist/_virtual/rolldown_runtime.cjs +21 -0
  13. package/dist/hooks/useDiffItem.cjs +19 -1
  14. package/dist/hooks/useDiffItem.js +16 -20
  15. package/dist/hooks/useFrameWheel.cjs +63 -1
  16. package/dist/hooks/useFrameWheel.js +60 -51
  17. package/dist/hooks/useGetSize.cjs +29 -1
  18. package/dist/hooks/useGetSize.d.ts +2 -2
  19. package/dist/hooks/useGetSize.js +27 -23
  20. package/dist/hooks/useHeights.cjs +66 -1
  21. package/dist/hooks/useHeights.d.ts +1 -1
  22. package/dist/hooks/useHeights.js +62 -41
  23. package/dist/hooks/useMobileTouchMove.cjs +82 -1
  24. package/dist/hooks/useMobileTouchMove.js +79 -43
  25. package/dist/hooks/useOriginScroll.cjs +23 -1
  26. package/dist/hooks/useOriginScroll.js +20 -16
  27. package/dist/hooks/useScrollDrag.cjs +83 -1
  28. package/dist/hooks/useScrollDrag.js +77 -48
  29. package/dist/hooks/useScrollTo.cjs +97 -0
  30. package/dist/hooks/useScrollTo.d.ts +19 -0
  31. package/dist/hooks/useScrollTo.js +94 -0
  32. package/dist/index.cjs +4 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +3 -4
  35. package/dist/interface.cjs +0 -1
  36. package/dist/interface.d.ts +1 -1
  37. package/dist/interface.js +0 -1
  38. package/dist/utils/CacheMap.cjs +25 -1
  39. package/dist/utils/CacheMap.d.ts +1 -1
  40. package/dist/utils/CacheMap.js +23 -28
  41. package/dist/utils/isFirefox.cjs +4 -1
  42. package/dist/utils/isFirefox.js +2 -4
  43. package/dist/utils/scrollbarUtil.cjs +8 -1
  44. package/dist/utils/scrollbarUtil.js +7 -6
  45. package/docs/animate.less +31 -0
  46. package/docs/animate.vue +159 -0
  47. package/docs/basic.vue +2 -1
  48. package/docs/nest.vue +1 -1
  49. package/docs/switch.vue +2 -1
  50. package/docs/virtual-list.stories.vue +4 -0
  51. package/package.json +16 -14
  52. package/src/Filler.tsx +2 -1
  53. package/src/Item.tsx +2 -1
  54. package/src/List.tsx +189 -124
  55. package/src/ScrollBar.tsx +33 -44
  56. package/src/hooks/useDiffItem.ts +3 -2
  57. package/src/hooks/useFrameWheel.ts +2 -1
  58. package/src/hooks/useGetSize.ts +5 -4
  59. package/src/hooks/useHeights.ts +7 -6
  60. package/src/hooks/useMobileTouchMove.ts +2 -1
  61. package/src/hooks/useOriginScroll.ts +2 -1
  62. package/src/hooks/useScrollDrag.ts +2 -1
  63. package/src/hooks/useScrollTo.tsx +184 -0
  64. package/src/index.ts +1 -1
  65. package/tsconfig.json +7 -0
  66. package/vitest.config.ts +1 -1
package/src/List.tsx CHANGED
@@ -1,26 +1,28 @@
1
1
  import type { Key } from '@v-c/util/dist/type'
2
2
 
3
+ import type { CSSProperties, PropType, VNode } from 'vue'
4
+ import type { InnerProps } from './Filler'
3
5
  import type { ExtraRenderInfo } from './interface'
6
+ import type { ScrollBarDirectionType, ScrollBarRef } from './ScrollBar'
4
7
  import ResizeObserver from '@v-c/resize-observer'
8
+ import { pureAttrs } from '@v-c/util/dist/props-util'
5
9
  import {
6
10
  computed,
7
- type CSSProperties,
8
11
  defineComponent,
9
- type PropType,
10
12
  ref,
11
13
  shallowRef,
12
- type VNode,
13
14
  watch,
14
15
  } from 'vue'
15
- import Filler, { type InnerProps } from './Filler'
16
+ import Filler from './Filler'
16
17
  import useDiffItem from './hooks/useDiffItem'
17
18
  import useFrameWheel from './hooks/useFrameWheel'
18
19
  import { useGetSize } from './hooks/useGetSize'
19
20
  import useHeights from './hooks/useHeights'
20
21
  import useMobileTouchMove from './hooks/useMobileTouchMove'
21
22
  import useScrollDrag from './hooks/useScrollDrag'
23
+ import useScrollTo from './hooks/useScrollTo'
22
24
  import Item from './Item'
23
- import ScrollBar, { type ScrollBarRef } from './ScrollBar'
25
+ import ScrollBar from './ScrollBar'
24
26
  import { getSpinSize } from './utils/scrollbarUtil'
25
27
 
26
28
  const EMPTY_DATA: any[] = []
@@ -35,9 +37,11 @@ export interface ScrollInfo {
35
37
  y: number
36
38
  }
37
39
 
40
+ export type ScrollTo = (arg?: number | ScrollConfig | null) => void
41
+
38
42
  export interface ListRef {
39
43
  nativeElement?: HTMLDivElement
40
- scrollTo: (arg?: number | ScrollConfig) => void
44
+ scrollTo: ScrollTo
41
45
  getScrollInfo: () => ScrollInfo
42
46
  }
43
47
 
@@ -55,18 +59,33 @@ export interface ScrollTarget {
55
59
 
56
60
  export type ScrollConfig = ScrollTarget | ScrollPos
57
61
 
58
- export interface ListProps<T = any> {
62
+ export interface ListProps {
59
63
  prefixCls?: string
60
- data?: T[]
64
+ data?: any[]
61
65
  height?: number
62
66
  itemHeight?: number
63
67
  fullHeight?: boolean
64
- itemKey: Key | ((item: T) => Key)
68
+ itemKey: Key | ((item: any) => Key)
65
69
  component?: string
66
70
  virtual?: boolean
71
+ direction?: ScrollBarDirectionType
72
+ /**
73
+ * By default `scrollWidth` is same as container.
74
+ * When set this, it will show the horizontal scrollbar and
75
+ * `scrollWidth` will be used as the real width instead of container width.
76
+ * When set, `virtual` will always be enabled.
77
+ */
78
+ scrollWidth?: number
79
+ styles?: {
80
+ horizontalScrollBar?: CSSProperties
81
+ horizontalScrollBarThumb?: CSSProperties
82
+ verticalScrollBar?: CSSProperties
83
+ verticalScrollBarThumb?: CSSProperties
84
+ }
85
+ showScrollBar?: boolean | 'optional'
67
86
  onScroll?: (e: Event) => void
68
87
  onVirtualScroll?: (info: ScrollInfo) => void
69
- onVisibleChange?: (visibleList: T[], fullList: T[]) => void
88
+ onVisibleChange?: (visibleList: any[], fullList: any[]) => void
70
89
  innerProps?: InnerProps
71
90
  extraRender?: (info: ExtraRenderInfo) => VNode
72
91
  }
@@ -81,6 +100,10 @@ export default defineComponent({
81
100
  fullHeight: { type: Boolean, default: true },
82
101
  itemKey: { type: [String, Number, Function] as PropType<Key | ((item: any) => Key)>, required: true },
83
102
  component: { type: String, default: 'div' },
103
+ direction: { type: String as PropType<ScrollBarDirectionType> },
104
+ scrollWidth: Number,
105
+ styles: Object,
106
+ showScrollBar: { type: [Boolean, String] as PropType<boolean | 'optional'>, default: 'optional' },
84
107
  virtual: { type: Boolean, default: true },
85
108
  onScroll: Function as PropType<(e: Event) => void>,
86
109
  onVirtualScroll: Function as PropType<(info: ScrollInfo) => void>,
@@ -88,7 +111,9 @@ export default defineComponent({
88
111
  innerProps: Object as PropType<InnerProps>,
89
112
  extraRender: Function as PropType<(info: ExtraRenderInfo) => VNode>,
90
113
  },
114
+ inheritAttrs: false,
91
115
  setup(props, { expose, attrs, slots }) {
116
+ const itemHeight = computed(() => props.itemHeight)
92
117
  // =============================== Item Key ===============================
93
118
  const getKey = (item: any): Key => {
94
119
  if (typeof props.itemKey === 'function') {
@@ -120,7 +145,8 @@ export default defineComponent({
120
145
  return (
121
146
  useVirtual.value
122
147
  && data
123
- && Math.max(props.itemHeight! * data.length, containerHeight.value) > props.height!
148
+ && (Math.max(props.itemHeight! * data.length, containerHeight.value) > props.height!
149
+ || !!props.scrollWidth)
124
150
  )
125
151
  })
126
152
 
@@ -128,6 +154,7 @@ export default defineComponent({
128
154
  const fillerInnerRef = ref<HTMLDivElement>()
129
155
  const containerRef = ref<HTMLDivElement>()
130
156
  const verticalScrollBarRef = shallowRef<ScrollBarRef>()
157
+ const horizontalScrollBarRef = shallowRef<ScrollBarRef>()
131
158
 
132
159
  const offsetTop = ref(0)
133
160
  const offsetLeft = ref(0)
@@ -135,7 +162,14 @@ export default defineComponent({
135
162
 
136
163
  // ScrollBar related
137
164
  const verticalScrollBarSpinSize = ref(0)
138
- const scrollWidth = ref(0)
165
+ const horizontalScrollBarSpinSize = ref(0)
166
+ const contentScrollWidth = ref<number>(props.scrollWidth || 0)
167
+
168
+ // ========================== Visible Calculation =========================
169
+ const scrollHeight = ref(0)
170
+ const start = ref(0)
171
+ const end = ref(0)
172
+ const fillerOffset = ref<number | undefined>(undefined)
139
173
 
140
174
  // ================================ Scroll ================================
141
175
  function syncScrollTop(newTop: number | ((prev: number) => number)) {
@@ -159,19 +193,13 @@ export default defineComponent({
159
193
  // ================================ Range ================================
160
194
  useDiffItem(mergedData, getKey)
161
195
 
162
- // ========================== Visible Calculation =========================
163
- const scrollHeight = ref(0)
164
- const start = ref(0)
165
- const end = ref(0)
166
- const fillerOffset = ref<number | undefined>(undefined)
167
-
168
196
  watch(
169
197
  [
170
- () => inVirtual.value,
171
- () => useVirtual.value,
172
- () => offsetTop.value,
173
- () => mergedData.value,
174
- () => heightUpdatedMark.value,
198
+ inVirtual,
199
+ useVirtual,
200
+ offsetTop,
201
+ mergedData,
202
+ heightUpdatedMark,
175
203
  () => props.height,
176
204
  ],
177
205
  () => {
@@ -239,7 +267,7 @@ export default defineComponent({
239
267
 
240
268
  // Sync scroll top when height changes
241
269
  watch(
242
- () => scrollHeight.value,
270
+ scrollHeight,
243
271
  () => {
244
272
  const changedRecord = heights.getRecord()
245
273
  if (changedRecord.size === 1) {
@@ -269,11 +297,14 @@ export default defineComponent({
269
297
  width: sizeInfo.offsetWidth,
270
298
  height: sizeInfo.offsetHeight,
271
299
  }
300
+ contentScrollWidth.value = props.scrollWidth ?? sizeInfo.offsetWidth
272
301
  }
273
302
 
274
303
  // =============================== Scroll ===============================
304
+ const isRTL = computed(() => props.direction === 'rtl')
305
+
275
306
  const getVirtualScrollInfo = () => ({
276
- x: offsetLeft.value,
307
+ x: isRTL.value ? -offsetLeft.value : offsetLeft.value,
277
308
  y: offsetTop.value,
278
309
  })
279
310
 
@@ -294,14 +325,24 @@ export default defineComponent({
294
325
  }
295
326
 
296
327
  // ========================== Scroll Position ===========================
328
+ const horizontalRange = computed(() =>
329
+ Math.max(0, (contentScrollWidth.value || 0) - size.value.width),
330
+ )
331
+
297
332
  const isScrollAtTop = computed(() => offsetTop.value === 0)
298
333
  const isScrollAtBottom = computed(() => offsetTop.value + props.height! >= scrollHeight.value)
299
334
  const isScrollAtLeft = computed(() => offsetLeft.value === 0)
300
- const isScrollAtRight = computed(() => offsetLeft.value + size.value.width >= scrollWidth.value)
335
+ const isScrollAtRight = computed(() => offsetLeft.value >= horizontalRange.value)
336
+
337
+ const keepInHorizontalRange = (nextOffsetLeft: number) => {
338
+ const max = horizontalRange.value
339
+ return Math.max(0, Math.min(nextOffsetLeft, max))
340
+ }
301
341
 
302
342
  // ========================== Wheel & Touch =========================
303
343
  const delayHideScrollBar = () => {
304
344
  verticalScrollBarRef.value?.delayHidden()
345
+ horizontalScrollBarRef.value?.delayHidden()
305
346
  }
306
347
 
307
348
  const [onWheel] = useFrameWheel(
@@ -310,10 +351,13 @@ export default defineComponent({
310
351
  isScrollAtBottom,
311
352
  isScrollAtLeft,
312
353
  isScrollAtRight,
313
- false, // horizontalScroll
354
+ horizontalRange.value > 0,
314
355
  (offsetY, isHorizontal) => {
315
356
  if (isHorizontal) {
316
- // Not implemented yet
357
+ const next = isRTL.value ? offsetLeft.value - offsetY : offsetLeft.value + offsetY
358
+ const aligned = keepInHorizontalRange(next)
359
+ offsetLeft.value = aligned
360
+ triggerScroll({ x: isRTL.value ? -aligned : aligned })
317
361
  }
318
362
  else {
319
363
  syncScrollTop(top => top + offsetY)
@@ -326,8 +370,11 @@ export default defineComponent({
326
370
  componentRef,
327
371
  (isHorizontal, offset, _smoothOffset, _e) => {
328
372
  if (isHorizontal) {
329
- // Not implemented yet
330
- return false
373
+ const next = isRTL.value ? offsetLeft.value - offset : offsetLeft.value + offset
374
+ const aligned = keepInHorizontalRange(next)
375
+ offsetLeft.value = aligned
376
+ triggerScroll({ x: isRTL.value ? -aligned : aligned })
377
+ return true
331
378
  }
332
379
  else {
333
380
  syncScrollTop(top => top + offset)
@@ -348,7 +395,8 @@ export default defineComponent({
348
395
  const onScrollBar = (newScrollOffset: number, horizontal?: boolean) => {
349
396
  const newOffset = newScrollOffset
350
397
  if (horizontal) {
351
- // Not implemented yet
398
+ offsetLeft.value = newOffset
399
+ triggerScroll({ x: isRTL.value ? -newOffset : newOffset })
352
400
  }
353
401
  else {
354
402
  syncScrollTop(newOffset)
@@ -365,16 +413,34 @@ export default defineComponent({
365
413
 
366
414
  // Calculate ScrollBar spin size
367
415
  watch(
368
- [() => props.height, () => scrollHeight.value, () => inVirtual.value, () => size.value.height],
416
+ [() => props.height, scrollHeight, inVirtual, () => size.value.height],
369
417
  () => {
370
418
  if (inVirtual.value && props.height && scrollHeight.value) {
371
- // First parameter is container size, second is scroll range
372
419
  verticalScrollBarSpinSize.value = getSpinSize(size.value.height, scrollHeight.value)
373
420
  }
374
421
  },
375
422
  { immediate: true },
376
423
  )
377
424
 
425
+ watch(
426
+ [() => size.value.width, () => contentScrollWidth.value],
427
+ () => {
428
+ if (inVirtual.value && contentScrollWidth.value) {
429
+ horizontalScrollBarSpinSize.value = getSpinSize(size.value.width, contentScrollWidth.value)
430
+ }
431
+ },
432
+ { immediate: true },
433
+ )
434
+
435
+ watch(
436
+ () => props.scrollWidth,
437
+ (val) => {
438
+ contentScrollWidth.value = val ?? size.value.width
439
+ offsetLeft.value = keepInHorizontalRange(offsetLeft.value)
440
+ },
441
+ { immediate: true },
442
+ )
443
+
378
444
  function onFallbackScroll(e: Event) {
379
445
  const target = e.currentTarget as HTMLDivElement
380
446
  const newScrollTop = target.scrollTop
@@ -387,65 +453,39 @@ export default defineComponent({
387
453
  }
388
454
 
389
455
  // ================================= Ref ==================================
390
- const scrollTo = (config?: number | ScrollConfig) => {
391
- if (config === null || config === undefined) {
392
- return
393
- }
394
-
395
- if (typeof config === 'number') {
396
- syncScrollTop(config)
397
- }
398
- else if (config && typeof config === 'object') {
399
- let scrollTop: number | undefined
400
-
401
- if ('left' in config) {
402
- offsetLeft.value = config.left || 0
403
- }
456
+ const scrollTo = useScrollTo(
457
+ componentRef as any,
458
+ mergedData,
459
+ heights,
460
+ itemHeight as any,
461
+ getKey,
462
+ () => collectHeight(true),
463
+ syncScrollTop,
464
+ delayHideScrollBar,
465
+ )
404
466
 
405
- if ('top' in config) {
406
- scrollTop = config.top
407
- }
408
- else if ('index' in config) {
409
- const index = config.index || 0
410
- const item = mergedData.value[index]
411
- if (item) {
412
- let itemTop = 0
413
- for (let i = 0; i < index; i += 1) {
414
- const key = getKey(mergedData.value[i])
415
- const cacheHeight = heights.get(key)
416
- itemTop += cacheHeight === undefined ? props.itemHeight! : cacheHeight
417
- }
418
- scrollTop = itemTop
419
- }
467
+ expose({
468
+ nativeElement: containerRef,
469
+ getScrollInfo: getVirtualScrollInfo,
470
+ scrollTo: (config: any) => {
471
+ function isPosScroll(arg: any): arg is ScrollPos {
472
+ return arg && typeof arg === 'object' && ('left' in arg || 'top' in arg)
420
473
  }
421
- else if ('key' in config) {
422
- const index = mergedData.value.findIndex(item => getKey(item) === config.key)
423
- if (index >= 0) {
424
- let itemTop = 0
425
- for (let i = 0; i < index; i += 1) {
426
- const key = getKey(mergedData.value[i])
427
- const cacheHeight = heights.get(key)
428
- itemTop += cacheHeight === undefined ? props.itemHeight! : cacheHeight
429
- }
430
- scrollTop = itemTop
474
+ if (isPosScroll(config)) {
475
+ if (config.left !== undefined) {
476
+ offsetLeft.value = keepInHorizontalRange(config.left)
431
477
  }
478
+ scrollTo(config.top as any)
432
479
  }
433
-
434
- if (scrollTop !== undefined) {
435
- syncScrollTop(scrollTop)
480
+ else {
481
+ scrollTo(config)
436
482
  }
437
- }
438
- }
439
-
440
- expose({
441
- nativeElement: containerRef,
442
- getScrollInfo: getVirtualScrollInfo,
443
- scrollTo,
483
+ },
444
484
  })
445
485
 
446
486
  // ================================ Effect ================================
447
487
  watch(
448
- [() => start.value, () => end.value, () => mergedData.value],
488
+ [start, end, mergedData],
449
489
  () => {
450
490
  if (props.onVisibleChange) {
451
491
  const renderList = mergedData.value.slice(start.value, end.value + 1)
@@ -454,54 +494,52 @@ export default defineComponent({
454
494
  },
455
495
  )
456
496
 
457
- // ================================ Render ================================
458
- const renderChildren = () => {
459
- const children: VNode[] = []
460
- const data = mergedData.value
461
- const defaultSlot = slots.default
497
+ const getSize = useGetSize(mergedData, getKey, heights, itemHeight as any)
462
498
 
463
- if (!defaultSlot) {
464
- return children
465
- }
499
+ return () => {
500
+ // ================================ Render ================================
501
+ const renderChildren = () => {
502
+ const children: VNode[] = []
503
+ const data = mergedData.value
504
+ const defaultSlot = slots.default
466
505
 
467
- for (let i = start.value; i <= end.value; i += 1) {
468
- const item = data[i]
469
- const key = getKey(item)
470
- // Call the slot function with item, index, and props
471
- const nodes = defaultSlot({
472
- item,
473
- index: i,
474
- style: {},
475
- offsetX: offsetLeft.value,
476
- })
477
-
478
- // Wrap each node in Item component
479
- const node = Array.isArray(nodes) ? nodes[0] : nodes
480
- if (node) {
481
- children.push(
482
- <Item key={key} setRef={ele => setInstanceRef(item, ele)}>
483
- {node}
484
- </Item>,
485
- )
506
+ if (!defaultSlot) {
507
+ return children
486
508
  }
487
- }
488
509
 
489
- return children
490
- }
491
-
492
- const getSize = useGetSize(mergedData, getKey, heights, props.itemHeight!)
510
+ for (let i = start.value; i <= end.value; i += 1) {
511
+ const item = data[i]
512
+ const key = getKey(item)
513
+ // Call the slot function with item, index, and props
514
+ const nodes = defaultSlot({
515
+ item,
516
+ index: i,
517
+ style: {},
518
+ offsetX: offsetLeft.value,
519
+ })
520
+
521
+ // Wrap each node in Item component
522
+ const node = Array.isArray(nodes) ? nodes[0] : nodes
523
+ if (node) {
524
+ children.push(
525
+ <Item key={key} setRef={ele => setInstanceRef(item, ele)}>
526
+ {node}
527
+ </Item>,
528
+ )
529
+ }
530
+ }
493
531
 
494
- return () => {
532
+ return children
533
+ }
495
534
  const componentStyle: CSSProperties = {}
496
535
  if (props.height) {
497
536
  componentStyle[props.fullHeight ? 'height' : 'maxHeight'] = `${props.height}px`
498
537
  Object.assign(componentStyle, ScrollStyle)
499
538
 
500
- // Use custom ScrollBar when virtual scrolling is enabled
501
539
  if (useVirtual.value) {
502
540
  componentStyle.overflowY = 'hidden'
503
541
 
504
- if (scrollWidth.value) {
542
+ if (horizontalRange.value > 0) {
505
543
  componentStyle.overflowX = 'hidden'
506
544
  }
507
545
 
@@ -517,7 +555,7 @@ export default defineComponent({
517
555
  virtual: inVirtual.value,
518
556
  offsetX: offsetLeft.value,
519
557
  offsetY: fillerOffset.value || 0,
520
- rtl: false,
558
+ rtl: isRTL.value,
521
559
  getSize: getSize.value,
522
560
  })
523
561
 
@@ -526,8 +564,14 @@ export default defineComponent({
526
564
  return (
527
565
  <div
528
566
  ref={containerRef}
567
+ {...pureAttrs(attrs)}
529
568
  style={{ position: 'relative', ...(attrs.style as CSSProperties) }}
530
- class={[props.prefixCls, attrs.class]}
569
+ dir={isRTL.value ? 'rtl' : undefined}
570
+ class={[
571
+ props.prefixCls,
572
+ { [`${props.prefixCls}-rtl`]: isRTL.value },
573
+ attrs.class,
574
+ ]}
531
575
  >
532
576
  <ResizeObserver onResize={onHolderResize}>
533
577
  <Component
@@ -543,10 +587,11 @@ export default defineComponent({
543
587
  height={scrollHeight.value}
544
588
  offsetX={offsetLeft.value}
545
589
  offsetY={fillerOffset.value}
590
+ scrollWidth={contentScrollWidth.value}
546
591
  onInnerResize={collectHeight}
547
592
  ref={fillerInnerRef}
548
593
  innerProps={props.innerProps}
549
- rtl={false}
594
+ rtl={isRTL.value}
550
595
  extra={extraContent}
551
596
  >
552
597
  {renderChildren()}
@@ -554,20 +599,40 @@ export default defineComponent({
554
599
  </Component>
555
600
  </ResizeObserver>
556
601
 
557
- {/* ScrollBar */}
558
602
  {inVirtual.value && scrollHeight.value > (props.height || 0) && (
559
603
  <ScrollBar
560
604
  ref={verticalScrollBarRef}
561
605
  prefixCls={props.prefixCls}
562
606
  scrollOffset={offsetTop.value}
563
607
  scrollRange={scrollHeight.value}
564
- rtl={false}
608
+ rtl={isRTL.value}
565
609
  onScroll={onScrollBar}
566
610
  onStartMove={onScrollbarStartMove}
567
611
  onStopMove={onScrollbarStopMove}
568
612
  spinSize={verticalScrollBarSpinSize.value}
569
613
  containerSize={size.value.height}
570
- showScrollBar="optional"
614
+ showScrollBar={props.showScrollBar}
615
+ style={(props.styles as any)?.verticalScrollBar}
616
+ thumbStyle={(props.styles as any)?.verticalScrollBarThumb}
617
+ />
618
+ )}
619
+
620
+ {inVirtual.value && contentScrollWidth.value > size.value.width && (
621
+ <ScrollBar
622
+ ref={horizontalScrollBarRef}
623
+ prefixCls={props.prefixCls}
624
+ scrollOffset={offsetLeft.value}
625
+ scrollRange={contentScrollWidth.value}
626
+ rtl={isRTL.value}
627
+ onScroll={onScrollBar}
628
+ onStartMove={onScrollbarStartMove}
629
+ onStopMove={onScrollbarStopMove}
630
+ spinSize={horizontalScrollBarSpinSize.value}
631
+ containerSize={size.value.width}
632
+ horizontal
633
+ showScrollBar={props.showScrollBar}
634
+ style={(props.styles as any)?.horizontalScrollBar}
635
+ thumbStyle={(props.styles as any)?.horizontalScrollBarThumb}
571
636
  />
572
637
  )}
573
638
  </div>