@libs-ui/components-scroll-measure-items-direction-vertical 0.2.356-42 → 0.2.357-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/README.md CHANGED
@@ -1,3 +1,419 @@
1
- # scroll-measure-items-direction-vertical
1
+ # @libs-ui/components-scroll-measure-items-direction-vertical
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ > Directive Angular thực hiện virtual scrolling theo chiều dọc với kích thước item động (dynamic height).
4
+
5
+ ## Giới thiệu
6
+
7
+ `LibsUiScrollMeasureItemDirectionVerticalDirective` là một Angular directive cho phép ảo hóa danh sách lớn theo chiều dọc (vertical virtual scrolling). Directive đo chiều cao từng item bất đồng bộ thông qua hàm callback do consumer cung cấp, từ đó chỉ render các item thực sự nằm trong viewport, giúp hiệu năng không bị ảnh hưởng dù list có hàng nghìn phần tử. Khác với các thư viện cố định item height, directive này hỗ trợ các item có chiều cao khác nhau (heterogeneous heights).
8
+
9
+ ## Tính năng
10
+
11
+ - ✅ Virtual scrolling chiều dọc — chỉ render item trong viewport
12
+ - ✅ Hỗ trợ item có chiều cao động (dynamic height) qua hàm đo bất đồng bộ
13
+ - ✅ Tìm kiếm binary search O(log N) cho mọi sự kiện scroll
14
+ - ✅ Buffer item (3 items) phía dưới viewport để tránh trắng màn khi cuộn nhanh
15
+ - ✅ API điều khiển: `scrollInto`, `scrollToPosition`, `scrollToIndex`, `reCalculatorViewPort`
16
+ - ✅ ResizeObserver — tự tính lại viewport khi container thay đổi kích thước
17
+ - ✅ Cleanup tự động qua `DestroyRef` khi directive bị destroy
18
+ - ✅ Standalone directive, không cần NgModule
19
+
20
+ ## Khi nào sử dụng
21
+
22
+ - Danh sách có số lượng item lớn (> 200 phần tử) cần scroll theo chiều dọc
23
+ - Các item trong list có chiều cao khác nhau (chat messages, feed, timeline)
24
+ - Cần điều khiển scroll bằng code: scroll đến item cụ thể, scroll đến vị trí, scroll theo index
25
+ - Bảng dữ liệu, danh sách sản phẩm, log viewer, chat history
26
+
27
+ ## Cài đặt
28
+
29
+ ```bash
30
+ npm install @libs-ui/components-scroll-measure-items-direction-vertical
31
+ ```
32
+
33
+ ## Import
34
+
35
+ ```typescript
36
+ import { LibsUiScrollMeasureItemDirectionVerticalDirective } from '@libs-ui/components-scroll-measure-items-direction-vertical';
37
+ import { IScrollMeasureItemDirectionVerticalFunctionsControl } from '@libs-ui/components-scroll-measure-items-direction-vertical';
38
+ import { IScrollMeasureStoreItemVerticalConvert } from '@libs-ui/components-scroll-measure-items-direction-vertical';
39
+ ```
40
+
41
+ ## Ví dụ sử dụng
42
+
43
+ ### Ví dụ 1 — Danh sách cơ bản với chiều cao cố định
44
+
45
+ ```typescript
46
+ // component.ts
47
+ import { Component, signal } from '@angular/core';
48
+ import {
49
+ LibsUiScrollMeasureItemDirectionVerticalDirective,
50
+ IScrollMeasureItemDirectionVerticalFunctionsControl,
51
+ } from '@libs-ui/components-scroll-measure-items-direction-vertical';
52
+
53
+ interface T_product {
54
+ id: number;
55
+ name: string;
56
+ price: number;
57
+ }
58
+
59
+ @Component({
60
+ selector: 'app-product-list',
61
+ standalone: true,
62
+ imports: [LibsUiScrollMeasureItemDirectionVerticalDirective],
63
+ templateUrl: './product-list.component.html',
64
+ })
65
+ export class ProductListComponent {
66
+ protected items = signal<T_product[]>(
67
+ Array.from({ length: 500 }, (_, i) => ({
68
+ id: i + 1,
69
+ name: `Sản phẩm ${i + 1}`,
70
+ price: (i + 1) * 10000,
71
+ }))
72
+ );
73
+ protected viewPortItems = signal<T_product[]>([]);
74
+ private scrollControl: IScrollMeasureItemDirectionVerticalFunctionsControl | null = null;
75
+
76
+ // Hàm đo chiều cao — phải cộng cả margin/padding nếu có
77
+ protected measureHeight = async (item: T_product): Promise<number> => {
78
+ return 64; // item cao cố định 64px
79
+ };
80
+
81
+ protected handlerViewPortItems(items: T_product[]): void {
82
+ this.viewPortItems.set(items);
83
+ }
84
+
85
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionVerticalFunctionsControl): void {
86
+ this.scrollControl = control;
87
+ }
88
+
89
+ protected handlerScrollToTop(): void {
90
+ this.scrollControl?.scrollToIndex(0);
91
+ }
92
+ }
93
+ ```
94
+
95
+ ```html
96
+ <!-- product-list.component.html -->
97
+ <div
98
+ #scrollContainer
99
+ class="w-full h-[500px] overflow-y-auto border border-gray-200 rounded relative"
100
+ >
101
+ <div
102
+ class="block w-full"
103
+ LibsUiScrollMeasureItemDirectionVerticalDirective
104
+ [elementScroll]="scrollContainer"
105
+ [items]="items()"
106
+ [functionGetHeightItem]="measureHeight"
107
+ (outViewPortItem)="handlerViewPortItems($event)"
108
+ (outFunctionControl)="handlerFunctionControl($event)"
109
+ >
110
+ @for (item of viewPortItems(); track item.id) {
111
+ <div class="flex items-center justify-between px-4 py-3 border-b border-gray-100 h-[64px]">
112
+ <span class="font-medium">{{ item.name }}</span>
113
+ <span class="text-blue-600">{{ item.price | number }} đ</span>
114
+ </div>
115
+ }
116
+ </div>
117
+ </div>
118
+
119
+ <button
120
+ (click)="handlerScrollToTop()"
121
+ class="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
122
+ >
123
+ Lên đầu
124
+ </button>
125
+ ```
126
+
127
+ ---
128
+
129
+ ### Ví dụ 2 — Danh sách với chiều cao động (dynamic height)
130
+
131
+ ```typescript
132
+ // chat-list.component.ts
133
+ import { Component, signal } from '@angular/core';
134
+ import {
135
+ LibsUiScrollMeasureItemDirectionVerticalDirective,
136
+ IScrollMeasureItemDirectionVerticalFunctionsControl,
137
+ } from '@libs-ui/components-scroll-measure-items-direction-vertical';
138
+
139
+ interface T_chatMessage {
140
+ id: number;
141
+ text: string;
142
+ sender: string;
143
+ }
144
+
145
+ @Component({
146
+ selector: 'app-chat-list',
147
+ standalone: true,
148
+ imports: [LibsUiScrollMeasureItemDirectionVerticalDirective],
149
+ templateUrl: './chat-list.component.html',
150
+ })
151
+ export class ChatListComponent {
152
+ protected messages = signal<T_chatMessage[]>([
153
+ { id: 1, sender: 'Alice', text: 'Xin chào!' },
154
+ { id: 2, sender: 'Bob', text: 'Chào bạn! Hôm nay bạn có khỏe không? Mình vừa xem xong bộ phim rất hay, bạn có muốn nghe kể không?' },
155
+ { id: 3, sender: 'Alice', text: 'Mình khỏe, cảm ơn bạn!' },
156
+ // ... nhiều messages hơn
157
+ ]);
158
+ protected viewPortMessages = signal<T_chatMessage[]>([]);
159
+ private scrollControl: IScrollMeasureItemDirectionVerticalFunctionsControl | null = null;
160
+
161
+ // Chiều cao động: tính theo độ dài text (48px base + 20px/100 ký tự)
162
+ // BẮT BUỘC cộng margin: my-[4px] = 8px (4px top + 4px bottom)
163
+ protected measureMessageHeight = async (msg: T_chatMessage): Promise<number> => {
164
+ const lineCount = Math.ceil(msg.text.length / 40);
165
+ return 48 + lineCount * 20 + 8;
166
+ };
167
+
168
+ protected handlerViewPortMessages(items: T_chatMessage[]): void {
169
+ this.viewPortMessages.set(items);
170
+ }
171
+
172
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionVerticalFunctionsControl): void {
173
+ this.scrollControl = control;
174
+ }
175
+
176
+ // Scroll đến tin nhắn mới nhất
177
+ protected handlerScrollToLatest(): void {
178
+ const lastIndex = this.messages().length - 1;
179
+ this.scrollControl?.scrollToIndex(lastIndex);
180
+ }
181
+
182
+ // Scroll đến tin nhắn cụ thể theo object reference
183
+ protected handlerScrollToMessage(msg: T_chatMessage): void {
184
+ this.scrollControl?.scrollInto(msg);
185
+ }
186
+ }
187
+ ```
188
+
189
+ ```html
190
+ <!-- chat-list.component.html -->
191
+ <div class="flex flex-col h-[600px]">
192
+ <div
193
+ #chatContainer
194
+ class="flex-1 overflow-y-auto relative"
195
+ >
196
+ <div
197
+ class="block w-full px-4"
198
+ LibsUiScrollMeasureItemDirectionVerticalDirective
199
+ [elementScroll]="chatContainer"
200
+ [items]="messages()"
201
+ [functionGetHeightItem]="measureMessageHeight"
202
+ (outViewPortItem)="handlerViewPortMessages($event)"
203
+ (outFunctionControl)="handlerFunctionControl($event)"
204
+ >
205
+ @for (msg of viewPortMessages(); track msg.id) {
206
+ <div class="my-[4px] p-3 rounded-lg bg-gray-50 border border-gray-100">
207
+ <span class="text-xs font-semibold text-blue-600">{{ msg.sender }}</span>
208
+ <p class="text-sm text-gray-800 mt-1">{{ msg.text }}</p>
209
+ </div>
210
+ }
211
+ </div>
212
+ </div>
213
+
214
+ <button
215
+ (click)="handlerScrollToLatest()"
216
+ class="mx-4 mb-4 py-2 bg-blue-500 text-white rounded text-sm"
217
+ >
218
+ Tin nhắn mới nhất
219
+ </button>
220
+ </div>
221
+ ```
222
+
223
+ ---
224
+
225
+ ### Ví dụ 3 — Sử dụng `reCalculatorViewPort` khi item thay đổi chiều cao
226
+
227
+ ```typescript
228
+ // expandable-list.component.ts
229
+ import { Component, signal } from '@angular/core';
230
+ import {
231
+ LibsUiScrollMeasureItemDirectionVerticalDirective,
232
+ IScrollMeasureItemDirectionVerticalFunctionsControl,
233
+ } from '@libs-ui/components-scroll-measure-items-direction-vertical';
234
+
235
+ interface T_expandableItem {
236
+ id: number;
237
+ title: string;
238
+ detail: string;
239
+ expanded: boolean;
240
+ }
241
+
242
+ @Component({
243
+ selector: 'app-expandable-list',
244
+ standalone: true,
245
+ imports: [LibsUiScrollMeasureItemDirectionVerticalDirective],
246
+ templateUrl: './expandable-list.component.html',
247
+ })
248
+ export class ExpandableListComponent {
249
+ protected items = signal<T_expandableItem[]>(
250
+ Array.from({ length: 200 }, (_, i) => ({
251
+ id: i + 1,
252
+ title: `Mục ${i + 1}`,
253
+ detail: `Chi tiết cho mục ${i + 1}. Thông tin này xuất hiện khi expand.`,
254
+ expanded: false,
255
+ }))
256
+ );
257
+ protected viewPortItems = signal<T_expandableItem[]>([]);
258
+ private scrollControl: IScrollMeasureItemDirectionVerticalFunctionsControl | null = null;
259
+
260
+ protected measureHeight = async (item: T_expandableItem): Promise<number> => {
261
+ // 48px thu gọn, 96px khi mở rộng, cộng 4px margin
262
+ return item.expanded ? 96 + 4 : 48 + 4;
263
+ };
264
+
265
+ protected handlerViewPortItems(items: T_expandableItem[]): void {
266
+ this.viewPortItems.set(items);
267
+ }
268
+
269
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionVerticalFunctionsControl): void {
270
+ this.scrollControl = control;
271
+ }
272
+
273
+ // Sau khi toggle, gọi reCalculatorViewPort để tính lại heights
274
+ protected handlerToggleItem(item: T_expandableItem): void {
275
+ item.expanded = !item.expanded;
276
+ this.items.set([...this.items()]);
277
+ // Tính lại viewport sau khi chiều cao thay đổi
278
+ this.scrollControl?.reCalculatorViewPort();
279
+ }
280
+ }
281
+ ```
282
+
283
+ ```html
284
+ <!-- expandable-list.component.html -->
285
+ <div
286
+ #listContainer
287
+ class="w-full h-[400px] overflow-y-auto border border-gray-200 rounded relative"
288
+ >
289
+ <div
290
+ class="block w-full"
291
+ LibsUiScrollMeasureItemDirectionVerticalDirective
292
+ [elementScroll]="listContainer"
293
+ [items]="items()"
294
+ [functionGetHeightItem]="measureHeight"
295
+ (outViewPortItem)="handlerViewPortItems($event)"
296
+ (outFunctionControl)="handlerFunctionControl($event)"
297
+ >
298
+ @for (item of viewPortItems(); track item.id) {
299
+ <div
300
+ class="my-[2px] px-4 border border-gray-100 rounded cursor-pointer"
301
+ [class.h-[48px]]="!item.expanded"
302
+ [class.h-[96px]]="item.expanded"
303
+ (click)="handlerToggleItem(item)"
304
+ >
305
+ <div class="flex items-center h-[48px]">
306
+ <span class="font-medium">{{ item.title }}</span>
307
+ <span class="ml-auto text-gray-400 text-xs">{{ item.expanded ? '▲' : '▼' }}</span>
308
+ </div>
309
+ @if (item.expanded) {
310
+ <p class="text-sm text-gray-600 pb-2">{{ item.detail }}</p>
311
+ }
312
+ </div>
313
+ }
314
+ </div>
315
+ </div>
316
+ ```
317
+
318
+ ## @Input()
319
+
320
+ | Input | Type | Default | Mô tả | Ví dụ |
321
+ |---|---|---|---|---|
322
+ | `elementScroll` | `HTMLElement` | **required** | Element chứa thanh cuộn dọc (`overflow-y: auto/scroll`). Thường là outer container được gắn template reference `#scrollContainer`. | `[elementScroll]="scrollContainer"` |
323
+ | `items` | `Array<any>` | **required** | Toàn bộ danh sách dữ liệu gốc. Directive đọc signal này để tính toán viewport và tái tính lại khi array thay đổi. | `[items]="items()"` |
324
+ | `functionGetHeightItem` | `(item: any) => Promise<number>` | **required** | Hàm bất đồng bộ nhận vào một item và trả về chiều cao (px) của item đó, bao gồm cả margin trên/dưới. Được gọi song song cho toàn bộ mảng để tối ưu tốc độ. | `[functionGetHeightItem]="measureHeight"` |
325
+
326
+ ## @Output()
327
+
328
+ | Output | Type | Mô tả | Handler TS | Binding HTML |
329
+ |---|---|---|---|---|
330
+ | `(outViewPortItem)` | `Array<any>` | Emit danh sách items cần render trong viewport hiện tại (bao gồm buffer 3 items phía dưới). Component con phải lưu vào signal riêng và dùng trong `@for`. | `handlerViewPortItems(items: any[]): void { this.viewPortItems.set(items); }` | `(outViewPortItem)="handlerViewPortItems($event)"` |
331
+ | `(outFunctionControl)` | `IScrollMeasureItemDirectionVerticalFunctionsControl` | Emit object chứa đầy đủ API điều khiển scroll. Emit một lần sau `ngAfterViewInit`. | `handlerFunctionControl(ctrl: IScrollMeasureItemDirectionVerticalFunctionsControl): void { this.scrollControl = ctrl; }` | `(outFunctionControl)="handlerFunctionControl($event)"` |
332
+ | `(outDivVirtual)` | `HTMLDivElement` | Emit element ảo (absolute, visibility hidden) được inject vào scroll container để giữ tổng chiều cao scroll đúng. Ít khi cần dùng trực tiếp. | `handlerDivVirtual(el: HTMLDivElement): void { this.virtualEl = el; }` | `(outDivVirtual)="handlerDivVirtual($event)"` |
333
+ | `(outPaddingTop)` | `number` | Emit giá trị `padding-top` (px) đang được áp dụng lên inner container để bù khoảng trống các item bị ẩn phía trên viewport. | `handlerPaddingTop(value: number): void { this.currentPaddingTop = value; }` | `(outPaddingTop)="handlerPaddingTop($event)"` |
334
+
335
+ ## FunctionsControl API
336
+
337
+ Nhận đối tượng `IScrollMeasureItemDirectionVerticalFunctionsControl` qua output `(outFunctionControl)` để điều khiển scroll bằng code:
338
+
339
+ ```typescript
340
+ import { IScrollMeasureItemDirectionVerticalFunctionsControl } from '@libs-ui/components-scroll-measure-items-direction-vertical';
341
+
342
+ private scrollControl: IScrollMeasureItemDirectionVerticalFunctionsControl | null = null;
343
+ ```
344
+
345
+ | Method | Signature | Mô tả |
346
+ |---|---|---|
347
+ | `scrollInto` | `(item: any) => Promise<void>` | Scroll container đến vị trí của item theo object reference. Item phải tồn tại trong `items()`. |
348
+ | `scrollToPosition` | `(position: number) => Promise<void>` | Scroll container đến vị trí `scrollTop` tuyệt đối (px) chỉ định. |
349
+ | `scrollToIndex` | `(index: number) => Promise<void>` | Scroll container đến item tại index chỉ định trong mảng `items()`. |
350
+ | `reCalculatorViewPort` | `() => Promise<void>` | Tính lại toàn bộ chiều cao và viewport. Gọi sau khi chiều cao item thay đổi (expand/collapse, nội dung thay đổi). |
351
+ | `getViewPortItems` | `() => Array<any>` | Trả về mảng items hiện đang trong viewport (đồng bộ). |
352
+
353
+ ```typescript
354
+ // Cách sử dụng FunctionsControl
355
+ protected handlerScrollToTop(): void {
356
+ this.scrollControl?.scrollToIndex(0);
357
+ }
358
+
359
+ protected handlerScrollToItem(item: T_product): void {
360
+ this.scrollControl?.scrollInto(item);
361
+ }
362
+
363
+ protected handlerScrollToMiddle(): void {
364
+ const middleIndex = Math.floor(this.items().length / 2);
365
+ this.scrollControl?.scrollToIndex(middleIndex);
366
+ }
367
+
368
+ protected handlerRefreshHeights(): void {
369
+ this.scrollControl?.reCalculatorViewPort();
370
+ }
371
+ ```
372
+
373
+ ## Types & Interfaces
374
+
375
+ ```typescript
376
+ import {
377
+ IScrollMeasureItemDirectionVerticalFunctionsControl,
378
+ IScrollMeasureStoreItemVerticalConvert,
379
+ } from '@libs-ui/components-scroll-measure-items-direction-vertical';
380
+
381
+ // API điều khiển scroll — nhận qua (outFunctionControl)
382
+ interface IScrollMeasureItemDirectionVerticalFunctionsControl {
383
+ scrollInto: (item: any) => Promise<void>;
384
+ scrollToPosition: (position: number) => Promise<void>;
385
+ scrollToIndex: (index: number) => Promise<void>;
386
+ reCalculatorViewPort: () => Promise<void>;
387
+ getViewPortItems: () => Array<any>;
388
+ }
389
+
390
+ // Cấu trúc nội bộ của item đã được đo — dùng khi cần truy cập tọa độ
391
+ interface IScrollMeasureStoreItemVerticalConvert {
392
+ ref: any; // Tham chiếu item gốc từ mảng items
393
+ itemHeight: number; // Chiều cao đã đo (px)
394
+ start: number; // Tọa độ Y bắt đầu của item (px từ đỉnh container)
395
+ end: number; // Tọa độ Y kết thúc của item (px từ đỉnh container)
396
+ }
397
+ ```
398
+
399
+ ## Lưu ý quan trọng
400
+
401
+ ⚠️ **elementScroll là outer container**: Truyền `[elementScroll]` là element có `overflow-y: auto/scroll`, không phải element được gắn directive. Directive gắn vào inner container (div wrapper bên trong), còn `elementScroll` là outer div chứa thanh cuộn.
402
+
403
+ ⚠️ **Inner container phải là `block`**: Element được gắn directive (inner container) PHẢI có class `block` hoặc `display: block`. Directive tự thêm `position: relative` và điều chỉnh `padding-top` lên element này, nên layout `flex` hay `grid` trực tiếp trên element đó sẽ gây sai vị trí.
404
+
405
+ ⚠️ **Chiều cao trong `functionGetHeightItem` phải bao gồm margin**: Nếu item có `my-[4px]` (4px top + 4px bottom = 8px), cộng 8px vào giá trị trả về. Ví dụ: item cao 60px + margin 8px → trả về `68`.
406
+
407
+ ⚠️ **Gọi `reCalculatorViewPort` sau khi item thay đổi chiều cao**: Khi expand/collapse item hoặc nội dung thay đổi chiều cao, phải gọi `scrollControl.reCalculatorViewPort()` để directive tính lại. Chỉ thay đổi `items()` signal không đủ vì directive chỉ đo lại heights khi array reference thay đổi.
408
+
409
+ ⚠️ **Không dùng `track $index` trong `@for`**: Theo coding convention dự án, bắt buộc dùng `track item.id` hoặc `track item` (track object reference). Dùng `track $index` gây bug re-render lộn xộn với virtual scroll.
410
+
411
+ ⚠️ **`viewPortItems` signal phải được khai báo riêng**: Directive không quản lý state của consumer. Consumer phải tự khai báo `protected viewPortItems = signal<T[]>([])` và cập nhật trong handler `(outViewPortItem)`.
412
+
413
+ ## Demo
414
+
415
+ ```bash
416
+ npx nx serve core-ui
417
+ ```
418
+
419
+ Truy cập: http://localhost:4500/scroll-measure-items/direction-vertical
@@ -1 +1 @@
1
- {"version":3,"file":"libs-ui-components-scroll-measure-items-direction-vertical.mjs","sources":["../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/direction-vertical.directive.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/interfaces/direction-vertical.interface.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/libs-ui-components-scroll-measure-items-direction-vertical.ts"],"sourcesContent":["// CONVENTION-EXCEPT: any — Generic directive works with unknown item types from consumer\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { AfterViewInit, DestroyRef, Directive, ElementRef, effect, inject, input, output, signal, untracked } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { fromEvent } from 'rxjs';\nimport { IScrollMeasureItemDirectionVerticalFunctionsControl, IScrollMeasureStoreItemVerticalConvert } from './interfaces/direction-vertical.interface';\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[LibsUiScrollMeasureItemDirectionVerticalDirective]',\n standalone: true,\n})\nexport class LibsUiScrollMeasureItemDirectionVerticalDirective implements AfterViewInit {\n // #region PROPERTY\n /** Số item buffer render thêm sau viewport để tránh trắng vùng khi scroll. */\n private readonly bufferAhead = 3;\n\n private readonly viewPortItems = signal<Array<any>>([]);\n private readonly start = signal<number>(0);\n private readonly end = signal<number>(0);\n private readonly storeItems = signal<Array<IScrollMeasureStoreItemVerticalConvert>>([]);\n private readonly divVirtual = document.createElement('div');\n private resizeObserver?: ResizeObserver;\n /** Tham chiếu setTimeout của ngAfterViewInit để clear khi destroy — chặn ResizeObserver mồ côi. */\n private timerAfterViewInit?: number;\n /** Token nhận dạng lần effect chạy hiện tại. Async đo height xong sẽ bỏ kết quả nếu token đổi. */\n private executionToken = 0;\n /** Style + DOM hook của divVirtual chỉ cần khởi tạo một lần cho cả vòng đời directive. */\n private divVirtualInitialized = false;\n\n // #region INPUT\n readonly elementScroll = input.required<HTMLElement>();\n readonly functionGetHeightItem = input.required<(item: any) => Promise<number>>();\n readonly items = input.required<Array<any>>();\n\n // #region OUTPUT\n readonly outViewPortItem = output<Array<any>>();\n readonly outFunctionControl = output<IScrollMeasureItemDirectionVerticalFunctionsControl>();\n readonly outDivVirtual = output<HTMLDivElement>();\n readonly outPaddingTop = output<number>();\n\n // #region INJECT\n private readonly elementRef = inject(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n\n constructor() {\n effect(() => {\n const items = this.items();\n const myToken = ++this.executionToken;\n untracked(async () => {\n const scrollEl = this.ScrollContainer;\n const functionGetHeightItem = this.functionGetHeightItem();\n if (!scrollEl || !functionGetHeightItem) {\n return;\n }\n\n // Đo height tất cả item song song — rút ngắn thời gian cho list lớn.\n const heights = await Promise.all(items.map((it) => functionGetHeightItem(it)));\n // Bị huỷ vì có lượt items() mới chạy sau — bỏ kết quả cũ tránh đè state.\n if (myToken !== this.executionToken) {\n return;\n }\n\n this.start.set(0);\n const scrollTop = scrollEl.scrollTop;\n const containerHeight = scrollEl.getBoundingClientRect().height;\n let height = 0;\n let endLocal = 0;\n const newItems: Array<IScrollMeasureStoreItemVerticalConvert> = [];\n\n for (let i = 0; i < items.length; i++) {\n const itemHeight = heights[i];\n const newItem: IScrollMeasureStoreItemVerticalConvert = { ref: items[i], itemHeight, start: height, end: 0 };\n height += itemHeight;\n newItem.end = height;\n if (newItem.end <= containerHeight + scrollTop) {\n endLocal = i;\n }\n newItems.push(newItem);\n }\n // Set end một lần thay vì ghi mỗi vòng lặp — tránh notify consumer N lần.\n this.end.set(endLocal);\n this.storeItems.set(newItems);\n\n // Khởi tạo style + DOM hook của divVirtual đúng một lần.\n if (!this.divVirtualInitialized) {\n this.divVirtual.style.position = 'absolute';\n this.divVirtual.style.top = '0px';\n this.divVirtual.style.left = '0px';\n this.divVirtual.style.width = '1px';\n this.divVirtual.style.visibility = 'hidden'; // Đảm bảo an toàn không hiển thị\n this.elementRef.nativeElement.classList.add('relative');\n this.elementRef.nativeElement.classList.remove('h-full');\n this.elementRef.nativeElement.style.paddingTop = '0px';\n scrollEl.append(this.divVirtual);\n this.divVirtualInitialized = true;\n this.outDivVirtual.emit(this.divVirtual);\n }\n // Height tổng thể đổi theo dataset → cần update mỗi lượt.\n this.divVirtual.style.height = `${height}px`;\n\n // containerHeight lúc đầu có thể = 0 (DOM chưa layout), khiến end giữ\n // nguyên 0 và chỉ render bufferAhead item đầu. Re-đo bằng handlerScroll sau khi vòng\n // lặp await xong (layout thường đã hoàn tất) để end ra đúng giá trị.\n this.handlerScroll();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n clearTimeout(this.timerAfterViewInit);\n this.divVirtual.remove();\n this.resizeObserver?.disconnect();\n });\n }\n\n ngAfterViewInit() {\n this.timerAfterViewInit = setTimeout(() => {\n fromEvent<Event>(this.ScrollContainer, 'scroll')\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.handlerScroll());\n this.outFunctionControl.emit(this.FunctionsControl);\n\n // Re-tính viewport sau khi container có kích thước thật.\n // Lần đầu render, getBoundingClientRect().height có thể = 0 nên end giữ giá trị 0,\n // chỉ hiển thị bufferAhead item đầu. ResizeObserver fire ngay khi observe và mỗi lần\n // container đổi size sẽ kéo end về đúng giá trị.\n this.resizeObserver = new ResizeObserver(() => this.handlerScroll());\n this.resizeObserver.observe(this.ScrollContainer);\n });\n }\n\n // #region PUBLIC API\n public get FunctionsControl(): IScrollMeasureItemDirectionVerticalFunctionsControl {\n return {\n scrollInto: this.scrollInto.bind(this),\n scrollToPosition: this.scrollToPosition.bind(this),\n scrollToIndex: this.scrollToIndex.bind(this),\n reCalculatorViewPort: this.reCalculatorViewPort.bind(this),\n getViewPortItems: () => this.viewPortItems(),\n };\n }\n\n // #region PRIVATE METHODS\n private get ScrollContainer(): HTMLElement {\n return this.elementScroll() || this.elementRef.nativeElement;\n }\n\n private findStartIndex(store: Array<IScrollMeasureStoreItemVerticalConvert>, scrollTop: number): number {\n let low = 0;\n let high = store.length - 1;\n let ans = store.length - 1; // Default to last item if scrolled past bottom (rubber band effect)\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n if (store[mid].end >= scrollTop) {\n ans = mid;\n high = mid - 1; // Try to find earlier\n } else {\n low = mid + 1;\n }\n }\n return ans;\n }\n\n private findEndIndex(store: Array<IScrollMeasureStoreItemVerticalConvert>, scrollBottom: number): number {\n let low = 0;\n let high = store.length - 1;\n let ans = 0; // Default to first item if rubber-banded above the top\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n if (store[mid].start <= scrollBottom) {\n ans = mid;\n low = mid + 1; // Try to find later\n } else {\n high = mid - 1;\n }\n }\n return ans;\n }\n\n private handlerScroll() {\n const store = this.storeItems();\n if (!store?.length) {\n return;\n }\n const scrollEl = this.ScrollContainer;\n const scrollTop = scrollEl.scrollTop;\n const containerHeight = scrollEl.getBoundingClientRect().height;\n\n // Utilize Binary Search for optimal O(log N) discovery\n const start = this.findStartIndex(store, scrollTop);\n const end = this.findEndIndex(store, scrollTop + containerHeight);\n\n if (end !== this.end()) {\n this.end.set(end);\n }\n\n if (start !== this.start()) {\n this.start.set(start);\n // Binary search guarantees `start` là index hợp lệ trong store.\n const paddingTop = store[start].start;\n this.elementRef.nativeElement.style.paddingTop = `${paddingTop}px`;\n this.outPaddingTop.emit(paddingTop);\n }\n this.viewPortItems.set(store.slice(this.start(), this.end() + this.bufferAhead).map((item) => item.ref));\n this.outViewPortItem.emit(this.viewPortItems());\n }\n\n private async scrollInto(itemScroll: any) {\n const itemFound = this.storeItems().find((item) => item.ref === itemScroll);\n if (!itemFound) {\n return;\n }\n this.ScrollContainer.scrollTop = itemFound.start;\n }\n\n private async scrollToPosition(position: number) {\n this.ScrollContainer.scrollTop = position;\n }\n\n private async scrollToIndex(index: number) {\n const itemOfIndex = this.storeItems()[index];\n if (!itemOfIndex) {\n return;\n }\n this.ScrollContainer.scrollTop = itemOfIndex.start;\n }\n\n private async reCalculatorViewPort() {\n const items = this.storeItems();\n const fn = this.functionGetHeightItem();\n // Đo song song để rút ngắn thời gian thay vì await tuần tự.\n const heights = await Promise.all(items.map((it) => fn(it.ref)));\n\n let height = 0;\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const itemHeight = heights[i];\n item.itemHeight = itemHeight;\n item.start = height;\n height += itemHeight;\n item.end = height;\n }\n\n this.storeItems.set([...items]);\n this.divVirtual.style.height = `${height}px`;\n this.handlerScroll();\n }\n}\n","// CONVENTION-EXCEPT: any — Generic data structures from consumers\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface IScrollMeasureStoreItemVerticalConvert {\n ref: any;\n itemHeight: number;\n start: number; // Tọa độ Y (px) bắt đầu của item\n end: number; // Tọa độ Y (px) kết thúc của item\n}\n\nexport interface IScrollMeasureItemDirectionVerticalFunctionsControl {\n scrollInto: (item: any) => Promise<void>;\n scrollToPosition: (position: number) => Promise<void>;\n scrollToIndex: (index: number) => Promise<void>;\n reCalculatorViewPort: () => Promise<void>;\n getViewPortItems: () => Array<any>;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"libs-ui-components-scroll-measure-items-direction-vertical.mjs","sources":["../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/direction-vertical.directive.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/interfaces/direction-vertical.interface.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-vertical/src/libs-ui-components-scroll-measure-items-direction-vertical.ts"],"sourcesContent":["// CONVENTION-EXCEPT: any — Generic directive works with unknown item types from consumer\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { AfterViewInit, DestroyRef, Directive, ElementRef, effect, inject, input, output, signal, untracked } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { fromEvent } from 'rxjs';\nimport { IScrollMeasureItemDirectionVerticalFunctionsControl, IScrollMeasureStoreItemVerticalConvert } from './interfaces/direction-vertical.interface';\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[LibsUiScrollMeasureItemDirectionVerticalDirective]',\n standalone: true,\n})\nexport class LibsUiScrollMeasureItemDirectionVerticalDirective implements AfterViewInit {\n // #region PROPERTY\n /** Số item buffer render thêm sau viewport để tránh trắng vùng khi scroll. */\n private readonly bufferAhead = 3;\n\n private readonly viewPortItems = signal<Array<any>>([]);\n private readonly start = signal<number>(0);\n private readonly end = signal<number>(0);\n private readonly storeItems = signal<Array<IScrollMeasureStoreItemVerticalConvert>>([]);\n private readonly divVirtual = document.createElement('div');\n private resizeObserver?: ResizeObserver;\n /** Tham chiếu setTimeout của ngAfterViewInit để clear khi destroy — chặn ResizeObserver mồ côi. */\n private timerAfterViewInit?: number;\n /** Token nhận dạng lần effect chạy hiện tại. Async đo height xong sẽ bỏ kết quả nếu token đổi. */\n private executionToken = 0;\n /** Style + DOM hook của divVirtual chỉ cần khởi tạo một lần cho cả vòng đời directive. */\n private divVirtualInitialized = false;\n\n // #region INPUT\n readonly elementScroll = input.required<HTMLElement>();\n readonly functionGetHeightItem = input.required<(item: any) => Promise<number>>();\n readonly items = input.required<Array<any>>();\n\n // #region OUTPUT\n readonly outViewPortItem = output<Array<any>>();\n readonly outFunctionControl = output<IScrollMeasureItemDirectionVerticalFunctionsControl>();\n readonly outDivVirtual = output<HTMLDivElement>();\n readonly outPaddingTop = output<number>();\n\n // #region INJECT\n private readonly elementRef = inject(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n\n constructor() {\n effect(() => {\n const items = this.items();\n const myToken = ++this.executionToken;\n untracked(async () => {\n const scrollEl = this.ScrollContainer;\n const functionGetHeightItem = this.functionGetHeightItem();\n if (!scrollEl || !functionGetHeightItem) {\n return;\n }\n\n // Đo height tất cả item song song — rút ngắn thời gian cho list lớn.\n const heights = await Promise.all(items.map((it) => functionGetHeightItem(it)));\n // Bị huỷ vì có lượt items() mới chạy sau — bỏ kết quả cũ tránh đè state.\n if (myToken !== this.executionToken) {\n return;\n }\n\n this.start.set(0);\n const scrollTop = scrollEl.scrollTop;\n const containerHeight = scrollEl.getBoundingClientRect().height;\n let height = 0;\n let endLocal = 0;\n const newItems: Array<IScrollMeasureStoreItemVerticalConvert> = [];\n\n for (let i = 0; i < items.length; i++) {\n const itemHeight = heights[i];\n const newItem: IScrollMeasureStoreItemVerticalConvert = { ref: items[i], itemHeight, start: height, end: 0 };\n height += itemHeight;\n newItem.end = height;\n if (newItem.end <= containerHeight + scrollTop) {\n endLocal = i;\n }\n newItems.push(newItem);\n }\n // Set end một lần thay vì ghi mỗi vòng lặp — tránh notify consumer N lần.\n this.end.set(endLocal);\n this.storeItems.set(newItems);\n\n // Khởi tạo style + DOM hook của divVirtual đúng một lần.\n if (!this.divVirtualInitialized) {\n this.divVirtual.style.position = 'absolute';\n this.divVirtual.style.top = '0px';\n this.divVirtual.style.left = '0px';\n this.divVirtual.style.width = '1px';\n this.divVirtual.style.visibility = 'hidden'; // Đảm bảo an toàn không hiển thị\n this.elementRef.nativeElement.classList.add('relative');\n this.elementRef.nativeElement.classList.remove('h-full');\n this.elementRef.nativeElement.style.paddingTop = '0px';\n scrollEl.append(this.divVirtual);\n this.divVirtualInitialized = true;\n this.outDivVirtual.emit(this.divVirtual);\n }\n // Height tổng thể đổi theo dataset → cần update mỗi lượt.\n this.divVirtual.style.height = `${height}px`;\n\n // containerHeight lúc đầu có thể = 0 (DOM chưa layout), khiến end giữ\n // nguyên 0 và chỉ render bufferAhead item đầu. Re-đo bằng handlerScroll sau khi vòng\n // lặp await xong (layout thường đã hoàn tất) để end ra đúng giá trị.\n this.handlerScroll();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n clearTimeout(this.timerAfterViewInit);\n this.divVirtual.remove();\n this.resizeObserver?.disconnect();\n });\n }\n\n ngAfterViewInit() {\n this.timerAfterViewInit = setTimeout(() => {\n fromEvent<Event>(this.ScrollContainer, 'scroll')\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.handlerScroll());\n this.outFunctionControl.emit(this.FunctionsControl);\n\n // Re-tính viewport sau khi container có kích thước thật.\n // Lần đầu render, getBoundingClientRect().height có thể = 0 nên end giữ giá trị 0,\n // chỉ hiển thị bufferAhead item đầu. ResizeObserver fire ngay khi observe và mỗi lần\n // container đổi size sẽ kéo end về đúng giá trị.\n this.resizeObserver = new ResizeObserver(() => this.handlerScroll());\n this.resizeObserver.observe(this.ScrollContainer);\n });\n }\n\n // #region PUBLIC API\n public get FunctionsControl(): IScrollMeasureItemDirectionVerticalFunctionsControl {\n return {\n scrollInto: this.scrollInto.bind(this),\n scrollToPosition: this.scrollToPosition.bind(this),\n scrollToIndex: this.scrollToIndex.bind(this),\n reCalculatorViewPort: this.reCalculatorViewPort.bind(this),\n getViewPortItems: () => this.viewPortItems(),\n };\n }\n\n // #region PRIVATE METHODS\n private get ScrollContainer(): HTMLElement {\n return this.elementScroll() || this.elementRef.nativeElement;\n }\n\n private findStartIndex(store: Array<IScrollMeasureStoreItemVerticalConvert>, scrollTop: number): number {\n let low = 0;\n let high = store.length - 1;\n let ans = store.length - 1; // Default to last item if scrolled past bottom (rubber band effect)\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n if (store[mid].end >= scrollTop) {\n ans = mid;\n high = mid - 1; // Try to find earlier\n } else {\n low = mid + 1;\n }\n }\n return ans;\n }\n\n private findEndIndex(store: Array<IScrollMeasureStoreItemVerticalConvert>, scrollBottom: number): number {\n let low = 0;\n let high = store.length - 1;\n let ans = 0; // Default to first item if rubber-banded above the top\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n if (store[mid].start <= scrollBottom) {\n ans = mid;\n low = mid + 1; // Try to find later\n } else {\n high = mid - 1;\n }\n }\n return ans;\n }\n\n private handlerScroll() {\n const store = this.storeItems();\n if (!store?.length) {\n return;\n }\n const scrollEl = this.ScrollContainer;\n const scrollTop = scrollEl.scrollTop;\n const containerHeight = scrollEl.getBoundingClientRect().height;\n\n // Utilize Binary Search for optimal O(log N) discovery\n const start = this.findStartIndex(store, scrollTop);\n const end = this.findEndIndex(store, scrollTop + containerHeight);\n\n if (end !== this.end()) {\n this.end.set(end);\n }\n\n if (start !== this.start()) {\n this.start.set(start);\n // Binary search guarantees `start` là index hợp lệ trong store.\n const paddingTop = store[start].start;\n this.elementRef.nativeElement.style.paddingTop = `${paddingTop}px`;\n this.outPaddingTop.emit(paddingTop);\n }\n this.viewPortItems.set(store.slice(this.start(), this.end() + this.bufferAhead).map((item) => item.ref));\n this.outViewPortItem.emit(this.viewPortItems());\n }\n\n private async scrollInto(itemScroll: any) {\n const itemFound = this.storeItems().find((item) => item.ref === itemScroll);\n if (!itemFound) {\n return;\n }\n this.ScrollContainer.scrollTop = itemFound.start;\n }\n\n private async scrollToPosition(position: number) {\n this.ScrollContainer.scrollTop = position;\n }\n\n private async scrollToIndex(index: number) {\n const itemOfIndex = this.storeItems()[index];\n if (!itemOfIndex) {\n return;\n }\n this.ScrollContainer.scrollTop = itemOfIndex.start;\n }\n\n private async reCalculatorViewPort() {\n const items = this.storeItems();\n const fn = this.functionGetHeightItem();\n // Đo song song để rút ngắn thời gian thay vì await tuần tự.\n const heights = await Promise.all(items.map((it) => fn(it.ref)));\n\n let height = 0;\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const itemHeight = heights[i];\n item.itemHeight = itemHeight;\n item.start = height;\n height += itemHeight;\n item.end = height;\n }\n\n this.storeItems.set([...items]);\n this.divVirtual.style.height = `${height}px`;\n this.handlerScroll();\n }\n}\n","// CONVENTION-EXCEPT: any — Generic data structures from consumers\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface IScrollMeasureStoreItemVerticalConvert {\n ref: any;\n itemHeight: number;\n start: number; // Tọa độ Y (px) bắt đầu của item\n end: number; // Tọa độ Y (px) kết thúc của item\n}\n\nexport interface IScrollMeasureItemDirectionVerticalFunctionsControl {\n scrollInto: (item: any) => Promise<void>;\n scrollToPosition: (position: number) => Promise<void>;\n scrollToIndex: (index: number) => Promise<void>;\n reCalculatorViewPort: () => Promise<void>;\n getViewPortItems: () => Array<any>;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libs-ui/components-scroll-measure-items-direction-vertical",
3
- "version": "0.2.356-42",
3
+ "version": "0.2.357-0",
4
4
  "peerDependencies": {
5
5
  "@angular/core": "^18.2.0",
6
6
  "rxjs": "~7.8.0"