@shival99/z-ui 2.2.0 → 2.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shival99/z-ui",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Z-UI: Modern Angular UI Component Library - A comprehensive, high-performance design system built with Angular 22, featuring 40+ customizable components with dark mode, accessibility, and enterprise-ready features.",
5
5
  "keywords": [
6
6
  "angular",
@@ -1,3 +1,4 @@
1
+ import * as _tanstack_angular_virtual from '@tanstack/angular-virtual';
1
2
  import * as _angular_core from '@angular/core';
2
3
  import { OnDestroy } from '@angular/core';
3
4
  import { ZIcon } from '@shival99/z-ui/components/z-icon';
@@ -81,9 +82,22 @@ declare class ZChatComponent implements OnDestroy {
81
82
  private readonly _messagesScrollbarRef;
82
83
  private readonly _chatInputRef;
83
84
  private readonly _fileInputRef;
85
+ private readonly _virtualMessageRefs;
86
+ /** Signal, not a field: requesting a pin must itself wake `_pinToNewest`. */
87
+ private readonly _pinAttemptsLeft;
84
88
  private _mockReplyTimer;
85
89
  private _rippleTimer;
86
- private _scrollTimer;
90
+ private readonly _measureMessages;
91
+ protected readonly messageVirtualizer: _tanstack_angular_virtual.AngularVirtualizer<HTMLElement, Element>;
92
+ /**
93
+ * Drives a requested pin to completion across renders.
94
+ *
95
+ * One `scrollToEnd()` is not enough on a cold list: the end is computed from estimates, and it
96
+ * moves as rows are measured. The scroll box also arrives late — ng-scrollbar reports its
97
+ * viewport only after init, and a scroll issued before then leaves the virtualizer at offset 0
98
+ * with no event to resync it. Re-asserting each render until `isAtEnd` holds covers both.
99
+ */
100
+ private readonly _pinToNewest;
87
101
  protected readonly wrapperClasses: _angular_core.Signal<string>;
88
102
  protected readonly wrapperTop: _angular_core.Signal<string | null>;
89
103
  protected readonly wrapperRight: _angular_core.Signal<string | null>;
@@ -94,6 +108,8 @@ declare class ZChatComponent implements OnDestroy {
94
108
  protected readonly isTyping: _angular_core.Signal<boolean>;
95
109
  protected readonly canSend: _angular_core.Signal<boolean>;
96
110
  protected readonly hasMessages: _angular_core.Signal<boolean>;
111
+ /** Only the newest bubble plays the push-in animation; recycled rows must not replay it. */
112
+ protected readonly newestMessageId: _angular_core.Signal<string | null>;
97
113
  protected readonly showEmptyState: _angular_core.Signal<boolean>;
98
114
  protected readonly previewImageSizeLabel: _angular_core.Signal<string>;
99
115
  ngOnDestroy(): void;
@@ -120,9 +136,16 @@ declare class ZChatComponent implements OnDestroy {
120
136
  private _triggerRipple;
121
137
  protected onMessagesScrollbarInit(): void;
122
138
  protected onEscapeKey(): void;
123
- private _scheduleScrollToBottom;
139
+ /**
140
+ * Requests a pin to the newest message; `_pinToNewest` performs it once rendering can honour it.
141
+ *
142
+ * Never scroll synchronously from here. Callers fire right after mutating `zMessages` or
143
+ * `zOpen`, before change detection has fed the new length into the virtualizer, so the scroll
144
+ * would target the previous last index and strand the list mid-history.
145
+ */
124
146
  private _ensureBottomVisible;
125
- private _scrollToBottom;
147
+ /** ng-scrollbar owns the real scrolling box; the virtualizer must observe that, not the host. */
148
+ private _getMessagesViewportElement;
126
149
  private _focusInputSoon;
127
150
  private _releaseAttachmentUrls;
128
151
  private _appendFiles;
@@ -458,6 +458,13 @@ interface ZTableGroupingConfig<T = unknown> {
458
458
  allowHeaderMenu?: boolean;
459
459
  defaultExpanded?: boolean;
460
460
  showCount?: boolean;
461
+ /**
462
+ * Ghim group header đang active lên đầu vùng cuộn (chạy ở cả virtual lẫn non-virtual).
463
+ * Header của group hiện tại dính ở đầu bảng và bị group kế tiếp đẩy đi khi cuộn tới.
464
+ * Multi-level grouping thì header con xếp chồng ngay dưới header cha.
465
+ * @default false
466
+ */
467
+ stickyHeader?: boolean;
461
468
  /** Cấu hình các cột cần hiển thị tổng trên group row. */
462
469
  totals?: ZTableGroupTotalConfig<T>[];
463
470
  /**
@@ -1150,6 +1157,7 @@ declare class ZTableComponent<T> implements AfterViewInit {
1150
1157
  protected readonly tfootWrapper: _angular_core.Signal<ElementRef<HTMLDivElement> | undefined>;
1151
1158
  protected readonly expandedRowTemplate: _angular_core.Signal<TemplateRef<unknown> | undefined>;
1152
1159
  protected readonly virtualRowElements: _angular_core.Signal<readonly ElementRef<HTMLDivElement>[]>;
1160
+ protected readonly groupRowElements: _angular_core.Signal<readonly ElementRef<HTMLTableRowElement>[]>;
1153
1161
  protected readonly columnPinning: _angular_core.WritableSignal<ColumnPinningState>;
1154
1162
  protected readonly columnVisibility: _angular_core.WritableSignal<ColumnVisibilityState>;
1155
1163
  protected readonly columnOrder: _angular_core.WritableSignal<ColumnOrderState>;
@@ -1241,6 +1249,21 @@ declare class ZTableComponent<T> implements AfterViewInit {
1241
1249
  protected readonly hasScrollTop: _angular_core.WritableSignal<boolean>;
1242
1250
  /** True khi bottom-pinned rows đang đè lên nội dung body còn scroll xuống được */
1243
1251
  protected readonly hasScrollBottom: _angular_core.WritableSignal<boolean>;
1252
+ /**
1253
+ * Group header (non-virtual) đang ghim ở mép dưới cùng của chồng sticky — chỉ mình nó đổ shadow.
1254
+ * Nhiều header ghim chung một chỗ, mỗi cái một shadow thì alpha cộng dồn thành vệt đen đậm dần.
1255
+ * Chỉ dùng cho shadow: class này bám theo scroll nên luôn trễ một frame, chấp nhận được với shadow
1256
+ * nhưng không dùng được cho z-index (xem `tr.z-group-row-sticky`).
1257
+ */
1258
+ protected readonly shadowGroupRowId: _angular_core.WritableSignal<string | null>;
1259
+ /** Chiều cao thật của group header row (non-virtual), dùng để xếp chồng sticky theo depth. */
1260
+ private readonly _groupRowHeight;
1261
+ /**
1262
+ * Tổng chiều cao khối pinned top rows (non-virtual), đo trực tiếp từ DOM sau mỗi render.
1263
+ * Không dùng `pinnedRowHeights` vì signal đó chỉ refresh trong `_checkScrollState`, nên có thể
1264
+ * còn giữ số đo cũ khi bảng vừa đổi cấu hình — sticky group header sẽ hở một khoảng vài px.
1265
+ */
1266
+ private readonly _nonVirtualPinnedTopHeight;
1244
1267
  /** Maps row IDs to measured DOM heights for pinned row offset calculations */
1245
1268
  protected readonly pinnedRowHeights: _angular_core.WritableSignal<Record<string, number>>;
1246
1269
  protected readonly virtualMeasuredLeftSpacerWidth: _angular_core.WritableSignal<number>;
@@ -1357,6 +1380,36 @@ declare class ZTableComponent<T> implements AfterViewInit {
1357
1380
  protected readonly renderedBodyRows: _angular_core.Signal<Row<T>[]>;
1358
1381
  protected readonly bodyRowSpanRows: _angular_core.Signal<Row<T>[]>;
1359
1382
  protected readonly virtualGroupedFlatItems: _angular_core.Signal<ZVirtualFlatItem<T>[]>;
1383
+ /** Sticky group header đang bật (config cho phép và đang thực sự group theo cột nào đó). */
1384
+ protected readonly isStickyGroupHeaderEnabled: _angular_core.Signal<boolean>;
1385
+ /**
1386
+ * `top` (px) của sticky group header theo depth ở chế độ non-virtual.
1387
+ * Ở đây mọi group header đều là `<tr>` thật trong cùng một `<tbody>`, nên chỉ cần `position: sticky`:
1388
+ * header sau (đứng sau trong DOM) tự đè lên header trước khi trôi tới rồi lộ lại khi cuộn ngược —
1389
+ * không cần rangeExtractor như bên virtual (cơ chế đó chỉ để giữ row đã ra khỏi range).
1390
+ * Header con xếp chồng ngay dưới header cha nên cộng thêm `depth * chiều cao group row`.
1391
+ */
1392
+ protected readonly stickyGroupTopByDepth: _angular_core.Signal<number[]>;
1393
+ /**
1394
+ * Lookup phục vụ sticky group header trong virtual scroll:
1395
+ * - `nearest[i]`: index group header gần nhất tại hoặc trước `i` (-1 nếu chưa có).
1396
+ * - `parent`: index group header cha của một header (multi-level grouping), -1 nếu là cấp gốc.
1397
+ * Trả `null` khi tắt sticky hoặc không ở chế độ virtual + grouping.
1398
+ */
1399
+ private readonly _stickyGroupHeaderMeta;
1400
+ /** Chuỗi header (gốc → sâu nhất) cần ghim khi viewport bắt đầu tại `startIndex`. */
1401
+ private _buildStickyGroupChain;
1402
+ /**
1403
+ * Trạng thái sticky group header theo virtual index:
1404
+ * - `tops`: offset `top` (px) của từng header đang ghim; header cấp sâu hơn xếp chồng bên dưới
1405
+ * header cha nên cộng dồn chiều cao.
1406
+ * - `shadowIndex`: header cuối chuỗi mang shadow (giống pinned row), -1 khi chưa có row nào
1407
+ * cuộn xuống dưới nó — lúc đó header vẫn ở đúng vị trí thật nên không cần shadow.
1408
+ */
1409
+ protected readonly stickyGroupHeaderState: _angular_core.Signal<{
1410
+ shadowIndex: number;
1411
+ tops: Record<number, number>;
1412
+ }>;
1360
1413
  protected _getVirtualGroupHeader(item: ZVirtualFlatItem<T> | undefined): ZTableGroupRowItem | null;
1361
1414
  protected _isVirtualGroupChild(item: ZVirtualFlatItem<T> | undefined): boolean;
1362
1415
  protected _getVirtualGroupDepth(item: ZVirtualFlatItem<T> | undefined): number;
@@ -1629,6 +1682,16 @@ declare class ZTableComponent<T> implements AfterViewInit {
1629
1682
  rowSelectionFeature: _tanstack_angular_table.TableFeature;
1630
1683
  rowSortingFeature: _tanstack_angular_table.TableFeature;
1631
1684
  }, T & Record<string, unknown>>[]>;
1685
+ /**
1686
+ * Id row ở hai biên của mỗi khối pinned, theo THỨ TỰ HIỂN THỊ (pinned bottom render ngược lại).
1687
+ * Dùng signal thay cho `zTableRow` pipe: pipe là pure, chỉ chạy lại khi một tham số đổi reference.
1688
+ * Khi ghim thêm/bớt row thì `row` và `table` giữ nguyên reference, nên row từng là biên vẫn giữ
1689
+ * class cũ — pin bottom lộ rõ nhất vì shadow ở lại row cũ và row mới cũng có shadow.
1690
+ */
1691
+ protected readonly firstTopPinnedRowId: _angular_core.Signal<string>;
1692
+ protected readonly lastTopPinnedRowId: _angular_core.Signal<string>;
1693
+ protected readonly firstBottomPinnedRowId: _angular_core.Signal<string>;
1694
+ protected readonly lastBottomPinnedRowId: _angular_core.Signal<string>;
1632
1695
  protected readonly leftHeaderRow: _angular_core.Signal<_tanstack_angular_table.Header<{
1633
1696
  expandedRowModel: (table: _tanstack_angular_table.Table<any, any>) => () => _tanstack_angular_table.RowModel<any, any>;
1634
1697
  facetedMinMaxValues: (table: _tanstack_angular_table.Table<_tanstack_angular_table.TableFeatures, any>, columnId: string) => () => undefined | [number, number];
@@ -1795,9 +1858,42 @@ declare class ZTableComponent<T> implements AfterViewInit {
1795
1858
  protected readonly effectiveDynamicRowSizing: _angular_core.Signal<boolean>;
1796
1859
  /** Virtualizer instance — operates on groups rather than individual rows */
1797
1860
  protected readonly virtualizer: _tanstack_angular_virtual.AngularVirtualizer<HTMLElement, Element>;
1861
+ /**
1862
+ * Nhận diện group header đang ghim sau MỖI render, không chỉ khi scroll.
1863
+ * Đổi virtual/non-virtual, đổi cột group hay pin/unpin row đều giữ nguyên scrollOffset nhưng dựng
1864
+ * lại DOM và đổi đường sticky — nếu chỉ trông vào scroll handler thì header đang ghim sẽ thiếu
1865
+ * class `z-group-row-stuck` cho tới lần cuộn kế tiếp, và bị data row đè trong khoảng đó.
1866
+ * `_refreshStuckGroupRows` đọc `groupRowElements` + `stickyGroupTopByDepth` trước mọi early-return
1867
+ * nên effect này tự chạy lại khi hai nguồn đó đổi (kể cả khi số đo tới muộn hơn một pass).
1868
+ */
1869
+ private readonly _trackStuckGroupRows;
1870
+ /** Measures in the render `read` phase so corrected offsets land before paint, not a frame later. */
1798
1871
  private readonly _measureVirtualItems;
1799
- /** Learn estimate height from measured rows (reads the measurement cache, no reflow). Locks once. */
1800
- private _learnDynamicEstimate;
1872
+ private _stuckGroupRowsFrame;
1873
+ /** Gom nhiều lần scroll trong cùng một frame — mỗi lần chạy đều đọc rect nên phải giới hạn tần suất. */
1874
+ private _scheduleStuckGroupRowsRefresh;
1875
+ private _cancelStuckGroupRowsRefresh;
1876
+ /**
1877
+ * Tìm group header (non-virtual) đang ghim ở mép dưới cùng của chồng sticky để trao shadow.
1878
+ * Header đã ghim có `rect.top` đúng bằng đường sticky của depth đó; header chưa tới thì lớn hơn.
1879
+ * Ưu tiên đường sticky thấp nhất (multi-level: header con), tie thì lấy cái sau cùng trong DOM vì
1880
+ * đó là cái đang nằm trên khi nhiều header chồng nhau.
1881
+ */
1882
+ private _refreshStuckGroupRows;
1883
+ /** Đo các mốc mà `stickyGroupTopByDepth` cần (non-virtual): chiều cao group row và khối pinned top. */
1884
+ private _refreshStickyGroupMetrics;
1885
+ private _hasWarnedOverflowingFixedRows;
1886
+ /**
1887
+ * Cảnh báo một lần mỗi bảng khi body row cao hơn `virtual.size`.
1888
+ * CSS table layout coi `height` trên `<tr>`/`<td>` là min-height, nên nội dung cell không nén được
1889
+ * (tag, button, nhiều dòng…) sẽ đẩy row vượt slot của virtualizer; row kế tiếp định vị tuyệt đối
1890
+ * đúng theo `virtual.size` nên đè lên phần dôi ra — nhìn như mỗi row bị hụt mất vài px.
1891
+ * Không tự sửa: chiều cao đúng phụ thuộc dữ liệu, chọn tăng `size` hay bật `dynamicSize` là của app.
1892
+ */
1893
+ private _warnOnOverflowingFixedRows;
1894
+ private _dynamicEstimateSampleCount;
1895
+ /** Averages the rows measured so far to estimate the ones not yet rendered. */
1896
+ private _refreshDynamicEstimate;
1801
1897
  private readonly _rowDragAutoScroll;
1802
1898
  private readonly _rowDragCellSelectionCleanup;
1803
1899
  constructor();
@@ -2061,7 +2157,7 @@ declare class ZTableFilterComponent<T> {
2061
2157
  protected readonly filterBadgeCount: _angular_core.Signal<number>;
2062
2158
  protected readonly hasFilterValue: _angular_core.Signal<boolean>;
2063
2159
  protected readonly isActive: _angular_core.Signal<boolean>;
2064
- protected readonly draftSortValue: _angular_core.Signal<"none" | "asc" | "desc">;
2160
+ protected readonly draftSortValue: _angular_core.Signal<"asc" | "desc" | "none">;
2065
2161
  protected readonly rangeMinValue: _angular_core.Signal<any>;
2066
2162
  protected readonly rangeMaxValue: _angular_core.Signal<any>;
2067
2163
  protected readonly dateValue: _angular_core.Signal<Date | null>;