apex-grid 3.2.0 → 3.4.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 (205) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/README.md +229 -52
  3. package/components/cell.d.ts +16 -3
  4. package/components/cell.js +78 -8
  5. package/components/cell.js.map +1 -1
  6. package/components/filter-row.d.ts +3 -6
  7. package/components/filter-row.js +144 -115
  8. package/components/filter-row.js.map +1 -1
  9. package/components/grid.d.ts +421 -77
  10. package/components/grid.js +432 -13
  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 +209 -8
  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/expansion.js +4 -4
  39. package/controllers/expansion.js.map +1 -1
  40. package/controllers/filter.d.ts +1 -1
  41. package/controllers/filter.js +27 -5
  42. package/controllers/filter.js.map +1 -1
  43. package/controllers/history.d.ts +86 -0
  44. package/controllers/history.js +125 -0
  45. package/controllers/history.js.map +1 -0
  46. package/controllers/navigation.d.ts +49 -2
  47. package/controllers/navigation.js +145 -3
  48. package/controllers/navigation.js.map +1 -1
  49. package/controllers/pagination.js +1 -1
  50. package/controllers/pagination.js.map +1 -1
  51. package/controllers/reorder.js +3 -1
  52. package/controllers/reorder.js.map +1 -1
  53. package/controllers/row-pin.d.ts +59 -0
  54. package/controllers/row-pin.js +75 -0
  55. package/controllers/row-pin.js.map +1 -0
  56. package/controllers/row-reorder.d.ts +161 -0
  57. package/controllers/row-reorder.js +332 -0
  58. package/controllers/row-reorder.js.map +1 -0
  59. package/controllers/selection.js +3 -3
  60. package/controllers/selection.js.map +1 -1
  61. package/controllers/sort.js +13 -3
  62. package/controllers/sort.js.map +1 -1
  63. package/controllers/state.d.ts +33 -0
  64. package/controllers/state.js +37 -2
  65. package/controllers/state.js.map +1 -1
  66. package/controllers/tree.d.ts +8 -0
  67. package/controllers/tree.js +0 -0
  68. package/controllers/tree.js.map +1 -1
  69. package/custom-elements.json +4667 -926
  70. package/docs/assets/hierarchy.js +1 -1
  71. package/docs/assets/highlight.css +28 -0
  72. package/docs/assets/navigation.js +1 -1
  73. package/docs/assets/search.js +1 -1
  74. package/docs/functions/custom.html +3 -0
  75. package/docs/functions/interpolate.html +4 -0
  76. package/docs/functions/localize.html +8 -0
  77. package/docs/functions/max.html +3 -0
  78. package/docs/functions/min.html +3 -0
  79. package/docs/functions/pattern.html +3 -0
  80. package/docs/functions/required.html +3 -0
  81. package/docs/hierarchy.html +1 -1
  82. package/docs/index.html +245 -54
  83. package/docs/interfaces/ApexCellValidationFailedEvent.html +13 -0
  84. package/docs/interfaces/ApexCellValueChangedEvent.html +1 -1
  85. package/docs/interfaces/ApexCellValueChangingEvent.html +2 -2
  86. package/docs/interfaces/ApexColumnGroupContext.html +6 -0
  87. package/docs/interfaces/ApexColumnMovedEvent.html +2 -2
  88. package/docs/interfaces/ApexColumnMovingEvent.html +1 -1
  89. package/docs/interfaces/ApexDetailContext.html +2 -2
  90. package/docs/interfaces/ApexGridEventMap.html +37 -9
  91. package/docs/interfaces/ApexHistoryChangedEvent.html +6 -0
  92. package/docs/interfaces/ApexPageChangedEvent.html +1 -1
  93. package/docs/interfaces/ApexRowMovingEvent.html +9 -0
  94. package/docs/interfaces/ApexRowPinningEvent.html +8 -0
  95. package/docs/interfaces/ApexStateChangedEvent.html +4 -0
  96. package/docs/interfaces/BaseApexCellContext.html +1 -1
  97. package/docs/interfaces/BaseApexEditorContext.html +1 -1
  98. package/docs/interfaces/BaseColumnConfiguration.html +42 -17
  99. package/docs/interfaces/CSVExportOptions.html +2 -2
  100. package/docs/interfaces/ColumnGroupConfiguration.html +12 -0
  101. package/docs/interfaces/ColumnLayoutState.html +2 -0
  102. package/docs/interfaces/ColumnSchema.html +22 -0
  103. package/docs/interfaces/DataPipelineConfiguration.html +2 -2
  104. package/docs/interfaces/EditingHistoryConfiguration.html +10 -0
  105. package/docs/interfaces/ExportFormat.html +2 -2
  106. package/docs/interfaces/ExportOptions.html +1 -1
  107. package/docs/interfaces/FilterStateSnapshot.html +4 -0
  108. package/docs/interfaces/GetStateOptions.html +4 -0
  109. package/docs/interfaces/GridCapabilities.html +8 -0
  110. package/docs/interfaces/GridEditingConfiguration.html +4 -2
  111. package/docs/interfaces/GridRowPinningConfiguration.html +5 -0
  112. package/docs/interfaces/GridRowReorderingConfiguration.html +22 -0
  113. package/docs/interfaces/GridSchema.html +19 -0
  114. package/docs/interfaces/GridState.html +41 -0
  115. package/docs/interfaces/GridTreeConfiguration.html +2 -2
  116. package/docs/interfaces/PaginationConfiguration.html +1 -1
  117. package/docs/interfaces/PaginationState.html +1 -1
  118. package/docs/interfaces/SetStateOptions.html +9 -0
  119. package/docs/interfaces/SetStateResult.html +11 -0
  120. package/docs/interfaces/SortStateSnapshot.html +2 -0
  121. package/docs/interfaces/ValidatorContext.html +8 -0
  122. package/docs/media/_tokens.scss +70 -81
  123. package/docs/media/package.json +2 -2
  124. package/docs/media/state-persistence.html +189 -0
  125. package/docs/modules.html +1 -1
  126. package/docs/types/DataPipelineParams.html +2 -2
  127. package/docs/types/EditMode.html +2 -2
  128. package/docs/types/GridLocaleKey.html +3 -0
  129. package/docs/types/GridLocaleText.html +5 -0
  130. package/docs/types/LocaleParams.html +2 -0
  131. package/docs/types/PaginationMode.html +1 -1
  132. package/docs/types/RowDropPosition.html +2 -0
  133. package/docs/types/RowPinPosition.html +2 -0
  134. package/docs/types/RowRef.html +3 -0
  135. package/docs/types/Validator.html +4 -0
  136. package/docs/variables/EN_LOCALE.html +9 -0
  137. package/docs/variables/esLocale.html +8 -0
  138. package/i18n/en.d.ts +245 -0
  139. package/i18n/en.js +219 -0
  140. package/i18n/en.js.map +1 -0
  141. package/i18n/es.d.ts +18 -0
  142. package/i18n/es.js +219 -0
  143. package/i18n/es.js.map +1 -0
  144. package/i18n/index.d.ts +3 -0
  145. package/i18n/index.js +4 -0
  146. package/i18n/index.js.map +1 -0
  147. package/i18n/localize.d.ts +23 -0
  148. package/i18n/localize.js +15 -0
  149. package/i18n/localize.js.map +1 -0
  150. package/index.d.ts +8 -2
  151. package/index.js +2 -0
  152. package/index.js.map +1 -1
  153. package/internal/column-types.js +1 -1
  154. package/internal/column-types.js.map +1 -1
  155. package/internal/feature-module.d.ts +46 -0
  156. package/internal/feature-module.js +11 -0
  157. package/internal/feature-module.js.map +1 -1
  158. package/internal/flip.js +1 -1
  159. package/internal/flip.js.map +1 -1
  160. package/internal/icons.d.ts +9 -7
  161. package/internal/icons.js +75 -35
  162. package/internal/icons.js.map +1 -1
  163. package/internal/index.d.ts +5 -3
  164. package/internal/index.js +2 -1
  165. package/internal/index.js.map +1 -1
  166. package/internal/state-schema.d.ts +85 -0
  167. package/internal/state-schema.js +27 -0
  168. package/internal/state-schema.js.map +1 -0
  169. package/internal/state-snapshot.d.ts +152 -0
  170. package/internal/state-snapshot.js +115 -0
  171. package/internal/state-snapshot.js.map +1 -0
  172. package/internal/tags.d.ts +1 -0
  173. package/internal/tags.js +1 -0
  174. package/internal/tags.js.map +1 -1
  175. package/internal/types.d.ts +162 -2
  176. package/internal/types.js.map +1 -1
  177. package/internal/utils.d.ts +12 -0
  178. package/internal/utils.js +17 -0
  179. package/internal/utils.js.map +1 -1
  180. package/internal/validators.d.ts +31 -0
  181. package/internal/validators.js +44 -0
  182. package/internal/validators.js.map +1 -0
  183. package/package.json +1 -1
  184. package/styles/_tokens.css.js +1 -1
  185. package/styles/_tokens.css.js.map +1 -1
  186. package/styles/body-cell/body-cell.css.js +1 -1
  187. package/styles/body-cell/body-cell.css.js.map +1 -1
  188. package/styles/body-row/body-row.css.js +1 -1
  189. package/styles/body-row/body-row.css.js.map +1 -1
  190. package/styles/filter-row/filter-row.css.js +1 -1
  191. package/styles/filter-row/filter-row.css.js.map +1 -1
  192. package/styles/grid/_grid.base.css.js +1 -1
  193. package/styles/grid/_grid.base.css.js.map +1 -1
  194. package/styles/grid/grid.css.js +1 -1
  195. package/styles/grid/grid.css.js.map +1 -1
  196. package/styles/group-header-row/group-header-row.css.d.ts +1 -0
  197. package/styles/group-header-row/group-header-row.css.js +3 -0
  198. package/styles/group-header-row/group-header-row.css.js.map +1 -0
  199. package/styles/header-cell/header-cell.css.js +1 -1
  200. package/styles/header-cell/header-cell.css.js.map +1 -1
  201. package/styles/header-row/header-row.base.css.js +1 -1
  202. package/styles/header-row/header-row.base.css.js.map +1 -1
  203. package/styles/toolbar/toolbar.css.js +1 -1
  204. package/styles/toolbar/toolbar.css.js.map +1 -1
  205. 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 { DEFAULT_COLUMN_CONFIG, PIPELINE } from '../internal/constants.js';
14
+ import { localize as resolveLocaleText, } from '../i18n/index.js';
15
+ import { DEFAULT_COLUMN_CONFIG, PIPELINE, SENTINEL_NODE } 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,26 @@ 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.#autoAriaLabel = null;
72
+ this.#stateChangedListeners = 0;
73
+ this.#lastStateJson = null;
51
74
  }
52
75
  static get tagName() {
53
76
  return GRID_TAG;
54
77
  }
55
78
  static { this.styles = gridStyles; }
56
79
  static register() {
57
- registerComponent(ApexGrid, ApexVirtualizer, ApexGridRow, ApexGridHeaderRow, ApexFilterRow, ApexGridPaginator, ApexGridToolbar);
80
+ registerComponent(ApexGrid, ApexVirtualizer, ApexGridRow, ApexGridGroupHeaderRow, ApexGridHeaderRow, ApexFilterRow, ApexGridPaginator, ApexGridToolbar);
58
81
  }
59
82
  createStateController() {
60
83
  return new StateController(this);
61
84
  }
85
+ localize(key, params, fallback) {
86
+ return resolveLocaleText(this.localeText, key, params, fallback);
87
+ }
62
88
  get selectedRows() {
63
89
  return this.stateController.selection.selectedRows();
64
90
  }
@@ -83,6 +109,15 @@ export class ApexGrid extends EventEmitterBase {
83
109
  isRowSelected(row) {
84
110
  return this.stateController.selection.isSelected(row);
85
111
  }
112
+ get hasColumnGroups() {
113
+ if (!this.columnGroups?.length)
114
+ return false;
115
+ const ids = new Set(this.columnGroups.map((group) => group.id));
116
+ return this.columns.some((column) => !column.hidden && column.group && ids.has(column.group));
117
+ }
118
+ get columnGroupDepth() {
119
+ return this.hasColumnGroups ? 1 : 0;
120
+ }
86
121
  get expandedRows() {
87
122
  return this.stateController.expansion.expandedRows();
88
123
  }
@@ -161,15 +196,19 @@ export class ApexGrid extends EventEmitterBase {
161
196
  return this.dataState.length;
162
197
  }
163
198
  get pageItems() {
199
+ const rowPin = this.stateController.rowPin;
200
+ const base = rowPin?.enabled && rowPin.hasPinnedRows
201
+ ? this.dataState.filter((row) => !rowPin.isPinned(row))
202
+ : this.dataState;
164
203
  if (!this.stateController.pagination.enabled)
165
- return this.dataState;
204
+ return base;
166
205
  if (this.pagination?.mode === 'remote')
167
- return this.dataState;
206
+ return base;
168
207
  const { page, pageSize } = this.stateController.pagination;
169
208
  if (!pageSize)
170
- return this.dataState;
209
+ return base;
171
210
  const start = page * pageSize;
172
- return this.dataState.slice(start, start + pageSize);
211
+ return base.slice(start, start + pageSize);
173
212
  }
174
213
  get page() {
175
214
  return this.stateController.pagination.page;
@@ -227,13 +266,58 @@ export class ApexGrid extends EventEmitterBase {
227
266
  }
228
267
  updated() {
229
268
  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));
269
+ const pinned = this.stateController.rowPin.pinnedRows;
270
+ this.setAttribute('aria-rowcount', String(this.headerRowCount + pinned.top.length + this.pageItems.length + pinned.bottom.length));
233
271
  const visibleColumns = this.columns.filter((column) => !column.hidden).length;
234
272
  const extras = (this.stateController.selection.showCheckboxColumn ? 1 : 0) +
235
273
  (this.stateController.expansion.showToggleColumn ? 1 : 0);
236
274
  this.setAttribute('aria-colcount', String(visibleColumns + extras));
275
+ const selection = this.stateController.selection;
276
+ if (selection.enabled && selection.mode === 'multiple') {
277
+ this.setAttribute('aria-multiselectable', 'true');
278
+ }
279
+ else {
280
+ this.removeAttribute('aria-multiselectable');
281
+ }
282
+ if (this.scrollContainer) {
283
+ this.scrollContainer.tabIndex = this.stateController.active === SENTINEL_NODE ? 0 : -1;
284
+ }
285
+ const currentLabel = this.getAttribute('aria-label');
286
+ const authorLabeled = this.hasAttribute('aria-labelledby') ||
287
+ (currentLabel !== null && currentLabel !== this.#autoAriaLabel);
288
+ if (!authorLabeled) {
289
+ const label = this.localize('grid.label');
290
+ if (currentLabel !== label)
291
+ this.setAttribute('aria-label', label);
292
+ this.#autoAriaLabel = label;
293
+ }
294
+ this.#emitStateChangedIfNeeded();
295
+ }
296
+ #autoAriaLabel;
297
+ #stateChangedListeners;
298
+ #lastStateJson;
299
+ addEventListener(type, listener, options) {
300
+ if (type === 'stateChanged') {
301
+ this.#stateChangedListeners += 1;
302
+ this.#lastStateJson = this.hasUpdated ? JSON.stringify(this.getState()) : null;
303
+ }
304
+ super.addEventListener(type, listener, options);
305
+ }
306
+ removeEventListener(type, listener, options) {
307
+ if (type === 'stateChanged') {
308
+ this.#stateChangedListeners = Math.max(0, this.#stateChangedListeners - 1);
309
+ }
310
+ super.removeEventListener(type, listener, options);
311
+ }
312
+ #emitStateChangedIfNeeded() {
313
+ if (this.#stateChangedListeners === 0)
314
+ return;
315
+ const snapshot = this.getState();
316
+ const json = JSON.stringify(snapshot);
317
+ if (json === this.#lastStateJson)
318
+ return;
319
+ this.#lastStateJson = json;
320
+ this.emitEvent('stateChanged', { detail: { state: snapshot } });
237
321
  }
238
322
  firstUpdated() {
239
323
  if (typeof getComputedStyle === 'function' && getComputedStyle(this).display !== 'grid') {
@@ -254,6 +338,201 @@ export class ApexGrid extends EventEmitterBase {
254
338
  sort(expressions) {
255
339
  this.stateController.sorting.sort(expressions);
256
340
  }
341
+ getState(options) {
342
+ const rowId = options?.rowId ?? this.rowId;
343
+ const pinned = this.stateController.rowPin.pinnedRows;
344
+ const manualOrder = this.stateController.rowReorder.getManualOrder();
345
+ return {
346
+ version: 1,
347
+ columns: serializeColumnLayout(this.columns),
348
+ sort: serializeSort(this.sortExpressions),
349
+ filter: serializeFilter(this.filterExpressions),
350
+ quickFilter: this.quickFilter,
351
+ pagination: { page: this.page, pageSize: this.pageSize },
352
+ selection: serializeRowRefs(this.selectedRows, this.data, rowId),
353
+ expansion: serializeRowRefs(this.expandedRows, this.data, rowId),
354
+ treeExpanded: serializeRowRefs([...this.stateController.tree.expanded], this.data, rowId),
355
+ treeExpandedKeys: [...this.stateController.tree.expandedKeys],
356
+ rowPinning: {
357
+ top: serializeRowRefs(pinned.top, this.data, rowId),
358
+ bottom: serializeRowRefs(pinned.bottom, this.data, rowId),
359
+ },
360
+ rowOrder: manualOrder ? serializeRowRefs(manualOrder, this.data, rowId) : null,
361
+ modules: this.stateController.serializeModuleState(),
362
+ };
363
+ }
364
+ getSchema() {
365
+ return {
366
+ version: 1,
367
+ columns: this.columns.map((column) => columnSchema(column)),
368
+ capabilities: {
369
+ sort: {
370
+ directions: ['ascending', 'descending'],
371
+ multi: Boolean(this.sortConfiguration?.multiple),
372
+ },
373
+ filter: { operandsByType: operandsByType(this.columns) },
374
+ pagination: Boolean(this.pagination?.enabled),
375
+ selection: this.selection?.enabled ? (this.selection.mode ?? 'multiple') : false,
376
+ rowPinning: Boolean(this.rowPinning?.enabled),
377
+ rowReordering: Boolean(this.rowReordering?.enabled),
378
+ },
379
+ state: this.getState(),
380
+ };
381
+ }
382
+ setState(state, options) {
383
+ const rowId = options?.rowId ?? this.rowId;
384
+ const strict = options?.strict ?? false;
385
+ const applied = [];
386
+ const warnings = [];
387
+ const warn = (message) => {
388
+ if (strict)
389
+ throw new Error(`[apex-grid] setState: ${message}`);
390
+ warnings.push(message);
391
+ };
392
+ const asArray = (value, label) => {
393
+ if (value === undefined)
394
+ return null;
395
+ if (!Array.isArray(value)) {
396
+ warn(`${label}: expected an array, got ${typeof value}; skipped`);
397
+ return null;
398
+ }
399
+ return value;
400
+ };
401
+ const resolveCounted = (refs, label) => {
402
+ const rows = resolveRowRefs(refs, this.data, rowId);
403
+ if (rows.length < refs.length) {
404
+ warn(`${label}: ${refs.length - rows.length} of ${refs.length} row(s) could not be resolved`);
405
+ }
406
+ return rows;
407
+ };
408
+ if (state.version !== undefined && state.version !== 1) {
409
+ warn(`unsupported snapshot version ${String(state.version)}; applying recognized slices best-effort`);
410
+ }
411
+ const columns = asArray(state.columns, 'columns');
412
+ if (columns) {
413
+ this.columns = applyColumnLayout(this.columns, columns);
414
+ applied.push('columns');
415
+ }
416
+ const sort = asArray(state.sort, 'sort');
417
+ if (sort) {
418
+ this.stateController.sorting.reset();
419
+ const valid = [];
420
+ for (const entry of sort) {
421
+ if (this.getColumn(entry.key)) {
422
+ valid.push(entry);
423
+ }
424
+ else {
425
+ warn(`sort: unknown column "${entry.key}"; dropped`);
426
+ }
427
+ }
428
+ if (valid.length)
429
+ this.sort(valid);
430
+ applied.push('sort');
431
+ }
432
+ const filter = asArray(state.filter, 'filter');
433
+ if (filter) {
434
+ this.stateController.filtering.reset();
435
+ const expressions = deserializeFilter(filter, (key) => this.getColumn(key), (snapshot, reason) => warn(`filter: "${snapshot.key}" dropped (${reason})`));
436
+ if (expressions.length)
437
+ this.filter(expressions);
438
+ applied.push('filter');
439
+ }
440
+ if (state.quickFilter !== undefined) {
441
+ if (typeof state.quickFilter === 'string') {
442
+ this.quickFilter = state.quickFilter;
443
+ applied.push('quickFilter');
444
+ }
445
+ else {
446
+ warn(`quickFilter: expected a string, got ${typeof state.quickFilter}; skipped`);
447
+ }
448
+ }
449
+ if (state.modules !== undefined) {
450
+ if (state.modules && typeof state.modules === 'object') {
451
+ this.stateController.restoreModuleState(state.modules);
452
+ applied.push('modules');
453
+ }
454
+ else {
455
+ warn(`modules: expected an object, got ${typeof state.modules}; skipped`);
456
+ }
457
+ }
458
+ if (state.rowOrder !== undefined) {
459
+ if (state.rowOrder === null) {
460
+ this.stateController.rowReorder.restoreManualOrder(null);
461
+ applied.push('rowOrder');
462
+ }
463
+ else {
464
+ const order = asArray(state.rowOrder, 'rowOrder');
465
+ if (order) {
466
+ if (this.sortExpressions.length) {
467
+ warn('rowOrder dropped: a manual order is mutually exclusive with an active sort');
468
+ }
469
+ else {
470
+ this.stateController.rowReorder.restoreManualOrder(resolveCounted(order, 'rowOrder'));
471
+ }
472
+ applied.push('rowOrder');
473
+ }
474
+ }
475
+ }
476
+ if (state.treeExpanded !== undefined || state.treeExpandedKeys !== undefined) {
477
+ const refs = asArray(state.treeExpanded, 'treeExpanded') ?? [];
478
+ const keys = asArray(state.treeExpandedKeys, 'treeExpandedKeys') ?? [];
479
+ this.stateController.tree.restoreExpansion(resolveCounted(refs, 'treeExpanded'), keys);
480
+ applied.push('tree');
481
+ }
482
+ const expansion = asArray(state.expansion, 'expansion');
483
+ if (expansion) {
484
+ this.expandedRows = resolveCounted(expansion, 'expansion');
485
+ applied.push('expansion');
486
+ }
487
+ if (state.rowPinning !== undefined) {
488
+ const pinning = state.rowPinning;
489
+ if (pinning && typeof pinning === 'object') {
490
+ this.stateController.rowPin.restore(resolveCounted(asArray(pinning.top, 'rowPinning.top') ?? [], 'rowPinning.top'), resolveCounted(asArray(pinning.bottom, 'rowPinning.bottom') ?? [], 'rowPinning.bottom'));
491
+ applied.push('rowPinning');
492
+ }
493
+ else {
494
+ warn(`rowPinning: expected an object, got ${typeof pinning}; skipped`);
495
+ }
496
+ }
497
+ const selection = asArray(state.selection, 'selection');
498
+ if (selection) {
499
+ this.selectedRows = resolveCounted(selection, 'selection');
500
+ applied.push('selection');
501
+ }
502
+ if (state.pagination !== undefined) {
503
+ const pagination = state.pagination;
504
+ if (pagination && typeof pagination === 'object') {
505
+ const { page, pageSize } = pagination;
506
+ if (pageSize !== undefined) {
507
+ if (typeof pageSize === 'number' && Number.isFinite(pageSize) && pageSize > 0) {
508
+ this.pageSize = pageSize;
509
+ }
510
+ else {
511
+ warn(`pagination: invalid pageSize ${String(pageSize)}; ignored`);
512
+ }
513
+ }
514
+ if (page !== undefined) {
515
+ if (typeof page === 'number' && Number.isFinite(page)) {
516
+ const requested = Math.trunc(page);
517
+ this.page = requested;
518
+ if (this.page !== requested) {
519
+ warn(`pagination: page ${requested} out of range; clamped to ${this.page}`);
520
+ }
521
+ }
522
+ else {
523
+ warn(`pagination: invalid page ${String(page)}; ignored`);
524
+ }
525
+ }
526
+ applied.push('pagination');
527
+ }
528
+ else {
529
+ warn(`pagination: expected an object, got ${typeof pagination}; skipped`);
530
+ }
531
+ }
532
+ this.requestUpdate(PIPELINE);
533
+ const skipped = SET_STATE_SLICES.filter((slice) => !applied.includes(slice));
534
+ return { applied, skipped, warnings };
535
+ }
257
536
  clearSort(key) {
258
537
  this.stateController.sorting.reset(key);
259
538
  this.requestUpdate(PIPELINE);
@@ -367,6 +646,33 @@ export class ApexGrid extends EventEmitterBase {
367
646
  editing.cancelCell();
368
647
  }
369
648
  }
649
+ get canUndo() {
650
+ return this.stateController.history.canUndo;
651
+ }
652
+ get canRedo() {
653
+ return this.stateController.history.canRedo;
654
+ }
655
+ undo() {
656
+ this.stateController.history.undo();
657
+ }
658
+ redo() {
659
+ this.stateController.history.redo();
660
+ }
661
+ clearHistory() {
662
+ this.stateController.history.clear();
663
+ }
664
+ pinRow(row, position = 'top') {
665
+ return this.stateController.rowPin.pinRow(row, position);
666
+ }
667
+ unpinRow(row) {
668
+ return this.stateController.rowPin.unpinRow(row);
669
+ }
670
+ get pinnedRows() {
671
+ return this.stateController.rowPin.pinnedRows;
672
+ }
673
+ moveRow(from, to, position = 'before') {
674
+ return this.stateController.rowReorder.moveRow(from, to, position);
675
+ }
370
676
  exportToCSV(options = {}) {
371
677
  const csv = buildCSV(this, options);
372
678
  const filename = options.filename;
@@ -381,6 +687,9 @@ export class ApexGrid extends EventEmitterBase {
381
687
  get exportFormats() {
382
688
  return [{ id: 'csv', label: 'Export CSV' }];
383
689
  }
690
+ get toolbarActions() {
691
+ return [];
692
+ }
384
693
  exportAs(formatId, options = {}) {
385
694
  if (formatId === 'csv') {
386
695
  this.exportToCSV(options);
@@ -428,16 +737,28 @@ export class ApexGrid extends EventEmitterBase {
428
737
  column: target.column.key,
429
738
  row: target.row.index,
430
739
  };
740
+ if (!target.editing)
741
+ target.focus({ preventScroll: true });
431
742
  }
432
743
  }
433
744
  bodyKeydownHandler(event) {
434
- if (this.scrollContainer.isSameNode(event.target)) {
745
+ const origin = event.composedPath()[0];
746
+ const fromBody = this.scrollContainer.isSameNode(event.target);
747
+ const fromCell = origin instanceof ApexGridCell && !origin.editing;
748
+ if (fromBody || fromCell) {
435
749
  this.stateController.navigation.navigate(event);
436
750
  }
437
751
  }
752
+ bodyFocusHandler(event) {
753
+ if (this.scrollContainer.isSameNode(event.target)) {
754
+ this.stateController.navigation.focusActiveCell();
755
+ }
756
+ }
438
757
  bodyPointerHandler(event) {
439
758
  if (this.stateController.modules.size === 0)
440
759
  return;
760
+ if (event.type === 'pointermove' && event.buttons === 0)
761
+ return;
441
762
  const target = event.composedPath().find((el) => el instanceof ApexGridCell);
442
763
  if (!target?.row)
443
764
  return;
@@ -453,12 +774,25 @@ export class ApexGrid extends EventEmitterBase {
453
774
  originalEvent: event,
454
775
  });
455
776
  }
777
+ renderGroupHeaderRow() {
778
+ if (!this.hasColumnGroups)
779
+ return nothing;
780
+ return html `
781
+ <apex-grid-group-header-row
782
+ style=${styleMap(this.DOM.columnSizes)}
783
+ .columns=${this.DOM.displayColumns}
784
+ .pinOffsets=${this.DOM.pinOffsets}
785
+ .groups=${this.columnGroups ?? []}
786
+ ></apex-grid-group-header-row>
787
+ `;
788
+ }
456
789
  renderHeaderRow() {
457
790
  return html `
458
791
  <apex-grid-header-row
459
792
  style=${styleMap(this.DOM.columnSizes)}
460
793
  .columns=${this.DOM.displayColumns}
461
794
  .pinOffsets=${this.DOM.pinOffsets}
795
+ .coordinateHints=${this.coordinateHints}
462
796
  ></apex-grid-header-row>
463
797
  `;
464
798
  }
@@ -469,20 +803,48 @@ export class ApexGrid extends EventEmitterBase {
469
803
  .renderItem=${this.DOM.rowRenderer}
470
804
  @click=${this.bodyClickHandler}
471
805
  @keydown=${this.bodyKeydownHandler}
806
+ @focusin=${this.bodyFocusHandler}
472
807
  @pointerdown=${this.bodyPointerHandler}
473
- @pointerover=${this.bodyPointerHandler}
808
+ @pointermove=${this.bodyPointerHandler}
474
809
  @pointerup=${this.bodyPointerHandler}
475
810
  ></apex-virtualizer>
476
811
  `;
477
812
  }
813
+ get headerRowCount() {
814
+ return this.columnGroupDepth + 1 + (this.columns.some((column) => column.filter) ? 1 : 0);
815
+ }
816
+ renderPinnedTop() {
817
+ const rowPin = this.stateController.rowPin;
818
+ if (!rowPin.enabled)
819
+ return nothing;
820
+ const rows = rowPin.pinnedRows.top;
821
+ if (rows.length === 0)
822
+ return nothing;
823
+ return html `<div part="pinned-rows pinned-top">
824
+ ${rows.map((data, i) => this.DOM.pinnedRowRenderer(data, i, this.headerRowCount))}
825
+ </div>`;
826
+ }
827
+ renderPinnedBottom() {
828
+ const rowPin = this.stateController.rowPin;
829
+ if (!rowPin.enabled)
830
+ return nothing;
831
+ const rows = rowPin.pinnedRows.bottom;
832
+ if (rows.length === 0)
833
+ return nothing;
834
+ const offset = this.headerRowCount + rowPin.pinnedRows.top.length + this.pageItems.length;
835
+ return html `<div part="pinned-rows pinned-bottom">
836
+ ${rows.map((data, i) => this.DOM.pinnedRowRenderer(data, i, offset))}
837
+ </div>`;
838
+ }
478
839
  renderFilterRow() {
479
840
  return this.columns.some((column) => column.filter)
480
- ? html `<apex-filter-row style=${styleMap(this.DOM.columnSizes)}></apex-filter-row>`
841
+ ? html `<apex-filter-row></apex-filter-row>`
481
842
  : nothing;
482
843
  }
483
844
  renderToolbar() {
484
- if (!this.showQuickFilter && !this.showExport)
845
+ if (!this.showQuickFilter && !this.showExport && this.toolbarActions.length === 0) {
485
846
  return nothing;
847
+ }
486
848
  return html `<apex-grid-toolbar
487
849
  .value=${this.quickFilter}
488
850
  .showQuickFilter=${this.showQuickFilter}
@@ -512,15 +874,48 @@ export class ApexGrid extends EventEmitterBase {
512
874
  ${this.stateController.announcement}
513
875
  </div>`;
514
876
  }
877
+ renderRowDragGhost() {
878
+ const ghost = this.stateController.rowReorder.ghost;
879
+ if (!ghost)
880
+ return nothing;
881
+ return html `<div
882
+ part="row-drag-ghost"
883
+ style=${styleMap({
884
+ left: `${ghost.x}px`,
885
+ top: `${ghost.y}px`,
886
+ height: `${ghost.height}px`,
887
+ })}
888
+ >
889
+ <span part="row-drag-ghost-grip" aria-hidden="true">
890
+ <svg viewBox="0 0 16 16" width="16" height="16">
891
+ <circle cx="6" cy="3.5" r="1.25" />
892
+ <circle cx="10" cy="3.5" r="1.25" />
893
+ <circle cx="6" cy="8" r="1.25" />
894
+ <circle cx="10" cy="8" r="1.25" />
895
+ <circle cx="6" cy="12.5" r="1.25" />
896
+ <circle cx="10" cy="12.5" r="1.25" />
897
+ </svg>
898
+ </span>
899
+ ${ghost.cells.map((cell) => html `<span
900
+ part="row-drag-ghost-cell"
901
+ style=${styleMap({ width: `${cell.width}px`, textAlign: cell.align })}
902
+ >${cell.text}</span
903
+ >`)}
904
+ </div>`;
905
+ }
515
906
  render() {
516
907
  return html `
517
908
  ${this.stateController.resizing.renderIndicator()}
518
909
  ${this.renderToolbar()}
910
+ ${this.renderGroupHeaderRow()}
519
911
  ${this.renderHeaderRow()}
520
912
  ${this.renderFilterRow()}
913
+ ${this.renderPinnedTop()}
521
914
  ${this.renderBody()}
915
+ ${this.renderPinnedBottom()}
522
916
  ${this.renderPaginator()}
523
917
  ${this.renderLiveRegion()}
918
+ ${this.renderRowDragGhost()}
524
919
  `;
525
920
  }
526
921
  announce(message) {
@@ -554,6 +949,9 @@ __decorate([
554
949
  __decorate([
555
950
  property({ attribute: false })
556
951
  ], ApexGrid.prototype, "data", void 0);
952
+ __decorate([
953
+ property({ attribute: false })
954
+ ], ApexGrid.prototype, "rowId", void 0);
557
955
  __decorate([
558
956
  property({ type: Boolean, attribute: 'auto-generate' })
559
957
  ], ApexGrid.prototype, "autoGenerate", void 0);
@@ -575,9 +973,15 @@ __decorate([
575
973
  __decorate([
576
974
  property({ type: Boolean, attribute: 'show-export' })
577
975
  ], ApexGrid.prototype, "showExport", void 0);
976
+ __decorate([
977
+ property({ attribute: false })
978
+ ], ApexGrid.prototype, "localeText", void 0);
578
979
  __decorate([
579
980
  property({ type: Boolean, attribute: 'column-reordering' })
580
981
  ], ApexGrid.prototype, "columnReordering", void 0);
982
+ __decorate([
983
+ property({ type: Boolean, attribute: 'column-separator', reflect: true })
984
+ ], ApexGrid.prototype, "columnSeparator", void 0);
581
985
  __decorate([
582
986
  property({ attribute: false })
583
987
  ], ApexGrid.prototype, "editing", void 0);
@@ -587,6 +991,21 @@ __decorate([
587
991
  __decorate([
588
992
  property({ attribute: false })
589
993
  ], ApexGrid.prototype, "expansion", void 0);
994
+ __decorate([
995
+ property({ attribute: false })
996
+ ], ApexGrid.prototype, "rowPinning", void 0);
997
+ __decorate([
998
+ property({ attribute: false })
999
+ ], ApexGrid.prototype, "rowReordering", void 0);
1000
+ __decorate([
1001
+ property({ type: Boolean, reflect: true, attribute: 'coordinate-hints' })
1002
+ ], ApexGrid.prototype, "coordinateHints", void 0);
1003
+ __decorate([
1004
+ property({ attribute: false })
1005
+ ], ApexGrid.prototype, "columnMenu", void 0);
1006
+ __decorate([
1007
+ property({ attribute: false })
1008
+ ], ApexGrid.prototype, "columnGroups", void 0);
590
1009
  __decorate([
591
1010
  property({ attribute: false })
592
1011
  ], ApexGrid.prototype, "tree", void 0);