@libs-ui/components-scroll-measure-items-direction-horizontal 0.2.356-41 → 0.2.356-43

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,111 +1,393 @@
1
1
  # @libs-ui/components-scroll-measure-items-direction-horizontal
2
2
 
3
- > Directive hỗ trợ ảo hóa danh sách (virtual scrolling) theo chiều ngang với kích thước item động (dynamic width).
3
+ > Directive ảo hóa danh sách theo chiều ngang (horizontal virtual scrolling) với kích thước item động (dynamic width).
4
4
 
5
5
  ## Giới thiệu
6
6
 
7
- `LibsUiScrollMeasureItemDirectionHorizontalDirective` là một directive giúp tối ưu hóa hiệu năng khi render danh sách lớn các item theo chiều ngang. chỉ render các item đang nằm trong vùng nhìn thấy (viewport) sử dụng các element ảo để duy trì kích thước scroll, đồng thời hỗ trợ các hàm điều khiển scroll mạnh mẽ.
7
+ `LibsUiScrollMeasureItemDirectionHorizontalDirective` là directive Angular giúp tối ưu hiệu năng khi render danh sách lớn theo chiều ngang. Thay render toàn bộ DOM, directive chỉ giữ trong DOM các item đang nằm trong viewport (cộng thêm một số buffer), đồng thời dùng một element ảo để duy trì kích thước scroll chính xác. Directive hỗ trợ item có chiều rộng không đồng đều thông qua hàm đo width bất đồng bộ do consumer cung cấp.
8
8
 
9
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - ✅ **Virtual Scrolling**: Chỉ render các item trong viewport, giúp giảm số lượng DOM node.
12
- - ✅ **Dynamic Width**: Hỗ trợ item có kích thước thay đổi (không cố định) thông qua hàm đo kích thước bất đồng bộ.
13
- - ✅ **Scroll Control API**: Cung cấp các phương thức `scrollToIndex`, `scrollToPosition`, `scrollInto` item cụ thể.
14
- - ✅ **Performance**: Tối ưu hóa cho danh sách lớn (hàng nghìn item).
11
+ - ✅ **Virtual Scrolling chiều ngang**: Chỉ render item trong vùng nhìn thấy, giảm số lượng DOM node đáng kể.
12
+ - ✅ **Dynamic Width**: Hỗ trợ mỗi item có chiều rộng khác nhau thông qua hàm `functionGetWidthItem` bất đồng bộ.
13
+ - ✅ **Buffer ahead**: Tự động render thêm 3 item ngoài viewport để tránh khoảng trắng khi scroll nhanh.
14
+ - ✅ **Scroll Control API**: Cung cấp `scrollInto`, `scrollToIndex`, `scrollToPosition`, `reCalculatorViewPort` qua output `outFunctionControl`.
15
+ - ✅ **ResizeObserver tích hợp**: Tự động tính lại viewport khi kích thước container thay đổi.
16
+ - ✅ **Đo width song song**: Dùng `Promise.all` để đo width toàn bộ item cùng lúc — tối ưu cho list hàng nghìn cột.
17
+ - ✅ **Tự cleanup**: Hủy observer, xóa element ảo và timer khi directive bị destroy.
15
18
 
16
19
  ## Khi nào sử dụng
17
20
 
18
- - Hiển thị danh sách ảnh, thẻ, hoặc item theo chiều ngang với số lượng lớn.
19
- - Kích thước các item không đều nhau (ví dụ: text dài ngắn khác nhau).
20
- - Cần tính năng scroll đến item cụ thể hoặc index cụ thể.
21
+ - Hiển thị danh sách ảnh, thẻ, tab, cột bảng theo chiều ngang với số lượng lớn (hàng trăm đến hàng nghìn item).
22
+ - Kích thước các item không đều nhau (ví dụ: tên cột dài ngắn khác nhau trong bảng dữ liệu lớn).
23
+ - Cần cuộn đến một item hoặc index cụ thể theo chương trình (`scrollToIndex`, `scrollInto`).
24
+ - Cần tính lại viewport khi dữ liệu hoặc kích thước container thay đổi động (`reCalculatorViewPort`).
21
25
 
22
26
  ## Cài đặt
23
27
 
24
28
  ```bash
25
- # npm
26
29
  npm install @libs-ui/components-scroll-measure-items-direction-horizontal
27
-
28
- # yarn
29
- yarn add @libs-ui/components-scroll-measure-items-direction-horizontal
30
30
  ```
31
31
 
32
32
  ## Import
33
33
 
34
34
  ```typescript
35
- import { LibsUiScrollMeasureItemDirectionHorizontalDirective } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
35
+ import {
36
+ LibsUiScrollMeasureItemDirectionHorizontalDirective,
37
+ IScrollMeasureItemDirectionHorizontalFunctionsControl,
38
+ IScrollMeasureStoreItemConvert,
39
+ } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
36
40
 
37
41
  @Component({
38
42
  standalone: true,
39
43
  imports: [LibsUiScrollMeasureItemDirectionHorizontalDirective],
40
44
  // ...
41
45
  })
42
- export class YourComponent {}
46
+ export class MyComponent {}
43
47
  ```
44
48
 
45
- ## Ví dụ
49
+ ## Ví dụ sử dụng
50
+
51
+ ### Ví dụ 1 — Virtual scroll cơ bản (1000 item, chiều rộng đồng đều)
52
+
53
+ ```typescript
54
+ // my-list.component.ts
55
+ import { Component, signal } from '@angular/core';
56
+ import {
57
+ LibsUiScrollMeasureItemDirectionHorizontalDirective,
58
+ IScrollMeasureItemDirectionHorizontalFunctionsControl,
59
+ } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
60
+
61
+ interface T_Column {
62
+ id: number;
63
+ label: string;
64
+ }
65
+
66
+ @Component({
67
+ selector: 'app-my-list',
68
+ standalone: true,
69
+ imports: [LibsUiScrollMeasureItemDirectionHorizontalDirective],
70
+ templateUrl: './my-list.component.html',
71
+ })
72
+ export class MyListComponent {
73
+ protected columns = signal<T_Column[]>(
74
+ Array.from({ length: 1000 }, (_, i) => ({ id: i + 1, label: `Cột ${i + 1}` }))
75
+ );
76
+ protected viewPortColumns = signal<T_Column[]>([]);
77
+ protected scrollControl: IScrollMeasureItemDirectionHorizontalFunctionsControl | null = null;
78
+
79
+ protected measureColumnWidth = async (_item: T_Column): Promise<number> => {
80
+ // Chiều rộng cố định 120px mỗi cột
81
+ return 120;
82
+ };
46
83
 
47
- ### Basic Usage
84
+ protected handlerViewPortItem(items: T_Column[]): void {
85
+ this.viewPortColumns.set(items);
86
+ }
87
+
88
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionHorizontalFunctionsControl): void {
89
+ this.scrollControl = control;
90
+ }
91
+
92
+ protected handlerScrollToFirst(): void {
93
+ this.scrollControl?.scrollToIndex(0);
94
+ }
95
+
96
+ protected handlerScrollToLast(): void {
97
+ this.scrollControl?.scrollToIndex(this.columns().length - 1);
98
+ }
99
+ }
100
+ ```
48
101
 
49
102
  ```html
103
+ <!-- my-list.component.html -->
104
+ <div class="flex gap-2 mb-4">
105
+ <button (click)="handlerScrollToFirst()">Đến đầu</button>
106
+ <button (click)="handlerScrollToLast()">Đến cuối</button>
107
+ </div>
108
+
109
+ <!-- Outer container: overflow-x-auto, có thanh cuộn ngang -->
50
110
  <div
51
111
  #scrollContainer
52
- class="scroll-container flex overflow-x-auto"
53
- LibsUiScrollMeasureItemDirectionHorizontalDirective
54
- [elementScroll]="scrollContainer"
55
- [items]="items"
56
- [functionGetWidthItem]="measureWidth"
57
- (outViewPortItem)="viewPortItems = $event">
58
- @for (item of viewPortItems; track item.id) {
112
+ class="w-full h-[80px] overflow-x-auto border border-gray-300 rounded relative">
113
+
114
+ <!-- Inner container: gắn directive, nằm bên trong outer -->
59
115
  <div
60
- class="item"
61
- [style.width.px]="item.width">
62
- {{ item.text }}
116
+ class="flex items-center h-full"
117
+ LibsUiScrollMeasureItemDirectionHorizontalDirective
118
+ [elementScroll]="scrollContainer"
119
+ [items]="columns()"
120
+ [functionGetWidthItem]="measureColumnWidth"
121
+ (outViewPortItem)="handlerViewPortItem($event)"
122
+ (outFunctionControl)="handlerFunctionControl($event)">
123
+
124
+ @for (col of viewPortColumns(); track col.id) {
125
+ <div class="shrink-0 w-[120px] h-[60px] flex items-center justify-center border-r border-gray-200 text-sm">
126
+ {{ col.label }}
127
+ </div>
128
+ }
63
129
  </div>
130
+ </div>
131
+ ```
132
+
133
+ ---
134
+
135
+ ### Ví dụ 2 — Item có chiều rộng động (tính từ nội dung)
136
+
137
+ ```typescript
138
+ // dynamic-width-list.component.ts
139
+ import { Component, signal } from '@angular/core';
140
+ import {
141
+ LibsUiScrollMeasureItemDirectionHorizontalDirective,
142
+ IScrollMeasureItemDirectionHorizontalFunctionsControl,
143
+ } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
144
+
145
+ interface T_Tag {
146
+ id: number;
147
+ name: string;
148
+ }
149
+
150
+ @Component({
151
+ selector: 'app-dynamic-width-list',
152
+ standalone: true,
153
+ imports: [LibsUiScrollMeasureItemDirectionHorizontalDirective],
154
+ templateUrl: './dynamic-width-list.component.html',
155
+ })
156
+ export class DynamicWidthListComponent {
157
+ protected tags = signal<T_Tag[]>([
158
+ { id: 1, name: 'Angular' },
159
+ { id: 2, name: 'Virtual Scroll' },
160
+ { id: 3, name: 'Performance Optimization' },
161
+ { id: 4, name: 'UI' },
162
+ { id: 5, name: 'Horizontal Scrolling with Dynamic Items' },
163
+ { id: 6, name: 'TypeScript' },
164
+ { id: 7, name: 'RxJS' },
165
+ { id: 8, name: 'Signal' },
166
+ ]);
167
+ protected viewPortTags = signal<T_Tag[]>([]);
168
+ protected scrollControl: IScrollMeasureItemDirectionHorizontalFunctionsControl | null = null;
169
+
170
+ /**
171
+ * Ước tính chiều rộng dựa trên số ký tự.
172
+ * Cộng thêm 32px padding trái/phải (16px mỗi bên) và 8px gap.
173
+ */
174
+ protected measureTagWidth = async (item: T_Tag): Promise<number> => {
175
+ const charWidth = 8;
176
+ return item.name.length * charWidth + 32 + 8;
177
+ };
178
+
179
+ protected handlerViewPortItem(items: T_Tag[]): void {
180
+ this.viewPortTags.set(items);
64
181
  }
182
+
183
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionHorizontalFunctionsControl): void {
184
+ this.scrollControl = control;
185
+ }
186
+
187
+ protected handlerScrollIntoTag(tag: T_Tag): void {
188
+ this.scrollControl?.scrollInto(tag);
189
+ }
190
+ }
191
+ ```
192
+
193
+ ```html
194
+ <!-- dynamic-width-list.component.html -->
195
+ <div
196
+ #tagScrollContainer
197
+ class="w-full h-[60px] overflow-x-auto relative">
198
+
199
+ <div
200
+ class="flex items-center gap-2 h-full"
201
+ LibsUiScrollMeasureItemDirectionHorizontalDirective
202
+ [elementScroll]="tagScrollContainer"
203
+ [items]="tags()"
204
+ [functionGetWidthItem]="measureTagWidth"
205
+ (outViewPortItem)="handlerViewPortItem($event)"
206
+ (outFunctionControl)="handlerFunctionControl($event)">
207
+
208
+ @for (tag of viewPortTags(); track tag.id) {
209
+ <span
210
+ class="shrink-0 px-4 py-1 bg-blue-100 text-blue-800 rounded-full text-sm whitespace-nowrap cursor-pointer"
211
+ (click)="handlerScrollIntoTag(tag)">
212
+ {{ tag.name }}
213
+ </span>
214
+ }
215
+ </div>
65
216
  </div>
66
217
  ```
67
218
 
219
+ ---
220
+
221
+ ### Ví dụ 3 — Cuộn theo chương trình và tính lại viewport
222
+
68
223
  ```typescript
69
- items = [...]; // Data source
70
- viewPortItems = []; // Items processed for display
71
-
72
- // Function to measure item width (async)
73
- measureWidth = async (item: any): Promise<number> => {
74
- // Logic to calculate width, e.g., based on content length or fixed rules
75
- return item.text.length * 10 + 20;
76
- };
224
+ // programmatic-scroll.component.ts
225
+ import { Component, signal } from '@angular/core';
226
+ import {
227
+ LibsUiScrollMeasureItemDirectionHorizontalDirective,
228
+ IScrollMeasureItemDirectionHorizontalFunctionsControl,
229
+ } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
230
+
231
+ interface T_Photo {
232
+ id: number;
233
+ width: number;
234
+ src: string;
235
+ }
236
+
237
+ @Component({
238
+ selector: 'app-programmatic-scroll',
239
+ standalone: true,
240
+ imports: [LibsUiScrollMeasureItemDirectionHorizontalDirective],
241
+ templateUrl: './programmatic-scroll.component.html',
242
+ })
243
+ export class ProgrammaticScrollComponent {
244
+ protected photos = signal<T_Photo[]>(
245
+ Array.from({ length: 500 }, (_, i) => ({
246
+ id: i + 1,
247
+ width: 100 + (i % 4) * 50, // 100, 150, 200, 250 lặp lại
248
+ src: `https://picsum.photos/id/${i + 1}/${100 + (i % 4) * 50}/80`,
249
+ }))
250
+ );
251
+ protected viewPortPhotos = signal<T_Photo[]>([]);
252
+ protected scrollControl: IScrollMeasureItemDirectionHorizontalFunctionsControl | null = null;
253
+
254
+ protected measurePhotoWidth = async (item: T_Photo): Promise<number> => {
255
+ // Cộng thêm 8px gap giữa các ảnh
256
+ return item.width + 8;
257
+ };
258
+
259
+ protected handlerViewPortItem(items: T_Photo[]): void {
260
+ this.viewPortPhotos.set(items);
261
+ }
262
+
263
+ protected handlerFunctionControl(control: IScrollMeasureItemDirectionHorizontalFunctionsControl): void {
264
+ this.scrollControl = control;
265
+ }
266
+
267
+ protected handlerScrollToIndex(index: number): void {
268
+ this.scrollControl?.scrollToIndex(index);
269
+ }
270
+
271
+ protected handlerScrollToPosition(px: number): void {
272
+ this.scrollControl?.scrollToPosition(px);
273
+ }
274
+
275
+ protected handlerRecalculate(): void {
276
+ this.scrollControl?.reCalculatorViewPort();
277
+ }
278
+ }
77
279
  ```
78
280
 
79
- ## API
281
+ ```html
282
+ <!-- programmatic-scroll.component.html -->
283
+ <div class="flex gap-2 mb-3">
284
+ <button (click)="handlerScrollToIndex(0)">Đầu (0)</button>
285
+ <button (click)="handlerScrollToIndex(249)">Giữa (249)</button>
286
+ <button (click)="handlerScrollToIndex(499)">Cuối (499)</button>
287
+ <button (click)="handlerScrollToPosition(1000)">Vị trí 1000px</button>
288
+ <button (click)="handlerRecalculate()">Tính lại viewport</button>
289
+ </div>
290
+
291
+ <div
292
+ #photoScroll
293
+ class="w-full h-[100px] overflow-x-auto relative border border-gray-200 rounded">
294
+
295
+ <div
296
+ class="flex items-center gap-2 h-full"
297
+ LibsUiScrollMeasureItemDirectionHorizontalDirective
298
+ [elementScroll]="photoScroll"
299
+ [items]="photos()"
300
+ [functionGetWidthItem]="measurePhotoWidth"
301
+ (outViewPortItem)="handlerViewPortItem($event)"
302
+ (outFunctionControl)="handlerFunctionControl($event)">
303
+
304
+ @for (photo of viewPortPhotos(); track photo.id) {
305
+ <img
306
+ class="shrink-0 h-[80px] rounded object-cover"
307
+ [style.width.px]="photo.width"
308
+ [src]="photo.src"
309
+ [alt]="'Photo ' + photo.id" />
310
+ }
311
+ </div>
312
+ </div>
313
+ ```
314
+
315
+ ## @Input()
80
316
 
81
- ### LibsUiScrollMeasureItemDirectionHorizontalDirective
317
+ | Input | Type | Default | Mô tả | Ví dụ |
318
+ |---|---|---|---|---|
319
+ | `[elementScroll]` | `HTMLElement` | **required** | Element cha có `overflow-x: auto` chứa thanh cuộn ngang. Thường dùng template variable `#scrollContainer`. | `[elementScroll]="scrollContainer"` |
320
+ | `[items]` | `Array<any>` | **required** | Toàn bộ mảng dữ liệu nguồn. Directive tự tính toán item nào cần render dựa trên viewport. | `[items]="columns()"` |
321
+ | `[functionGetWidthItem]` | `(item: any) => Promise<number>` | **required** | Hàm bất đồng bộ trả về chiều rộng (px) của một item, bao gồm cả padding và margin/gap. Được gọi song song cho toàn bộ mảng khi `items` thay đổi. | `[functionGetWidthItem]="measureWidth"` |
82
322
 
83
- #### Inputs
323
+ ## @Output()
324
+
325
+ | Output | Type | Mô tả | Handler TS | Binding HTML |
326
+ |---|---|---|---|---|
327
+ | `(outViewPortItem)` | `Array<any>` | Emit danh sách item cần render trong viewport (kèm buffer 3 item). Consumer dùng mảng này cho `@for`. | `handlerViewPortItem(items: T_Item[]): void { items.stopPropagation?.(); this.viewPortItems.set(items); }` | `(outViewPortItem)="handlerViewPortItem($event)"` |
328
+ | `(outFunctionControl)` | `IScrollMeasureItemDirectionHorizontalFunctionsControl` | Emit object chứa các hàm điều khiển scroll. Lưu lại để gọi `scrollToIndex`, `scrollInto`,... theo chương trình. | `handlerFunctionControl(ctrl: IScrollMeasureItemDirectionHorizontalFunctionsControl): void { this.scrollControl = ctrl; }` | `(outFunctionControl)="handlerFunctionControl($event)"` |
329
+ | `(outDivVirtual)` | `HTMLDivElement` | Emit element ảo (invisible) được chèn vào container để duy trì tổng chiều rộng scroll. Ít khi cần dùng trực tiếp. | `handlerDivVirtual(el: HTMLDivElement): void { this.virtualEl = el; }` | `(outDivVirtual)="handlerDivVirtual($event)"` |
330
+ | `(outPaddingLeft)` | `number` | Emit giá trị `paddingLeft` (px) hiện tại của inner container — bù cho các item đã scroll qua khỏi viewport bên trái. | `handlerPaddingLeft(value: number): void { this.currentPaddingLeft = value; }` | `(outPaddingLeft)="handlerPaddingLeft($event)"` |
331
+
332
+ ## Types & Interfaces
333
+
334
+ ```typescript
335
+ import {
336
+ IScrollMeasureItemDirectionHorizontalFunctionsControl,
337
+ IScrollMeasureStoreItemConvert,
338
+ } from '@libs-ui/components-scroll-measure-items-direction-horizontal';
339
+
340
+ /**
341
+ * Object điều khiển scroll được emit qua (outFunctionControl).
342
+ * Lưu lại reference sau khi nhận để gọi các phương thức theo chương trình.
343
+ */
344
+ interface IScrollMeasureItemDirectionHorizontalFunctionsControl {
345
+ /** Scroll container đến vị trí bắt đầu của item được truyền vào. */
346
+ scrollInto: (item: any) => Promise<void>;
347
+
348
+ /** Scroll container đến vị trí pixel chỉ định (scrollLeft). */
349
+ scrollToPosition: (position: number) => Promise<void>;
350
+
351
+ /** Scroll container đến vị trí bắt đầu của item tại index chỉ định. */
352
+ scrollToIndex: (index: number) => Promise<void>;
353
+
354
+ /**
355
+ * Gọi lại hàm đo width toàn bộ item và tính lại vùng hiển thị.
356
+ * Dùng khi kích thước item thay đổi sau khi render (ví dụ: font load xong, dữ liệu cập nhật).
357
+ */
358
+ reCalculatorViewPort: () => Promise<void>;
359
+
360
+ /** Trả về mảng item đang hiển thị trong viewport tại thời điểm gọi. */
361
+ getViewPortItems: () => Array<any>;
362
+ }
363
+
364
+ /**
365
+ * Cấu trúc nội bộ lưu trữ thông tin vị trí từng item.
366
+ * Được emit qua (outDivVirtual) nếu consumer cần debug layout.
367
+ */
368
+ interface IScrollMeasureStoreItemConvert {
369
+ /** Tham chiếu đến item gốc trong mảng [items]. */
370
+ ref: any;
371
+ /** Chiều rộng tính toán (px) của item này. */
372
+ itemWidth: number;
373
+ /** Vị trí bắt đầu (scrollLeft) của item, tính từ đầu container. */
374
+ start: number;
375
+ /** Vị trí kết thúc (scrollLeft + itemWidth) của item. */
376
+ end: number;
377
+ }
378
+ ```
84
379
 
85
- | Property | Type | Default | Description |
86
- | ------------------------ | -------------------------------- | ---------- | ----------------------------------------------- |
87
- | `[items]` | `Array<any>` | `required` | Danh sách dữ liệu gốc cần hiển thị. |
88
- | `[elementScroll]` | `HTMLElement` | `required` | Element chứa thanh cuộn (container chính). |
89
- | `[functionGetWidthItem]` | `(item: any) => Promise<number>` | `required` | Hàm async trả về chiều rộng (px) của từng item. |
380
+ ## Lưu ý quan trọng
90
381
 
91
- #### Outputs
382
+ ⚠️ **Kiến trúc 2 lớp bắt buộc**: Directive phải được gắn vào **inner container** (thẻ flex bên trong), KHÔNG phải outer container có `overflow-x: auto`. Input `[elementScroll]` phải trỏ đến outer container — đây là element có thanh cuộn thực sự.
92
383
 
93
- | Property | Type | Description |
94
- | ---------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------- |
95
- | `(outViewPortItem)` | `Array<Record<string, any>>` | Emit danh sách item cần render trong viewport. Item gốc nằm trong property `ref`. |
96
- | `(outFunctionControl)` | `IScrollMeasureItemDirectionHorizontalFunctionsControl` | Emit controller chứa các hàm điều khiển scroll. |
97
- | `(outDivVirtual)` | `HTMLDivElement` | Emit element ảo được tạo ra để giữ chiều rộng scroll của container. |
384
+ ⚠️ **Hàm `functionGetWidthItem` phải tính cả margin/gap**: Chiều rộng trả về phải bao gồm toàn bộ không gian mà item chiếm (bao gồm margin, gap). Nếu thiếu, tổng chiều rộng ảo sẽ sai và scroll sẽ bị lệch vị trí.
98
385
 
99
- #### Interfaces
386
+ ⚠️ **Directive thêm `position: relative` và điều chỉnh `paddingLeft` vào inner container**: Không đặt các style này thủ công trên element gắn directive vì sẽ bị override. Thay vào đó, dùng output `(outPaddingLeft)` nếu cần biết giá trị hiện tại.
100
387
 
101
- **IScrollMeasureItemDirectionHorizontalFunctionsControl**
388
+ ⚠️ **Element ảo `divVirtual` được chèn vào outer container**: Directive tự tạo và quản lý một `<div>` ẩn bên trong `elementScroll` để giữ đúng tổng chiều rộng scroll. Không cần tạo thủ công. Element này tự xóa khi directive destroy.
102
389
 
103
- | Method | Description |
104
- | --------------------------------------------------- | -------------------------------------------------------------------------- |
105
- | `scrollInto(item: any): Promise<void>` | Scroll đến item cụ thể. |
106
- | `scrollToPosition(position: number): Promise<void>` | Scroll đến vị trí pixel cụ thể. |
107
- | `scrollToIndex(index: number): Promise<void>` | Scroll đến item tại index cụ thể. |
108
- | `reCalculatorViewPort(): Promise<void>` | Tính toán lại viewport (dùng khi kích thước container hoặc item thay đổi). |
390
+ ⚠️ **`items` thay đổi reference mới kích hoạt đo lại**: Mỗi khi `items` signal thay đổi, toàn bộ width được đo lại song song qua `Promise.all`. Với list rất lớn (> 5000 item) và hàm đo nặng, cân nhắc debounce ở phía consumer trước khi cập nhật `items`.
109
391
 
110
392
  ## Demo
111
393
 
@@ -113,4 +395,4 @@ measureWidth = async (item: any): Promise<number> => {
113
395
  npx nx serve core-ui
114
396
  ```
115
397
 
116
- Truy cập path: `/components/scroll-measure-items/direction-horizontal`
398
+ Truy cập: http://localhost:4500/components/scroll-measure-items/direction-horizontal
@@ -1 +1 @@
1
- {"version":3,"file":"libs-ui-components-scroll-measure-items-direction-horizontal.mjs","sources":["../../../../../../libs-ui/components/scroll-measure-items/direction-horizontal/src/direction-horizontal.directive.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-horizontal/src/libs-ui-components-scroll-measure-items-direction-horizontal.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 { IScrollMeasureItemDirectionHorizontalFunctionsControl, IScrollMeasureStoreItemConvert } from './interfaces/direction-horizontal.interface';\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[LibsUiScrollMeasureItemDirectionHorizontalDirective]',\n standalone: true,\n})\nexport class LibsUiScrollMeasureItemDirectionHorizontalDirective 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<IScrollMeasureStoreItemConvert>>([]);\n private readonly divVirtual = document.createElement('div');\n private resizeObserver?: ResizeObserver;\n /** Chặn ResizeObserver / subscribe được tạo trong setTimeout sau khi directive đã destroy. */\n private timerAfterViewInit?: number;\n /** Token nhận dạng lần effect chạy hiện tại. Async đo width 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 functionGetWidthItem = 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<IScrollMeasureItemDirectionHorizontalFunctionsControl>();\n readonly outDivVirtual = output<HTMLDivElement>();\n readonly outPaddingLeft = 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 functionGetWidthItem = this.functionGetWidthItem();\n if (!scrollEl || !functionGetWidthItem) {\n return;\n }\n\n // Đo width tất cả item song song — rút ngắn thời gian cho list lớn (vd 3000 cột).\n const widths = await Promise.all(items.map((it) => functionGetWidthItem(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 scrollLeft = scrollEl.scrollLeft;\n const containerWidth = scrollEl.getBoundingClientRect().width;\n let width = 0;\n let endLocal = 0;\n const newItems: Array<IScrollMeasureStoreItemConvert> = [];\n\n for (let i = 0; i < items.length; i++) {\n const itemWidth = widths[i];\n const newItem: IScrollMeasureStoreItemConvert = { ref: items[i], itemWidth, start: width, end: 0 };\n width += itemWidth;\n newItem.end = width;\n if (newItem.end <= containerWidth + scrollLeft) {\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.height = '1px';\n this.divVirtual.style.visibility = 'hidden';\n this.elementRef.nativeElement.classList.add('relative');\n this.elementRef.nativeElement.classList.remove('w-full');\n this.elementRef.nativeElement.style.paddingLeft = '0px';\n scrollEl.append(this.divVirtual);\n this.divVirtualInitialized = true;\n this.outDivVirtual.emit(this.divVirtual);\n }\n // Width tổng thể đổi theo dataset → cần update mỗi lượt.\n this.divVirtual.style.width = `${width}px`;\n\n // containerWidth lúc đầu có thể = 0 (DOM chưa layout), khiến end giữ\n // nguyên 0 và chỉ render 3 cột đầ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().width có thể = 0 nên end giữ giá trị 0,\n // chỉ hiển thị 3 cột đầu (do slice(start, end + bufferAhead)). ResizeObserver fire ngay khi\n // observe và mỗi lầ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(): IScrollMeasureItemDirectionHorizontalFunctionsControl {\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 handlerScroll() {\n const store = this.storeItems();\n if (!store?.length) {\n return;\n }\n const scrollEl = this.ScrollContainer;\n const scrollLeft = scrollEl.scrollLeft;\n const containerWidth = scrollEl.getBoundingClientRect().width;\n let start = this.start();\n let endLocal = this.end();\n let isSetStartPositioned = false;\n\n for (let i = 0; i < store.length; i++) {\n const item = store[i];\n if (item.end > containerWidth + scrollLeft) {\n break;\n }\n endLocal = i;\n if (item.end >= scrollLeft && !isSetStartPositioned) {\n start = i;\n isSetStartPositioned = true;\n }\n }\n if (endLocal !== this.end()) {\n this.end.set(endLocal);\n }\n\n if (start !== this.start()) {\n this.start.set(start);\n const paddingLeft = store[start].start;\n this.elementRef.nativeElement.style.paddingLeft = `${paddingLeft}px`;\n this.outPaddingLeft.emit(paddingLeft);\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.scrollLeft = itemFound.start;\n }\n\n private async scrollToPosition(position: number) {\n this.ScrollContainer.scrollLeft = position;\n }\n\n private async scrollToIndex(index: number) {\n const itemOfIndex = this.storeItems()[index];\n if (!itemOfIndex) {\n return;\n }\n this.ScrollContainer.scrollLeft = itemOfIndex.start;\n }\n\n private async reCalculatorViewPort() {\n const items = this.storeItems();\n const fn = this.functionGetWidthItem();\n // Đo song song để rút ngắn thời gian thay vì await tuần tự.\n const widths = await Promise.all(items.map((it) => fn(it.ref)));\n\n let width = 0;\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const itemWidth = widths[i];\n item.itemWidth = itemWidth;\n item.start = width;\n width += itemWidth;\n item.end = width;\n }\n\n this.storeItems.set([...items]);\n this.divVirtual.style.width = `${width}px`;\n this.handlerScroll();\n }\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-horizontal.mjs","sources":["../../../../../../libs-ui/components/scroll-measure-items/direction-horizontal/src/direction-horizontal.directive.ts","../../../../../../libs-ui/components/scroll-measure-items/direction-horizontal/src/libs-ui-components-scroll-measure-items-direction-horizontal.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 { IScrollMeasureItemDirectionHorizontalFunctionsControl, IScrollMeasureStoreItemConvert } from './interfaces/direction-horizontal.interface';\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[LibsUiScrollMeasureItemDirectionHorizontalDirective]',\n standalone: true,\n})\nexport class LibsUiScrollMeasureItemDirectionHorizontalDirective 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<IScrollMeasureStoreItemConvert>>([]);\n private readonly divVirtual = document.createElement('div');\n private resizeObserver?: ResizeObserver;\n /** Chặn ResizeObserver / subscribe được tạo trong setTimeout sau khi directive đã destroy. */\n private timerAfterViewInit?: number;\n /** Token nhận dạng lần effect chạy hiện tại. Async đo width 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 functionGetWidthItem = 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<IScrollMeasureItemDirectionHorizontalFunctionsControl>();\n readonly outDivVirtual = output<HTMLDivElement>();\n readonly outPaddingLeft = 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 functionGetWidthItem = this.functionGetWidthItem();\n if (!scrollEl || !functionGetWidthItem) {\n return;\n }\n\n // Đo width tất cả item song song — rút ngắn thời gian cho list lớn (vd 3000 cột).\n const widths = await Promise.all(items.map((it) => functionGetWidthItem(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 scrollLeft = scrollEl.scrollLeft;\n const containerWidth = scrollEl.getBoundingClientRect().width;\n let width = 0;\n let endLocal = 0;\n const newItems: Array<IScrollMeasureStoreItemConvert> = [];\n\n for (let i = 0; i < items.length; i++) {\n const itemWidth = widths[i];\n const newItem: IScrollMeasureStoreItemConvert = { ref: items[i], itemWidth, start: width, end: 0 };\n width += itemWidth;\n newItem.end = width;\n if (newItem.end <= containerWidth + scrollLeft) {\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.height = '1px';\n this.divVirtual.style.visibility = 'hidden';\n this.elementRef.nativeElement.classList.add('relative');\n this.elementRef.nativeElement.classList.remove('w-full');\n this.elementRef.nativeElement.style.paddingLeft = '0px';\n scrollEl.append(this.divVirtual);\n this.divVirtualInitialized = true;\n this.outDivVirtual.emit(this.divVirtual);\n }\n // Width tổng thể đổi theo dataset → cần update mỗi lượt.\n this.divVirtual.style.width = `${width}px`;\n\n // containerWidth lúc đầu có thể = 0 (DOM chưa layout), khiến end giữ\n // nguyên 0 và chỉ render 3 cột đầ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().width có thể = 0 nên end giữ giá trị 0,\n // chỉ hiển thị 3 cột đầu (do slice(start, end + bufferAhead)). ResizeObserver fire ngay khi\n // observe và mỗi lầ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(): IScrollMeasureItemDirectionHorizontalFunctionsControl {\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 handlerScroll() {\n const store = this.storeItems();\n if (!store?.length) {\n return;\n }\n const scrollEl = this.ScrollContainer;\n const scrollLeft = scrollEl.scrollLeft;\n const containerWidth = scrollEl.getBoundingClientRect().width;\n let start = this.start();\n let endLocal = this.end();\n let isSetStartPositioned = false;\n\n for (let i = 0; i < store.length; i++) {\n const item = store[i];\n if (item.end > containerWidth + scrollLeft) {\n break;\n }\n endLocal = i;\n if (item.end >= scrollLeft && !isSetStartPositioned) {\n start = i;\n isSetStartPositioned = true;\n }\n }\n if (endLocal !== this.end()) {\n this.end.set(endLocal);\n }\n\n if (start !== this.start()) {\n this.start.set(start);\n const paddingLeft = store[start].start;\n this.elementRef.nativeElement.style.paddingLeft = `${paddingLeft}px`;\n this.outPaddingLeft.emit(paddingLeft);\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.scrollLeft = itemFound.start;\n }\n\n private async scrollToPosition(position: number) {\n this.ScrollContainer.scrollLeft = position;\n }\n\n private async scrollToIndex(index: number) {\n const itemOfIndex = this.storeItems()[index];\n if (!itemOfIndex) {\n return;\n }\n this.ScrollContainer.scrollLeft = itemOfIndex.start;\n }\n\n private async reCalculatorViewPort() {\n const items = this.storeItems();\n const fn = this.functionGetWidthItem();\n // Đo song song để rút ngắn thời gian thay vì await tuần tự.\n const widths = await Promise.all(items.map((it) => fn(it.ref)));\n\n let width = 0;\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const itemWidth = widths[i];\n item.itemWidth = itemWidth;\n item.start = width;\n width += itemWidth;\n item.end = width;\n }\n\n this.storeItems.set([...items]);\n this.divVirtual.style.width = `${width}px`;\n this.handlerScroll();\n }\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-horizontal",
3
- "version": "0.2.356-41",
3
+ "version": "0.2.356-43",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
6
  "rxjs": "~7.8.0"