vue-virtual-flow 0.0.4 → 0.0.6
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 +422 -97
- package/README.vi.md +423 -95
- package/dist/components/ChatVirtualScroll.d.cts +2 -0
- package/dist/components/ChatVirtualScroll.d.ts +2 -0
- package/dist/components/SkeletonLoader.d.cts +9 -0
- package/dist/components/SkeletonLoader.d.ts +9 -0
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/types.d.cts +11 -0
- package/dist/types.d.ts +11 -0
- package/dist/vue-virtual-flow.cjs +1 -1
- package/dist/vue-virtual-flow.css +1 -1
- package/dist/vue-virtual-flow.js +1179 -1143
- package/package.json +1 -1
package/README.vi.md
CHANGED
|
@@ -22,6 +22,7 @@ vẫn phản hồi nhanh mà component không áp đặt giao diện của từn
|
|
|
22
22
|
- [ChatVirtualScroll](#chatvirtualscroll)
|
|
23
23
|
- [ShortMediaFeed](#shortmediafeed)
|
|
24
24
|
- [VirtualCarousel](#virtualcarousel)
|
|
25
|
+
- [SkeletonLoader](#skeletonloader)
|
|
25
26
|
- [Cách dùng chung](#cách-dùng-chung)
|
|
26
27
|
- [Chiều cao viewport](#chiều-cao-viewport)
|
|
27
28
|
- [Key ổn định cho item](#key-ổn-định-cho-item)
|
|
@@ -44,10 +45,19 @@ Import stylesheet của thư viện một lần tại entry point của ứng d
|
|
|
44
45
|
import 'vue-virtual-flow/style.css'
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
| Dependency | Yêu cầu |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| Vue | `^3.4.0` |
|
|
51
|
+
| Node.js | `>=18` |
|
|
48
52
|
|
|
49
53
|
## Bắt đầu nhanh
|
|
50
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
|
+
|
|
51
61
|
Import component cục bộ:
|
|
52
62
|
|
|
53
63
|
```vue
|
|
@@ -85,7 +95,7 @@ createApp(App).use(VueVirtualScroll).mount('#app')
|
|
|
85
95
|
|
|
86
96
|
Plugin đăng ký `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`,
|
|
87
97
|
`WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll`, `ChatVirtualScroll`,
|
|
88
|
-
`ShortMediaFeed` và `
|
|
98
|
+
`ShortMediaFeed`, `VirtualCarousel` và `SkeletonLoader`.
|
|
89
99
|
|
|
90
100
|
## Chọn component
|
|
91
101
|
|
|
@@ -99,9 +109,23 @@ Plugin đăng ký `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`,
|
|
|
99
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 |
|
|
100
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 |
|
|
101
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 |
|
|
102
113
|
|
|
103
114
|
## Hướng dẫn từng component
|
|
104
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
|
+
|
|
105
129
|
### VirtualList và DynamicVirtualScroll
|
|
106
130
|
|
|
107
131
|
`VirtualList` là alias của `DynamicVirtualScroll`. Hai tên này render cùng một
|
|
@@ -199,19 +223,19 @@ và thao tác nhảy tới item chưa được đo chính xác hơn.
|
|
|
199
223
|
|
|
200
224
|
#### Events
|
|
201
225
|
|
|
202
|
-
| Event | Payload | Thời điểm emit |
|
|
203
|
-
| --- | --- | --- |
|
|
204
|
-
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. `endIndex` là mốc không bao gồm item tại index đó. |
|
|
205
|
-
| `load-more` | không có | Gần cuối danh sách khi `hasMore` là true và `loading` là false. |
|
|
206
|
-
| `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"` |
|
|
207
231
|
|
|
208
232
|
#### API qua ref
|
|
209
233
|
|
|
210
|
-
| Method | Mô tả |
|
|
211
|
-
| --- | --- |
|
|
212
|
-
| `scrollTo(position, options?)` | Cuộn tới offset pixel bên trong danh sách. |
|
|
213
|
-
| `scrollToIndex(index,
|
|
214
|
-
| `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()` |
|
|
215
239
|
|
|
216
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
|
|
217
241
|
đo có thể làm thay đổi offset đích trong lúc cuộn.
|
|
@@ -285,13 +309,30 @@ thay đổi kích thước, hãy dùng `VirtualList`.
|
|
|
285
309
|
| `refreshing` | `boolean` | `false` | Giữ indicator mở trong lúc refresh bất đồng bộ. |
|
|
286
310
|
| `pullRefreshThreshold` | `number` | `64` | Khoảng kéo cần thiết để emit `refresh`, tính bằng pixel. |
|
|
287
311
|
|
|
288
|
-
#### Slots
|
|
312
|
+
#### Slots
|
|
289
313
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
330
|
+
|
|
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()` |
|
|
295
336
|
|
|
296
337
|
### WindowDynamicVirtualScroll
|
|
297
338
|
|
|
@@ -355,14 +396,30 @@ const list = ref<VirtualScrollExpose>()
|
|
|
355
396
|
| `refreshing` | `boolean` | `false` | Giữ indicator mở trong lúc refresh bất đồng bộ. |
|
|
356
397
|
| `pullRefreshThreshold` | `number` | `64` | Khoảng kéo cần thiết để emit `refresh`, tính bằng pixel. |
|
|
357
398
|
|
|
358
|
-
#### Slots
|
|
399
|
+
#### Slots
|
|
359
400
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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()` |
|
|
366
423
|
|
|
367
424
|
### WindowGirdVirtualScroll
|
|
368
425
|
|
|
@@ -370,6 +427,8 @@ Dùng `WindowGirdVirtualScroll` cho grid nhiều cột có card với chiều ca
|
|
|
370
427
|
đã biết và dùng cửa sổ trình duyệt làm viewport. Component virtualize theo cả
|
|
371
428
|
hàng để các cột đang hiển thị luôn được mount cùng nhau.
|
|
372
429
|
|
|
430
|
+
#### Ví dụ
|
|
431
|
+
|
|
373
432
|
```vue
|
|
374
433
|
<script setup lang="ts">
|
|
375
434
|
import { WindowGirdVirtualScroll } from 'vue-virtual-flow'
|
|
@@ -399,11 +458,49 @@ const products = Array.from({ length: 10_000 }, (_, id) => ({
|
|
|
399
458
|
</template>
|
|
400
459
|
```
|
|
401
460
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
|
498
|
+
|
|
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()` |
|
|
407
504
|
|
|
408
505
|
### ChatVirtualScroll
|
|
409
506
|
|
|
@@ -474,6 +571,7 @@ chiều cao được thêm phía trên viewport và khôi phục vị trí của
|
|
|
474
571
|
| `estimatedItemSize` | `number` | `48` | Chiều cao trung bình ban đầu của tin nhắn, tính bằng pixel. |
|
|
475
572
|
| `height` | `number \| string` | `400` | Chiều cao viewport. Số được hiểu là pixel; `"fill"` tương đương `100%`. |
|
|
476
573
|
| `overscan` | `number` | `5` | Số tin nhắn mount thêm trước và sau vùng đang hiển thị. |
|
|
574
|
+
| `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. |
|
|
477
575
|
| `itemKey` | `keyof T \| (item, index) => PropertyKey` | `id`, `key` hoặc index | Định danh ổn định, đặc biệt quan trọng khi thêm vào đầu. |
|
|
478
576
|
| `ariaLabel` | `string` | `"Chat messages"` | Nhãn hỗ trợ khả năng tiếp cận cho live log. |
|
|
479
577
|
| `stickToBottom` | `boolean` | `true` | Bám theo tin nhắn mới nếu hiện tại đang gần cuối. |
|
|
@@ -493,21 +591,21 @@ chiều cao được thêm phía trên viewport và khôi phục vị trí của
|
|
|
493
591
|
|
|
494
592
|
#### Events
|
|
495
593
|
|
|
496
|
-
| Event | Payload | Thời điểm emit |
|
|
497
|
-
| --- | --- | --- |
|
|
498
|
-
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. |
|
|
499
|
-
| `load-older` | không có | Gần đầu khi `hasOlder` là true và `loadingOlder` là false. |
|
|
500
|
-
| `bottom-change` | `boolean` | Khi viewport đi vào hoặc rời ngưỡng ở cuối. |
|
|
594
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
595
|
+
| --- | --- | --- | --- |
|
|
596
|
+
| `scroll` | `{ scrollTop, startIndex, endIndex }` | Khi container cuộn. | `@scroll="onScroll"` |
|
|
597
|
+
| `load-older` | không có | Gần đầu khi `hasOlder` là true và `loadingOlder` là false. | `@load-older="loadOlder"` |
|
|
598
|
+
| `bottom-change` | `boolean` | Khi viewport đi vào hoặc rời ngưỡng ở cuối. | `@bottom-change="onBottomChange"` |
|
|
501
599
|
|
|
502
600
|
#### API qua ref
|
|
503
601
|
|
|
504
|
-
|
|
|
505
|
-
| --- | --- |
|
|
506
|
-
| `isAtBottom` | Boolean readonly cho biết người dùng có đang gần cuối hay không. |
|
|
507
|
-
| `scrollTo(position, options?)` | Cuộn tới offset pixel. |
|
|
508
|
-
| `scrollToIndex(index,
|
|
509
|
-
| `scrollToTop(behavior?)` | Cuộn tới tin nhắn cũ nhất đã tải. |
|
|
510
|
-
| `scrollToBottom(behavior?)` | Cuộn tới tin nhắn mới nhất đã tải. |
|
|
602
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
603
|
+
| --- | --- | --- |
|
|
604
|
+
| `isAtBottom` | Boolean readonly cho biết người dùng có đang gần cuối hay không. | `chat?.isAtBottom` |
|
|
605
|
+
| `scrollTo(position, options?)` | Cuộn tới offset pixel. | `chat?.scrollTo(200, { behavior: 'smooth' })` |
|
|
606
|
+
| `scrollToIndex(index, options?)` | Cuộn tới index tin nhắn bắt đầu từ 0. | `chat?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
607
|
+
| `scrollToTop(behavior?)` | Cuộn tới tin nhắn cũ nhất đã tải. | `chat?.scrollToTop()` |
|
|
608
|
+
| `scrollToBottom(behavior?)` | Cuộn tới tin nhắn mới nhất đã tải. | `chat?.scrollToBottom('smooth')` |
|
|
511
609
|
|
|
512
610
|
### ShortMediaFeed
|
|
513
611
|
|
|
@@ -575,22 +673,39 @@ vậy hãy lưu playback state cần duy trì ở component cha.
|
|
|
575
673
|
| `loading` | `boolean` | `false` | Ngăn request `load-more` bị lặp. |
|
|
576
674
|
| `loadMoreThreshold` | `number` | `2` | Số item còn lại để kích hoạt `load-more`. |
|
|
577
675
|
|
|
578
|
-
#### Slots
|
|
676
|
+
#### Slots
|
|
677
|
+
|
|
678
|
+
| Slot | Scope | Mô tả |
|
|
679
|
+
| --- | --- | --- |
|
|
680
|
+
| `default` | `{ item, index, active }` | Render item đang mount; `active` đánh dấu item đang snap. |
|
|
681
|
+
| `empty` | không có | Render khi `items` rỗng. |
|
|
682
|
+
|
|
683
|
+
#### Events
|
|
579
684
|
|
|
580
|
-
|
|
|
685
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
581
686
|
| --- | --- | --- | --- |
|
|
582
|
-
|
|
|
583
|
-
|
|
|
584
|
-
|
|
|
585
|
-
|
|
|
586
|
-
|
|
|
587
|
-
| Event | `reach-end` | không có | Khi điều hướng chuyển tới item cuối. |
|
|
588
|
-
| Event | `load-more` | không có | Khi số item còn lại đạt `loadMoreThreshold`. |
|
|
687
|
+
| `update:activeIndex` | `number` | Hỗ trợ `v-model:active-index`. | `v-model:active-index="activeIndex"` |
|
|
688
|
+
| `change` | `{ index, item }` | Khi item active thay đổi. | `@change="onChange"` |
|
|
689
|
+
| `reach-start` | không có | Khi điều hướng chuyển tới item đầu. | `@reach-start="onReachStart"` |
|
|
690
|
+
| `reach-end` | không có | Khi điều hướng chuyển tới item cuối. | `@reach-end="onReachEnd"` |
|
|
691
|
+
| `load-more` | không có | Khi số item còn lại đạt `loadMoreThreshold`. | `@load-more="loadMore"` |
|
|
589
692
|
|
|
590
|
-
#### API qua ref
|
|
693
|
+
#### API qua ref
|
|
694
|
+
|
|
695
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
696
|
+
| --- | --- | --- |
|
|
697
|
+
| `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' })` |
|
|
591
698
|
|
|
592
|
-
|
|
593
|
-
|
|
699
|
+
#### Điều khiển bàn phím
|
|
700
|
+
|
|
701
|
+
Các phím sau hoạt động khi viewport có focus:
|
|
702
|
+
|
|
703
|
+
| Phím | Thao tác |
|
|
704
|
+
| --- | --- |
|
|
705
|
+
| `ArrowDown`, `PageDown` | Chuyển tới item tiếp theo. |
|
|
706
|
+
| `ArrowUp`, `PageUp` | Chuyển tới item trước. |
|
|
707
|
+
| `Home` | Chuyển tới vị trí đầu. |
|
|
708
|
+
| `End` | Chuyển tới item cuối. |
|
|
594
709
|
|
|
595
710
|
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
|
|
596
711
|
sổ dữ liệu hợp lý ở component cha. Virtualization giới hạn DOM và media element
|
|
@@ -673,31 +788,74 @@ thường tối đa năm slide được mount ở giữa collection.
|
|
|
673
788
|
| `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. |
|
|
674
789
|
| `autoplayLoop` | `boolean` | `true` | Quay lại slide đầu tiên khi đến cuối. |
|
|
675
790
|
| `pauseOnHover` | `boolean` | `true` | Tạm dừng autoplay khi con trỏ nằm trên carousel. |
|
|
791
|
+
| `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. |
|
|
676
792
|
|
|
677
|
-
####
|
|
793
|
+
#### Cấu hình responsive
|
|
678
794
|
|
|
679
|
-
|
|
795
|
+
Component chọn `breakpoint` lớn nhất không vượt quá chiều rộng container của
|
|
796
|
+
carousel. Nếu không có breakpoint phù hợp, component dùng các prop gốc.
|
|
797
|
+
Trường bị bỏ qua dùng prop gốc, không kế thừa từ breakpoint khác.
|
|
798
|
+
|
|
799
|
+
| Trường | Kiểu | Mặc định | Mô tả |
|
|
680
800
|
| --- | --- | --- | --- |
|
|
681
|
-
|
|
|
682
|
-
|
|
|
683
|
-
|
|
|
684
|
-
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
801
|
+
| `breakpoint` | `number` | bắt buộc | Chiều rộng tối thiểu của container, tính bằng pixel. |
|
|
802
|
+
| `slidesPerView` | `number` | `slidesPerView` | Số slide hiển thị tại breakpoint này. |
|
|
803
|
+
| `slidesToScroll` | `number` | `slidesToScroll` | Số slide di chuyển bởi `next()` và `previous()`. |
|
|
804
|
+
| `gap` | `number` | `gap` | Khoảng cách giữa các slide, tính bằng pixel. |
|
|
805
|
+
|
|
806
|
+
```vue
|
|
807
|
+
<VirtualCarousel
|
|
808
|
+
:items="products"
|
|
809
|
+
:slides-per-view="1"
|
|
810
|
+
:responsive="[
|
|
811
|
+
{ breakpoint: 640, slidesPerView: 2, gap: 12 },
|
|
812
|
+
{ breakpoint: 1024, slidesPerView: 3, slidesToScroll: 2, gap: 16 },
|
|
813
|
+
]"
|
|
814
|
+
>
|
|
815
|
+
<template #default="{ item }"><ProductCard :product="item" /></template>
|
|
816
|
+
</VirtualCarousel>
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
#### Slots
|
|
820
|
+
|
|
821
|
+
| Slot | Scope | Mô tả |
|
|
822
|
+
| --- | --- | --- |
|
|
823
|
+
| `default` | `{ item, index, active, visible }` | Render từng slide đang mount. |
|
|
824
|
+
| `empty` | không có | Render khi `items` rỗng. |
|
|
825
|
+
|
|
826
|
+
#### Events
|
|
827
|
+
|
|
828
|
+
| Event | Payload | Thời điểm emit | Cách dùng |
|
|
829
|
+
| --- | --- | --- | --- |
|
|
830
|
+
| `update:activeIndex` | `number` | Hỗ trợ `v-model:active-index`. | `v-model:active-index="activeIndex"` |
|
|
831
|
+
| `change` | `{ index, item }` | Khi slide đầu tiên trong vùng nhìn thay đổi. | `@change="onChange"` |
|
|
832
|
+
| `reach-start` | không có | Khi điều hướng chuyển tới vị trí đầu. | `@reach-start="onReachStart"` |
|
|
833
|
+
| `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"` |
|
|
834
|
+
| `load-more` | không có | Khi số slide còn lại đạt `loadMoreThreshold`. | `@load-more="loadMore"` |
|
|
835
|
+
|
|
836
|
+
#### API qua ref
|
|
837
|
+
|
|
838
|
+
| Method / trạng thái | Mô tả | Ví dụ trong template |
|
|
839
|
+
| --- | --- | --- |
|
|
840
|
+
| `next(behavior?)` | Tiến thêm `slidesToScroll` slide; mặc định cuộn mượt. | `carousel?.next()` |
|
|
841
|
+
| `previous(behavior?)` | Lùi lại `slidesToScroll` slide. | `carousel?.previous()` |
|
|
842
|
+
| `scrollToIndex(index, options?)` | Đưa index thành slide đầu tiên trong vùng nhìn. | `carousel?.scrollToIndex(10, { behavior: 'smooth' })` |
|
|
843
|
+
| `startAutoplay()` | Bắt đầu hoặc khởi động lại timer khi `autoplay` là true và có item. | `carousel?.startAutoplay()` |
|
|
844
|
+
| `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()` |
|
|
688
845
|
|
|
689
|
-
####
|
|
846
|
+
#### Điều khiển bàn phím
|
|
690
847
|
|
|
691
|
-
|
|
848
|
+
Các phím sau hoạt động khi viewport có focus:
|
|
849
|
+
|
|
850
|
+
| Phím | Thao tác |
|
|
692
851
|
| --- | --- |
|
|
693
|
-
| `
|
|
694
|
-
| `
|
|
695
|
-
| `
|
|
696
|
-
| `
|
|
697
|
-
| `stopAutoplay()` | Dừng quá trình tự động chuyển slide. |
|
|
852
|
+
| `ArrowRight`, `PageDown` | Tiến thêm `slidesToScroll` slide, tương đương `next()`. |
|
|
853
|
+
| `ArrowLeft`, `PageUp` | Lùi lại `slidesToScroll` slide, tương đương `previous()`. |
|
|
854
|
+
| `Home` | Chuyển tới vị trí đầu. |
|
|
855
|
+
| `End` | Chuyển tới vị trí đầu-slide hợp lệ cuối cùng. |
|
|
698
856
|
|
|
699
|
-
|
|
700
|
-
|
|
857
|
+
Smooth scroll ở khoảng cách xa tự chuyển thành tức thời để không lộ khoảng
|
|
858
|
+
trống virtualized.
|
|
701
859
|
|
|
702
860
|
#### Ví dụ autoplay
|
|
703
861
|
|
|
@@ -730,18 +888,134 @@ const products = ref(loadProducts())
|
|
|
730
888
|
</template>
|
|
731
889
|
```
|
|
732
890
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
`autoplay`
|
|
736
|
-
`
|
|
891
|
+
| Thao tác / điều kiện | Kết quả |
|
|
892
|
+
| --- | --- |
|
|
893
|
+
| `autoplay=true` | Tự chạy, kể cả khi collection `items` ban đầu rỗng nhận được dữ liệu. |
|
|
894
|
+
| `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. |
|
|
895
|
+
| `startAutoplay()` | Bắt đầu hoặc khởi động lại timer khi `autoplay` là true và có item. |
|
|
896
|
+
| `autoplay=false` | Tắt tự động chuyển slide cho tới khi prop được bật lại. |
|
|
897
|
+
| `autoplayLoop` | Khi tới cuối, true quay về đầu; false dừng timer. |
|
|
898
|
+
| `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. |
|
|
899
|
+
| Đổi cấu hình autoplay | `autoplayDelay`, `autoplayLoop` và `pauseOnHover` có hiệu lực ngay khi component đang chạy. |
|
|
900
|
+
|
|
901
|
+
### SkeletonLoader
|
|
902
|
+
|
|
903
|
+
`SkeletonLoader` hiển thị khối giữ chỗ với hiệu ứng shimmer. Có thể dùng độc lập
|
|
904
|
+
hoặc trong slot `loading` của danh sách. Component luôn hiển thị khi được mount;
|
|
905
|
+
dùng `v-if` để điều khiển khi dùng độc lập, hoặc để prop `loading` của danh sách
|
|
906
|
+
điều khiển slot.
|
|
907
|
+
|
|
908
|
+
#### Ví dụ
|
|
909
|
+
|
|
910
|
+
Import stylesheet của thư viện một lần như bên dưới hoặc tại entry point của
|
|
911
|
+
ứng dụng. Đặt `loading` thành false khi request dữ liệu hoàn tất.
|
|
912
|
+
|
|
913
|
+
```vue
|
|
914
|
+
<script setup lang="ts">
|
|
915
|
+
import { ref } from 'vue'
|
|
916
|
+
import { SkeletonLoader } from 'vue-virtual-flow'
|
|
917
|
+
import 'vue-virtual-flow/style.css'
|
|
918
|
+
|
|
919
|
+
const loading = ref(true)
|
|
920
|
+
</script>
|
|
921
|
+
|
|
922
|
+
<template>
|
|
923
|
+
<SkeletonLoader
|
|
924
|
+
v-if="loading"
|
|
925
|
+
width="100%"
|
|
926
|
+
:height="120"
|
|
927
|
+
aria-label="Đang tải nội dung…"
|
|
928
|
+
/>
|
|
929
|
+
<p v-else>Nội dung đã sẵn sàng.</p>
|
|
930
|
+
</template>
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
#### Props
|
|
934
|
+
|
|
935
|
+
| Prop | Kiểu | Mặc định | Mô tả |
|
|
936
|
+
| --- | --- | --- | --- |
|
|
937
|
+
| `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'`. |
|
|
938
|
+
| `height` | `number \| string` | `16` | Chiều cao: số tính bằng pixel, chuỗi nhận độ dài CSS. |
|
|
939
|
+
| `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. |
|
|
940
|
+
| `animated` | `boolean` | `true` | Bật hiệu ứng shimmer. |
|
|
941
|
+
| `ariaLabel` | `string` | `'Loading…'` | Nhãn cho trình đọc màn hình của khối có `role="status"`. |
|
|
942
|
+
|
|
943
|
+
```vue
|
|
944
|
+
<SkeletonLoader variant="circle" :width="48" :height="48" />
|
|
945
|
+
<SkeletonLoader variant="rectangle" :height="180" />
|
|
946
|
+
<SkeletonLoader width="60%" :height="20" :animated="false" />
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
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
|
|
950
|
+
skeleton trang trí cùng một thông báo loading chung, truyền `aria-hidden="true"`
|
|
951
|
+
cho từng skeleton để tránh lặp nhãn với trình đọc màn hình.
|
|
952
|
+
|
|
953
|
+
#### Slots, events và API qua ref
|
|
954
|
+
|
|
955
|
+
| API | Hỗ trợ |
|
|
956
|
+
| --- | --- |
|
|
957
|
+
| Slots | Không có. Component render một khối giữ chỗ. |
|
|
958
|
+
| Events | Không có event tùy chỉnh. |
|
|
959
|
+
| Method qua ref | Không có. Điều khiển component bằng props và `v-if`. |
|
|
960
|
+
|
|
961
|
+
#### Thao tác
|
|
962
|
+
|
|
963
|
+
| Thao tác | Cách dùng |
|
|
964
|
+
| --- | --- |
|
|
965
|
+
| Hiện hoặc ẩn | `<SkeletonLoader v-if="loading" />` |
|
|
966
|
+
| Dùng hình tròn | `<SkeletonLoader variant="circle" :width="48" :height="48" />` |
|
|
967
|
+
| Tắt shimmer | `<SkeletonLoader :animated="false" />` |
|
|
968
|
+
| Đặt nhãn cho trình đọc màn hình | `aria-label="Đang tải…"` |
|
|
969
|
+
| Ẩn skeleton trang trí khỏi trình đọc màn hình | `aria-hidden="true"` |
|
|
970
|
+
|
|
971
|
+
#### Dùng trong slot loading
|
|
972
|
+
|
|
973
|
+
Import `SkeletonLoader` cùng với `VirtualList`. Với state `items` và `loading`
|
|
974
|
+
hiện có, thay thông báo loading bằng skeleton:
|
|
975
|
+
|
|
976
|
+
```vue
|
|
977
|
+
<VirtualList :items="items" :loading="loading" :loading-item-size="64">
|
|
978
|
+
<template #default="{ item }"><FeedCard :item="item" /></template>
|
|
979
|
+
<template #loading>
|
|
980
|
+
<SkeletonLoader :height="40" aria-label="Đang tải trang tiếp theo…" />
|
|
981
|
+
</template>
|
|
982
|
+
</VirtualList>
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
| Cấu hình | Cách dùng |
|
|
986
|
+
| --- | --- |
|
|
987
|
+
| Component hỗ trợ slot `loading` | `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`, `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll`. |
|
|
988
|
+
| `loading` trên danh sách | Đặt thành true để hiển thị slot; false để ẩn. |
|
|
989
|
+
| `height` trên skeleton | Đặt chiều cao khối giữ chỗ, ví dụ `40` pixel. |
|
|
990
|
+
| `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. |
|
|
991
|
+
|
|
992
|
+
#### Tùy chỉnh giao diện
|
|
993
|
+
|
|
994
|
+
Đặt các biến CSS sau trên skeleton hoặc phần tử cha:
|
|
995
|
+
|
|
996
|
+
| Biến CSS | Mặc định | Mô tả |
|
|
997
|
+
| --- | --- | --- |
|
|
998
|
+
| `--skeleton-background` | `#e2e8f0` | Màu nền. |
|
|
999
|
+
| `--skeleton-highlight` | `rgb(255 255 255 / 60%)` | Màu vệt sáng của hiệu ứng shimmer. |
|
|
1000
|
+
| `--skeleton-radius` | `8px` | Bán kính bo góc cho variant `rounded`. |
|
|
1001
|
+
|
|
1002
|
+
```vue
|
|
1003
|
+
<SkeletonLoader
|
|
1004
|
+
:height="80"
|
|
1005
|
+
style="--skeleton-background: #334155; --skeleton-highlight: #475569; --skeleton-radius: 12px"
|
|
1006
|
+
/>
|
|
1007
|
+
```
|
|
737
1008
|
|
|
738
1009
|
## Cách dùng chung
|
|
739
1010
|
|
|
740
1011
|
### Chiều cao viewport
|
|
741
1012
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1013
|
+
| Giá trị / component | Cách hoạt động |
|
|
1014
|
+
| --- | --- |
|
|
1015
|
+
| `:height="400"` | Giá trị số được đổi thành pixel. |
|
|
1016
|
+
| `height="60vh"` | Chuỗi chiều cao CSS được giữ nguyên. |
|
|
1017
|
+
| `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`. |
|
|
1018
|
+
| `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | Dùng cửa sổ trình duyệt và không nhận prop `height`. |
|
|
745
1019
|
|
|
746
1020
|
```vue
|
|
747
1021
|
<div class="list-container">
|
|
@@ -758,15 +1032,14 @@ Các component dùng container nhận một số hoặc chuỗi chiều cao CSS.
|
|
|
758
1032
|
}
|
|
759
1033
|
```
|
|
760
1034
|
|
|
761
|
-
Chiều cao phần trăm chỉ hoạt động khi thẻ cha có chiều cao xác định. Bản
|
|
762
|
-
development cảnh báo nếu viewport được tính thành `0px`.
|
|
763
|
-
`WindowDynamicVirtualScroll` và `WindowGirdVirtualScroll` dùng cửa sổ trình
|
|
764
|
-
duyệt nên không có prop `height`.
|
|
765
|
-
|
|
766
1035
|
### Key ổn định cho item
|
|
767
1036
|
|
|
768
|
-
|
|
769
|
-
|
|
1037
|
+
| Cấu hình / dữ liệu | Cách chọn key |
|
|
1038
|
+
| --- | --- |
|
|
1039
|
+
| `:item-key="(item, index) => …"` | Dùng giá trị hàm trả về. |
|
|
1040
|
+
| `item-key="uuid"` | Dùng `item.uuid` hợp lệ; nếu không có, thử `id`, `key`, rồi index. |
|
|
1041
|
+
| Object không truyền `itemKey` | Thử `item.id`, sau đó `item.key`, rồi index. |
|
|
1042
|
+
| Giá trị nguyên thủy không có hàm key | Dùng index làm fallback. |
|
|
770
1043
|
|
|
771
1044
|
```vue
|
|
772
1045
|
<VirtualList :items="users" item-key="uuid" />
|
|
@@ -782,8 +1055,11 @@ lại dữ liệu. Key cũng gắn kết quả đo chiều cao với đúng item
|
|
|
782
1055
|
|
|
783
1056
|
### Tải thêm dữ liệu
|
|
784
1057
|
|
|
785
|
-
|
|
786
|
-
|
|
1058
|
+
| Component | `loadMoreThreshold` | Event |
|
|
1059
|
+
| --- | --- | --- |
|
|
1060
|
+
| `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`, `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | Khoảng cách tới cuối danh sách, tính bằng pixel. | `load-more` |
|
|
1061
|
+
| `ShortMediaFeed` | Số item phía sau item active. | `load-more` |
|
|
1062
|
+
| `VirtualCarousel` | Số slide phía sau nhóm đang hiển thị. | `load-more` |
|
|
787
1063
|
|
|
788
1064
|
```vue
|
|
789
1065
|
<script setup lang="ts">
|
|
@@ -815,14 +1091,20 @@ async function loadMore() {
|
|
|
815
1091
|
</template>
|
|
816
1092
|
```
|
|
817
1093
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
`
|
|
1094
|
+
| Bước / điều kiện | Thao tác / kết quả |
|
|
1095
|
+
| --- | --- |
|
|
1096
|
+
| Nhận `load-more` | Chỉ bắt đầu request tiếp theo khi `loading` là false và `hasMore` là true. |
|
|
1097
|
+
| Request đang chạy | Giữ `loading=true` trong toàn bộ request. |
|
|
1098
|
+
| 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. |
|
|
1099
|
+
| Request hoàn tất | Đặt `loading=false`; dùng `finally` khi request có thể thất bại. |
|
|
1100
|
+
| Trang cuối | Đặt `hasMore=false` để ngừng request tiếp theo. |
|
|
821
1101
|
|
|
822
1102
|
### Kéo để làm mới
|
|
823
1103
|
|
|
824
|
-
|
|
825
|
-
|
|
1104
|
+
| Component | Vị trí bắt đầu thao tác |
|
|
1105
|
+
| --- | --- |
|
|
1106
|
+
| `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll` | Đầu container của component. |
|
|
1107
|
+
| `WindowDynamicVirtualScroll`, `WindowGirdVirtualScroll` | `window.scrollY = 0` |
|
|
826
1108
|
|
|
827
1109
|
```vue
|
|
828
1110
|
<VirtualList
|
|
@@ -839,21 +1121,51 @@ Pull-to-refresh có trên `VirtualList`, `DynamicVirtualScroll`, `VirtualScroll`
|
|
|
839
1121
|
</VirtualList>
|
|
840
1122
|
```
|
|
841
1123
|
|
|
842
|
-
|
|
843
|
-
|
|
1124
|
+
| Bước / trạng thái | Thao tác / kết quả |
|
|
1125
|
+
| --- | --- |
|
|
1126
|
+
| Bật thao tác | `pull-to-refresh` |
|
|
1127
|
+
| 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`. |
|
|
1128
|
+
| Thả sau khi đạt `pullRefreshThreshold` | Emit `refresh` không có payload. |
|
|
1129
|
+
| Xử lý `refresh` | Đặt `refreshing=true` trong lúc tải lại dữ liệu để giữ indicator mở. |
|
|
1130
|
+
| Hoàn tất làm mới | Đặt `refreshing=false` để đóng indicator. |
|
|
844
1131
|
|
|
845
1132
|
## Các type được export
|
|
846
1133
|
|
|
1134
|
+
| Type | Mục đích |
|
|
1135
|
+
| --- | --- |
|
|
1136
|
+
| `ChatVirtualScrollExpose` | API ref và trạng thái `isAtBottom` của chat. |
|
|
1137
|
+
| `ChatVirtualScrollProps<T>` | Props của component chat. |
|
|
1138
|
+
| `DynamicVirtualScrollProps<T>` | Props của danh sách chiều cao động. |
|
|
1139
|
+
| `ItemKey<T>` | Tên thuộc tính hoặc hàm xác định key của item. |
|
|
1140
|
+
| `ResponsiveBreakpoint` | Breakpoint container và cấu hình ghi đè của carousel. |
|
|
1141
|
+
| `ScrollAlignment` | `'start'`, `'center'` hoặc `'end'` khi cuộn tới index trong danh sách. |
|
|
1142
|
+
| `ShortMediaFeedChangeEvent<T>` | Payload event `change` của feed: `{ index, item }`. |
|
|
1143
|
+
| `ShortMediaFeedExpose` | API ref của feed. |
|
|
1144
|
+
| `ShortMediaFeedProps<T>` | Props của feed. |
|
|
1145
|
+
| `SkeletonLoaderProps` | Kích thước, hình dạng, hiệu ứng và nhãn hỗ trợ tiếp cận của skeleton. |
|
|
1146
|
+
| `VirtualCarouselChangeEvent<T>` | Payload event `change` của carousel: `{ index, item }`. |
|
|
1147
|
+
| `VirtualCarouselExpose` | API ref điều hướng và autoplay của carousel. |
|
|
1148
|
+
| `VirtualCarouselProps<T>` | Props của carousel, gồm cấu hình responsive. |
|
|
1149
|
+
| `VirtualListExpose` | Alias của `VirtualScrollExpose`. |
|
|
1150
|
+
| `VirtualListProps<T>` | Alias của `DynamicVirtualScrollProps<T>`. |
|
|
1151
|
+
| `VirtualScrollEvent` | Payload scroll: `{ scrollTop, startIndex, endIndex }`; `endIndex` không bao gồm item tại index đó. |
|
|
1152
|
+
| `VirtualScrollExpose` | API ref chung cho danh sách cố định, động và danh sách/grid cuộn theo cửa sổ. |
|
|
1153
|
+
| `VirtualScrollProps<T>` | Props của danh sách chiều cao cố định. |
|
|
1154
|
+
| `WindowDynamicVirtualScrollProps<T>` | Props của danh sách chiều cao động cuộn theo cửa sổ. |
|
|
1155
|
+
| `WindowGirdVirtualScrollProps<T>` | Props của grid hàng cố định cuộn theo cửa sổ. |
|
|
1156
|
+
|
|
847
1157
|
```ts
|
|
848
1158
|
import type {
|
|
849
1159
|
ChatVirtualScrollExpose,
|
|
850
1160
|
ChatVirtualScrollProps,
|
|
851
1161
|
DynamicVirtualScrollProps,
|
|
852
1162
|
ItemKey,
|
|
1163
|
+
ResponsiveBreakpoint,
|
|
853
1164
|
ScrollAlignment,
|
|
854
1165
|
ShortMediaFeedChangeEvent,
|
|
855
1166
|
ShortMediaFeedExpose,
|
|
856
1167
|
ShortMediaFeedProps,
|
|
1168
|
+
SkeletonLoaderProps,
|
|
857
1169
|
VirtualCarouselChangeEvent,
|
|
858
1170
|
VirtualCarouselExpose,
|
|
859
1171
|
VirtualCarouselProps,
|
|
@@ -869,6 +1181,22 @@ import type {
|
|
|
869
1181
|
|
|
870
1182
|
## Phát triển và phát hành
|
|
871
1183
|
|
|
1184
|
+
| Lệnh / lifecycle | Thao tác |
|
|
1185
|
+
| --- | --- |
|
|
1186
|
+
| `bun install` | Cài đặt dependency. |
|
|
1187
|
+
| `bunx playwright install chromium firefox webkit` | Cài trình duyệt cho smoke test. |
|
|
1188
|
+
| `bun run dev` | Chạy playground. |
|
|
1189
|
+
| `bun run typecheck` | Kiểm tra type TypeScript và Vue. |
|
|
1190
|
+
| `bun run test` | Chạy unit test. |
|
|
1191
|
+
| `bun run test:watch` | Chạy unit test ở chế độ theo dõi thay đổi. |
|
|
1192
|
+
| `bun run build` | Kiểm tra type và build package kèm khai báo type. |
|
|
1193
|
+
| `bun run typecheck:package` | Kiểm tra type phía dùng package ESM và CommonJS sau khi build. |
|
|
1194
|
+
| `bun run lint:package` | Kiểm tra exports và cách đóng gói type sau khi build. |
|
|
1195
|
+
| `bun run test:browser` | Chạy test trình duyệt bằng Playwright. |
|
|
1196
|
+
| `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. |
|
|
1197
|
+
| `npm pack --dry-run` | Kiểm tra các file sẽ được publish. |
|
|
1198
|
+
| `prepublishOnly` | Tự chạy `bun run check` trước khi publish. |
|
|
1199
|
+
|
|
872
1200
|
```bash
|
|
873
1201
|
bun install
|
|
874
1202
|
bunx playwright install chromium firefox webkit
|