vue-virtual-flow 0.0.8 → 0.1.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.
- package/CHANGELOG.md +98 -0
- package/README.md +99 -12
- package/README.vi.md +91 -10
- package/dist/components/ChatVirtualScroll.d.cts +29 -2
- package/dist/components/ChatVirtualScroll.d.ts +29 -2
- package/dist/components/DynamicVirtualScroll.d.cts +3 -3
- package/dist/components/DynamicVirtualScroll.d.ts +3 -3
- package/dist/components/ShortMediaFeed.d.cts +2 -2
- package/dist/components/ShortMediaFeed.d.ts +2 -2
- package/dist/components/VirtualCarousel.d.cts +15 -2
- package/dist/components/VirtualCarousel.d.ts +15 -2
- package/dist/components/VirtualScroll.d.cts +3 -3
- package/dist/components/VirtualScroll.d.ts +3 -3
- package/dist/components/WindowDynamicVirtualScroll.d.cts +3 -3
- package/dist/components/WindowDynamicVirtualScroll.d.ts +3 -3
- package/dist/components/WindowGirdVirtualScroll.d.cts +3 -3
- package/dist/components/WindowGirdVirtualScroll.d.ts +3 -3
- package/dist/composables/useCarouselAutoplay.d.cts +15 -0
- package/dist/composables/useCarouselAutoplay.d.ts +15 -0
- package/dist/composables/useCarouselInteraction.d.cts +40 -0
- package/dist/composables/useCarouselInteraction.d.ts +40 -0
- package/dist/composables/useScrollActivity.d.cts +28 -0
- package/dist/composables/useScrollActivity.d.ts +28 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.cts +46 -0
- package/dist/types.d.ts +46 -0
- package/dist/utils/carouselLayout.d.cts +8 -0
- package/dist/utils/carouselLayout.d.ts +8 -0
- package/dist/utils/carouselMotion.d.cts +5 -0
- package/dist/utils/carouselMotion.d.ts +5 -0
- package/dist/utils/keyedSizeIndex.d.cts +19 -0
- package/dist/utils/keyedSizeIndex.d.ts +19 -0
- package/dist/vue-virtual-flow.cjs +1 -1
- package/dist/vue-virtual-flow.css +1 -1
- package/dist/vue-virtual-flow.js +2193 -1510
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-10-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **VirtualCarousel**: Add configurable mouse dragging, momentum, controlled snapping, intrinsic `autoWidth` slides, RTL support, continuous looping, bounded keep-alive caching, and pinned slide instances
|
|
14
|
+
- **ChatVirtualScroll**: Add per-message size estimates, scroll activity state, visible-range events, key-based scrolling, logical position restoration, pinned/focused message retention, forced bottom-follow rules, and configurable synchronous measurement
|
|
15
|
+
- **ChatVirtualScroll**: Add an accessible new-message counter with a customizable slot and localized built-in label
|
|
16
|
+
- **ChatVirtualScroll**: Add `itemsVersion` and `refresh()` for explicitly reconciling arbitrary in-place mutations without an always-on deep watcher
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **ChatVirtualScroll**: Anchor native scrolling at the bottom so prepended history is immediately reachable during touch scrolling, without rebasing the scroll position
|
|
20
|
+
- **ChatVirtualScroll**: Preserve the reading position when messages below the viewport resize or new messages arrive during a gesture
|
|
21
|
+
- **ChatVirtualScroll**: Keep the new-message control inside the viewport instead of scrolling it away with message content
|
|
22
|
+
- **ChatVirtualScroll**: Defer measurement corrections during wheel, keyboard, touch, smooth-scroll, and scrollbar-drag activity so native momentum is not interrupted
|
|
23
|
+
- **ChatVirtualScroll**: Use stable identity fallbacks instead of array indexes and reject duplicate keys before they can corrupt measurements or anchors
|
|
24
|
+
|
|
25
|
+
### Improved
|
|
26
|
+
- **VirtualCarousel**: Extract autoplay, interaction, layout, and motion responsibilities and add cross-browser regression coverage for dragging, looping, RTL, auto-width, and cached state
|
|
27
|
+
- **ChatVirtualScroll**: Measure previously unseen messages before paint and update append, prepend, measurement, offset, and key operations through an incremental keyed index
|
|
28
|
+
- **ChatVirtualScroll**: Remove the duplicate measured-height map and split input activity from layout/measurement logic
|
|
29
|
+
- Cover cold variable-height scrolling, native Chromium touch input, new-message overlay geometry, and prepend/resize anchoring across Chromium, Firefox, and WebKit
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- The chat's internal DOM `scrollTop` is now negative above the bottom. Exposed scrolling methods and `scroll` event offsets remain measured from the top; use those APIs instead of accessing the native offset directly
|
|
33
|
+
- **ChatVirtualScroll**: Add a `.vue-chat-virtual-scroll__root` layout wrapper while keeping inherited attributes on the scroll viewport
|
|
34
|
+
- **ChatVirtualScroll**: Duplicate keys now fail fast; every message must have a unique stable key
|
|
35
|
+
|
|
36
|
+
## [0.0.7] - 2026-09-29
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- **ChatVirtualScroll**: Preserve visible message DOM and scroll position when older messages are prepended
|
|
40
|
+
- **ChatVirtualScroll**: Defer native scroll corrections during touch gestures and momentum while visually compensating for measured height changes
|
|
41
|
+
- **ChatVirtualScroll**: Prevent gaps at the top and double scroll adjustments near the bottom when message heights shrink
|
|
42
|
+
|
|
43
|
+
### Improved
|
|
44
|
+
- Reserve image dimensions in the chat playground to reduce layout shifts while media loads
|
|
45
|
+
- Added regression coverage for touch scrolling, prepend anchoring, height changes, imperative scrolling, and cleanup across unit and browser tests
|
|
46
|
+
|
|
47
|
+
## [0.0.6] - 2026-09-29
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
- **ChatVirtualScroll**: Added an optional `size` prop to limit mounted messages including overscan, while always keeping enough messages to fill the viewport
|
|
51
|
+
|
|
52
|
+
### Improved
|
|
53
|
+
- Added English and Vietnamese documentation for `size` and configured the chat playground with a render budget of 12 messages
|
|
54
|
+
- Added regression tests for render budgets, reactive updates, variable message heights, append/prepend behavior, and scrolling up in a portrait mobile viewport
|
|
55
|
+
|
|
56
|
+
## [0.0.5] - 2026-09-29
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
- **SkeletonLoader**: Added configurable dimensions, rectangle/rounded/circle variants, shimmer animation, reduced-motion support, and an accessible loading label
|
|
60
|
+
- **Public API**: Exported `SkeletonLoader` and `SkeletonLoaderProps`, and registered the component in the Vue plugin
|
|
61
|
+
- **Playground**: Added skeleton placeholders to the load-more demo
|
|
62
|
+
|
|
63
|
+
### Improved
|
|
64
|
+
- Added English and Vietnamese SkeletonLoader usage guides
|
|
65
|
+
- Converted API references and usage recipes into tables, including event listeners, ref actions, responsive configuration, autoplay, and release commands
|
|
66
|
+
|
|
67
|
+
## [0.0.4] - 2026-09-11
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
- **WindowGirdVirtualScroll**: Added fixed-height, multi-column window virtualization with configurable columns, gaps, overscan, loading, pull-to-refresh, and imperative scrolling
|
|
71
|
+
- **Playground**: Added an interactive window grid demo with column controls and direct item navigation
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
- **WindowDynamicVirtualScroll**: Reset stale measurements and load-more state when the item key set changes without changing the item count
|
|
75
|
+
- **WindowDynamicVirtualScroll**: Batched window scroll and ResizeObserver updates with `requestAnimationFrame` to reduce redundant work
|
|
76
|
+
- **WindowDynamicVirtualScroll**: Cancelled pending frames and guarded deferred callbacks during component cleanup
|
|
77
|
+
|
|
78
|
+
### Improved
|
|
79
|
+
- Added regression coverage for window scroll batching, resize compensation, same-size dataset replacement, and lifecycle cleanup
|
|
80
|
+
|
|
81
|
+
## [0.0.3] - 2026-08-08
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
- **All Components**: Smooth scroll animation now works correctly for all distances instead of being force-disabled when scrolling > 3 viewports
|
|
85
|
+
- **VirtualCarousel**: Fixed responsive mode stability when window is resized
|
|
86
|
+
- **VirtualCarousel**: Fixed circular dependency in computed properties causing scroll calculation issues
|
|
87
|
+
- **All Components**: Improved ResizeObserver cleanup to prevent potential memory leaks
|
|
88
|
+
|
|
89
|
+
### Added
|
|
90
|
+
- **VirtualCarousel**: Exported `ResponsiveBreakpoint` type for TypeScript users
|
|
91
|
+
|
|
92
|
+
### Improved
|
|
93
|
+
- Better memory management in all components with explicit observer cleanup
|
|
94
|
+
- More consistent scroll behavior respecting user's `behavior` option
|
|
95
|
+
|
|
96
|
+
## [0.0.2] - Previous release
|
|
97
|
+
|
|
98
|
+
Initial public release with core functionality.
|
package/README.md
CHANGED
|
@@ -551,6 +551,9 @@ async function loadOlder() {
|
|
|
551
551
|
</template>
|
|
552
552
|
|
|
553
553
|
<template #loadingOlder>Loading older messages…</template>
|
|
554
|
+
<template #newItems="{ count, scrollToBottom }">
|
|
555
|
+
<button @click="scrollToBottom('smooth')">{{ count }} new</button>
|
|
556
|
+
</template>
|
|
554
557
|
<template #empty>No messages.</template>
|
|
555
558
|
</ChatVirtualScroll>
|
|
556
559
|
|
|
@@ -563,12 +566,19 @@ async function loadOlder() {
|
|
|
563
566
|
Prepend older data to the same keyed collection. The scroll container is anchored
|
|
564
567
|
at the bottom, so adding or measuring older messages above the reader does not
|
|
565
568
|
need a native scroll correction. Previously unseen messages are measured before
|
|
566
|
-
paint, and cached height updates use an incremental
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
569
|
+
paint, and cached height updates use an incremental keyed tree. Normal append,
|
|
570
|
+
prepend, measurement, offset and key lookups are `O(log n)` per changed message;
|
|
571
|
+
arbitrary replacement, removal or reorder falls back to rebuilding the index.
|
|
572
|
+
Replace the `items` array for arbitrary edits. If application constraints require
|
|
573
|
+
mutating the same array instance, increment `itemsVersion` (or call `refresh()`)
|
|
574
|
+
after a same-length removal/reorder so the component can reconcile without an
|
|
575
|
+
always-on `O(n)` deep watcher.
|
|
576
|
+
|
|
577
|
+
Use the exposed methods, `getScrollPosition()`, and the `scroll` event for
|
|
578
|
+
positive positions measured from the top. The internal DOM scroller uses
|
|
579
|
+
negative `scrollTop` values (zero at the bottom) to preserve native momentum
|
|
580
|
+
while history is prepended; consumers should not read or write that native
|
|
581
|
+
property directly. Message DOM order remains oldest to newest.
|
|
572
582
|
|
|
573
583
|
For smooth media conversations, reserve image/video dimensions with `width`,
|
|
574
584
|
`height`, or `aspect-ratio`, use an estimate close to your message heights, and
|
|
@@ -581,11 +591,13 @@ virtualization cannot hide network latency once that boundary is reached.
|
|
|
581
591
|
| Prop | Type | Default | Description |
|
|
582
592
|
| --- | --- | --- | --- |
|
|
583
593
|
| `items` | `readonly T[]` | required | Messages ordered from oldest to newest. |
|
|
594
|
+
| `itemsVersion` | `PropertyKey` | unset | Increment after an arbitrary in-place mutation that keeps the same array reference/length. Immutable array replacement does not need it. |
|
|
584
595
|
| `estimatedItemSize` | `number` | `48` | Initial average message height in pixels. |
|
|
596
|
+
| `estimateItemSize` | `(item, index) => number` | unset | Per-message estimate for cold rows, useful when text and media have very different heights. |
|
|
585
597
|
| `height` | `number \| string` | `400` | Viewport height. A number is pixels; `"fill"` maps to `100%`. |
|
|
586
598
|
| `overscan` | `number` | `5` | Extra messages mounted before and after the visible range. |
|
|
587
599
|
| `size` | `number` | unset | Optional total render budget including overscan, e.g. `:size="12"` for mobile. Trims overscan first; always renders enough messages to fill the viewport, even if this exceeds `size`. Does not add messages beyond `overscan`. Positive values are floored (minimum 1); non-positive or non-finite values disable the budget. |
|
|
588
|
-
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key`, or
|
|
600
|
+
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key`, or item identity | Stable unique identity; especially important when prepending. Duplicate keys fail fast instead of corrupting anchors. |
|
|
589
601
|
| `ariaLabel` | `string` | `"Chat messages"` | Accessible label for the live log. |
|
|
590
602
|
| `stickToBottom` | `boolean` | `true` | Follows appended messages while currently near the bottom. |
|
|
591
603
|
| `bottomThreshold` | `number` | `80` | Distance still considered “at bottom”, in pixels. |
|
|
@@ -593,6 +605,19 @@ virtualization cannot hide network latency once that boundary is reached.
|
|
|
593
605
|
| `hasOlder` | `boolean` | `false` | Indicates that older history is available. |
|
|
594
606
|
| `loadingOlder` | `boolean` | `false` | Prevents duplicate requests and shows the top indicator. |
|
|
595
607
|
| `loadOlderThreshold` | `number` | `120` | Distance from the top that triggers `load-older`, in pixels. |
|
|
608
|
+
| `scrollEndDelay` | `number` | `150` | Idle fallback in milliseconds for browsers without reliable `scrollend`. |
|
|
609
|
+
| `syncMeasurement` | `boolean` | `true` | Measure new rows before paint. Set false to avoid synchronous layout reads and rely on `ResizeObserver`. |
|
|
610
|
+
| `pinnedKeys` | `readonly PropertyKey[]` | `[]` | Messages kept mounted outside the visible range to preserve local state. |
|
|
611
|
+
| `maxPinnedItems` | `number` | `20` | Maximum number of explicit pinned keys retained. |
|
|
612
|
+
| `keepFocusedItem` | `boolean` | `true` | Keeps the message containing keyboard focus mounted while off-screen. |
|
|
613
|
+
| `showNewItemsIndicator` | `boolean` | `true` | Shows the built-in new-message button while reading history. |
|
|
614
|
+
| `newItemsLabel` | `(count) => string` | English label | Localizes the built-in new-message button without replacing the slot. |
|
|
615
|
+
| `forceStickToBottom` | `(item, index) => boolean` | unset | Force-follows selected appended messages, commonly messages sent by the local user. |
|
|
616
|
+
| `contain` | `boolean` | `true` | Enables layout/paint containment. Disable for overlays that intentionally escape without `Teleport`. |
|
|
617
|
+
|
|
618
|
+
Pinned and keyboard-focused messages are intentionally additional to the
|
|
619
|
+
`size` render budget. Keep `pinnedKeys` small; the `maxPinnedItems` guard does
|
|
620
|
+
not cap the currently focused message.
|
|
596
621
|
|
|
597
622
|
#### Slots
|
|
598
623
|
|
|
@@ -601,6 +626,7 @@ virtualization cannot hide network latency once that boundary is reached.
|
|
|
601
626
|
| `default` | `{ item, index }` | Renders each mounted message. |
|
|
602
627
|
| `empty` | none | Rendered when there are no messages. |
|
|
603
628
|
| `loadingOlder` | none | Sticky top status; defaults to “Loading older messages…”. |
|
|
629
|
+
| `newItems` | `{ count, scrollToBottom }` | Customizes the floating new-message control. |
|
|
604
630
|
|
|
605
631
|
#### Events
|
|
606
632
|
|
|
@@ -609,14 +635,23 @@ virtualization cannot hide network latency once that boundary is reached.
|
|
|
609
635
|
| `scroll` | `{ scrollTop, startIndex, endIndex }` | On container scroll. | `@scroll="onScroll"` |
|
|
610
636
|
| `load-older` | none | Near the top when `hasOlder` is true and `loadingOlder` is false. | `@load-older="loadOlder"` |
|
|
611
637
|
| `bottom-change` | `boolean` | When the viewport enters or leaves the bottom threshold. | `@bottom-change="onBottomChange"` |
|
|
638
|
+
| `scrolling-change` | `boolean` | When user-driven scrolling starts or settles. | `@scrolling-change="onScrollingChange"` |
|
|
639
|
+
| `new-items-change` | `number` | When the number of appended messages unseen at the bottom changes. | `@new-items-change="onNewItemsChange"` |
|
|
640
|
+
| `visible-change` | `{ scrollTop, startIndex, endIndex }` | When the actual visible range changes, excluding overscan; useful for read tracking. | `@visible-change="markVisibleAsRead"` |
|
|
612
641
|
|
|
613
642
|
#### Exposed API
|
|
614
643
|
|
|
615
644
|
| Method / state | Description | Template example |
|
|
616
645
|
| --- | --- | --- |
|
|
617
646
|
| `isAtBottom` | Readonly boolean indicating whether the user is near the bottom. | `chat?.isAtBottom` |
|
|
647
|
+
| `isScrolling` | Readonly boolean indicating an active user scroll gesture. | `chat?.isScrolling` |
|
|
648
|
+
| `newItemsCount` | Number of appended messages received while reading history. | `chat?.newItemsCount` |
|
|
649
|
+
| `clearNewItems()` | Clears the new-message count without moving the viewport. | `chat?.clearNewItems()` |
|
|
650
|
+
| `refresh()` | Reconciles keys and measurements after mutating the same `items` array in place. | `chat?.refresh()` |
|
|
651
|
+
| `getScrollPosition()` | Returns the positive logical pixel offset from the list start. | `chat?.getScrollPosition()` |
|
|
618
652
|
| `scrollTo(position, options?)` | Scrolls to a pixel offset. | `chat?.scrollTo(200, { behavior: 'smooth' })` |
|
|
619
653
|
| `scrollToIndex(index, options?)` | Scrolls to a zero-based message index. | `chat?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
654
|
+
| `scrollToKey(key, options?)` | Scrolls to a message by its stable key. | `chat?.scrollToKey(messageId, { align: 'center' })` |
|
|
620
655
|
| `scrollToTop(behavior?)` | Scrolls to the oldest loaded message. | `chat?.scrollToTop()` |
|
|
621
656
|
| `scrollToBottom(behavior?)` | Scrolls to the newest loaded message. | `chat?.scrollToBottom('smooth')` |
|
|
622
657
|
|
|
@@ -720,8 +755,11 @@ These keys work while the viewport is focused:
|
|
|
720
755
|
| `Home` | Moves to the first position. |
|
|
721
756
|
| `End` | Moves to the last item. |
|
|
722
757
|
|
|
723
|
-
|
|
724
|
-
|
|
758
|
+
The viewport also converts mouse-wheel input into slide transitions. The wheel
|
|
759
|
+
handler in the component is enabled by default; nothing extra is required from
|
|
760
|
+
the parent. For touch input, native scroll-snap continues to handle swipes.
|
|
761
|
+
|
|
762
|
+
For an unbounded feed, page data with `load-more` and handle parents. Virtualization limits mounted DOM and media elements,
|
|
725
763
|
but it does not remove objects from `items`.
|
|
726
764
|
|
|
727
765
|
### VirtualCarousel
|
|
@@ -802,8 +840,56 @@ five slides are normally mounted in the middle of the collection.
|
|
|
802
840
|
| `autoplayDelay` | `number` | `3000` | Delay in milliseconds between automatic transitions; values below `1` are clamped and non-finite values use the default. |
|
|
803
841
|
| `autoplayLoop` | `boolean` | `true` | Returns to the first slide when reaching the end. |
|
|
804
842
|
| `pauseOnHover` | `boolean` | `true` | Pauses autoplay when the carousel is hovered. |
|
|
843
|
+
| `mouseDrag` | `boolean` | `true` | Enables click-and-drag navigation with the primary mouse button. |
|
|
844
|
+
| `dragThreshold` | `number` | `8` | Pointer movement in pixels required to recognize and commit a drag. |
|
|
845
|
+
| `momentum` | `boolean` | `true` | Projects release velocity and decelerates into the destination. |
|
|
846
|
+
| `settleDuration` | `number` | `250` | Base duration in milliseconds for drag/loop settling; `0` makes it instant. Native API scrolling still uses its `behavior` argument. |
|
|
847
|
+
| `nativeSnap` | `boolean` | `true` | Uses native CSS snap for wheel/API scrolling. Set to `false` inside another snap container to settle with the motion controller instead. Loop always uses controlled settling. |
|
|
848
|
+
| `autoWidth` | `boolean` | `false` | Measures each slide's intrinsic width; set the width of the slot content. Overrides `slidesPerView` for sizing. |
|
|
849
|
+
| `estimatedItemWidth` | `number` | `240` | Estimated width in pixels for unmounted slides in `autoWidth` mode. |
|
|
850
|
+
| `rtl` | `boolean` | `false` | Right-to-left layout, pointer movement, native scrolling, and arrow keys. |
|
|
851
|
+
| `loop` | `boolean` | `false` | Continuous looping in either direction, separate from `autoplayLoop`. Disabled when all items fit in the viewport. |
|
|
852
|
+
| `keepAlive` | `boolean` | `false` | Retains recently viewed slide instances and their local state in a bounded cache. |
|
|
853
|
+
| `maxCachedItems` | `number` | `20` | Maximum additional offscreen cached/pinned slides. Visible/buffered slides and one focused slide are not counted. |
|
|
854
|
+
| `pinnedIndexes` | `readonly number[]` | `[]` | Slide indices to retain even with `keepAlive=false`; consumes the offscreen cache budget. |
|
|
805
855
|
| `responsive` | `ResponsiveBreakpoint[]` | `undefined` | Overrides slide count, scroll step, and gap at container-width breakpoints. |
|
|
806
856
|
|
|
857
|
+
Touch gestures use directional locking: vertical gestures continue scrolling the
|
|
858
|
+
page, while horizontal gestures move the carousel and snap to the nearest slide.
|
|
859
|
+
While dragging, the track moves with compositor-friendly `translate3d` and the
|
|
860
|
+
selected `activeIndex` remains unchanged until drag settling finishes. The render
|
|
861
|
+
window and `inViewport` update only when slides enter or leave view. A fast release
|
|
862
|
+
continues with the release velocity and decelerates to rest, with momentum limited
|
|
863
|
+
to two extra slides. Holding before release disables this momentum. A deliberate
|
|
864
|
+
horizontal drag of at least `dragThreshold` pixels commits one slide even when it does not cross the
|
|
865
|
+
slide midpoint.
|
|
866
|
+
|
|
867
|
+
Buttons and links keep their normal click behavior. Form fields and
|
|
868
|
+
`data-carousel-no-drag` descendants do not start dragging; form fields also keep
|
|
869
|
+
their own arrow-key behavior. Wheel/trackpad input interrupts an active animation
|
|
870
|
+
at its displayed position.
|
|
871
|
+
|
|
872
|
+
`autoWidth` uses `ResizeObserver`, cumulative offsets, and binary search. Widths
|
|
873
|
+
of slides not yet mounted are estimates, so distant jumps may be refined when
|
|
874
|
+
those slides are first measured. Late measurements are applied after dragging
|
|
875
|
+
finishes, preserving the selected slide. Give content a resolvable width, for
|
|
876
|
+
example `<ProductCard :style="{ width: item.width + 'px' }" />`.
|
|
877
|
+
|
|
878
|
+
`keepAlive` retains local input/component state until LRU eviction. Only settled
|
|
879
|
+
views update cache recency; intermediate frames do not evict the destination's
|
|
880
|
+
state. Cached offscreen slides are hidden and inert. Use `pinnedIndexes` to retain
|
|
881
|
+
playing media explicitly; pause media with the slot's `inViewport` flag. Persistent
|
|
882
|
+
state should still be stored outside slides by a stable `itemKey`.
|
|
883
|
+
|
|
884
|
+
`loop` repositions each keyed slide around the current cycle without creating
|
|
885
|
+
duplicate component instances. `autoplayLoop` only rewinds a non-looping carousel.
|
|
886
|
+
Loop mode uses the same motion controller to settle wheel input after it stops,
|
|
887
|
+
instead of relying on native snap points that change during cycle recentering.
|
|
888
|
+
The demo disables incremental loading while continuous loop is enabled so that
|
|
889
|
+
the collection's boundaries remain fixed. The component still supports
|
|
890
|
+
`load-more`; changing data or geometry cancels the current transition and realigns
|
|
891
|
+
the selected index.
|
|
892
|
+
|
|
807
893
|
#### Responsive configuration
|
|
808
894
|
|
|
809
895
|
The largest `breakpoint` not exceeding the carousel container width is used.
|
|
@@ -834,7 +920,7 @@ the base props, not to another breakpoint.
|
|
|
834
920
|
|
|
835
921
|
| Slot | Scope | Description |
|
|
836
922
|
| --- | --- | --- |
|
|
837
|
-
| `default` | `{ item, index, active, visible }` |
|
|
923
|
+
| `default` | `{ item, index, active, visible, inViewport }` | `active`/`visible` describe the selected group; `inViewport` reflects actual overlap during motion. |
|
|
838
924
|
| `empty` | none | Rendered when `items` is empty. |
|
|
839
925
|
|
|
840
926
|
#### Events
|
|
@@ -1210,6 +1296,7 @@ import type {
|
|
|
1210
1296
|
| `bun run test:browser` | Run Playwright browser tests. |
|
|
1211
1297
|
| `bun run check` | Run all checks: unit tests, build, consumer types, package lint, and browser tests. |
|
|
1212
1298
|
| `npm pack --dry-run` | Inspect the files that will be published. |
|
|
1299
|
+
| `npm publish --access public` | Publish the already-versioned public package after the release commit is ready. |
|
|
1213
1300
|
| `prepublishOnly` | Automatically runs `bun run check` before publishing. |
|
|
1214
1301
|
|
|
1215
1302
|
```bash
|
|
@@ -1221,8 +1308,8 @@ bun run check
|
|
|
1221
1308
|
|
|
1222
1309
|
The playground includes fixed-height, variable-height, window, chat,
|
|
1223
1310
|
load-more, short-media, and carousel demos. Before publishing, also run
|
|
1224
|
-
`npm pack --dry-run`.
|
|
1225
|
-
|
|
1311
|
+
`npm pack --dry-run`. Commit the release, publish it with
|
|
1312
|
+
`npm publish --access public`, then create and push the matching Git tag.
|
|
1226
1313
|
|
|
1227
1314
|
## Contributing
|
|
1228
1315
|
|
package/README.vi.md
CHANGED
|
@@ -551,6 +551,9 @@ async function loadOlder() {
|
|
|
551
551
|
</template>
|
|
552
552
|
|
|
553
553
|
<template #loadingOlder>Đang tải tin nhắn cũ…</template>
|
|
554
|
+
<template #newItems="{ count, scrollToBottom }">
|
|
555
|
+
<button @click="scrollToBottom('smooth')">{{ count }} tin mới</button>
|
|
556
|
+
</template>
|
|
554
557
|
<template #empty>Chưa có tin nhắn.</template>
|
|
555
558
|
</ChatVirtualScroll>
|
|
556
559
|
|
|
@@ -563,12 +566,18 @@ async function loadOlder() {
|
|
|
563
566
|
Hãy thêm dữ liệu cũ vào đầu cùng một collection có key ổn định. Vùng cuộn lấy
|
|
564
567
|
đáy làm mốc nên việc thêm hoặc đo tin nhắn phía trên người đọc không cần sửa vị
|
|
565
568
|
trí cuộn native. Tin nhắn xuất hiện lần đầu được đo trước khi vẽ; các thay đổi chiều cao
|
|
566
|
-
được cập nhật
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
569
|
+
được cập nhật trong cây chỉ mục theo key. Append, prepend, đo chiều cao, tìm
|
|
570
|
+
offset và key thông thường có độ phức tạp `O(log n)` cho mỗi tin thay đổi; thay
|
|
571
|
+
thế, xóa hoặc sắp xếp tùy ý sẽ dựng lại index.
|
|
572
|
+
Nên thay cả array `items` khi sửa dữ liệu tùy ý. Nếu ứng dụng bắt buộc mutate
|
|
573
|
+
cùng một array, hãy tăng `itemsVersion` (hoặc gọi `refresh()`) sau thao tác xóa/
|
|
574
|
+
sắp xếp không đổi độ dài. Cách này giữ được hiệu năng mà không cần deep watcher
|
|
575
|
+
`O(n)` chạy sau mọi tin nhắn mới.
|
|
576
|
+
|
|
577
|
+
Dùng các method qua ref, `getScrollPosition()` và sự kiện `scroll` để thao tác
|
|
578
|
+
với offset dương tính từ đầu danh sách. Phần tử DOM bên trong dùng `scrollTop`
|
|
579
|
+
âm (bằng 0 ở đáy) nhằm giữ momentum native khi prepend; code sử dụng component
|
|
580
|
+
không nên đọc hoặc ghi trực tiếp thuộc tính này. Thứ tự DOM vẫn từ cũ đến mới.
|
|
572
581
|
|
|
573
582
|
Để chat có ảnh/video cuộn mượt, hãy dành sẵn kích thước bằng `width`, `height`
|
|
574
583
|
hoặc `aspect-ratio`, đặt chiều cao ước lượng gần thực tế và tránh giới hạn `size`
|
|
@@ -581,11 +590,13 @@ kế tiếp trước khi người dùng chạm tin cũ nhất đã có; virtuali
|
|
|
581
590
|
| Prop | Kiểu | Mặc định | Mô tả |
|
|
582
591
|
| --- | --- | --- | --- |
|
|
583
592
|
| `items` | `readonly T[]` | bắt buộc | Tin nhắn được sắp từ cũ nhất tới mới nhất. |
|
|
593
|
+
| `itemsVersion` | `PropertyKey` | không đặt | Tăng giá trị sau mutation tại chỗ tùy ý không đổi reference/độ dài. Không cần dùng khi thay array theo kiểu immutable. |
|
|
584
594
|
| `estimatedItemSize` | `number` | `48` | Chiều cao trung bình ban đầu của tin nhắn, tính bằng pixel. |
|
|
595
|
+
| `estimateItemSize` | `(item, index) => number` | không đặt | Ước lượng riêng cho từng tin chưa đo, hữu ích khi text và media chênh lệch chiều cao lớn. |
|
|
585
596
|
| `height` | `number \| string` | `400` | Chiều cao viewport. Số được hiểu là pixel; `"fill"` tương đương `100%`. |
|
|
586
597
|
| `overscan` | `number` | `5` | Số tin nhắn mount thêm trước và sau vùng đang hiển thị. |
|
|
587
598
|
| `size` | `number` | không đặt | Giới hạn tổng số tin nhắn render, bao gồm overscan, ví dụ `:size="12"` cho mobile. Giảm overscan trước; luôn render đủ vùng nhìn dù phải vượt `size`. Không tăng số tin nhắn vượt phạm vi `overscan`. Số dương được làm tròn xuống (tối thiểu 1); số không dương hoặc không hữu hạn sẽ tắt giới hạn. |
|
|
588
|
-
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key` hoặc
|
|
599
|
+
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key` hoặc identity của item | Định danh ổn định và duy nhất, đặc biệt quan trọng khi thêm vào đầu. Key trùng sẽ báo lỗi ngay thay vì làm hỏng anchor. |
|
|
589
600
|
| `ariaLabel` | `string` | `"Chat messages"` | Nhãn hỗ trợ khả năng tiếp cận cho live log. |
|
|
590
601
|
| `stickToBottom` | `boolean` | `true` | Bám theo tin nhắn mới nếu hiện tại đang gần cuối. |
|
|
591
602
|
| `bottomThreshold` | `number` | `80` | Khoảng cách vẫn được xem là “ở cuối”, tính bằng pixel. |
|
|
@@ -593,6 +604,18 @@ kế tiếp trước khi người dùng chạm tin cũ nhất đã có; virtuali
|
|
|
593
604
|
| `hasOlder` | `boolean` | `false` | Cho biết vẫn còn lịch sử cũ. |
|
|
594
605
|
| `loadingOlder` | `boolean` | `false` | Ngăn request trùng và hiển thị indicator phía trên. |
|
|
595
606
|
| `loadOlderThreshold` | `number` | `120` | Khoảng cách tới đầu để emit `load-older`, tính bằng pixel. |
|
|
607
|
+
| `scrollEndDelay` | `number` | `150` | Thời gian chờ fallback tính bằng ms khi trình duyệt không có `scrollend` ổn định. |
|
|
608
|
+
| `syncMeasurement` | `boolean` | `true` | Đo hàng mới trước paint. Đặt false để tránh đọc layout đồng bộ và chỉ dùng `ResizeObserver`. |
|
|
609
|
+
| `pinnedKeys` | `readonly PropertyKey[]` | `[]` | Các tin nhắn luôn được mount để giữ state cục bộ. |
|
|
610
|
+
| `maxPinnedItems` | `number` | `20` | Số key pin tối đa được giữ lại. |
|
|
611
|
+
| `keepFocusedItem` | `boolean` | `true` | Giữ DOM của tin nhắn đang chứa keyboard focus dù ra ngoài viewport. |
|
|
612
|
+
| `showNewItemsIndicator` | `boolean` | `true` | Hiển thị nút tin nhắn mới khi người dùng đang đọc lịch sử. |
|
|
613
|
+
| `newItemsLabel` | `(count) => string` | nhãn tiếng Anh | Bản địa hóa nút tin nhắn mới mặc định mà không cần thay slot. |
|
|
614
|
+
| `forceStickToBottom` | `(item, index) => boolean` | không đặt | Luôn bám đáy với tin append được chọn, thường dùng cho tin do người dùng hiện tại gửi. |
|
|
615
|
+
| `contain` | `boolean` | `true` | Bật containment layout/paint; tắt nếu overlay không dùng `Teleport` cần tràn ra ngoài. |
|
|
616
|
+
|
|
617
|
+
Tin nhắn được pin và tin đang chứa keyboard focus được render thêm ngoài giới
|
|
618
|
+
hạn `size`. Nên giữ `pinnedKeys` nhỏ; `maxPinnedItems` không giới hạn tin đang focus.
|
|
596
619
|
|
|
597
620
|
#### Slots
|
|
598
621
|
|
|
@@ -601,6 +624,7 @@ kế tiếp trước khi người dùng chạm tin cũ nhất đã có; virtuali
|
|
|
601
624
|
| `default` | `{ item, index }` | Render từng tin nhắn đang được mount. |
|
|
602
625
|
| `empty` | không có | Render khi chưa có tin nhắn. |
|
|
603
626
|
| `loadingOlder` | không có | Status sticky phía trên; mặc định “Loading older messages…”. |
|
|
627
|
+
| `newItems` | `{ count, scrollToBottom }` | Tùy chỉnh nút tin nhắn mới nổi phía dưới. |
|
|
604
628
|
|
|
605
629
|
#### Events
|
|
606
630
|
|
|
@@ -609,14 +633,23 @@ kế tiếp trước khi người dùng chạm tin cũ nhất đã có; virtuali
|
|
|
609
633
|
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. | `@scroll="onScroll"` |
|
|
610
634
|
| `load-older` | không có | Gần đầu khi `hasOlder` là true và `loadingOlder` là false. | `@load-older="loadOlder"` |
|
|
611
635
|
| `bottom-change` | `boolean` | Khi viewport đi vào hoặc rời ngưỡng ở cuối. | `@bottom-change="onBottomChange"` |
|
|
636
|
+
| `scrolling-change` | `boolean` | Khi thao tác cuộn của người dùng bắt đầu hoặc dừng. | `@scrolling-change="onScrollingChange"` |
|
|
637
|
+
| `new-items-change` | `number` | Khi số tin append chưa được xem ở đáy thay đổi. | `@new-items-change="onNewItemsChange"` |
|
|
638
|
+
| `visible-change` | `{ scrollTop, startIndex, endIndex }` | Khi vùng thực sự nhìn thấy thay đổi, không tính overscan; dùng để theo dõi đã đọc. | `@visible-change="markVisibleAsRead"` |
|
|
612
639
|
|
|
613
640
|
#### API qua ref
|
|
614
641
|
|
|
615
642
|
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
616
643
|
| --- | --- | --- |
|
|
617
644
|
| `isAtBottom` | Boolean readonly cho biết người dùng có đang gần cuối hay không. | `chat?.isAtBottom` |
|
|
645
|
+
| `isScrolling` | Boolean readonly cho biết người dùng đang thực hiện thao tác cuộn. | `chat?.isScrolling` |
|
|
646
|
+
| `newItemsCount` | Số tin append nhận được trong lúc đang đọc lịch sử. | `chat?.newItemsCount` |
|
|
647
|
+
| `clearNewItems()` | Xóa bộ đếm tin mới mà không đổi vị trí cuộn. | `chat?.clearNewItems()` |
|
|
648
|
+
| `refresh()` | Đồng bộ lại key và measurement sau khi mutate tại chỗ cùng một array `items`. | `chat?.refresh()` |
|
|
649
|
+
| `getScrollPosition()` | Trả về offset pixel logic dương tính từ đầu danh sách. | `chat?.getScrollPosition()` |
|
|
618
650
|
| `scrollTo(position, options?)` | Cuộn tới offset pixel. | `chat?.scrollTo(200, { behavior: 'smooth' })` |
|
|
619
651
|
| `scrollToIndex(index, options?)` | Cuộn tới index tin nhắn bắt đầu từ 0. | `chat?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
652
|
+
| `scrollToKey(key, options?)` | Cuộn tới tin nhắn theo key ổn định. | `chat?.scrollToKey(messageId, { align: 'center' })` |
|
|
620
653
|
| `scrollToTop(behavior?)` | Cuộn tới tin nhắn cũ nhất đã tải. | `chat?.scrollToTop()` |
|
|
621
654
|
| `scrollToBottom(behavior?)` | Cuộn tới tin nhắn mới nhất đã tải. | `chat?.scrollToBottom('smooth')` |
|
|
622
655
|
|
|
@@ -720,6 +753,10 @@ Các phím sau hoạt động khi viewport có focus:
|
|
|
720
753
|
| `Home` | Chuyển tới vị trí đầu. |
|
|
721
754
|
| `End` | Chuyển tới item cuối. |
|
|
722
755
|
|
|
756
|
+
Viewport còn chuyển cả input bánh xe chuột thành chuyển slide. Handler wheel đã
|
|
757
|
+
bật sẵn trong component, parent không phải làm gì thêm. Còn touch để trackpad
|
|
758
|
+
snap gốc của trình duyệt xử lý.
|
|
759
|
+
|
|
723
760
|
Với feed không giới hạn, hãy tải theo trang bằng `load-more` và chỉ giữ một cửa
|
|
724
761
|
sổ dữ liệu hợp lý ở component cha. Virtualization giới hạn DOM và media element
|
|
725
762
|
đang mount, nhưng không xóa object khỏi `items`.
|
|
@@ -801,8 +838,51 @@ thường tối đa năm slide được mount ở giữa collection.
|
|
|
801
838
|
| `autoplayDelay` | `number` | `3000` | Thời gian chờ giữa các lần tự động chuyển, tính bằng mili giây; giá trị dưới `1` được giới hạn và giá trị không hữu hạn dùng mặc định. |
|
|
802
839
|
| `autoplayLoop` | `boolean` | `true` | Quay lại slide đầu tiên khi đến cuối. |
|
|
803
840
|
| `pauseOnHover` | `boolean` | `true` | Tạm dừng autoplay khi con trỏ nằm trên carousel. |
|
|
841
|
+
| `mouseDrag` | `boolean` | `true` | Cho phép giữ và kéo bằng nút chuột trái để chuyển slide trên máy tính. |
|
|
842
|
+
| `dragThreshold` | `number` | `8` | Khoảng di chuyển con trỏ để nhận diện và commit thao tác kéo, tính bằng pixel. |
|
|
843
|
+
| `momentum` | `boolean` | `true` | Chiếu vận tốc lúc thả thành quán tính rồi giảm tốc về slide đích. |
|
|
844
|
+
| `settleDuration` | `number` | `250` | Thời lượng nền của animation drag/loop; `0` chuyển tức thì. Cuộn native qua API vẫn dùng tham số `behavior`. |
|
|
845
|
+
| `nativeSnap` | `boolean` | `true` | Dùng CSS snap cho wheel/API. Đặt `false` khi lồng trong container có snap để căn slide bằng bộ điều khiển chuyển động. Loop luôn dùng cơ chế này. |
|
|
846
|
+
| `autoWidth` | `boolean` | `false` | Đo chiều rộng riêng của từng slide; nội dung slot cần có chiều rộng xác định. Thay thế `slidesPerView` khi tính chiều rộng. |
|
|
847
|
+
| `estimatedItemWidth` | `number` | `240` | Chiều rộng ước lượng cho slide chưa được mount khi bật `autoWidth`. |
|
|
848
|
+
| `rtl` | `boolean` | `false` | Layout phải sang trái, bao gồm drag, native scroll và phím mũi tên. |
|
|
849
|
+
| `loop` | `boolean` | `false` | Loop liên tục theo hai hướng, độc lập với `autoplayLoop`. Tự tắt khi toàn bộ item vừa viewport. |
|
|
850
|
+
| `keepAlive` | `boolean` | `false` | Giữ instance và state cục bộ của slide đã xem trong cache giới hạn. |
|
|
851
|
+
| `maxCachedItems` | `number` | `20` | Số slide cache/pin ngoài vùng render tối đa; không tính slide đang hiển thị/buffer và một slide có focus. |
|
|
852
|
+
| `pinnedIndexes` | `readonly number[]` | `[]` | Giữ các index này mounted ngay cả khi `keepAlive=false`; dùng ngân sách cache ngoài vùng render. |
|
|
804
853
|
| `responsive` | `ResponsiveBreakpoint[]` | `undefined` | Ghi đè số slide hiển thị, bước cuộn và khoảng cách theo breakpoint chiều rộng container. |
|
|
805
854
|
|
|
855
|
+
Thao tác cảm ứng có khóa hướng: vuốt dọc tiếp tục cuộn trang, còn vuốt ngang
|
|
856
|
+
di chuyển carousel và tự căn vào slide gần nhất. Trong lúc kéo, track di chuyển
|
|
857
|
+
bằng `translate3d` và `activeIndex` được giữ nguyên tới khi animation drag kết thúc.
|
|
858
|
+
Vùng render và `inViewport` chỉ cập nhật khi slide đi vào hoặc ra khỏi vùng nhìn.
|
|
859
|
+
Khi thả nhanh, track tiếp tục theo vận tốc lúc thả rồi
|
|
860
|
+
giảm tốc về 0, với quán tính giới hạn tối đa hai slide thêm. Giữ tay trước khi
|
|
861
|
+
thả sẽ bỏ qua quán tính. Kéo ngang từ ngưỡng `dragThreshold` sẽ chuyển một slide ngay cả
|
|
862
|
+
khi chưa đi qua nửa chiều rộng slide.
|
|
863
|
+
|
|
864
|
+
Button và link giữ hành vi click bình thường. Input và phần tử có
|
|
865
|
+
`data-carousel-no-drag` không bắt đầu drag; input giữ hành vi phím mũi tên riêng.
|
|
866
|
+
Wheel/trackpad ngắt animation tại vị trí đang hiển thị.
|
|
867
|
+
|
|
868
|
+
`autoWidth` dùng `ResizeObserver`, vị trí cộng dồn và binary search. Slide chưa
|
|
869
|
+
mount dùng chiều rộng ước lượng, nên bước nhảy xa có thể được tinh chỉnh sau khi
|
|
870
|
+
đo. Kết quả đo muộn được áp dụng khi drag kết thúc để giữ slide đang chọn. Nội dung
|
|
871
|
+
slot cần có chiều rộng rõ ràng, ví dụ `<ProductCard :style="{ width: item.width + 'px' }" />`.
|
|
872
|
+
|
|
873
|
+
`keepAlive` giữ state của input/component tới khi bị loại khỏi cache LRU. Chỉ nhóm
|
|
874
|
+
slide khi chuyển động dừng mới cập nhật độ gần đây; frame trung gian không đẩy state
|
|
875
|
+
của slide đích ra khỏi cache. Slide cache ngoài vùng render bị ẩn và inert.
|
|
876
|
+
`pinnedIndexes` giữ media đang chạy; dùng `inViewport` để quyết định pause media.
|
|
877
|
+
State cần tồn tại lâu dài vẫn nên lưu bên ngoài theo `itemKey` ổn định.
|
|
878
|
+
|
|
879
|
+
`loop` di chuyển các slide có key quanh chu kỳ hiện tại, không tạo instance trùng.
|
|
880
|
+
`autoplayLoop` chỉ quay về đầu khi không bật loop liên tục. Demo tắt tải thêm khi
|
|
881
|
+
bật loop để biên collection cố định. Loop dùng cùng bộ điều khiển chuyển động để
|
|
882
|
+
căn slide sau khi wheel dừng, tránh native snap bám theo node vừa đổi chu kỳ.
|
|
883
|
+
Component vẫn hỗ trợ `load-more`. Thay đổi
|
|
884
|
+
dữ liệu hoặc layout sẽ hủy chuyển động hiện tại và căn lại index đang chọn.
|
|
885
|
+
|
|
806
886
|
#### Cấu hình responsive
|
|
807
887
|
|
|
808
888
|
Component chọn `breakpoint` lớn nhất không vượt quá chiều rộng container của
|
|
@@ -833,7 +913,7 @@ Trường bị bỏ qua dùng prop gốc, không kế thừa từ breakpoint kh
|
|
|
833
913
|
|
|
834
914
|
| Slot | Scope | Mô tả |
|
|
835
915
|
| --- | --- | --- |
|
|
836
|
-
| `default` | `{ item, index, active, visible }` |
|
|
916
|
+
| `default` | `{ item, index, active, visible, inViewport }` | `active`/`visible` theo nhóm slide đã chọn; `inViewport` theo vùng nhìn thực tế trong lúc di chuyển. |
|
|
837
917
|
| `empty` | không có | Render khi `items` rỗng. |
|
|
838
918
|
|
|
839
919
|
#### Events
|
|
@@ -1208,6 +1288,7 @@ import type {
|
|
|
1208
1288
|
| `bun run test:browser` | Chạy test trình duyệt bằng Playwright. |
|
|
1209
1289
|
| `bun run check` | Chạy toàn bộ: unit test, build, type phía dùng package, lint package và test trình duyệt. |
|
|
1210
1290
|
| `npm pack --dry-run` | Kiểm tra các file sẽ được publish. |
|
|
1291
|
+
| `npm publish --access public` | Publish package public đã được cập nhật version sau khi release commit sẵn sàng. |
|
|
1211
1292
|
| `prepublishOnly` | Tự chạy `bun run check` trước khi publish. |
|
|
1212
1293
|
|
|
1213
1294
|
```bash
|
|
@@ -1219,8 +1300,8 @@ bun run check
|
|
|
1219
1300
|
|
|
1220
1301
|
Playground có các demo riêng cho chiều cao cố định, chiều cao động, cuộn theo
|
|
1221
1302
|
cửa sổ, chat, load-more, short-media và carousel. Trước khi publish, hãy chạy
|
|
1222
|
-
thêm `npm pack --dry-run`.
|
|
1223
|
-
|
|
1303
|
+
thêm `npm pack --dry-run`. Sau đó commit release, chạy
|
|
1304
|
+
`npm publish --access public`, rồi tạo và push Git tag tương ứng.
|
|
1224
1305
|
|
|
1225
1306
|
## Đóng góp
|
|
1226
1307
|
|
|
@@ -4,9 +4,15 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
4
4
|
readonly onScroll?: ((event: VirtualScrollEvent) => any) | undefined;
|
|
5
5
|
readonly onLoadOlder?: (() => any) | undefined;
|
|
6
6
|
readonly onBottomChange?: ((isAtBottom: boolean) => any) | undefined;
|
|
7
|
-
|
|
7
|
+
readonly onScrollingChange?: ((isScrolling: boolean) => any) | undefined;
|
|
8
|
+
readonly onNewItemsChange?: ((count: number) => any) | undefined;
|
|
9
|
+
readonly onVisibleChange?: ((event: VirtualScrollEvent) => any) | undefined;
|
|
10
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onScroll" | "onLoadOlder" | "onBottomChange" | "onScrollingChange" | "onNewItemsChange" | "onVisibleChange"> & {
|
|
8
11
|
items: readonly T[];
|
|
12
|
+
/** Increment after mutating/reordering the same array instance in place. */
|
|
13
|
+
itemsVersion?: PropertyKey;
|
|
9
14
|
estimatedItemSize?: number;
|
|
15
|
+
estimateItemSize?: (item: T, index: number) => number;
|
|
10
16
|
height?: number | string;
|
|
11
17
|
overscan?: number;
|
|
12
18
|
/** Render budget including overscan; always keeps the viewport filled. */
|
|
@@ -19,13 +25,30 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
19
25
|
hasOlder?: boolean;
|
|
20
26
|
loadingOlder?: boolean;
|
|
21
27
|
loadOlderThreshold?: number;
|
|
28
|
+
scrollEndDelay?: number;
|
|
29
|
+
syncMeasurement?: boolean;
|
|
30
|
+
pinnedKeys?: readonly PropertyKey[];
|
|
31
|
+
maxPinnedItems?: number;
|
|
32
|
+
keepFocusedItem?: boolean;
|
|
33
|
+
showNewItemsIndicator?: boolean;
|
|
34
|
+
newItemsLabel?: (count: number) => string;
|
|
35
|
+
forceStickToBottom?: (item: T, index: number) => boolean;
|
|
36
|
+
contain?: boolean;
|
|
22
37
|
} & Partial<{}>> & import('vue').PublicProps;
|
|
23
38
|
expose(exposed: import('vue').ShallowUnwrapRef<{
|
|
24
39
|
isAtBottom: import('vue').Ref<boolean, boolean>;
|
|
40
|
+
isScrolling: import('vue').Ref<boolean, boolean>;
|
|
41
|
+
newItemsCount: import('vue').Ref<number, number>;
|
|
42
|
+
clearNewItems: () => void;
|
|
43
|
+
refresh: () => void;
|
|
44
|
+
getScrollPosition: () => number;
|
|
25
45
|
scrollTo: (position: number, options?: ScrollToOptions) => void;
|
|
26
46
|
scrollToIndex: (index: number, options?: ScrollToOptions & {
|
|
27
47
|
align?: ScrollAlignment;
|
|
28
48
|
}) => void;
|
|
49
|
+
scrollToKey: (key: PropertyKey, options?: ScrollToOptions & {
|
|
50
|
+
align?: ScrollAlignment;
|
|
51
|
+
}) => void;
|
|
29
52
|
scrollToTop: (behavior?: ScrollBehavior) => void;
|
|
30
53
|
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
31
54
|
}>): void;
|
|
@@ -37,8 +60,12 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
37
60
|
}): unknown;
|
|
38
61
|
empty?(): unknown;
|
|
39
62
|
loadingOlder?(): unknown;
|
|
63
|
+
newItems?(props: {
|
|
64
|
+
count: number;
|
|
65
|
+
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
66
|
+
}): unknown;
|
|
40
67
|
};
|
|
41
|
-
emit: ((evt: "scroll", event: VirtualScrollEvent) => void) & ((evt: "loadOlder") => void) & ((evt: "bottomChange", isAtBottom: boolean) => void);
|
|
68
|
+
emit: ((evt: "scroll", event: VirtualScrollEvent) => void) & ((evt: "loadOlder") => void) & ((evt: "bottomChange", isAtBottom: boolean) => void) & ((evt: "scrollingChange", isScrolling: boolean) => void) & ((evt: "newItemsChange", count: number) => void) & ((evt: "visibleChange", event: VirtualScrollEvent) => void);
|
|
42
69
|
}>) => import('vue').VNode & {
|
|
43
70
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
44
71
|
};
|
|
@@ -4,9 +4,15 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
4
4
|
readonly onScroll?: ((event: VirtualScrollEvent) => any) | undefined;
|
|
5
5
|
readonly onLoadOlder?: (() => any) | undefined;
|
|
6
6
|
readonly onBottomChange?: ((isAtBottom: boolean) => any) | undefined;
|
|
7
|
-
|
|
7
|
+
readonly onScrollingChange?: ((isScrolling: boolean) => any) | undefined;
|
|
8
|
+
readonly onNewItemsChange?: ((count: number) => any) | undefined;
|
|
9
|
+
readonly onVisibleChange?: ((event: VirtualScrollEvent) => any) | undefined;
|
|
10
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onScroll" | "onLoadOlder" | "onBottomChange" | "onScrollingChange" | "onNewItemsChange" | "onVisibleChange"> & {
|
|
8
11
|
items: readonly T[];
|
|
12
|
+
/** Increment after mutating/reordering the same array instance in place. */
|
|
13
|
+
itemsVersion?: PropertyKey;
|
|
9
14
|
estimatedItemSize?: number;
|
|
15
|
+
estimateItemSize?: (item: T, index: number) => number;
|
|
10
16
|
height?: number | string;
|
|
11
17
|
overscan?: number;
|
|
12
18
|
/** Render budget including overscan; always keeps the viewport filled. */
|
|
@@ -19,13 +25,30 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
19
25
|
hasOlder?: boolean;
|
|
20
26
|
loadingOlder?: boolean;
|
|
21
27
|
loadOlderThreshold?: number;
|
|
28
|
+
scrollEndDelay?: number;
|
|
29
|
+
syncMeasurement?: boolean;
|
|
30
|
+
pinnedKeys?: readonly PropertyKey[];
|
|
31
|
+
maxPinnedItems?: number;
|
|
32
|
+
keepFocusedItem?: boolean;
|
|
33
|
+
showNewItemsIndicator?: boolean;
|
|
34
|
+
newItemsLabel?: (count: number) => string;
|
|
35
|
+
forceStickToBottom?: (item: T, index: number) => boolean;
|
|
36
|
+
contain?: boolean;
|
|
22
37
|
} & Partial<{}>> & import('vue').PublicProps;
|
|
23
38
|
expose(exposed: import('vue').ShallowUnwrapRef<{
|
|
24
39
|
isAtBottom: import('vue').Ref<boolean, boolean>;
|
|
40
|
+
isScrolling: import('vue').Ref<boolean, boolean>;
|
|
41
|
+
newItemsCount: import('vue').Ref<number, number>;
|
|
42
|
+
clearNewItems: () => void;
|
|
43
|
+
refresh: () => void;
|
|
44
|
+
getScrollPosition: () => number;
|
|
25
45
|
scrollTo: (position: number, options?: ScrollToOptions) => void;
|
|
26
46
|
scrollToIndex: (index: number, options?: ScrollToOptions & {
|
|
27
47
|
align?: ScrollAlignment;
|
|
28
48
|
}) => void;
|
|
49
|
+
scrollToKey: (key: PropertyKey, options?: ScrollToOptions & {
|
|
50
|
+
align?: ScrollAlignment;
|
|
51
|
+
}) => void;
|
|
29
52
|
scrollToTop: (behavior?: ScrollBehavior) => void;
|
|
30
53
|
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
31
54
|
}>): void;
|
|
@@ -37,8 +60,12 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
37
60
|
}): unknown;
|
|
38
61
|
empty?(): unknown;
|
|
39
62
|
loadingOlder?(): unknown;
|
|
63
|
+
newItems?(props: {
|
|
64
|
+
count: number;
|
|
65
|
+
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
66
|
+
}): unknown;
|
|
40
67
|
};
|
|
41
|
-
emit: ((evt: "scroll", event: VirtualScrollEvent) => void) & ((evt: "loadOlder") => void) & ((evt: "bottomChange", isAtBottom: boolean) => void);
|
|
68
|
+
emit: ((evt: "scroll", event: VirtualScrollEvent) => void) & ((evt: "loadOlder") => void) & ((evt: "bottomChange", isAtBottom: boolean) => void) & ((evt: "scrollingChange", isScrolling: boolean) => void) & ((evt: "newItemsChange", count: number) => void) & ((evt: "visibleChange", event: VirtualScrollEvent) => void);
|
|
42
69
|
}>) => import('vue').VNode & {
|
|
43
70
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
44
71
|
};
|