apex-grid 3.2.0 → 3.3.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.
Files changed (197) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +200 -52
  3. package/components/cell.d.ts +14 -1
  4. package/components/cell.js +48 -6
  5. package/components/cell.js.map +1 -1
  6. package/components/filter-row.d.ts +3 -6
  7. package/components/filter-row.js +79 -109
  8. package/components/filter-row.js.map +1 -1
  9. package/components/grid.d.ts +416 -77
  10. package/components/grid.js +398 -11
  11. package/components/grid.js.map +1 -1
  12. package/components/group-header-row.d.ts +34 -0
  13. package/components/group-header-row.js +125 -0
  14. package/components/group-header-row.js.map +1 -0
  15. package/components/header-row.d.ts +13 -2
  16. package/components/header-row.js +33 -18
  17. package/components/header-row.js.map +1 -1
  18. package/components/header.d.ts +11 -0
  19. package/components/header.js +135 -5
  20. package/components/header.js.map +1 -1
  21. package/components/paginator.d.ts +6 -1
  22. package/components/paginator.js +17 -11
  23. package/components/paginator.js.map +1 -1
  24. package/components/row.d.ts +28 -0
  25. package/components/row.js +100 -3
  26. package/components/row.js.map +1 -1
  27. package/components/toolbar.d.ts +8 -1
  28. package/components/toolbar.js +25 -7
  29. package/components/toolbar.js.map +1 -1
  30. package/controllers/data-operation.js +1 -0
  31. package/controllers/data-operation.js.map +1 -1
  32. package/controllers/dom.d.ts +24 -6
  33. package/controllers/dom.js +62 -5
  34. package/controllers/dom.js.map +1 -1
  35. package/controllers/editing.d.ts +68 -1
  36. package/controllers/editing.js +129 -44
  37. package/controllers/editing.js.map +1 -1
  38. package/controllers/filter.d.ts +1 -1
  39. package/controllers/filter.js +16 -5
  40. package/controllers/filter.js.map +1 -1
  41. package/controllers/history.d.ts +86 -0
  42. package/controllers/history.js +118 -0
  43. package/controllers/history.js.map +1 -0
  44. package/controllers/navigation.d.ts +15 -0
  45. package/controllers/navigation.js +53 -0
  46. package/controllers/navigation.js.map +1 -1
  47. package/controllers/reorder.js +3 -1
  48. package/controllers/reorder.js.map +1 -1
  49. package/controllers/row-pin.d.ts +59 -0
  50. package/controllers/row-pin.js +75 -0
  51. package/controllers/row-pin.js.map +1 -0
  52. package/controllers/row-reorder.d.ts +161 -0
  53. package/controllers/row-reorder.js +332 -0
  54. package/controllers/row-reorder.js.map +1 -0
  55. package/controllers/sort.js +9 -1
  56. package/controllers/sort.js.map +1 -1
  57. package/controllers/state.d.ts +33 -0
  58. package/controllers/state.js +37 -2
  59. package/controllers/state.js.map +1 -1
  60. package/controllers/tree.d.ts +8 -0
  61. package/controllers/tree.js +0 -0
  62. package/controllers/tree.js.map +1 -1
  63. package/custom-elements.json +4411 -886
  64. package/docs/assets/hierarchy.js +1 -1
  65. package/docs/assets/highlight.css +28 -0
  66. package/docs/assets/navigation.js +1 -1
  67. package/docs/assets/search.js +1 -1
  68. package/docs/functions/custom.html +3 -0
  69. package/docs/functions/interpolate.html +4 -0
  70. package/docs/functions/localize.html +8 -0
  71. package/docs/functions/max.html +3 -0
  72. package/docs/functions/min.html +3 -0
  73. package/docs/functions/pattern.html +3 -0
  74. package/docs/functions/required.html +3 -0
  75. package/docs/hierarchy.html +1 -1
  76. package/docs/index.html +184 -54
  77. package/docs/interfaces/ApexCellValidationFailedEvent.html +13 -0
  78. package/docs/interfaces/ApexCellValueChangedEvent.html +1 -1
  79. package/docs/interfaces/ApexCellValueChangingEvent.html +2 -2
  80. package/docs/interfaces/ApexColumnGroupContext.html +6 -0
  81. package/docs/interfaces/ApexColumnMovedEvent.html +2 -2
  82. package/docs/interfaces/ApexColumnMovingEvent.html +1 -1
  83. package/docs/interfaces/ApexDetailContext.html +2 -2
  84. package/docs/interfaces/ApexGridEventMap.html +37 -9
  85. package/docs/interfaces/ApexHistoryChangedEvent.html +6 -0
  86. package/docs/interfaces/ApexPageChangedEvent.html +1 -1
  87. package/docs/interfaces/ApexRowMovingEvent.html +9 -0
  88. package/docs/interfaces/ApexRowPinningEvent.html +8 -0
  89. package/docs/interfaces/ApexStateChangedEvent.html +4 -0
  90. package/docs/interfaces/BaseApexCellContext.html +1 -1
  91. package/docs/interfaces/BaseApexEditorContext.html +1 -1
  92. package/docs/interfaces/BaseColumnConfiguration.html +42 -17
  93. package/docs/interfaces/CSVExportOptions.html +2 -2
  94. package/docs/interfaces/ColumnGroupConfiguration.html +12 -0
  95. package/docs/interfaces/ColumnLayoutState.html +2 -0
  96. package/docs/interfaces/ColumnSchema.html +22 -0
  97. package/docs/interfaces/DataPipelineConfiguration.html +2 -2
  98. package/docs/interfaces/EditingHistoryConfiguration.html +10 -0
  99. package/docs/interfaces/ExportFormat.html +2 -2
  100. package/docs/interfaces/ExportOptions.html +1 -1
  101. package/docs/interfaces/FilterStateSnapshot.html +4 -0
  102. package/docs/interfaces/GetStateOptions.html +4 -0
  103. package/docs/interfaces/GridCapabilities.html +8 -0
  104. package/docs/interfaces/GridEditingConfiguration.html +4 -2
  105. package/docs/interfaces/GridRowPinningConfiguration.html +5 -0
  106. package/docs/interfaces/GridRowReorderingConfiguration.html +22 -0
  107. package/docs/interfaces/GridSchema.html +19 -0
  108. package/docs/interfaces/GridState.html +41 -0
  109. package/docs/interfaces/GridTreeConfiguration.html +2 -2
  110. package/docs/interfaces/PaginationConfiguration.html +1 -1
  111. package/docs/interfaces/PaginationState.html +1 -1
  112. package/docs/interfaces/SetStateOptions.html +9 -0
  113. package/docs/interfaces/SetStateResult.html +11 -0
  114. package/docs/interfaces/SortStateSnapshot.html +2 -0
  115. package/docs/interfaces/ValidatorContext.html +8 -0
  116. package/docs/media/_tokens.scss +70 -81
  117. package/docs/media/package.json +1 -1
  118. package/docs/media/state-persistence.html +189 -0
  119. package/docs/modules.html +1 -1
  120. package/docs/types/DataPipelineParams.html +2 -2
  121. package/docs/types/EditMode.html +2 -2
  122. package/docs/types/GridLocaleKey.html +3 -0
  123. package/docs/types/GridLocaleText.html +5 -0
  124. package/docs/types/LocaleParams.html +2 -0
  125. package/docs/types/PaginationMode.html +1 -1
  126. package/docs/types/RowDropPosition.html +2 -0
  127. package/docs/types/RowPinPosition.html +2 -0
  128. package/docs/types/RowRef.html +3 -0
  129. package/docs/types/Validator.html +4 -0
  130. package/docs/variables/EN_LOCALE.html +9 -0
  131. package/docs/variables/esLocale.html +8 -0
  132. package/i18n/en.d.ts +151 -0
  133. package/i18n/en.js +125 -0
  134. package/i18n/en.js.map +1 -0
  135. package/i18n/es.d.ts +18 -0
  136. package/i18n/es.js +125 -0
  137. package/i18n/es.js.map +1 -0
  138. package/i18n/index.d.ts +3 -0
  139. package/i18n/index.js +4 -0
  140. package/i18n/index.js.map +1 -0
  141. package/i18n/localize.d.ts +23 -0
  142. package/i18n/localize.js +15 -0
  143. package/i18n/localize.js.map +1 -0
  144. package/index.d.ts +8 -2
  145. package/index.js +2 -0
  146. package/index.js.map +1 -1
  147. package/internal/feature-module.d.ts +46 -0
  148. package/internal/feature-module.js +11 -0
  149. package/internal/feature-module.js.map +1 -1
  150. package/internal/flip.js +1 -1
  151. package/internal/flip.js.map +1 -1
  152. package/internal/icons.d.ts +9 -7
  153. package/internal/icons.js +75 -35
  154. package/internal/icons.js.map +1 -1
  155. package/internal/index.d.ts +5 -3
  156. package/internal/index.js +2 -1
  157. package/internal/index.js.map +1 -1
  158. package/internal/state-schema.d.ts +85 -0
  159. package/internal/state-schema.js +27 -0
  160. package/internal/state-schema.js.map +1 -0
  161. package/internal/state-snapshot.d.ts +152 -0
  162. package/internal/state-snapshot.js +115 -0
  163. package/internal/state-snapshot.js.map +1 -0
  164. package/internal/tags.d.ts +1 -0
  165. package/internal/tags.js +1 -0
  166. package/internal/tags.js.map +1 -1
  167. package/internal/types.d.ts +162 -2
  168. package/internal/types.js.map +1 -1
  169. package/internal/utils.d.ts +12 -0
  170. package/internal/utils.js +17 -0
  171. package/internal/utils.js.map +1 -1
  172. package/internal/validators.d.ts +31 -0
  173. package/internal/validators.js +44 -0
  174. package/internal/validators.js.map +1 -0
  175. package/package.json +1 -1
  176. package/styles/_tokens.css.js +1 -1
  177. package/styles/_tokens.css.js.map +1 -1
  178. package/styles/body-cell/body-cell.css.js +1 -1
  179. package/styles/body-cell/body-cell.css.js.map +1 -1
  180. package/styles/body-row/body-row.css.js +1 -1
  181. package/styles/body-row/body-row.css.js.map +1 -1
  182. package/styles/filter-row/filter-row.css.js +1 -1
  183. package/styles/filter-row/filter-row.css.js.map +1 -1
  184. package/styles/grid/_grid.base.css.js +1 -1
  185. package/styles/grid/_grid.base.css.js.map +1 -1
  186. package/styles/grid/grid.css.js +1 -1
  187. package/styles/grid/grid.css.js.map +1 -1
  188. package/styles/group-header-row/group-header-row.css.d.ts +1 -0
  189. package/styles/group-header-row/group-header-row.css.js +3 -0
  190. package/styles/group-header-row/group-header-row.css.js.map +1 -0
  191. package/styles/header-cell/header-cell.css.js +1 -1
  192. package/styles/header-cell/header-cell.css.js.map +1 -1
  193. package/styles/header-row/header-row.base.css.js +1 -1
  194. package/styles/header-row/header-row.base.css.js.map +1 -1
  195. package/styles/toolbar/toolbar.css.js +1 -1
  196. package/styles/toolbar/toolbar.css.js.map +1 -1
  197. package/docs/classes/ApexGrid.html +0 -399
@@ -11,21 +11,38 @@ import { styleMap } from 'lit/directives/style-map.js';
11
11
  import { DataOperationsController } from '../controllers/data-operation.js';
12
12
  import { GridDOMController } from '../controllers/dom.js';
13
13
  import { gridStateContext, StateController } from '../controllers/state.js';
14
+ import { localize as resolveLocaleText, } from '../i18n/index.js';
14
15
  import { DEFAULT_COLUMN_CONFIG, PIPELINE } from '../internal/constants.js';
15
16
  import { buildCSV, downloadBlob, } from '../internal/export.js';
16
17
  import { EventEmitterBase } from '../internal/mixins/event-emitter.js';
17
18
  import { registerComponent } from '../internal/register.js';
19
+ import { columnSchema, operandsByType } from '../internal/state-schema.js';
20
+ import { applyColumnLayout, deserializeFilter, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from '../internal/state-snapshot.js';
18
21
  import { GRID_TAG } from '../internal/tags.js';
19
22
  import { asArray, autoGenerateColumns, getDisplayColumns, getFilterOperandsFor, } from '../internal/utils.js';
20
23
  import { watch } from '../internal/watch.js';
21
24
  import { styles as gridStyles } from '../styles/grid/grid.css.js';
22
25
  import ApexGridCell from './cell.js';
23
26
  import ApexFilterRow from './filter-row.js';
27
+ import ApexGridGroupHeaderRow from './group-header-row.js';
24
28
  import ApexGridHeaderRow from './header-row.js';
25
29
  import ApexGridPaginator from './paginator.js';
26
30
  import ApexGridRow from './row.js';
27
31
  import ApexGridToolbar from './toolbar.js';
28
32
  import ApexVirtualizer from './virtualizer.js';
33
+ const SET_STATE_SLICES = [
34
+ 'columns',
35
+ 'sort',
36
+ 'filter',
37
+ 'quickFilter',
38
+ 'modules',
39
+ 'rowOrder',
40
+ 'tree',
41
+ 'expansion',
42
+ 'rowPinning',
43
+ 'selection',
44
+ 'pagination',
45
+ ];
29
46
  export class ApexGrid extends EventEmitterBase {
30
47
  constructor() {
31
48
  super(...arguments);
@@ -48,17 +65,25 @@ export class ApexGrid extends EventEmitterBase {
48
65
  this.showQuickFilter = false;
49
66
  this.showExport = false;
50
67
  this.columnReordering = false;
68
+ this.columnSeparator = true;
69
+ this.coordinateHints = false;
70
+ this.columnMenu = true;
71
+ this.#stateChangedListeners = 0;
72
+ this.#lastStateJson = null;
51
73
  }
52
74
  static get tagName() {
53
75
  return GRID_TAG;
54
76
  }
55
77
  static { this.styles = gridStyles; }
56
78
  static register() {
57
- registerComponent(ApexGrid, ApexVirtualizer, ApexGridRow, ApexGridHeaderRow, ApexFilterRow, ApexGridPaginator, ApexGridToolbar);
79
+ registerComponent(ApexGrid, ApexVirtualizer, ApexGridRow, ApexGridGroupHeaderRow, ApexGridHeaderRow, ApexFilterRow, ApexGridPaginator, ApexGridToolbar);
58
80
  }
59
81
  createStateController() {
60
82
  return new StateController(this);
61
83
  }
84
+ localize(key, params, fallback) {
85
+ return resolveLocaleText(this.localeText, key, params, fallback);
86
+ }
62
87
  get selectedRows() {
63
88
  return this.stateController.selection.selectedRows();
64
89
  }
@@ -83,6 +108,15 @@ export class ApexGrid extends EventEmitterBase {
83
108
  isRowSelected(row) {
84
109
  return this.stateController.selection.isSelected(row);
85
110
  }
111
+ get hasColumnGroups() {
112
+ if (!this.columnGroups?.length)
113
+ return false;
114
+ const ids = new Set(this.columnGroups.map((group) => group.id));
115
+ return this.columns.some((column) => !column.hidden && column.group && ids.has(column.group));
116
+ }
117
+ get columnGroupDepth() {
118
+ return this.hasColumnGroups ? 1 : 0;
119
+ }
86
120
  get expandedRows() {
87
121
  return this.stateController.expansion.expandedRows();
88
122
  }
@@ -161,15 +195,19 @@ export class ApexGrid extends EventEmitterBase {
161
195
  return this.dataState.length;
162
196
  }
163
197
  get pageItems() {
198
+ const rowPin = this.stateController.rowPin;
199
+ const base = rowPin?.enabled && rowPin.hasPinnedRows
200
+ ? this.dataState.filter((row) => !rowPin.isPinned(row))
201
+ : this.dataState;
164
202
  if (!this.stateController.pagination.enabled)
165
- return this.dataState;
203
+ return base;
166
204
  if (this.pagination?.mode === 'remote')
167
- return this.dataState;
205
+ return base;
168
206
  const { page, pageSize } = this.stateController.pagination;
169
207
  if (!pageSize)
170
- return this.dataState;
208
+ return base;
171
209
  const start = page * pageSize;
172
- return this.dataState.slice(start, start + pageSize);
210
+ return base.slice(start, start + pageSize);
173
211
  }
174
212
  get page() {
175
213
  return this.stateController.pagination.page;
@@ -227,13 +265,38 @@ export class ApexGrid extends EventEmitterBase {
227
265
  }
228
266
  updated() {
229
267
  this.setAttribute('role', this.stateController.tree.enabled ? 'treegrid' : 'grid');
230
- const hasFilter = this.columns.some((column) => column.filter);
231
- const headerRows = hasFilter ? 2 : 1;
232
- this.setAttribute('aria-rowcount', String(headerRows + this.pageItems.length));
268
+ const pinned = this.stateController.rowPin.pinnedRows;
269
+ this.setAttribute('aria-rowcount', String(this.headerRowCount + pinned.top.length + this.pageItems.length + pinned.bottom.length));
233
270
  const visibleColumns = this.columns.filter((column) => !column.hidden).length;
234
271
  const extras = (this.stateController.selection.showCheckboxColumn ? 1 : 0) +
235
272
  (this.stateController.expansion.showToggleColumn ? 1 : 0);
236
273
  this.setAttribute('aria-colcount', String(visibleColumns + extras));
274
+ this.#emitStateChangedIfNeeded();
275
+ }
276
+ #stateChangedListeners;
277
+ #lastStateJson;
278
+ addEventListener(type, listener, options) {
279
+ if (type === 'stateChanged') {
280
+ this.#stateChangedListeners += 1;
281
+ this.#lastStateJson = this.hasUpdated ? JSON.stringify(this.getState()) : null;
282
+ }
283
+ super.addEventListener(type, listener, options);
284
+ }
285
+ removeEventListener(type, listener, options) {
286
+ if (type === 'stateChanged') {
287
+ this.#stateChangedListeners = Math.max(0, this.#stateChangedListeners - 1);
288
+ }
289
+ super.removeEventListener(type, listener, options);
290
+ }
291
+ #emitStateChangedIfNeeded() {
292
+ if (this.#stateChangedListeners === 0)
293
+ return;
294
+ const snapshot = this.getState();
295
+ const json = JSON.stringify(snapshot);
296
+ if (json === this.#lastStateJson)
297
+ return;
298
+ this.#lastStateJson = json;
299
+ this.emitEvent('stateChanged', { detail: { state: snapshot } });
237
300
  }
238
301
  firstUpdated() {
239
302
  if (typeof getComputedStyle === 'function' && getComputedStyle(this).display !== 'grid') {
@@ -254,6 +317,201 @@ export class ApexGrid extends EventEmitterBase {
254
317
  sort(expressions) {
255
318
  this.stateController.sorting.sort(expressions);
256
319
  }
320
+ getState(options) {
321
+ const rowId = options?.rowId ?? this.rowId;
322
+ const pinned = this.stateController.rowPin.pinnedRows;
323
+ const manualOrder = this.stateController.rowReorder.getManualOrder();
324
+ return {
325
+ version: 1,
326
+ columns: serializeColumnLayout(this.columns),
327
+ sort: serializeSort(this.sortExpressions),
328
+ filter: serializeFilter(this.filterExpressions),
329
+ quickFilter: this.quickFilter,
330
+ pagination: { page: this.page, pageSize: this.pageSize },
331
+ selection: serializeRowRefs(this.selectedRows, this.data, rowId),
332
+ expansion: serializeRowRefs(this.expandedRows, this.data, rowId),
333
+ treeExpanded: serializeRowRefs([...this.stateController.tree.expanded], this.data, rowId),
334
+ treeExpandedKeys: [...this.stateController.tree.expandedKeys],
335
+ rowPinning: {
336
+ top: serializeRowRefs(pinned.top, this.data, rowId),
337
+ bottom: serializeRowRefs(pinned.bottom, this.data, rowId),
338
+ },
339
+ rowOrder: manualOrder ? serializeRowRefs(manualOrder, this.data, rowId) : null,
340
+ modules: this.stateController.serializeModuleState(),
341
+ };
342
+ }
343
+ getSchema() {
344
+ return {
345
+ version: 1,
346
+ columns: this.columns.map((column) => columnSchema(column)),
347
+ capabilities: {
348
+ sort: {
349
+ directions: ['ascending', 'descending'],
350
+ multi: Boolean(this.sortConfiguration?.multiple),
351
+ },
352
+ filter: { operandsByType: operandsByType(this.columns) },
353
+ pagination: Boolean(this.pagination?.enabled),
354
+ selection: this.selection?.enabled ? (this.selection.mode ?? 'multiple') : false,
355
+ rowPinning: Boolean(this.rowPinning?.enabled),
356
+ rowReordering: Boolean(this.rowReordering?.enabled),
357
+ },
358
+ state: this.getState(),
359
+ };
360
+ }
361
+ setState(state, options) {
362
+ const rowId = options?.rowId ?? this.rowId;
363
+ const strict = options?.strict ?? false;
364
+ const applied = [];
365
+ const warnings = [];
366
+ const warn = (message) => {
367
+ if (strict)
368
+ throw new Error(`[apex-grid] setState: ${message}`);
369
+ warnings.push(message);
370
+ };
371
+ const asArray = (value, label) => {
372
+ if (value === undefined)
373
+ return null;
374
+ if (!Array.isArray(value)) {
375
+ warn(`${label}: expected an array, got ${typeof value}; skipped`);
376
+ return null;
377
+ }
378
+ return value;
379
+ };
380
+ const resolveCounted = (refs, label) => {
381
+ const rows = resolveRowRefs(refs, this.data, rowId);
382
+ if (rows.length < refs.length) {
383
+ warn(`${label}: ${refs.length - rows.length} of ${refs.length} row(s) could not be resolved`);
384
+ }
385
+ return rows;
386
+ };
387
+ if (state.version !== undefined && state.version !== 1) {
388
+ warn(`unsupported snapshot version ${String(state.version)}; applying recognized slices best-effort`);
389
+ }
390
+ const columns = asArray(state.columns, 'columns');
391
+ if (columns) {
392
+ this.columns = applyColumnLayout(this.columns, columns);
393
+ applied.push('columns');
394
+ }
395
+ const sort = asArray(state.sort, 'sort');
396
+ if (sort) {
397
+ this.stateController.sorting.reset();
398
+ const valid = [];
399
+ for (const entry of sort) {
400
+ if (this.getColumn(entry.key)) {
401
+ valid.push(entry);
402
+ }
403
+ else {
404
+ warn(`sort: unknown column "${entry.key}"; dropped`);
405
+ }
406
+ }
407
+ if (valid.length)
408
+ this.sort(valid);
409
+ applied.push('sort');
410
+ }
411
+ const filter = asArray(state.filter, 'filter');
412
+ if (filter) {
413
+ this.stateController.filtering.reset();
414
+ const expressions = deserializeFilter(filter, (key) => this.getColumn(key), (snapshot, reason) => warn(`filter: "${snapshot.key}" dropped (${reason})`));
415
+ if (expressions.length)
416
+ this.filter(expressions);
417
+ applied.push('filter');
418
+ }
419
+ if (state.quickFilter !== undefined) {
420
+ if (typeof state.quickFilter === 'string') {
421
+ this.quickFilter = state.quickFilter;
422
+ applied.push('quickFilter');
423
+ }
424
+ else {
425
+ warn(`quickFilter: expected a string, got ${typeof state.quickFilter}; skipped`);
426
+ }
427
+ }
428
+ if (state.modules !== undefined) {
429
+ if (state.modules && typeof state.modules === 'object') {
430
+ this.stateController.restoreModuleState(state.modules);
431
+ applied.push('modules');
432
+ }
433
+ else {
434
+ warn(`modules: expected an object, got ${typeof state.modules}; skipped`);
435
+ }
436
+ }
437
+ if (state.rowOrder !== undefined) {
438
+ if (state.rowOrder === null) {
439
+ this.stateController.rowReorder.restoreManualOrder(null);
440
+ applied.push('rowOrder');
441
+ }
442
+ else {
443
+ const order = asArray(state.rowOrder, 'rowOrder');
444
+ if (order) {
445
+ if (this.sortExpressions.length) {
446
+ warn('rowOrder dropped: a manual order is mutually exclusive with an active sort');
447
+ }
448
+ else {
449
+ this.stateController.rowReorder.restoreManualOrder(resolveCounted(order, 'rowOrder'));
450
+ }
451
+ applied.push('rowOrder');
452
+ }
453
+ }
454
+ }
455
+ if (state.treeExpanded !== undefined || state.treeExpandedKeys !== undefined) {
456
+ const refs = asArray(state.treeExpanded, 'treeExpanded') ?? [];
457
+ const keys = asArray(state.treeExpandedKeys, 'treeExpandedKeys') ?? [];
458
+ this.stateController.tree.restoreExpansion(resolveCounted(refs, 'treeExpanded'), keys);
459
+ applied.push('tree');
460
+ }
461
+ const expansion = asArray(state.expansion, 'expansion');
462
+ if (expansion) {
463
+ this.expandedRows = resolveCounted(expansion, 'expansion');
464
+ applied.push('expansion');
465
+ }
466
+ if (state.rowPinning !== undefined) {
467
+ const pinning = state.rowPinning;
468
+ if (pinning && typeof pinning === 'object') {
469
+ this.stateController.rowPin.restore(resolveCounted(asArray(pinning.top, 'rowPinning.top') ?? [], 'rowPinning.top'), resolveCounted(asArray(pinning.bottom, 'rowPinning.bottom') ?? [], 'rowPinning.bottom'));
470
+ applied.push('rowPinning');
471
+ }
472
+ else {
473
+ warn(`rowPinning: expected an object, got ${typeof pinning}; skipped`);
474
+ }
475
+ }
476
+ const selection = asArray(state.selection, 'selection');
477
+ if (selection) {
478
+ this.selectedRows = resolveCounted(selection, 'selection');
479
+ applied.push('selection');
480
+ }
481
+ if (state.pagination !== undefined) {
482
+ const pagination = state.pagination;
483
+ if (pagination && typeof pagination === 'object') {
484
+ const { page, pageSize } = pagination;
485
+ if (pageSize !== undefined) {
486
+ if (typeof pageSize === 'number' && Number.isFinite(pageSize) && pageSize > 0) {
487
+ this.pageSize = pageSize;
488
+ }
489
+ else {
490
+ warn(`pagination: invalid pageSize ${String(pageSize)}; ignored`);
491
+ }
492
+ }
493
+ if (page !== undefined) {
494
+ if (typeof page === 'number' && Number.isFinite(page)) {
495
+ const requested = Math.trunc(page);
496
+ this.page = requested;
497
+ if (this.page !== requested) {
498
+ warn(`pagination: page ${requested} out of range; clamped to ${this.page}`);
499
+ }
500
+ }
501
+ else {
502
+ warn(`pagination: invalid page ${String(page)}; ignored`);
503
+ }
504
+ }
505
+ applied.push('pagination');
506
+ }
507
+ else {
508
+ warn(`pagination: expected an object, got ${typeof pagination}; skipped`);
509
+ }
510
+ }
511
+ this.requestUpdate(PIPELINE);
512
+ const skipped = SET_STATE_SLICES.filter((slice) => !applied.includes(slice));
513
+ return { applied, skipped, warnings };
514
+ }
257
515
  clearSort(key) {
258
516
  this.stateController.sorting.reset(key);
259
517
  this.requestUpdate(PIPELINE);
@@ -367,6 +625,33 @@ export class ApexGrid extends EventEmitterBase {
367
625
  editing.cancelCell();
368
626
  }
369
627
  }
628
+ get canUndo() {
629
+ return this.stateController.history.canUndo;
630
+ }
631
+ get canRedo() {
632
+ return this.stateController.history.canRedo;
633
+ }
634
+ undo() {
635
+ this.stateController.history.undo();
636
+ }
637
+ redo() {
638
+ this.stateController.history.redo();
639
+ }
640
+ clearHistory() {
641
+ this.stateController.history.clear();
642
+ }
643
+ pinRow(row, position = 'top') {
644
+ return this.stateController.rowPin.pinRow(row, position);
645
+ }
646
+ unpinRow(row) {
647
+ return this.stateController.rowPin.unpinRow(row);
648
+ }
649
+ get pinnedRows() {
650
+ return this.stateController.rowPin.pinnedRows;
651
+ }
652
+ moveRow(from, to, position = 'before') {
653
+ return this.stateController.rowReorder.moveRow(from, to, position);
654
+ }
370
655
  exportToCSV(options = {}) {
371
656
  const csv = buildCSV(this, options);
372
657
  const filename = options.filename;
@@ -381,6 +666,9 @@ export class ApexGrid extends EventEmitterBase {
381
666
  get exportFormats() {
382
667
  return [{ id: 'csv', label: 'Export CSV' }];
383
668
  }
669
+ get toolbarActions() {
670
+ return [];
671
+ }
384
672
  exportAs(formatId, options = {}) {
385
673
  if (formatId === 'csv') {
386
674
  this.exportToCSV(options);
@@ -438,6 +726,8 @@ export class ApexGrid extends EventEmitterBase {
438
726
  bodyPointerHandler(event) {
439
727
  if (this.stateController.modules.size === 0)
440
728
  return;
729
+ if (event.type === 'pointermove' && event.buttons === 0)
730
+ return;
441
731
  const target = event.composedPath().find((el) => el instanceof ApexGridCell);
442
732
  if (!target?.row)
443
733
  return;
@@ -453,12 +743,25 @@ export class ApexGrid extends EventEmitterBase {
453
743
  originalEvent: event,
454
744
  });
455
745
  }
746
+ renderGroupHeaderRow() {
747
+ if (!this.hasColumnGroups)
748
+ return nothing;
749
+ return html `
750
+ <apex-grid-group-header-row
751
+ style=${styleMap(this.DOM.columnSizes)}
752
+ .columns=${this.DOM.displayColumns}
753
+ .pinOffsets=${this.DOM.pinOffsets}
754
+ .groups=${this.columnGroups ?? []}
755
+ ></apex-grid-group-header-row>
756
+ `;
757
+ }
456
758
  renderHeaderRow() {
457
759
  return html `
458
760
  <apex-grid-header-row
459
761
  style=${styleMap(this.DOM.columnSizes)}
460
762
  .columns=${this.DOM.displayColumns}
461
763
  .pinOffsets=${this.DOM.pinOffsets}
764
+ .coordinateHints=${this.coordinateHints}
462
765
  ></apex-grid-header-row>
463
766
  `;
464
767
  }
@@ -470,19 +773,46 @@ export class ApexGrid extends EventEmitterBase {
470
773
  @click=${this.bodyClickHandler}
471
774
  @keydown=${this.bodyKeydownHandler}
472
775
  @pointerdown=${this.bodyPointerHandler}
473
- @pointerover=${this.bodyPointerHandler}
776
+ @pointermove=${this.bodyPointerHandler}
474
777
  @pointerup=${this.bodyPointerHandler}
475
778
  ></apex-virtualizer>
476
779
  `;
477
780
  }
781
+ get headerRowCount() {
782
+ return this.columnGroupDepth + 1 + (this.columns.some((column) => column.filter) ? 1 : 0);
783
+ }
784
+ renderPinnedTop() {
785
+ const rowPin = this.stateController.rowPin;
786
+ if (!rowPin.enabled)
787
+ return nothing;
788
+ const rows = rowPin.pinnedRows.top;
789
+ if (rows.length === 0)
790
+ return nothing;
791
+ return html `<div part="pinned-rows pinned-top">
792
+ ${rows.map((data, i) => this.DOM.pinnedRowRenderer(data, i, this.headerRowCount))}
793
+ </div>`;
794
+ }
795
+ renderPinnedBottom() {
796
+ const rowPin = this.stateController.rowPin;
797
+ if (!rowPin.enabled)
798
+ return nothing;
799
+ const rows = rowPin.pinnedRows.bottom;
800
+ if (rows.length === 0)
801
+ return nothing;
802
+ const offset = this.headerRowCount + rowPin.pinnedRows.top.length + this.pageItems.length;
803
+ return html `<div part="pinned-rows pinned-bottom">
804
+ ${rows.map((data, i) => this.DOM.pinnedRowRenderer(data, i, offset))}
805
+ </div>`;
806
+ }
478
807
  renderFilterRow() {
479
808
  return this.columns.some((column) => column.filter)
480
- ? html `<apex-filter-row style=${styleMap(this.DOM.columnSizes)}></apex-filter-row>`
809
+ ? html `<apex-filter-row></apex-filter-row>`
481
810
  : nothing;
482
811
  }
483
812
  renderToolbar() {
484
- if (!this.showQuickFilter && !this.showExport)
813
+ if (!this.showQuickFilter && !this.showExport && this.toolbarActions.length === 0) {
485
814
  return nothing;
815
+ }
486
816
  return html `<apex-grid-toolbar
487
817
  .value=${this.quickFilter}
488
818
  .showQuickFilter=${this.showQuickFilter}
@@ -512,15 +842,48 @@ export class ApexGrid extends EventEmitterBase {
512
842
  ${this.stateController.announcement}
513
843
  </div>`;
514
844
  }
845
+ renderRowDragGhost() {
846
+ const ghost = this.stateController.rowReorder.ghost;
847
+ if (!ghost)
848
+ return nothing;
849
+ return html `<div
850
+ part="row-drag-ghost"
851
+ style=${styleMap({
852
+ left: `${ghost.x}px`,
853
+ top: `${ghost.y}px`,
854
+ height: `${ghost.height}px`,
855
+ })}
856
+ >
857
+ <span part="row-drag-ghost-grip" aria-hidden="true">
858
+ <svg viewBox="0 0 16 16" width="16" height="16">
859
+ <circle cx="6" cy="3.5" r="1.25" />
860
+ <circle cx="10" cy="3.5" r="1.25" />
861
+ <circle cx="6" cy="8" r="1.25" />
862
+ <circle cx="10" cy="8" r="1.25" />
863
+ <circle cx="6" cy="12.5" r="1.25" />
864
+ <circle cx="10" cy="12.5" r="1.25" />
865
+ </svg>
866
+ </span>
867
+ ${ghost.cells.map((cell) => html `<span
868
+ part="row-drag-ghost-cell"
869
+ style=${styleMap({ width: `${cell.width}px`, textAlign: cell.align })}
870
+ >${cell.text}</span
871
+ >`)}
872
+ </div>`;
873
+ }
515
874
  render() {
516
875
  return html `
517
876
  ${this.stateController.resizing.renderIndicator()}
518
877
  ${this.renderToolbar()}
878
+ ${this.renderGroupHeaderRow()}
519
879
  ${this.renderHeaderRow()}
520
880
  ${this.renderFilterRow()}
881
+ ${this.renderPinnedTop()}
521
882
  ${this.renderBody()}
883
+ ${this.renderPinnedBottom()}
522
884
  ${this.renderPaginator()}
523
885
  ${this.renderLiveRegion()}
886
+ ${this.renderRowDragGhost()}
524
887
  `;
525
888
  }
526
889
  announce(message) {
@@ -554,6 +917,9 @@ __decorate([
554
917
  __decorate([
555
918
  property({ attribute: false })
556
919
  ], ApexGrid.prototype, "data", void 0);
920
+ __decorate([
921
+ property({ attribute: false })
922
+ ], ApexGrid.prototype, "rowId", void 0);
557
923
  __decorate([
558
924
  property({ type: Boolean, attribute: 'auto-generate' })
559
925
  ], ApexGrid.prototype, "autoGenerate", void 0);
@@ -575,9 +941,15 @@ __decorate([
575
941
  __decorate([
576
942
  property({ type: Boolean, attribute: 'show-export' })
577
943
  ], ApexGrid.prototype, "showExport", void 0);
944
+ __decorate([
945
+ property({ attribute: false })
946
+ ], ApexGrid.prototype, "localeText", void 0);
578
947
  __decorate([
579
948
  property({ type: Boolean, attribute: 'column-reordering' })
580
949
  ], ApexGrid.prototype, "columnReordering", void 0);
950
+ __decorate([
951
+ property({ type: Boolean, attribute: 'column-separator', reflect: true })
952
+ ], ApexGrid.prototype, "columnSeparator", void 0);
581
953
  __decorate([
582
954
  property({ attribute: false })
583
955
  ], ApexGrid.prototype, "editing", void 0);
@@ -587,6 +959,21 @@ __decorate([
587
959
  __decorate([
588
960
  property({ attribute: false })
589
961
  ], ApexGrid.prototype, "expansion", void 0);
962
+ __decorate([
963
+ property({ attribute: false })
964
+ ], ApexGrid.prototype, "rowPinning", void 0);
965
+ __decorate([
966
+ property({ attribute: false })
967
+ ], ApexGrid.prototype, "rowReordering", void 0);
968
+ __decorate([
969
+ property({ type: Boolean, reflect: true, attribute: 'coordinate-hints' })
970
+ ], ApexGrid.prototype, "coordinateHints", void 0);
971
+ __decorate([
972
+ property({ attribute: false })
973
+ ], ApexGrid.prototype, "columnMenu", void 0);
974
+ __decorate([
975
+ property({ attribute: false })
976
+ ], ApexGrid.prototype, "columnGroups", void 0);
590
977
  __decorate([
591
978
  property({ attribute: false })
592
979
  ], ApexGrid.prototype, "tree", void 0);