@ds-mo/ui 14.9.0 → 14.11.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.
@@ -242,7 +242,8 @@
242
242
 
243
243
  :where(.ds-table__table--native-group-sticky .ds-table__group-row),
244
244
  :where(.ds-table__table--native-group-sticky .ds-table__state-row),
245
- :where(.ds-table__table--native-group-sticky .ds-table__load-row) {
245
+ :where(.ds-table__table--native-group-sticky .ds-table__load-row),
246
+ :where(.ds-table__table--native-group-sticky .ds-table__virtual-spacer-row) {
246
247
  display: block;
247
248
  inline-size: 100%;
248
249
  }
@@ -251,7 +252,8 @@
251
252
  :where(.ds-table__table--native-group-sticky .ds-table__cell),
252
253
  :where(.ds-table__table--native-group-sticky .ds-table__group-cell),
253
254
  :where(.ds-table__table--native-group-sticky .ds-table__state-cell),
254
- :where(.ds-table__table--native-group-sticky .ds-table__load-cell) {
255
+ :where(.ds-table__table--native-group-sticky .ds-table__load-cell),
256
+ :where(.ds-table__table--native-group-sticky .ds-table__virtual-spacer-cell) {
255
257
  display: block;
256
258
  inline-size: auto;
257
259
  width: auto;
@@ -261,11 +263,28 @@
261
263
 
262
264
  :where(.ds-table__table--native-group-sticky .ds-table__group-cell),
263
265
  :where(.ds-table__table--native-group-sticky .ds-table__state-cell),
264
- :where(.ds-table__table--native-group-sticky .ds-table__load-cell) {
266
+ :where(.ds-table__table--native-group-sticky .ds-table__load-cell),
267
+ :where(.ds-table__table--native-group-sticky .ds-table__virtual-spacer-cell) {
265
268
  inline-size: 100%;
266
269
  width: 100%;
267
270
  }
268
271
 
272
+ :where(.ds-table__virtual-spacer-row) {
273
+ border: 0;
274
+ }
275
+
276
+ :where(.ds-table__virtual-spacer-cell) {
277
+ box-sizing: border-box;
278
+ padding: 0;
279
+ border: 0;
280
+ overflow: hidden;
281
+ line-height: 0;
282
+ font-size: 0;
283
+ background: transparent;
284
+ block-size: var(--_table-virtual-spacer-block-size, 0);
285
+ min-block-size: var(--_table-virtual-spacer-block-size, 0);
286
+ }
287
+
269
288
  :where(.ds-table__caption-bar),
270
289
  :where(.ds-table__footer) {
271
290
  box-sizing: border-box;
@@ -31,12 +31,15 @@ export declare class Table {
31
31
  columnOrder: string[];
32
32
  /**
33
33
  * Optional result summary footer. When both `displayedCount` and `totalCount`
34
- * are finite numbers, the table shows “Displaying {displayed} of {total}”.
34
+ * are finite numbers, infinite mode shows “Displaying {displayed} of {total}”.
35
+ * Virtual mode ignores `displayedCount` and shows a total-only summary.
35
36
  */
36
37
  displayedCount: number | undefined;
37
38
  totalCount: number | undefined;
38
39
  /** Supports {displayed} and {total} placeholders. */
39
40
  resultSummaryLabel: string;
41
+ /** Supports the {total} placeholder. Used when dataMode is virtual. */
42
+ resultTotalSummaryLabel: string;
40
43
  stickyHeader: boolean;
41
44
  /** Maximum scroll-region height. Numbers resolve to CSS pixels. */
42
45
  maxHeight: string | number | undefined;
@@ -64,7 +67,7 @@ export declare class Table {
64
67
  errorHeading: string;
65
68
  errorBody: string;
66
69
  emptyCellLabel: string;
67
- /** Top-level data-window strategy. Group member loading remains group-owned. */
70
+ /** Top-level data-window strategy. Virtual mode recycles row DOM only. */
68
71
  dataMode: TableDataMode;
69
72
  /** Opt in to the table-owned control for choosing between supported data modes. */
70
73
  dataModeSwitcher: boolean;
@@ -72,6 +75,7 @@ export declare class Table {
72
75
  dataModeMenuLabel: string;
73
76
  infiniteModeLabel: string;
74
77
  paginationModeLabel: string;
78
+ virtualModeLabel: string;
75
79
  /** Controlled top-level pagination state. Required when dataMode is pagination. */
76
80
  pagination: TablePaginationState | null;
77
81
  loadMoreMode: TableLoadMoreMode;
@@ -124,12 +128,15 @@ export declare class Table {
124
128
  private columnCustomizerInitialFocusVisible;
125
129
  private dataModeSwitcherOpen;
126
130
  private dataModeSwitcherInitialFocusVisible;
131
+ private virtualWindow;
127
132
  private readonly actionMenuElementId;
128
133
  private readonly columnCustomizerElementId;
129
134
  private readonly dataModeSwitcherElementId;
130
135
  private truncateTooltipEl?;
131
136
  private truncateAnchor;
132
137
  private truncateTooltipBound;
138
+ private focusedRowId;
139
+ private virtualItems;
133
140
  private rootEl;
134
141
  private viewportEl;
135
142
  private frameEl;
@@ -152,6 +159,7 @@ export declare class Table {
152
159
  private readonly layoutController;
153
160
  private readonly loadController;
154
161
  private readonly groupLoadController;
162
+ private readonly virtualController;
155
163
  private readonly viewportFitController;
156
164
  componentWillLoad(): void;
157
165
  componentDidLoad(): void;
@@ -167,6 +175,7 @@ export declare class Table {
167
175
  handleStructureChange(): void;
168
176
  handleDataChange(): void;
169
177
  handleLoadIdentityChange(): void;
178
+ handleVirtualIndexReset(): void;
170
179
  handleLazyConfigurationChange(): void;
171
180
  handleDataModeChange(): void;
172
181
  handlePaginationChange(pagination: TablePaginationState | null, previous: TablePaginationState | null): void;
@@ -193,6 +202,12 @@ export declare class Table {
193
202
  private currentLoadedRows;
194
203
  private currentLoadedRowCount;
195
204
  private createRenderModel;
205
+ private get virtualizationEnabled();
206
+ private get virtualViewportMissing();
207
+ private estimateVirtualViewportSize;
208
+ private virtualPinnedRowIds;
209
+ private syncVirtualItems;
210
+ private shouldVirtualize;
196
211
  private get resolvedMaxHeight();
197
212
  private get resolvedHeight();
198
213
  private warnModelIssues;
@@ -219,6 +234,7 @@ export declare class Table {
219
234
  private renderTruncateTooltip;
220
235
  private truncateAttr;
221
236
  private handleRowKeydown;
237
+ private onVirtualFocusIn;
222
238
  private renderSelectionControl;
223
239
  private renderStickyEdge;
224
240
  private renderCollapseAllButton;
@@ -236,6 +252,10 @@ export declare class Table {
236
252
  private formatGroupLoadLabel;
237
253
  private renderGroupLoadRow;
238
254
  private renderStickyGroup;
255
+ private renderVirtualSpacer;
256
+ private renderVirtualRow;
257
+ private renderVirtualGroup;
258
+ private renderVirtualBodies;
239
259
  private renderDataBodies;
240
260
  private renderSkeletonBody;
241
261
  private renderSkeletonCell;
@@ -1,9 +1,11 @@
1
1
  import type { MenuItemData } from '../Menu/menu-types';
2
2
  import type { TableDataMode } from './table-types';
3
3
  export declare function nextTableDataModeSwitcherElementId(): string;
4
+ /** Menu of supported data-mode choices. */
4
5
  export declare function tableDataModeMenuItems(dataMode: TableDataMode, labels: {
5
6
  infinite: string;
6
7
  pagination: string;
8
+ virtual: string;
7
9
  }): MenuItemData[];
8
10
  export declare function tableDataModeFromMenuItem(item: MenuItemData): TableDataMode | null;
9
11
  //# sourceMappingURL=table-data-mode-switcher.d.ts.map
@@ -50,5 +50,7 @@ export declare function tableGroupIntentClass(intent: (typeof TABLE_GROUP_INTENT
50
50
  export declare function tableGroupLabelColor(intent: (typeof TABLE_GROUP_INTENTS)[number] | undefined): 'primary' | 'brand' | 'negative' | 'warning' | 'caution' | 'positive' | `var(--${string})`;
51
51
  /** Formats the optional table result footer. Returns null when either count is missing. */
52
52
  export declare function formatTableResultSummary(displayed: number | null | undefined, total: number | null | undefined, label?: string, locale?: string): string | null;
53
+ /** Formats the virtual-mode total-only result footer. */
54
+ export declare function formatTableTotalSummary(total: number | null | undefined, label?: string, locale?: string): string | null;
53
55
  export declare function tableModelIssues(columns: TableColumn[], rows: TableRow[], groups: TableGroup[], grouped: boolean): string[];
54
56
  //# sourceMappingURL=table-model.d.ts.map
@@ -1,9 +1,11 @@
1
1
  import { tableGroupLabelColor, type TableCollapseAllHost, type TableSelectionState } from './table-model';
2
2
  import type { TableColumn, TableGroup, TableGroupIntent, TableRow, TableSelectionMode } from './table-types';
3
+ export type TableGroupCountPresentation = 'loaded-progress' | 'total';
3
4
  export interface TableGroupRenderModel {
4
5
  group: TableGroup;
5
6
  count: number;
6
7
  loadedCount: number;
8
+ visibleCountText: string;
7
9
  countLabel: string;
8
10
  intent: TableGroupIntent | undefined;
9
11
  intentClass: string | undefined;
@@ -35,6 +37,7 @@ export interface TableRenderModelInput {
35
37
  selectionMode: TableSelectionMode;
36
38
  selectedRowIds: string[];
37
39
  collapsedGroupIds: string[];
40
+ groupCountPresentation?: TableGroupCountPresentation;
38
41
  }
39
42
  /** Create one immutable snapshot of all derived state consumed during a render. */
40
43
  export declare function createTableRenderModel(input: TableRenderModelInput): TableRenderModel;
@@ -9,7 +9,7 @@ export type TableCellLinkTarget = '_self' | '_blank';
9
9
  export type TableColumnSticky = 'start' | 'end';
10
10
  export type TableCaptionVisibility = 'visible' | 'hidden';
11
11
  export type TableLoadMoreMode = 'auto' | 'manual';
12
- export type TableDataMode = 'infinite' | 'pagination';
12
+ export type TableDataMode = 'infinite' | 'pagination' | 'virtual';
13
13
  export interface TableDataModeChangeDetail {
14
14
  dataMode: TableDataMode;
15
15
  }
@@ -0,0 +1,48 @@
1
+ import { type TableVirtualItem, type TableVirtualPlan } from './table-virtual-model';
2
+ export interface TableVirtualControllerState {
3
+ enabled: boolean;
4
+ items: readonly TableVirtualItem[];
5
+ pinnedRowIds: ReadonlySet<string>;
6
+ viewport: HTMLElement | null;
7
+ viewportSize: number;
8
+ }
9
+ export interface TableVirtualControllerOptions {
10
+ state: () => TableVirtualControllerState;
11
+ windowChanged: (plan: TableVirtualPlan | null) => void;
12
+ requestAnimationFrame?: (callback: FrameRequestCallback) => number;
13
+ cancelAnimationFrame?: (handle: number) => void;
14
+ }
15
+ /** Binds viewport scroll/resize to a recycled row window. Scroll offset stays off render state. */
16
+ export declare class TableVirtualController {
17
+ private readonly options;
18
+ private connected;
19
+ private connectedViewport;
20
+ private resizeObserver;
21
+ private frame;
22
+ private measures;
23
+ private plan;
24
+ private scrollOffset;
25
+ private pendingReset;
26
+ private readonly raf;
27
+ private readonly caf;
28
+ constructor(options: TableVirtualControllerOptions);
29
+ connect(): void;
30
+ disconnect(): void;
31
+ refresh(): void;
32
+ schedule: () => void;
33
+ /** Drop cached heights after column, density, or wrap geometry changes. */
34
+ invalidateMeasures(): void;
35
+ /** Rebuild from a new index and pin the viewport to the start. */
36
+ resetToTop(): void;
37
+ sizeFor(item: TableVirtualItem): number;
38
+ currentPlan(): TableVirtualPlan | null;
39
+ /** Measure painted rows and correct scroll when estimates were wrong. */
40
+ collectMeasurements(root: ParentNode | null): void;
41
+ private prefixForCurrentItems;
42
+ private readonly handleScroll;
43
+ private sync;
44
+ private emit;
45
+ private unbindViewport;
46
+ private cancelFrame;
47
+ }
48
+ //# sourceMappingURL=table-virtual-controller.d.ts.map
@@ -0,0 +1,77 @@
1
+ import type { TableColumn, TableGroup, TableRow } from './table-types';
2
+ /**
3
+ * Token-aligned body-row track contracts: 40 / 62 / 84, plus wrapping-secondary
4
+ * 106. Estimates only; measured heights replace these after paint.
5
+ */
6
+ export declare const TABLE_VIRTUAL_ROW_TRACK_SIZE: {
7
+ readonly 1: 40;
8
+ readonly 2: 62;
9
+ readonly 3: 84;
10
+ readonly 4: 106;
11
+ };
12
+ export declare const TABLE_VIRTUAL_GROUP_HEADER_SIZE: 40;
13
+ export declare const TABLE_VIRTUAL_MIN_OVERSCAN_ROWS = 8;
14
+ export declare const TABLE_VIRTUAL_VIEWPORT_REQUIRED_HEADING = "Bounded height required";
15
+ export declare const TABLE_VIRTUAL_VIEWPORT_REQUIRED_BODY = "Virtual mode requires height, maxHeight, or fitViewport.";
16
+ export declare const TABLE_VIRTUAL_TOTAL_SUMMARY_LABEL = "{total} items";
17
+ export type TableVirtualItemKind = 'group' | 'row';
18
+ export interface TableVirtualItem {
19
+ kind: TableVirtualItemKind;
20
+ id: string;
21
+ groupId?: string;
22
+ rowId?: string;
23
+ estimatedSize: number;
24
+ }
25
+ export type TableVirtualNode = {
26
+ kind: 'spacer';
27
+ size: number;
28
+ key: string;
29
+ } | {
30
+ kind: 'row';
31
+ index: number;
32
+ item: TableVirtualItem;
33
+ } | {
34
+ kind: 'group';
35
+ groupId: string;
36
+ headerIndex: number;
37
+ nodes: Array<Extract<TableVirtualNode, {
38
+ kind: 'spacer' | 'row';
39
+ }>>;
40
+ };
41
+ export interface TableVirtualPlan {
42
+ totalSize: number;
43
+ itemCount: number;
44
+ start: number;
45
+ end: number;
46
+ nodes: TableVirtualNode[];
47
+ mountedIds: ReadonlySet<string>;
48
+ mountedIndexes: ReadonlySet<number>;
49
+ }
50
+ export interface FlattenTableVirtualItemsInput {
51
+ grouped: boolean;
52
+ rows: TableRow[];
53
+ groups: TableGroup[];
54
+ collapsedGroupIds: ReadonlySet<string> | readonly string[];
55
+ columns: TableColumn[];
56
+ }
57
+ export interface ResolveTableVirtualPlanInput {
58
+ items: readonly TableVirtualItem[];
59
+ sizes: readonly number[];
60
+ scrollOffset: number;
61
+ viewportSize: number;
62
+ pinnedRowIds?: ReadonlySet<string>;
63
+ }
64
+ /** Map a cell recipe onto the named track stack used for first-paint estimates. */
65
+ export declare function estimateTableCellTrackCount(value: TableRow['cells'][string], column: TableColumn): number;
66
+ /** Tallest named track stack across the row's visible cells. */
67
+ export declare function estimateTableRowBlockSize(row: TableRow, columns: readonly TableColumn[]): number;
68
+ export declare function flattenTableVirtualItems(input: FlattenTableVirtualItemsInput): TableVirtualItem[];
69
+ export declare function tableVirtualOverscanPx(viewportSize: number): number;
70
+ export declare function buildTableVirtualPrefixSums(sizes: readonly number[]): number[];
71
+ /** Largest item index whose start offset is <= `offset`. */
72
+ export declare function findTableVirtualIndexAtOffset(prefix: readonly number[], offset: number): number;
73
+ export declare function tableVirtualItemSizes(items: readonly TableVirtualItem[], measures: ReadonlyMap<string, number>): number[];
74
+ export declare function sameTableVirtualPlan(left: TableVirtualPlan | null | undefined, right: TableVirtualPlan | null | undefined): boolean;
75
+ /** Resolve the mounted slice, sticky headers, and in-flow spacers for one scroll offset. */
76
+ export declare function resolveTableVirtualPlan(input: ResolveTableVirtualPlanInput): TableVirtualPlan;
77
+ //# sourceMappingURL=table-virtual-model.d.ts.map
@@ -2877,7 +2877,7 @@ export namespace Components {
2877
2877
  */
2878
2878
  "columns": TableColumn[];
2879
2879
  /**
2880
- * Top-level data-window strategy. Group member loading remains group-owned.
2880
+ * Top-level data-window strategy. Virtual mode recycles row DOM only.
2881
2881
  * @default 'infinite'
2882
2882
  */
2883
2883
  "dataMode": TableDataMode;
@@ -2895,7 +2895,7 @@ export namespace Components {
2895
2895
  */
2896
2896
  "dataModeSwitcherLabel": string;
2897
2897
  /**
2898
- * Optional result summary footer. When both `displayedCount` and `totalCount` are finite numbers, the table shows “Displaying {displayed} of {total}”.
2898
+ * Optional result summary footer. When both `displayedCount` and `totalCount` are finite numbers, infinite mode shows “Displaying {displayed} of {total}”. Virtual mode ignores `displayedCount` and shows a total-only summary.
2899
2899
  */
2900
2900
  "displayedCount": number | undefined;
2901
2901
  /**
@@ -3038,6 +3038,11 @@ export namespace Components {
3038
3038
  * @default 'Displaying {displayed} of {total}'
3039
3039
  */
3040
3040
  "resultSummaryLabel": string;
3041
+ /**
3042
+ * Supports the {total} placeholder. Used when dataMode is virtual.
3043
+ * @default '{total} items'
3044
+ */
3045
+ "resultTotalSummaryLabel": string;
3041
3046
  /**
3042
3047
  * @default 'Retry'
3043
3048
  */
@@ -3090,6 +3095,10 @@ export namespace Components {
3090
3095
  * @default 0
3091
3096
  */
3092
3097
  "viewportInsetBlockStart": string | number;
3098
+ /**
3099
+ * @default 'Virtual scroll'
3100
+ */
3101
+ "virtualModeLabel": string;
3093
3102
  }
3094
3103
  interface DsTableSavedViews {
3095
3104
  /**
@@ -7794,7 +7803,7 @@ declare namespace LocalJSX {
7794
7803
  */
7795
7804
  "columns"?: TableColumn[];
7796
7805
  /**
7797
- * Top-level data-window strategy. Group member loading remains group-owned.
7806
+ * Top-level data-window strategy. Virtual mode recycles row DOM only.
7798
7807
  * @default 'infinite'
7799
7808
  */
7800
7809
  "dataMode"?: TableDataMode;
@@ -7812,7 +7821,7 @@ declare namespace LocalJSX {
7812
7821
  */
7813
7822
  "dataModeSwitcherLabel"?: string;
7814
7823
  /**
7815
- * Optional result summary footer. When both `displayedCount` and `totalCount` are finite numbers, the table shows “Displaying {displayed} of {total}”.
7824
+ * Optional result summary footer. When both `displayedCount` and `totalCount` are finite numbers, infinite mode shows “Displaying {displayed} of {total}”. Virtual mode ignores `displayedCount` and shows a total-only summary.
7816
7825
  */
7817
7826
  "displayedCount"?: number | undefined;
7818
7827
  /**
@@ -7965,6 +7974,11 @@ declare namespace LocalJSX {
7965
7974
  * @default 'Displaying {displayed} of {total}'
7966
7975
  */
7967
7976
  "resultSummaryLabel"?: string;
7977
+ /**
7978
+ * Supports the {total} placeholder. Used when dataMode is virtual.
7979
+ * @default '{total} items'
7980
+ */
7981
+ "resultTotalSummaryLabel"?: string;
7968
7982
  /**
7969
7983
  * @default 'Retry'
7970
7984
  */
@@ -8017,6 +8031,10 @@ declare namespace LocalJSX {
8017
8031
  * @default 0
8018
8032
  */
8019
8033
  "viewportInsetBlockStart"?: string | number;
8034
+ /**
8035
+ * @default 'Virtual scroll'
8036
+ */
8037
+ "virtualModeLabel"?: string;
8020
8038
  }
8021
8039
  interface DsTableSavedViews {
8022
8040
  /**
@@ -9000,6 +9018,7 @@ declare namespace LocalJSX {
9000
9018
  "displayedCount": number | undefined;
9001
9019
  "totalCount": number | undefined;
9002
9020
  "resultSummaryLabel": string;
9021
+ "resultTotalSummaryLabel": string;
9003
9022
  "stickyHeader": boolean;
9004
9023
  "maxHeight": string;
9005
9024
  "height": string;
@@ -9022,6 +9041,7 @@ declare namespace LocalJSX {
9022
9041
  "dataModeMenuLabel": string;
9023
9042
  "infiniteModeLabel": string;
9024
9043
  "paginationModeLabel": string;
9044
+ "virtualModeLabel": string;
9025
9045
  "loadMoreMode": TableLoadMoreMode;
9026
9046
  "hasMore": boolean;
9027
9047
  "loadingMore": boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ds-mo/ui",
3
- "version": "14.9.0",
3
+ "version": "14.11.0",
4
4
  "description": "CompoMo — composable web components styled with TokoMo design tokens",
5
5
  "keywords": [
6
6
  "web-components",