vue-virtual-flow 0.0.3 → 0.0.5
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/README.md +462 -92
- package/README.vi.md +463 -92
- package/dist/components/SkeletonLoader.d.cts +9 -0
- package/dist/components/SkeletonLoader.d.ts +9 -0
- package/dist/components/WindowGirdVirtualScroll.d.cts +53 -0
- package/dist/components/WindowGirdVirtualScroll.d.ts +53 -0
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/types.d.cts +25 -0
- package/dist/types.d.ts +25 -0
- package/dist/vue-virtual-flow.cjs +1 -1
- package/dist/vue-virtual-flow.css +1 -1
- package/dist/vue-virtual-flow.js +1389 -1062
- package/package.json +1 -1
package/README.vi.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Bộ component virtual scrolling nhỏ gọn, có type đầy đủ và hỗ trợ khả năng tiếp
|
|
6
6
|
cận cho Vue 3. Thư viện hỗ trợ danh sách chiều cao cố định hoặc động, cuộn theo
|
|
7
|
-
cửa sổ, giao diện chat, feed media ngắn và carousel ngang.
|
|
7
|
+
cửa sổ, grid cuộn theo cửa sổ, giao diện chat, feed media ngắn và carousel ngang.
|
|
8
8
|
|
|
9
9
|
Chỉ các item cần thiết cho viewport hiện tại được mount, nhờ đó tập dữ liệu lớn
|
|
10
10
|
vẫn phản hồi nhanh mà component không áp đặt giao diện của từng item.
|
|
@@ -18,9 +18,11 @@ vẫn phản hồi nhanh mà component không áp đặt giao diện của từn
|
|
|
18
18
|
- [VirtualList và DynamicVirtualScroll](#virtuallist-và-dynamicvirtualscroll)
|
|
19
19
|
- [VirtualScroll](#virtualscroll)
|
|
20
20
|
- [WindowDynamicVirtualScroll](#windowdynamicvirtualscroll)
|
|
21
|
+
- [WindowGirdVirtualScroll](#windowgirdvirtualscroll)
|
|
21
22
|
- [ChatVirtualScroll](#chatvirtualscroll)
|
|
22
23
|
- [ShortMediaFeed](#shortmediafeed)
|
|
23
24
|
- [VirtualCarousel](#virtualcarousel)
|
|
25
|
+
- [SkeletonLoader](#skeletonloader)
|
|
24
26
|
- [Cách dùng chung](#cách-dùng-chung)
|
|
25
27
|
- [Chiều cao viewport](#chiều-cao-viewport)
|
|
26
28
|
- [Key ổn định cho item](#key-ổn-định-cho-item)
|
|
@@ -43,10 +45,19 @@ Import stylesheet của thư viện một lần tại entry point của ứng d
|
|
|
43
45
|
import 'vue-virtual-flow/style.css'
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
| Dependency | Yêu cầu |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| Vue | `^3.4.0` |
|
|
51
|
+
| Node.js | `>=18` |
|
|
47
52
|
|
|
48
53
|
## Bắt đầu nhanh
|
|
49
54
|
|
|
55
|
+
| Cách đăng ký | Cách dùng |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| Import cục bộ | `import { VirtualList, SkeletonLoader } from 'vue-virtual-flow'` |
|
|
58
|
+
| Plugin global | `createApp(App).use(VueVirtualScroll)` |
|
|
59
|
+
| Style dùng chung | `import 'vue-virtual-flow/style.css'` |
|
|
60
|
+
|
|
50
61
|
Import component cục bộ:
|
|
51
62
|
|
|
52
63
|
```vue
|
|
@@ -83,8 +94,8 @@ createApp(App).use(VueVirtualScroll).mount('#app')
|
|
|
83
94
|
```
|
|
84
95
|
|
|
85
96
|
Plugin đăng ký `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`,
|
|
86
|
-
`WindowDynamicVirtualScroll`, `
|
|
87
|
-
`VirtualCarousel`.
|
|
97
|
+
`WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll`, `ChatVirtualScroll`,
|
|
98
|
+
`ShortMediaFeed`, `VirtualCarousel` và `SkeletonLoader`.
|
|
88
99
|
|
|
89
100
|
## Chọn component
|
|
90
101
|
|
|
@@ -94,12 +105,27 @@ Plugin đăng ký `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`,
|
|
|
94
105
|
| `DynamicVirtualScroll` | Giống `VirtualList` nhưng tên gọi thể hiện rõ chiều cao động. | Container của component | Động, tự đo |
|
|
95
106
|
| `VirtualScroll` | Tất cả hàng có cùng chiều cao đã biết. | Container của component | Cố định |
|
|
96
107
|
| `WindowDynamicVirtualScroll` | Muốn trang cuộn thay vì tạo một container cuộn lồng bên trong. | Cửa sổ trình duyệt | Động, tự đo |
|
|
108
|
+
| `WindowGirdVirtualScroll` | Grid card có chiều cao cố định và dùng trang để cuộn. | Cửa sổ trình duyệt | Chiều cao hàng cố định |
|
|
97
109
|
| `ChatVirtualScroll` | Tin nhắn mới được thêm ở cuối và lịch sử cũ được thêm ở đầu. | Container của component | Động, tự đo |
|
|
98
110
|
| `ShortMediaFeed` | Mỗi lần cần snap một item đầy viewport. | Container của component | Một viewport cho mỗi item |
|
|
99
111
|
| `VirtualCarousel` | Cần hiển thị và snap nhiều slide theo chiều ngang. | Container của component | Tính từ chiều rộng container |
|
|
112
|
+
| `SkeletonLoader` | Cần hiển thị khối giữ chỗ khi đang tải nội dung. | Không có | Tùy chỉnh chiều rộng và chiều cao |
|
|
100
113
|
|
|
101
114
|
## Hướng dẫn từng component
|
|
102
115
|
|
|
116
|
+
Event được lắng nghe bằng cú pháp template của Vue; các method được gọi qua
|
|
117
|
+
ref của component. Các bảng bên dưới dùng tên ref là `list`, `chat`, `feed`
|
|
118
|
+
hoặc `carousel`.
|
|
119
|
+
|
|
120
|
+
| Thao tác | Cách dùng |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| Khai báo ref có type | `const list = ref<VirtualScrollExpose>()` |
|
|
123
|
+
| Gắn ref vào component | `<VirtualScroll ref="list" ... />` |
|
|
124
|
+
| Gọi sau khi mount trong script | `list.value?.scrollToIndex(10)` |
|
|
125
|
+
| Gọi từ event trong template | `@click="list?.scrollToIndex(10)"` |
|
|
126
|
+
| Chọn cách cuộn | Truyền `{ behavior: 'smooth' }` cho `scrollTo` / `scrollToIndex`, hoặc `'smooth'` cho `scrollToTop`, `scrollToBottom`, `next`, `previous`. |
|
|
127
|
+
| Căn vị trí item trong danh sách | `scrollToIndex(index, { align: 'start' })` / `'center'` / `'end'` |
|
|
128
|
+
|
|
103
129
|
### VirtualList và DynamicVirtualScroll
|
|
104
130
|
|
|
105
131
|
`VirtualList` là alias của `DynamicVirtualScroll`. Hai tên này render cùng một
|
|
@@ -197,19 +223,19 @@ và thao tác nhảy tới item chưa được đo chính xác hơn.
|
|
|
197
223
|
|
|
198
224
|
#### Events
|
|
199
225
|
|
|
200
|
-
| Event | Payload | Thời điểm emit |
|
|
201
|
-
| --- | --- | --- |
|
|
202
|
-
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. `endIndex` là mốc không bao gồm item tại index đó. |
|
|
203
|
-
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. |
|
|
204
|
-
| `refresh` | không có | Khi kéo qua ngưỡng refresh rồi thả tay. |
|
|
226
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
227
|
+
| --- | --- | --- | --- |
|
|
228
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. `endIndex` là mốc không bao gồm item tại index đó. | `@scroll="onScroll"` |
|
|
229
|
+
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. | `@load-more="loadMore"` |
|
|
230
|
+
| `refresh` | không có | Khi kéo qua ngưỡng refresh rồi thả tay. | `@refresh="refreshFirstPage"` |
|
|
205
231
|
|
|
206
232
|
#### API qua ref
|
|
207
233
|
|
|
208
|
-
| Method | Mô tả |
|
|
209
|
-
| --- | --- |
|
|
210
|
-
| `scrollTo(position, options?)` | Cuộn tới offset pixel bên trong danh sách. |
|
|
211
|
-
| `scrollToIndex(index,
|
|
212
|
-
| `scrollToTop(behavior?)` | Cuộn về đầu danh sách. |
|
|
234
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
235
|
+
| --- | --- | --- |
|
|
236
|
+
| `scrollTo(position, options?)` | Cuộn tới offset pixel bên trong danh sách. | `list?.scrollTo(200, { behavior: 'smooth' })` |
|
|
237
|
+
| `scrollToIndex(index, options?)` | Cuộn tới index bắt đầu từ 0. `align` nhận `start`, `center` hoặc `end`. | `list?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
238
|
+
| `scrollToTop(behavior?)` | Cuộn về đầu danh sách. | `list?.scrollToTop()` |
|
|
213
239
|
|
|
214
240
|
Smooth scroll ở khoảng cách xa tự chuyển thành nhảy tức thời vì các hàng chưa
|
|
215
241
|
đo có thể làm thay đổi offset đích trong lúc cuộn.
|
|
@@ -283,13 +309,30 @@ thay đổi kích thước, hãy dùng `VirtualList`.
|
|
|
283
309
|
| `refreshing` | `boolean` | `false` | Giữ indicator mở trong lúc refresh bất đồng bộ. |
|
|
284
310
|
| `pullRefreshThreshold` | `number` | `64` | Khoảng kéo cần thiết để emit `refresh`, tính bằng pixel. |
|
|
285
311
|
|
|
286
|
-
#### Slots
|
|
312
|
+
#### Slots
|
|
313
|
+
|
|
314
|
+
| Slot | Scope | Mô tả |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| `default` | `{ item, index }` | Render từng hàng đang được mount. |
|
|
317
|
+
| `empty` | không có | Render khi `items` rỗng và `loading` là false. |
|
|
318
|
+
| `loading` | không có | Hàng loading; mặc định là “Loading more…”. |
|
|
319
|
+
| `refresh` | `{ pullDistance, progress, refreshing }` | Indicator kéo để làm mới. `progress` được giới hạn từ `0` tới `1`. |
|
|
320
|
+
|
|
321
|
+
#### Events
|
|
322
|
+
|
|
323
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
324
|
+
| --- | --- | --- | --- |
|
|
325
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. `endIndex` là mốc không bao gồm item tại index đó. | `@scroll="onScroll"` |
|
|
326
|
+
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. | `@load-more="loadMore"` |
|
|
327
|
+
| `refresh` | không có | Khi kéo qua ngưỡng refresh rồi thả tay. | `@refresh="refreshFirstPage"` |
|
|
328
|
+
|
|
329
|
+
#### API qua ref
|
|
287
330
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
331
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
332
|
+
| --- | --- | --- |
|
|
333
|
+
| `scrollTo(position, options?)` | Cuộn tới offset pixel bên trong danh sách. | `list?.scrollTo(200, { behavior: 'smooth' })` |
|
|
334
|
+
| `scrollToIndex(index, options?)` | Cuộn tới item có index bắt đầu từ 0. `align` nhận `start`, `center` hoặc `end`. | `list?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
335
|
+
| `scrollToTop(behavior?)` | Cuộn về đầu danh sách. | `list?.scrollToTop()` |
|
|
293
336
|
|
|
294
337
|
### WindowDynamicVirtualScroll
|
|
295
338
|
|
|
@@ -353,14 +396,111 @@ const list = ref<VirtualScrollExpose>()
|
|
|
353
396
|
| `refreshing` | `boolean` | `false` | Giữ indicator mở trong lúc refresh bất đồng bộ. |
|
|
354
397
|
| `pullRefreshThreshold` | `number` | `64` | Khoảng kéo cần thiết để emit `refresh`, tính bằng pixel. |
|
|
355
398
|
|
|
356
|
-
#### Slots
|
|
399
|
+
#### Slots
|
|
400
|
+
|
|
401
|
+
| Slot | Scope | Mô tả |
|
|
402
|
+
| --- | --- | --- |
|
|
403
|
+
| `default` | `{ item, index }` | Render từng hàng đang được mount. |
|
|
404
|
+
| `empty` | không có | Render khi `items` rỗng và `loading` là false. |
|
|
405
|
+
| `loading` | không có | Hàng loading; mặc định là “Loading more…”. |
|
|
406
|
+
| `refresh` | `{ pullDistance, progress, refreshing }` | Indicator kéo để làm mới. `progress` được giới hạn từ `0` tới `1`. |
|
|
407
|
+
|
|
408
|
+
#### Events
|
|
409
|
+
|
|
410
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
411
|
+
| --- | --- | --- | --- |
|
|
412
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi cửa sổ cuộn. `scrollTop` tính tương đối từ đầu component. `endIndex` là mốc không bao gồm item tại index đó. | `@scroll="onScroll"` |
|
|
413
|
+
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. | `@load-more="loadMore"` |
|
|
414
|
+
| `refresh` | không có | Khi kéo qua ngưỡng refresh rồi thả tay. | `@refresh="refreshFirstPage"` |
|
|
415
|
+
|
|
416
|
+
#### API qua ref
|
|
417
|
+
|
|
418
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
419
|
+
| --- | --- | --- |
|
|
420
|
+
| `scrollTo(position, options?)` | Cuộn cửa sổ tới offset pixel tương đối so với đầu danh sách. | `list?.scrollTo(200, { behavior: 'smooth' })` |
|
|
421
|
+
| `scrollToIndex(index, options?)` | Cuộn cửa sổ tới item có index bắt đầu từ 0. `align` nhận `start`, `center` hoặc `end`. | `list?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
422
|
+
| `scrollToTop(behavior?)` | Cuộn cửa sổ về đầu component. | `list?.scrollToTop()` |
|
|
423
|
+
|
|
424
|
+
### WindowGirdVirtualScroll
|
|
425
|
+
|
|
426
|
+
Dùng `WindowGirdVirtualScroll` cho grid nhiều cột có card với chiều cao cố định
|
|
427
|
+
đã biết và dùng cửa sổ trình duyệt làm viewport. Component virtualize theo cả
|
|
428
|
+
hàng để các cột đang hiển thị luôn được mount cùng nhau.
|
|
429
|
+
|
|
430
|
+
#### Ví dụ
|
|
431
|
+
|
|
432
|
+
```vue
|
|
433
|
+
<script setup lang="ts">
|
|
434
|
+
import { WindowGirdVirtualScroll } from 'vue-virtual-flow'
|
|
435
|
+
|
|
436
|
+
const products = Array.from({ length: 10_000 }, (_, id) => ({
|
|
437
|
+
id,
|
|
438
|
+
name: `Sản phẩm ${id + 1}`,
|
|
439
|
+
}))
|
|
440
|
+
</script>
|
|
441
|
+
|
|
442
|
+
<template>
|
|
443
|
+
<WindowGirdVirtualScroll
|
|
444
|
+
:items="products"
|
|
445
|
+
:item-size="240"
|
|
446
|
+
:columns="4"
|
|
447
|
+
:gap="16"
|
|
448
|
+
item-key="id"
|
|
449
|
+
aria-label="Grid sản phẩm"
|
|
450
|
+
>
|
|
451
|
+
<template #default="{ item, rowIndex, columnIndex }">
|
|
452
|
+
<ProductCard
|
|
453
|
+
:product="item"
|
|
454
|
+
:data-position="`${rowIndex}:${columnIndex}`"
|
|
455
|
+
/>
|
|
456
|
+
</template>
|
|
457
|
+
</WindowGirdVirtualScroll>
|
|
458
|
+
</template>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
#### Props
|
|
462
|
+
|
|
463
|
+
| Prop | Kiểu | Mặc định | Mô tả |
|
|
464
|
+
| --- | --- | --- | --- |
|
|
465
|
+
| `items` | `readonly T[]` | bắt buộc | Dữ liệu truyền vào default slot. |
|
|
466
|
+
| `itemSize` | `number` | bắt buộc | Chiều cao cố định của mỗi hàng grid, tính bằng pixel. Nội dung vượt quá bị ẩn. |
|
|
467
|
+
| `columns` | `number` | `2` | Số cột trên mỗi hàng. |
|
|
468
|
+
| `gap` | `number` | `0` | Khoảng cách giữa các hàng và cột, tính bằng pixel. |
|
|
469
|
+
| `overscan` | `number` | `1` | Số hàng mount thêm trước và sau vùng đang hiển thị. |
|
|
470
|
+
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key` hoặc index | Định danh ổn định cho mỗi item. |
|
|
471
|
+
| `ariaLabel` | `string` | `"Window virtual grid"` | Nhãn hỗ trợ khả năng tiếp cận cho grid. |
|
|
472
|
+
| `hasMore` | `boolean` | `false` | Cho biết vẫn còn trang dữ liệu tiếp theo. |
|
|
473
|
+
| `loading` | `boolean` | `false` | Ngăn request trùng và hiển thị hàng loading. |
|
|
474
|
+
| `loadingItemSize` | `number` | `itemSize` | Chiều cao dành cho hàng loading, tính bằng pixel. |
|
|
475
|
+
| `loadMoreThreshold` | `number` | `200` | Khoảng cách từ đáy cửa sổ tới cuối grid để emit `load-more`, tính bằng pixel. |
|
|
476
|
+
| `pullToRefresh` | `boolean` | `false` | Bật refresh khi trang đang ở `window.scrollY = 0`. |
|
|
477
|
+
| `refreshing` | `boolean` | `false` | Giữ indicator mở trong lúc refresh bất đồng bộ. |
|
|
478
|
+
| `pullRefreshThreshold` | `number` | `64` | Khoảng kéo cần thiết để emit `refresh`, tính bằng pixel. |
|
|
479
|
+
|
|
480
|
+
#### Slots
|
|
481
|
+
|
|
482
|
+
| Slot | Scope | Mô tả |
|
|
483
|
+
| --- | --- | --- |
|
|
484
|
+
| `default` | `{ item, index, rowIndex, columnIndex }` | Render từng ô đang được mount. |
|
|
485
|
+
| `empty` | không có | Render khi `items` rỗng và `loading` là false. |
|
|
486
|
+
| `loading` | không có | Hàng loading; mặc định là “Loading more…”. |
|
|
487
|
+
| `refresh` | `{ pullDistance, progress, refreshing }` | Indicator kéo để làm mới. `progress` được giới hạn từ `0` tới `1`. |
|
|
488
|
+
|
|
489
|
+
#### Events
|
|
490
|
+
|
|
491
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
492
|
+
| --- | --- | --- | --- |
|
|
493
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi cửa sổ cuộn. `scrollTop` tính tương đối từ đầu component. `endIndex` là mốc không bao gồm item tại index đó. | `@scroll="onScroll"` |
|
|
494
|
+
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. | `@load-more="loadMore"` |
|
|
495
|
+
| `refresh` | không có | Khi kéo qua ngưỡng refresh rồi thả tay. | `@refresh="refreshFirstPage"` |
|
|
496
|
+
|
|
497
|
+
#### API qua ref
|
|
357
498
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
cửa sổ trình duyệt; `scrollTo` nhận offset tương đối so với đầu danh sách.
|
|
499
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
500
|
+
| --- | --- | --- |
|
|
501
|
+
| `scrollTo(position, options?)` | Cuộn cửa sổ tới offset pixel tương đối so với đầu danh sách. | `list?.scrollTo(200, { behavior: 'smooth' })` |
|
|
502
|
+
| `scrollToIndex(index, options?)` | Cuộn cửa sổ tới hàng chứa item có index bắt đầu từ 0. `align` nhận `start`, `center` hoặc `end`. | `list?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
503
|
+
| `scrollToTop(behavior?)` | Cuộn cửa sổ về đầu component. | `list?.scrollToTop()` |
|
|
364
504
|
|
|
365
505
|
### ChatVirtualScroll
|
|
366
506
|
|
|
@@ -450,21 +590,21 @@ chiều cao được thêm phía trên viewport và khôi phục vị trí của
|
|
|
450
590
|
|
|
451
591
|
#### Events
|
|
452
592
|
|
|
453
|
-
| Event | Payload | Thời điểm emit |
|
|
454
|
-
| --- | --- | --- |
|
|
455
|
-
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. |
|
|
456
|
-
| `load-older` | không có | Gần đầu khi `hasOlder` là true và `loadingOlder` là false. |
|
|
457
|
-
| `bottom-change` | `boolean` | Khi viewport đi vào hoặc rời ngưỡng ở cuối. |
|
|
593
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
594
|
+
| --- | --- | --- | --- |
|
|
595
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. | `@scroll="onScroll"` |
|
|
596
|
+
| `load-older` | không có | Gần đầu khi `hasOlder` là true và `loadingOlder` là false. | `@load-older="loadOlder"` |
|
|
597
|
+
| `bottom-change` | `boolean` | Khi viewport đi vào hoặc rời ngưỡng ở cuối. | `@bottom-change="onBottomChange"` |
|
|
458
598
|
|
|
459
599
|
#### API qua ref
|
|
460
600
|
|
|
461
|
-
|
|
|
462
|
-
| --- | --- |
|
|
463
|
-
| `isAtBottom` | Boolean readonly cho biết người dùng có đang gần cuối hay không. |
|
|
464
|
-
| `scrollTo(position, options?)` | Cuộn tới offset pixel. |
|
|
465
|
-
| `scrollToIndex(index,
|
|
466
|
-
| `scrollToTop(behavior?)` | Cuộn tới tin nhắn cũ nhất đã tải. |
|
|
467
|
-
| `scrollToBottom(behavior?)` | Cuộn tới tin nhắn mới nhất đã tải. |
|
|
601
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
602
|
+
| --- | --- | --- |
|
|
603
|
+
| `isAtBottom` | Boolean readonly cho biết người dùng có đang gần cuối hay không. | `chat?.isAtBottom` |
|
|
604
|
+
| `scrollTo(position, options?)` | Cuộn tới offset pixel. | `chat?.scrollTo(200, { behavior: 'smooth' })` |
|
|
605
|
+
| `scrollToIndex(index, options?)` | Cuộn tới index tin nhắn bắt đầu từ 0. | `chat?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
606
|
+
| `scrollToTop(behavior?)` | Cuộn tới tin nhắn cũ nhất đã tải. | `chat?.scrollToTop()` |
|
|
607
|
+
| `scrollToBottom(behavior?)` | Cuộn tới tin nhắn mới nhất đã tải. | `chat?.scrollToBottom('smooth')` |
|
|
468
608
|
|
|
469
609
|
### ShortMediaFeed
|
|
470
610
|
|
|
@@ -532,22 +672,39 @@ vậy hãy lưu playback state cần duy trì ở component cha.
|
|
|
532
672
|
| `loading` | `boolean` | `false` | Ngăn request `load-more` bị lặp. |
|
|
533
673
|
| `loadMoreThreshold` | `number` | `2` | Số item còn lại để kích hoạt `load-more`. |
|
|
534
674
|
|
|
535
|
-
#### Slots
|
|
675
|
+
#### Slots
|
|
676
|
+
|
|
677
|
+
| Slot | Scope | Mô tả |
|
|
678
|
+
| --- | --- | --- |
|
|
679
|
+
| `default` | `{ item, index, active }` | Render item đang mount; `active` đánh dấu item đang snap. |
|
|
680
|
+
| `empty` | không có | Render khi `items` rỗng. |
|
|
681
|
+
|
|
682
|
+
#### Events
|
|
536
683
|
|
|
537
|
-
|
|
|
684
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
538
685
|
| --- | --- | --- | --- |
|
|
539
|
-
|
|
|
540
|
-
|
|
|
541
|
-
|
|
|
542
|
-
|
|
|
543
|
-
|
|
|
544
|
-
|
|
545
|
-
|
|
686
|
+
| `update:activeIndex` | `number` | Hỗ trợ `v-model:active-index`. | `v-model:active-index="activeIndex"` |
|
|
687
|
+
| `change` | `{ index, item }` | Khi item active thay đổi. | `@change="onChange"` |
|
|
688
|
+
| `reach-start` | không có | Khi điều hướng chuyển tới item đầu. | `@reach-start="onReachStart"` |
|
|
689
|
+
| `reach-end` | không có | Khi điều hướng chuyển tới item cuối. | `@reach-end="onReachEnd"` |
|
|
690
|
+
| `load-more` | không có | Khi số item còn lại đạt `loadMoreThreshold`. | `@load-more="loadMore"` |
|
|
691
|
+
|
|
692
|
+
#### API qua ref
|
|
693
|
+
|
|
694
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
695
|
+
| --- | --- | --- |
|
|
696
|
+
| `scrollToIndex(index, options?)` | Cuộn tới item có index bắt đầu từ 0, nhận `ScrollToOptions` chuẩn. | `feed?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
546
697
|
|
|
547
|
-
####
|
|
698
|
+
#### Điều khiển bàn phím
|
|
548
699
|
|
|
549
|
-
|
|
550
|
-
|
|
700
|
+
Các phím sau hoạt động khi viewport có focus:
|
|
701
|
+
|
|
702
|
+
| Phím | Thao tác |
|
|
703
|
+
| --- | --- |
|
|
704
|
+
| `ArrowDown`, `PageDown` | Chuyển tới item tiếp theo. |
|
|
705
|
+
| `ArrowUp`, `PageUp` | Chuyển tới item trước. |
|
|
706
|
+
| `Home` | Chuyển tới vị trí đầu. |
|
|
707
|
+
| `End` | Chuyển tới item cuối. |
|
|
551
708
|
|
|
552
709
|
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
|
|
553
710
|
sổ dữ liệu hợp lý ở component cha. Virtualization giới hạn DOM và media element
|
|
@@ -630,31 +787,74 @@ thường tối đa năm slide được mount ở giữa collection.
|
|
|
630
787
|
| `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. |
|
|
631
788
|
| `autoplayLoop` | `boolean` | `true` | Quay lại slide đầu tiên khi đến cuối. |
|
|
632
789
|
| `pauseOnHover` | `boolean` | `true` | Tạm dừng autoplay khi con trỏ nằm trên carousel. |
|
|
790
|
+
| `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. |
|
|
791
|
+
|
|
792
|
+
#### Cấu hình responsive
|
|
793
|
+
|
|
794
|
+
Component chọn `breakpoint` lớn nhất không vượt quá chiều rộng container của
|
|
795
|
+
carousel. Nếu không có breakpoint phù hợp, component dùng các prop gốc.
|
|
796
|
+
Trường bị bỏ qua dùng prop gốc, không kế thừa từ breakpoint khác.
|
|
797
|
+
|
|
798
|
+
| Trường | Kiểu | Mặc định | Mô tả |
|
|
799
|
+
| --- | --- | --- | --- |
|
|
800
|
+
| `breakpoint` | `number` | bắt buộc | Chiều rộng tối thiểu của container, tính bằng pixel. |
|
|
801
|
+
| `slidesPerView` | `number` | `slidesPerView` | Số slide hiển thị tại breakpoint này. |
|
|
802
|
+
| `slidesToScroll` | `number` | `slidesToScroll` | Số slide di chuyển bởi `next()` và `previous()`. |
|
|
803
|
+
| `gap` | `number` | `gap` | Khoảng cách giữa các slide, tính bằng pixel. |
|
|
804
|
+
|
|
805
|
+
```vue
|
|
806
|
+
<VirtualCarousel
|
|
807
|
+
:items="products"
|
|
808
|
+
:slides-per-view="1"
|
|
809
|
+
:responsive="[
|
|
810
|
+
{ breakpoint: 640, slidesPerView: 2, gap: 12 },
|
|
811
|
+
{ breakpoint: 1024, slidesPerView: 3, slidesToScroll: 2, gap: 16 },
|
|
812
|
+
]"
|
|
813
|
+
>
|
|
814
|
+
<template #default="{ item }"><ProductCard :product="item" /></template>
|
|
815
|
+
</VirtualCarousel>
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
#### Slots
|
|
633
819
|
|
|
634
|
-
|
|
820
|
+
| Slot | Scope | Mô tả |
|
|
821
|
+
| --- | --- | --- |
|
|
822
|
+
| `default` | `{ item, index, active, visible }` | Render từng slide đang mount. |
|
|
823
|
+
| `empty` | không có | Render khi `items` rỗng. |
|
|
824
|
+
|
|
825
|
+
#### Events
|
|
635
826
|
|
|
636
|
-
|
|
|
827
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
637
828
|
| --- | --- | --- | --- |
|
|
638
|
-
|
|
|
639
|
-
|
|
|
640
|
-
|
|
|
641
|
-
|
|
|
642
|
-
|
|
|
643
|
-
|
|
644
|
-
|
|
829
|
+
| `update:activeIndex` | `number` | Hỗ trợ `v-model:active-index`. | `v-model:active-index="activeIndex"` |
|
|
830
|
+
| `change` | `{ index, item }` | Khi slide đầu tiên trong vùng nhìn thay đổi. | `@change="onChange"` |
|
|
831
|
+
| `reach-start` | không có | Khi điều hướng chuyển tới vị trí đầu. | `@reach-start="onReachStart"` |
|
|
832
|
+
| `reach-end` | không có | Khi điều hướng chuyển tới vị trí đầu-slide hợp lệ cuối cùng. | `@reach-end="onReachEnd"` |
|
|
833
|
+
| `load-more` | không có | Khi số slide còn lại đạt `loadMoreThreshold`. | `@load-more="loadMore"` |
|
|
834
|
+
|
|
835
|
+
#### API qua ref
|
|
645
836
|
|
|
646
|
-
|
|
837
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
838
|
+
| --- | --- | --- |
|
|
839
|
+
| `next(behavior?)` | Tiến thêm `slidesToScroll` slide; mặc định cuộn mượt. | `carousel?.next()` |
|
|
840
|
+
| `previous(behavior?)` | Lùi lại `slidesToScroll` slide. | `carousel?.previous()` |
|
|
841
|
+
| `scrollToIndex(index, options?)` | Đưa index thành slide đầu tiên trong vùng nhìn. | `carousel?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
842
|
+
| `startAutoplay()` | Bắt đầu hoặc khởi động lại timer khi `autoplay` là true và có item. | `carousel?.startAutoplay()` |
|
|
843
|
+
| `stopAutoplay()` | Xóa timer hiện tại; đặt `autoplay=false` nếu muốn giữ chế độ tự động ở trạng thái tắt. | `carousel?.stopAutoplay()` |
|
|
844
|
+
|
|
845
|
+
#### Điều khiển bàn phím
|
|
846
|
+
|
|
847
|
+
Các phím sau hoạt động khi viewport có focus:
|
|
647
848
|
|
|
648
|
-
|
|
|
849
|
+
| Phím | Thao tác |
|
|
649
850
|
| --- | --- |
|
|
650
|
-
| `
|
|
651
|
-
| `
|
|
652
|
-
| `
|
|
653
|
-
| `
|
|
654
|
-
| `stopAutoplay()` | Dừng quá trình tự động chuyển slide. |
|
|
851
|
+
| `ArrowRight`, `PageDown` | Tiến thêm `slidesToScroll` slide, tương đương `next()`. |
|
|
852
|
+
| `ArrowLeft`, `PageUp` | Lùi lại `slidesToScroll` slide, tương đương `previous()`. |
|
|
853
|
+
| `Home` | Chuyển tới vị trí đầu. |
|
|
854
|
+
| `End` | Chuyển tới vị trí đầu-slide hợp lệ cuối cùng. |
|
|
655
855
|
|
|
656
|
-
|
|
657
|
-
|
|
856
|
+
Smooth scroll ở khoảng cách xa tự chuyển thành tức thời để không lộ khoảng
|
|
857
|
+
trống virtualized.
|
|
658
858
|
|
|
659
859
|
#### Ví dụ autoplay
|
|
660
860
|
|
|
@@ -687,18 +887,134 @@ const products = ref(loadProducts())
|
|
|
687
887
|
</template>
|
|
688
888
|
```
|
|
689
889
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
`autoplay`
|
|
693
|
-
`
|
|
890
|
+
| Thao tác / điều kiện | Kết quả |
|
|
891
|
+
| --- | --- |
|
|
892
|
+
| `autoplay=true` | Tự chạy, kể cả khi collection `items` ban đầu rỗng nhận được dữ liệu. |
|
|
893
|
+
| `stopAutoplay()` | Xóa timer hiện tại. Điều hướng hoặc sự kiện con trỏ sau đó có thể khởi động lại nếu `autoplay` vẫn là true. |
|
|
894
|
+
| `startAutoplay()` | Bắt đầu hoặc khởi động lại timer khi `autoplay` là true và có item. |
|
|
895
|
+
| `autoplay=false` | Tắt tự động chuyển slide cho tới khi prop được bật lại. |
|
|
896
|
+
| `autoplayLoop` | Khi tới cuối, true quay về đầu; false dừng timer. |
|
|
897
|
+
| `pauseOnHover` | Khi là true, rê chuột vào sẽ tạm dừng; rời chuột sẽ chạy tiếp nếu autoplay đang bật. |
|
|
898
|
+
| Đổi cấu hình autoplay | `autoplayDelay`, `autoplayLoop` và `pauseOnHover` có hiệu lực ngay khi component đang chạy. |
|
|
899
|
+
|
|
900
|
+
### SkeletonLoader
|
|
901
|
+
|
|
902
|
+
`SkeletonLoader` hiển thị khối giữ chỗ với hiệu ứng shimmer. Có thể dùng độc lập
|
|
903
|
+
hoặc trong slot `loading` của danh sách. Component luôn hiển thị khi được mount;
|
|
904
|
+
dùng `v-if` để điều khiển khi dùng độc lập, hoặc để prop `loading` của danh sách
|
|
905
|
+
điều khiển slot.
|
|
906
|
+
|
|
907
|
+
#### Ví dụ
|
|
908
|
+
|
|
909
|
+
Import stylesheet của thư viện một lần như bên dưới hoặc tại entry point của
|
|
910
|
+
ứng dụng. Đặt `loading` thành false khi request dữ liệu hoàn tất.
|
|
911
|
+
|
|
912
|
+
```vue
|
|
913
|
+
<script setup lang="ts">
|
|
914
|
+
import { ref } from 'vue'
|
|
915
|
+
import { SkeletonLoader } from 'vue-virtual-flow'
|
|
916
|
+
import 'vue-virtual-flow/style.css'
|
|
917
|
+
|
|
918
|
+
const loading = ref(true)
|
|
919
|
+
</script>
|
|
920
|
+
|
|
921
|
+
<template>
|
|
922
|
+
<SkeletonLoader
|
|
923
|
+
v-if="loading"
|
|
924
|
+
width="100%"
|
|
925
|
+
:height="120"
|
|
926
|
+
aria-label="Đang tải nội dung…"
|
|
927
|
+
/>
|
|
928
|
+
<p v-else>Nội dung đã sẵn sàng.</p>
|
|
929
|
+
</template>
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
#### Props
|
|
933
|
+
|
|
934
|
+
| Prop | Kiểu | Mặc định | Mô tả |
|
|
935
|
+
| --- | --- | --- | --- |
|
|
936
|
+
| `width` | `number \| string` | `'100%'` | Chiều rộng: số tính bằng pixel, chuỗi nhận độ dài CSS như `'60%'` hoặc `'12rem'`. |
|
|
937
|
+
| `height` | `number \| string` | `16` | Chiều cao: số tính bằng pixel, chuỗi nhận độ dài CSS. |
|
|
938
|
+
| `variant` | `'rectangle' \| 'rounded' \| 'circle'` | `'rounded'` | Hình dạng khối giữ chỗ. Đặt chiều rộng bằng chiều cao để tạo hình tròn. |
|
|
939
|
+
| `animated` | `boolean` | `true` | Bật hiệu ứng shimmer. |
|
|
940
|
+
| `ariaLabel` | `string` | `'Loading…'` | Nhãn cho trình đọc màn hình của khối có `role="status"`. |
|
|
941
|
+
|
|
942
|
+
```vue
|
|
943
|
+
<SkeletonLoader variant="circle" :width="48" :height="48" />
|
|
944
|
+
<SkeletonLoader variant="rectangle" :height="180" />
|
|
945
|
+
<SkeletonLoader width="60%" :height="20" :animated="false" />
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
Hiệu ứng tự dừng khi người dùng bật tùy chọn giảm chuyển động. Khi ghép nhiều
|
|
949
|
+
skeleton trang trí cùng một thông báo loading chung, truyền `aria-hidden="true"`
|
|
950
|
+
cho từng skeleton để tránh lặp nhãn với trình đọc màn hình.
|
|
951
|
+
|
|
952
|
+
#### Slots, events và API qua ref
|
|
953
|
+
|
|
954
|
+
| API | Hỗ trợ |
|
|
955
|
+
| --- | --- |
|
|
956
|
+
| Slots | Không có. Component render một khối giữ chỗ. |
|
|
957
|
+
| Events | Không có event tùy chỉnh. |
|
|
958
|
+
| Method qua ref | Không có. Điều khiển component bằng props và `v-if`. |
|
|
959
|
+
|
|
960
|
+
#### Thao tác
|
|
961
|
+
|
|
962
|
+
| Thao tác | Cách dùng |
|
|
963
|
+
| --- | --- |
|
|
964
|
+
| Hiện hoặc ẩn | `<SkeletonLoader v-if="loading" />` |
|
|
965
|
+
| Dùng hình tròn | `<SkeletonLoader variant="circle" :width="48" :height="48" />` |
|
|
966
|
+
| Tắt shimmer | `<SkeletonLoader :animated="false" />` |
|
|
967
|
+
| Đặt nhãn cho trình đọc màn hình | `aria-label="Đang tải…"` |
|
|
968
|
+
| Ẩn skeleton trang trí khỏi trình đọc màn hình | `aria-hidden="true"` |
|
|
969
|
+
|
|
970
|
+
#### Dùng trong slot loading
|
|
971
|
+
|
|
972
|
+
Import `SkeletonLoader` cùng với `VirtualList`. Với state `items` và `loading`
|
|
973
|
+
hiện có, thay thông báo loading bằng skeleton:
|
|
974
|
+
|
|
975
|
+
```vue
|
|
976
|
+
<VirtualList :items="items" :loading="loading" :loading-item-size="64">
|
|
977
|
+
<template #default="{ item }"><FeedCard :item="item" /></template>
|
|
978
|
+
<template #loading>
|
|
979
|
+
<SkeletonLoader :height="40" aria-label="Đang tải trang tiếp theo…" />
|
|
980
|
+
</template>
|
|
981
|
+
</VirtualList>
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
| Cấu hình | Cách dùng |
|
|
985
|
+
| --- | --- |
|
|
986
|
+
| Component hỗ trợ slot `loading` | `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`, `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll`. |
|
|
987
|
+
| `loading` trên danh sách | Đặt thành true để hiển thị slot; false để ẩn. |
|
|
988
|
+
| `height` trên skeleton | Đặt chiều cao khối giữ chỗ, ví dụ `40` pixel. |
|
|
989
|
+
| `loadingItemSize` trên danh sách | Dành đủ chỗ cho skeleton và padding dọc của hàng. Ví dụ: `40px + 1.5rem = 64px` với cỡ chữ gốc 16px. |
|
|
990
|
+
|
|
991
|
+
#### Tùy chỉnh giao diện
|
|
992
|
+
|
|
993
|
+
Đặt các biến CSS sau trên skeleton hoặc phần tử cha:
|
|
994
|
+
|
|
995
|
+
| Biến CSS | Mặc định | Mô tả |
|
|
996
|
+
| --- | --- | --- |
|
|
997
|
+
| `--skeleton-background` | `#e2e8f0` | Màu nền. |
|
|
998
|
+
| `--skeleton-highlight` | `rgb(255 255 255 / 60%)` | Màu vệt sáng của hiệu ứng shimmer. |
|
|
999
|
+
| `--skeleton-radius` | `8px` | Bán kính bo góc cho variant `rounded`. |
|
|
1000
|
+
|
|
1001
|
+
```vue
|
|
1002
|
+
<SkeletonLoader
|
|
1003
|
+
:height="80"
|
|
1004
|
+
style="--skeleton-background: #334155; --skeleton-highlight: #475569; --skeleton-radius: 12px"
|
|
1005
|
+
/>
|
|
1006
|
+
```
|
|
694
1007
|
|
|
695
1008
|
## Cách dùng chung
|
|
696
1009
|
|
|
697
1010
|
### Chiều cao viewport
|
|
698
1011
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
1012
|
+
| Giá trị / component | Cách hoạt động |
|
|
1013
|
+
| --- | --- |
|
|
1014
|
+
| `:height="400"` | Giá trị số được đổi thành pixel. |
|
|
1015
|
+
| `height="60vh"` | Chuỗi chiều cao CSS được giữ nguyên. |
|
|
1016
|
+
| `height="fill"` / `height="100%"` | Dùng chiều cao phần tử cha. Thẻ cha cần có chiều cao xác định; bản development cảnh báo nếu viewport tính thành `0px`. |
|
|
1017
|
+
| `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | Dùng cửa sổ trình duyệt và không nhận prop `height`. |
|
|
702
1018
|
|
|
703
1019
|
```vue
|
|
704
1020
|
<div class="list-container">
|
|
@@ -715,15 +1031,14 @@ Các component dùng container nhận một số hoặc chuỗi chiều cao CSS.
|
|
|
715
1031
|
}
|
|
716
1032
|
```
|
|
717
1033
|
|
|
718
|
-
Chiều cao phần trăm chỉ hoạt động khi thẻ cha có chiều cao xác định. Bản
|
|
719
|
-
development cảnh báo nếu viewport được tính thành `0px`.
|
|
720
|
-
`WindowDynamicVirtualScroll` dùng cửa sổ trình duyệt nên không có prop
|
|
721
|
-
`height`.
|
|
722
|
-
|
|
723
1034
|
### Key ổn định cho item
|
|
724
1035
|
|
|
725
|
-
|
|
726
|
-
|
|
1036
|
+
| Cấu hình / dữ liệu | Cách chọn key |
|
|
1037
|
+
| --- | --- |
|
|
1038
|
+
| `:item-key="(item, index) => …"` | Dùng giá trị hàm trả về. |
|
|
1039
|
+
| `item-key="uuid"` | Dùng `item.uuid` hợp lệ; nếu không có, thử `id`, `key`, rồi index. |
|
|
1040
|
+
| Object không truyền `itemKey` | Thử `item.id`, sau đó `item.key`, rồi index. |
|
|
1041
|
+
| Giá trị nguyên thủy không có hàm key | Dùng index làm fallback. |
|
|
727
1042
|
|
|
728
1043
|
```vue
|
|
729
1044
|
<VirtualList :items="users" item-key="uuid" />
|
|
@@ -739,8 +1054,11 @@ lại dữ liệu. Key cũng gắn kết quả đo chiều cao với đúng item
|
|
|
739
1054
|
|
|
740
1055
|
### Tải thêm dữ liệu
|
|
741
1056
|
|
|
742
|
-
|
|
743
|
-
|
|
1057
|
+
| Component | `loadMoreThreshold` | Event |
|
|
1058
|
+
| --- | --- | --- |
|
|
1059
|
+
| `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`, `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | Khoảng cách tới cuối danh sách, tính bằng pixel. | `load-more` |
|
|
1060
|
+
| `ShortMediaFeed` | Số item phía sau item active. | `load-more` |
|
|
1061
|
+
| `VirtualCarousel` | Số slide phía sau nhóm đang hiển thị. | `load-more` |
|
|
744
1062
|
|
|
745
1063
|
```vue
|
|
746
1064
|
<script setup lang="ts">
|
|
@@ -772,14 +1090,20 @@ async function loadMore() {
|
|
|
772
1090
|
</template>
|
|
773
1091
|
```
|
|
774
1092
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
`
|
|
1093
|
+
| Bước / điều kiện | Thao tác / kết quả |
|
|
1094
|
+
| --- | --- |
|
|
1095
|
+
| Nhận `load-more` | Chỉ bắt đầu request tiếp theo khi `loading` là false và `hasMore` là true. |
|
|
1096
|
+
| Request đang chạy | Giữ `loading=true` trong toàn bộ request. |
|
|
1097
|
+
| Nhận trang tiếp theo | Nối thêm item. Event thông thường chỉ emit một lần với số lượng item hiện tại; tăng số lượng cho phép request tiếp theo. |
|
|
1098
|
+
| Request hoàn tất | Đặt `loading=false`; dùng `finally` khi request có thể thất bại. |
|
|
1099
|
+
| Trang cuối | Đặt `hasMore=false` để ngừng request tiếp theo. |
|
|
778
1100
|
|
|
779
1101
|
### Kéo để làm mới
|
|
780
1102
|
|
|
781
|
-
|
|
782
|
-
|
|
1103
|
+
| Component | Vị trí bắt đầu thao tác |
|
|
1104
|
+
| --- | --- |
|
|
1105
|
+
| `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll` | Đầu container của component. |
|
|
1106
|
+
| `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | `window.scrollY = 0` |
|
|
783
1107
|
|
|
784
1108
|
```vue
|
|
785
1109
|
<VirtualList
|
|
@@ -796,21 +1120,51 @@ và `WindowDynamicVirtualScroll`:
|
|
|
796
1120
|
</VirtualList>
|
|
797
1121
|
```
|
|
798
1122
|
|
|
799
|
-
|
|
800
|
-
|
|
1123
|
+
| Bước / trạng thái | Thao tác / kết quả |
|
|
1124
|
+
| --- | --- |
|
|
1125
|
+
| Bật thao tác | `pull-to-refresh` |
|
|
1126
|
+
| Kéo xuống bằng một ngón khi đang ở đầu | Cập nhật scope của slot `refresh`: `pullDistance`, `progress` và `refreshing`. |
|
|
1127
|
+
| Thả sau khi đạt `pullRefreshThreshold` | Emit `refresh` không có payload. |
|
|
1128
|
+
| Xử lý `refresh` | Đặt `refreshing=true` trong lúc tải lại dữ liệu để giữ indicator mở. |
|
|
1129
|
+
| Hoàn tất làm mới | Đặt `refreshing=false` để đóng indicator. |
|
|
801
1130
|
|
|
802
1131
|
## Các type được export
|
|
803
1132
|
|
|
1133
|
+
| Type | Mục đích |
|
|
1134
|
+
| --- | --- |
|
|
1135
|
+
| `ChatVirtualScrollExpose` | API ref và trạng thái `isAtBottom` của chat. |
|
|
1136
|
+
| `ChatVirtualScrollProps<T>` | Props của component chat. |
|
|
1137
|
+
| `DynamicVirtualScrollProps<T>` | Props của danh sách chiều cao động. |
|
|
1138
|
+
| `ItemKey<T>` | Tên thuộc tính hoặc hàm xác định key của item. |
|
|
1139
|
+
| `ResponsiveBreakpoint` | Breakpoint container và cấu hình ghi đè của carousel. |
|
|
1140
|
+
| `ScrollAlignment` | `'start'`, `'center'` hoặc `'end'` khi cuộn tới index trong danh sách. |
|
|
1141
|
+
| `ShortMediaFeedChangeEvent<T>` | Payload event `change` của feed: `{ index, item }`. |
|
|
1142
|
+
| `ShortMediaFeedExpose` | API ref của feed. |
|
|
1143
|
+
| `ShortMediaFeedProps<T>` | Props của feed. |
|
|
1144
|
+
| `SkeletonLoaderProps` | Kích thước, hình dạng, hiệu ứng và nhãn hỗ trợ tiếp cận của skeleton. |
|
|
1145
|
+
| `VirtualCarouselChangeEvent<T>` | Payload event `change` của carousel: `{ index, item }`. |
|
|
1146
|
+
| `VirtualCarouselExpose` | API ref điều hướng và autoplay của carousel. |
|
|
1147
|
+
| `VirtualCarouselProps<T>` | Props của carousel, gồm cấu hình responsive. |
|
|
1148
|
+
| `VirtualListExpose` | Alias của `VirtualScrollExpose`. |
|
|
1149
|
+
| `VirtualListProps<T>` | Alias của `DynamicVirtualScrollProps<T>`. |
|
|
1150
|
+
| `VirtualScrollEvent` | Payload scroll: `{ scrollTop, startIndex, endIndex }`; `endIndex` không bao gồm item tại index đó. |
|
|
1151
|
+
| `VirtualScrollExpose` | API ref chung cho danh sách cố định, động và danh sách/grid cuộn theo cửa sổ. |
|
|
1152
|
+
| `VirtualScrollProps<T>` | Props của danh sách chiều cao cố định. |
|
|
1153
|
+
| `WindowDynamicVirtualScrollProps<T>` | Props của danh sách chiều cao động cuộn theo cửa sổ. |
|
|
1154
|
+
| `WindowGirdVirtualScrollProps<T>` | Props của grid hàng cố định cuộn theo cửa sổ. |
|
|
1155
|
+
|
|
804
1156
|
```ts
|
|
805
1157
|
import type {
|
|
806
1158
|
ChatVirtualScrollExpose,
|
|
807
1159
|
ChatVirtualScrollProps,
|
|
808
1160
|
DynamicVirtualScrollProps,
|
|
809
1161
|
ItemKey,
|
|
1162
|
+
ResponsiveBreakpoint,
|
|
810
1163
|
ScrollAlignment,
|
|
811
1164
|
ShortMediaFeedChangeEvent,
|
|
812
1165
|
ShortMediaFeedExpose,
|
|
813
1166
|
ShortMediaFeedProps,
|
|
1167
|
+
SkeletonLoaderProps,
|
|
814
1168
|
VirtualCarouselChangeEvent,
|
|
815
1169
|
VirtualCarouselExpose,
|
|
816
1170
|
VirtualCarouselProps,
|
|
@@ -820,11 +1174,28 @@ import type {
|
|
|
820
1174
|
VirtualScrollExpose,
|
|
821
1175
|
VirtualScrollProps,
|
|
822
1176
|
WindowDynamicVirtualScrollProps,
|
|
1177
|
+
WindowGirdVirtualScrollProps,
|
|
823
1178
|
} from 'vue-virtual-flow'
|
|
824
1179
|
```
|
|
825
1180
|
|
|
826
1181
|
## Phát triển và phát hành
|
|
827
1182
|
|
|
1183
|
+
| Lệnh / lifecycle | Thao tác |
|
|
1184
|
+
| --- | --- |
|
|
1185
|
+
| `bun install` | Cài đặt dependency. |
|
|
1186
|
+
| `bunx playwright install chromium firefox webkit` | Cài trình duyệt cho smoke test. |
|
|
1187
|
+
| `bun run dev` | Chạy playground. |
|
|
1188
|
+
| `bun run typecheck` | Kiểm tra type TypeScript và Vue. |
|
|
1189
|
+
| `bun run test` | Chạy unit test. |
|
|
1190
|
+
| `bun run test:watch` | Chạy unit test ở chế độ theo dõi thay đổi. |
|
|
1191
|
+
| `bun run build` | Kiểm tra type và build package kèm khai báo type. |
|
|
1192
|
+
| `bun run typecheck:package` | Kiểm tra type phía dùng package ESM và CommonJS sau khi build. |
|
|
1193
|
+
| `bun run lint:package` | Kiểm tra exports và cách đóng gói type sau khi build. |
|
|
1194
|
+
| `bun run test:browser` | Chạy test trình duyệt bằng Playwright. |
|
|
1195
|
+
| `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. |
|
|
1196
|
+
| `npm pack --dry-run` | Kiểm tra các file sẽ được publish. |
|
|
1197
|
+
| `prepublishOnly` | Tự chạy `bun run check` trước khi publish. |
|
|
1198
|
+
|
|
828
1199
|
```bash
|
|
829
1200
|
bun install
|
|
830
1201
|
bunx playwright install chromium firefox webkit
|