@ni/nimble-components 20.18.1 → 21.0.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 (53) hide show
  1. package/dist/all-components-bundle.js +395 -107
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +505 -480
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/label-provider/table/index.d.ts +3 -3
  6. package/dist/esm/label-provider/table/index.js +4 -4
  7. package/dist/esm/label-provider/table/index.js.map +1 -1
  8. package/dist/esm/label-provider/table/label-token-defaults.js +1 -1
  9. package/dist/esm/label-provider/table/label-token-defaults.js.map +1 -1
  10. package/dist/esm/label-provider/table/label-tokens.d.ts +1 -1
  11. package/dist/esm/label-provider/table/label-tokens.js +3 -3
  12. package/dist/esm/label-provider/table/label-tokens.js.map +1 -1
  13. package/dist/esm/table/components/group-row/index.d.ts +1 -1
  14. package/dist/esm/table/components/group-row/index.js +1 -1
  15. package/dist/esm/table/components/group-row/index.js.map +1 -1
  16. package/dist/esm/table/components/group-row/template.js +1 -1
  17. package/dist/esm/table/components/group-row/template.js.map +1 -1
  18. package/dist/esm/table/components/row/index.d.ts +1 -0
  19. package/dist/esm/table/components/row/index.js +6 -0
  20. package/dist/esm/table/components/row/index.js.map +1 -1
  21. package/dist/esm/table/components/row/template.js +1 -1
  22. package/dist/esm/table/components/row/template.js.map +1 -1
  23. package/dist/esm/table/index.d.ts +14 -4
  24. package/dist/esm/table/index.js +105 -77
  25. package/dist/esm/table/index.js.map +1 -1
  26. package/dist/esm/table/models/array-to-tree.d.ts +17 -0
  27. package/dist/esm/table/models/array-to-tree.js +83 -0
  28. package/dist/esm/table/models/array-to-tree.js.map +1 -0
  29. package/dist/esm/table/models/data-hierarchy-manager.d.ts +15 -0
  30. package/dist/esm/table/models/data-hierarchy-manager.js +64 -0
  31. package/dist/esm/table/models/data-hierarchy-manager.js.map +1 -0
  32. package/dist/esm/table/models/expansion-manager.d.ts +25 -0
  33. package/dist/esm/table/models/expansion-manager.js +73 -0
  34. package/dist/esm/table/models/expansion-manager.js.map +1 -0
  35. package/dist/esm/table/models/interactive-selection-manager.d.ts +1 -0
  36. package/dist/esm/table/models/interactive-selection-manager.js +13 -0
  37. package/dist/esm/table/models/interactive-selection-manager.js.map +1 -1
  38. package/dist/esm/table/models/selection-managers/selection-manager-base.js +6 -4
  39. package/dist/esm/table/models/selection-managers/selection-manager-base.js.map +1 -1
  40. package/dist/esm/table/models/table-update-tracker.d.ts +3 -1
  41. package/dist/esm/table/models/table-update-tracker.js +12 -1
  42. package/dist/esm/table/models/table-update-tracker.js.map +1 -1
  43. package/dist/esm/table/models/table-validator.d.ts +5 -3
  44. package/dist/esm/table/models/table-validator.js +14 -7
  45. package/dist/esm/table/models/table-validator.js.map +1 -1
  46. package/dist/esm/table/template.js +12 -8
  47. package/dist/esm/table/template.js.map +1 -1
  48. package/dist/esm/table/testing/table.pageobject.d.ts +4 -0
  49. package/dist/esm/table/testing/table.pageobject.js +24 -0
  50. package/dist/esm/table/testing/table.pageobject.js.map +1 -1
  51. package/dist/esm/table/types.d.ts +6 -3
  52. package/dist/esm/table/types.js.map +1 -1
  53. package/package.json +1 -1
@@ -16301,7 +16301,7 @@
16301
16301
 
16302
16302
  /**
16303
16303
  * Do not edit directly
16304
- * Generated on Thu, 18 Jan 2024 17:06:46 GMT
16304
+ * Generated on Fri, 19 Jan 2024 22:07:28 GMT
16305
16305
  */
16306
16306
 
16307
16307
  const Information100DarkUi = "#a46eff";
@@ -23897,7 +23897,7 @@
23897
23897
  tableGroupExpandLabel: 'Expand group',
23898
23898
  tableRowCollapseLabel: 'Collapse row',
23899
23899
  tableRowExpandLabel: 'Expand row',
23900
- tableGroupsCollapseAllLabel: 'Collapse all groups',
23900
+ tableCollapseAllLabel: 'Collapse all',
23901
23901
  tableCellActionMenuLabel: 'Options',
23902
23902
  tableColumnHeaderGroupedLabel: 'Grouped',
23903
23903
  tableColumnHeaderSortedAscendingLabel: 'Sorted ascending',
@@ -23924,10 +23924,10 @@
23924
23924
  name: 'table-row-expand-label',
23925
23925
  cssCustomPropertyName: null
23926
23926
  }).withDefault(tableLabelDefaults.tableRowExpandLabel);
23927
- const tableGroupsCollapseAllLabel = DesignToken.create({
23928
- name: 'table-groups-collapse-all-label',
23927
+ const tableCollapseAllLabel = DesignToken.create({
23928
+ name: 'table-collapse-all-label',
23929
23929
  cssCustomPropertyName: null
23930
- }).withDefault(tableLabelDefaults.tableGroupsCollapseAllLabel);
23930
+ }).withDefault(tableLabelDefaults.tableCollapseAllLabel);
23931
23931
  const tableCellActionMenuLabel = DesignToken.create({
23932
23932
  name: 'table-cell-action-menu-label',
23933
23933
  cssCustomPropertyName: null
@@ -23966,7 +23966,7 @@
23966
23966
  groupExpand: tableGroupExpandLabel,
23967
23967
  rowCollapse: tableRowCollapseLabel,
23968
23968
  rowExpand: tableRowExpandLabel,
23969
- groupsCollapseAll: tableGroupsCollapseAllLabel,
23969
+ collapseAll: tableCollapseAllLabel,
23970
23970
  cellActionMenu: tableCellActionMenuLabel,
23971
23971
  columnHeaderGrouped: tableColumnHeaderGroupedLabel,
23972
23972
  columnHeaderSortedAscending: tableColumnHeaderSortedAscendingLabel,
@@ -23998,8 +23998,8 @@
23998
23998
  attr({ attribute: 'row-expand' })
23999
23999
  ], LabelProviderTable.prototype, "rowExpand", void 0);
24000
24000
  __decorate$1([
24001
- attr({ attribute: 'groups-collapse-all' })
24002
- ], LabelProviderTable.prototype, "groupsCollapseAll", void 0);
24001
+ attr({ attribute: 'collapse-all' })
24002
+ ], LabelProviderTable.prototype, "collapseAll", void 0);
24003
24003
  __decorate$1([
24004
24004
  attr({ attribute: 'cell-action-menu' })
24005
24005
  ], LabelProviderTable.prototype, "cellActionMenu", void 0);
@@ -61480,6 +61480,7 @@ img.ProseMirror-separator {
61480
61480
  this.duplicateGroupIndex = false;
61481
61481
  this.idFieldNameNotConfigured = false;
61482
61482
  this.invalidColumnConfiguration = false;
61483
+ this.invalidParentIdConfiguration = false;
61483
61484
  this.recordIds = new Set();
61484
61485
  }
61485
61486
  getValidity() {
@@ -61492,7 +61493,8 @@ img.ProseMirror-separator {
61492
61493
  duplicateSortIndex: this.duplicateSortIndex,
61493
61494
  duplicateGroupIndex: this.duplicateGroupIndex,
61494
61495
  idFieldNameNotConfigured: this.idFieldNameNotConfigured,
61495
- invalidColumnConfiguration: this.invalidColumnConfiguration
61496
+ invalidColumnConfiguration: this.invalidColumnConfiguration,
61497
+ invalidParentIdConfiguration: this.invalidParentIdConfiguration
61496
61498
  };
61497
61499
  }
61498
61500
  isValid() {
@@ -61504,12 +61506,14 @@ img.ProseMirror-separator {
61504
61506
  && !validity.missingRecordId
61505
61507
  && !validity.invalidRecordId);
61506
61508
  }
61507
- validateSelectionMode(selectionMode, idFieldName) {
61508
- if (selectionMode === TableRowSelectionMode.none) {
61509
- this.idFieldNameNotConfigured = false;
61509
+ validateIdFieldConfiguration(selectionMode, idFieldName, parentIdFieldName) {
61510
+ const idFieldNameRequired = selectionMode !== TableRowSelectionMode.none
61511
+ || typeof parentIdFieldName === 'string';
61512
+ if (idFieldNameRequired) {
61513
+ this.idFieldNameNotConfigured = typeof idFieldName !== 'string';
61510
61514
  }
61511
61515
  else {
61512
- this.idFieldNameNotConfigured = typeof idFieldName !== 'string';
61516
+ this.idFieldNameNotConfigured = false;
61513
61517
  }
61514
61518
  return !this.idFieldNameNotConfigured;
61515
61519
  }
@@ -61523,11 +61527,11 @@ img.ProseMirror-separator {
61523
61527
  return true;
61524
61528
  }
61525
61529
  for (const record of data) {
61526
- if (!Object.prototype.hasOwnProperty.call(record.clientRecord, idFieldName)) {
61530
+ if (!Object.prototype.hasOwnProperty.call(record, idFieldName)) {
61527
61531
  this.missingRecordId = true;
61528
61532
  continue;
61529
61533
  }
61530
- const id = record.clientRecord[idFieldName];
61534
+ const id = record[idFieldName];
61531
61535
  if (typeof id !== 'string') {
61532
61536
  this.invalidRecordId = true;
61533
61537
  continue;
@@ -61576,6 +61580,9 @@ img.ProseMirror-separator {
61576
61580
  getPresentRecordIds(requestedRecordIds) {
61577
61581
  return requestedRecordIds.filter(id => this.recordIds.has(id));
61578
61582
  }
61583
+ setParentIdConfigurationValidity(valid) {
61584
+ this.invalidParentIdConfiguration = !valid;
61585
+ }
61579
61586
  validateIndicesAreUnique(indices) {
61580
61587
  const numberSet = new Set(indices);
61581
61588
  return numberSet.size === indices.length;
@@ -62131,7 +62138,7 @@ img.ProseMirror-separator {
62131
62138
  `)}
62132
62139
  </span>
62133
62140
  `)}
62134
- <span class="row-front-spacer ${x => (x.isParentRow && x.nestingLevel === 0 ? 'top-level-parent' : '')}"></span>
62141
+ <span class="row-front-spacer ${x => (x.isTopLevelParentRow ? 'top-level-parent' : '')}"></span>
62135
62142
  ${when(x => x.isParentRow, html `
62136
62143
  <${buttonTag}
62137
62144
  appearance="${ButtonAppearance.ghost}"
@@ -62224,6 +62231,9 @@ img.ProseMirror-separator {
62224
62231
  this.expandIcon?.removeEventListener('transitionend', this.removeAnimatingClass);
62225
62232
  };
62226
62233
  }
62234
+ get isTopLevelParentRow() {
62235
+ return this.isParentRow && this.nestingLevel === 0;
62236
+ }
62227
62237
  get ariaSelected() {
62228
62238
  if (this.selectable) {
62229
62239
  return this.selected ? 'true' : 'false';
@@ -62411,6 +62421,9 @@ img.ProseMirror-separator {
62411
62421
  __decorate$1([
62412
62422
  observable
62413
62423
  ], TableRow.prototype, "animationClass", void 0);
62424
+ __decorate$1([
62425
+ volatile
62426
+ ], TableRow.prototype, "isTopLevelParentRow", null);
62414
62427
  __decorate$1([
62415
62428
  volatile
62416
62429
  ], TableRow.prototype, "ariaSelected", null);
@@ -62547,7 +62560,7 @@ img.ProseMirror-separator {
62547
62560
 
62548
62561
  <div class="group-row-header-content">
62549
62562
  ${x => x.groupColumn?.columnInternals.groupHeaderViewTemplate}
62550
- <span class="group-row-child-count">(${x => x.leafItemCount})</span>
62563
+ <span class="group-row-child-count">(${x => x.immediateChildCount})</span>
62551
62564
  </div>
62552
62565
  </template>
62553
62566
  `;
@@ -62628,7 +62641,7 @@ img.ProseMirror-separator {
62628
62641
  ], TableGroupRow.prototype, "nestingLevel", void 0);
62629
62642
  __decorate$1([
62630
62643
  observable
62631
- ], TableGroupRow.prototype, "leafItemCount", void 0);
62644
+ ], TableGroupRow.prototype, "immediateChildCount", void 0);
62632
62645
  __decorate$1([
62633
62646
  observable
62634
62647
  ], TableGroupRow.prototype, "groupColumn", void 0);
@@ -62658,7 +62671,7 @@ img.ProseMirror-separator {
62658
62671
  // prettier-ignore
62659
62672
  const template$e = html `
62660
62673
  <template
62661
- role="grid"
62674
+ role="treegrid"
62662
62675
  aria-multiselectable="${x => x.ariaMultiSelectable}"
62663
62676
  ${children$1({ property: 'childItems', filter: elements() })}
62664
62677
  >
@@ -62698,11 +62711,11 @@ img.ProseMirror-separator {
62698
62711
  class="collapse-all-button ${x => `${x.showCollapseAll ? 'visible' : ''}`}"
62699
62712
  content-hidden
62700
62713
  appearance="${ButtonAppearance.ghost}"
62701
- title="${x => tableGroupsCollapseAllLabel.getValueFor(x)}"
62702
- @click="${x => x.handleCollapseAllGroupRows()}"
62714
+ title="${x => tableCollapseAllLabel.getValueFor(x)}"
62715
+ @click="${x => x.handleCollapseAllRows()}"
62703
62716
  >
62704
62717
  <${iconTriangleTwoLinesHorizontalTag} slot="start"></${iconTriangleTwoLinesHorizontalTag}>
62705
- ${x => tableGroupsCollapseAllLabel.getValueFor(x)}
62718
+ ${x => tableCollapseAllLabel.getValueFor(x)}
62706
62719
  </${buttonTag}>
62707
62720
  </span>
62708
62721
  <span class="column-headers-container" ${ref('columnHeadersContainer')}>
@@ -62739,13 +62752,13 @@ img.ProseMirror-separator {
62739
62752
  role="rowgroup">
62740
62753
  ${when(x => x.columns.length > 0 && x.canRenderRows, html `
62741
62754
  ${repeat(x => x.virtualizer.visibleItems, html `
62742
- ${when((x, c) => c.parent.tableData[x.index]?.isGrouped, html `
62755
+ ${when((x, c) => c.parent.tableData[x.index]?.isGroupRow, html `
62743
62756
  <${tableGroupRowTag}
62744
62757
  class="group-row"
62745
62758
  :groupRowValue="${(x, c) => c.parent.tableData[x.index]?.groupRowValue}"
62746
62759
  ?expanded="${(x, c) => c.parent.tableData[x.index]?.isExpanded}"
62747
62760
  :nestingLevel="${(x, c) => c.parent.tableData[x.index]?.nestingLevel}"
62748
- :leafItemCount="${(x, c) => c.parent.tableData[x.index]?.leafItemCount}"
62761
+ :immediateChildCount="${(x, c) => c.parent.tableData[x.index]?.immediateChildCount}"
62749
62762
  :groupColumn="${(x, c) => c.parent.tableData[x.index]?.groupColumn}"
62750
62763
  ?selectable="${(_, c) => c.parent.selectionMode === TableRowSelectionMode.multiple}"
62751
62764
  selection-state="${(x, c) => c.parent.tableData[x.index]?.selectionState}"
@@ -62754,21 +62767,25 @@ img.ProseMirror-separator {
62754
62767
  >
62755
62768
  </${tableGroupRowTag}>
62756
62769
  `)}
62757
- ${when((x, c) => !c.parent.tableData[x.index]?.isGrouped, html `
62770
+ ${when((x, c) => !c.parent.tableData[x.index]?.isGroupRow, html `
62758
62771
  <${tableRowTag}
62759
62772
  class="row"
62760
62773
  record-id="${(x, c) => c.parent.tableData[x.index]?.id}"
62761
62774
  ?selectable="${(_, c) => c.parent.selectionMode !== TableRowSelectionMode.none}"
62762
62775
  ?selected="${(x, c) => c.parent.tableData[x.index]?.selectionState === TableRowSelectionState.selected}"
62776
+ ?expanded="${(x, c) => c.parent.tableData[x.index]?.isExpanded}"
62763
62777
  ?hide-selection="${(_, c) => c.parent.selectionMode !== TableRowSelectionMode.multiple}"
62764
62778
  :dataRecord="${(x, c) => c.parent.tableData[x.index]?.record}"
62765
62779
  :columns="${(_, c) => c.parent.columns}"
62780
+ :isParentRow="${(x, c) => c.parent.tableData[x.index]?.isParentRow}"
62766
62781
  :nestingLevel="${(x, c) => c.parent.tableData[x.index]?.nestingLevel}"
62767
62782
  ?row-operation-grid-cell-hidden="${(_, c) => !c.parent.showRowOperationColumn}"
62768
62783
  @click="${(x, c) => c.parent.onRowClick(x.index, c.event)}"
62769
62784
  @row-selection-toggle="${(x, c) => c.parent.onRowSelectionToggle(x.index, c.event)}"
62770
62785
  @row-action-menu-beforetoggle="${(x, c) => c.parent.onRowActionMenuBeforeToggle(x.index, c.event)}"
62771
62786
  @row-action-menu-toggle="${(_, c) => c.parent.onRowActionMenuToggle(c.event)}"
62787
+ @row-expand-toggle="${(x, c) => c.parent.handleRowExpanded(x.index)}"
62788
+ :dataIndex="${x => x.index}"
62772
62789
  >
62773
62790
  ${when((x, c) => c.parent.openActionMenuRecordId === c.parent.tableData[x.index]?.id, html `
62774
62791
  ${repeat((_, c) => c.parent.actionMenuSlots, html `
@@ -63892,6 +63909,7 @@ img.ProseMirror-separator {
63892
63909
  };
63893
63910
  const trackedItems$1 = [
63894
63911
  'rowIds',
63912
+ 'rowParentIds',
63895
63913
  'groupRows',
63896
63914
  'columnIds',
63897
63915
  'columnSort',
@@ -63913,6 +63931,9 @@ img.ProseMirror-separator {
63913
63931
  get updateRowIds() {
63914
63932
  return this.isTracked('rowIds');
63915
63933
  }
63934
+ get updateRowParentIds() {
63935
+ return this.isTracked('rowParentIds');
63936
+ }
63916
63937
  get updateGroupRows() {
63917
63938
  return this.isTracked('groupRows');
63918
63939
  }
@@ -63936,13 +63957,16 @@ img.ProseMirror-separator {
63936
63957
  }
63937
63958
  get requiresTanStackUpdate() {
63938
63959
  return (this.isTracked('rowIds')
63960
+ || this.isTracked('rowParentIds')
63939
63961
  || this.isTracked('columnSort')
63940
63962
  || this.isTracked('columnDefinition')
63941
63963
  || this.isTracked('groupRows')
63942
63964
  || this.isTracked('selectionMode'));
63943
63965
  }
63944
63966
  get requiresTanStackDataReset() {
63945
- return this.isTracked('rowIds') || this.isTracked('columnDefinition');
63967
+ return (this.isTracked('rowIds')
63968
+ || this.isTracked('columnDefinition')
63969
+ || this.isTracked('rowParentIds'));
63946
63970
  }
63947
63971
  trackAllStateChanged() {
63948
63972
  this.trackAll();
@@ -63987,6 +64011,10 @@ img.ProseMirror-separator {
63987
64011
  this.track('rowIds');
63988
64012
  this.queueUpdate();
63989
64013
  }
64014
+ trackParentIdFieldNameChanged() {
64015
+ this.track('rowParentIds');
64016
+ this.queueUpdate();
64017
+ }
63990
64018
  trackSelectionModeChanged() {
63991
64019
  this.track('selectionMode');
63992
64020
  this.queueUpdate();
@@ -64015,18 +64043,20 @@ img.ProseMirror-separator {
64015
64043
  }
64016
64044
  reset() { }
64017
64045
  toggleIsRowSelected(rowState, isSelecting) {
64018
- if (rowState.isGrouped
64046
+ if (rowState.isGroupRow
64019
64047
  && rowState.selectionState === TableRowSelectionState.selected) {
64020
64048
  // Work around for https://github.com/TanStack/table/issues/4759
64021
64049
  // Manually deselect all leaf rows when a fully selected group is being deselected.
64022
64050
  this.deselectAllLeafRows(rowState.id);
64023
64051
  }
64024
64052
  else {
64025
- this.tanStackTable.getRow(rowState.id).toggleSelected(isSelecting);
64053
+ this.tanStackTable.getRow(rowState.id).toggleSelected(isSelecting, {
64054
+ selectChildren: rowState.isGroupRow
64055
+ });
64026
64056
  }
64027
64057
  }
64028
64058
  selectSingleRow(rowState) {
64029
- if (rowState.isGrouped) {
64059
+ if (rowState.isGroupRow) {
64030
64060
  throw new Error('function not intended to select grouped rows');
64031
64061
  }
64032
64062
  const currentSelection = this.tanStackTable.getState().rowSelection;
@@ -64064,7 +64094,7 @@ img.ProseMirror-separator {
64064
64094
  }
64065
64095
  return row
64066
64096
  .getLeafRows()
64067
- .filter(leafRow => leafRow.getLeafRows().length === 0)
64097
+ .filter(leafRow => !leafRow.getIsGrouped())
64068
64098
  .map(leafRow => leafRow.id);
64069
64099
  }
64070
64100
  getAllOrderedRows() {
@@ -64251,6 +64281,19 @@ img.ProseMirror-separator {
64251
64281
  handleSelectionReset() {
64252
64282
  this.selectionManager.reset();
64253
64283
  }
64284
+ getCurrentSelectedRecordIds() {
64285
+ const tanStackSelectionState = this.tanStackTable.options.state.rowSelection;
64286
+ if (!tanStackSelectionState) {
64287
+ return [];
64288
+ }
64289
+ const selectedRecordIds = [];
64290
+ Object.entries(tanStackSelectionState).forEach(([recordId, isSelected]) => {
64291
+ if (isSelected) {
64292
+ selectedRecordIds.push(recordId);
64293
+ }
64294
+ });
64295
+ return selectedRecordIds;
64296
+ }
64254
64297
  createSelectionManager(selectionMode) {
64255
64298
  switch (selectionMode) {
64256
64299
  case TableRowSelectionMode.multiple:
@@ -64265,6 +64308,225 @@ img.ProseMirror-separator {
64265
64308
  }
64266
64309
  }
64267
64310
 
64311
+ // Modified from https://github.com/philipstanislaus/performant-array-to-tree/blob/v1.11.0/src/arrayToTree.ts
64312
+ // Copyright (c) 2022 philipstanislaus
64313
+ // SPDX-License-Identifier: MIT
64314
+ /**
64315
+ * Unflattens an array to a tree with runtime O(n)
64316
+ */
64317
+ function arrayToTree(items, config) {
64318
+ const conf = config;
64319
+ // the resulting unflattened tree
64320
+ const rootItems = [];
64321
+ // stores all already processed items with their ids as key so we can easily look them up
64322
+ const lookup = {};
64323
+ // stores all item ids that have not been added to the resulting unflattened tree yet
64324
+ // this is an opt-in property, since it has a slight runtime overhead
64325
+ const orphanIds = new Set();
64326
+ // idea of this loop:
64327
+ // whenever an item has a parent, but the parent is not yet in the lookup object, we store a preliminary parent
64328
+ // in the lookup object and fill it with the data of the parent later
64329
+ // if an item has no parentId, add it as a root element to rootItems
64330
+ // Convert to a for-loop to have access to the item's index in the flat list
64331
+ for (let i = 0; i < items.length; i++) {
64332
+ const item = items[i];
64333
+ const itemId = item[conf.id];
64334
+ const parentId = item[conf.parentId];
64335
+ // look whether item already exists in the lookup table
64336
+ if (!Object.prototype.hasOwnProperty.call(lookup, itemId)) {
64337
+ // item is not yet there, so add a preliminary item (its data will be added later)
64338
+ lookup[itemId] = {
64339
+ subRows: [],
64340
+ clientRecord: undefined,
64341
+ originalIndex: undefined
64342
+ };
64343
+ }
64344
+ // if we track orphans, delete this item from the orphan set if it is in it
64345
+ if (orphanIds) {
64346
+ orphanIds.delete(itemId);
64347
+ }
64348
+ // add the current item's data to the item in the lookup table
64349
+ lookup[itemId].clientRecord = item;
64350
+ const treeItem = lookup[itemId];
64351
+ // Add the index to the item
64352
+ treeItem.originalIndex = i;
64353
+ if (parentId === null || parentId === undefined) {
64354
+ // is a root item
64355
+ rootItems.push(treeItem);
64356
+ }
64357
+ else {
64358
+ // has a parent
64359
+ // look whether the parent already exists in the lookup table
64360
+ if (!Object.prototype.hasOwnProperty.call(lookup, parentId)) {
64361
+ // parent is not yet there, so add a preliminary parent (its data will be added later)
64362
+ lookup[parentId] = {
64363
+ subRows: [],
64364
+ clientRecord: undefined,
64365
+ originalIndex: undefined
64366
+ };
64367
+ // if we track orphans, add the generated parent to the orphan list
64368
+ if (orphanIds) {
64369
+ orphanIds.add(parentId);
64370
+ }
64371
+ }
64372
+ // add the current item to the parent
64373
+ lookup[parentId].subRows.push(treeItem);
64374
+ }
64375
+ }
64376
+ if (orphanIds?.size) {
64377
+ const orphans = Array.from(orphanIds.values()).join(',');
64378
+ throw new Error(`The items array contains orphans that point to the following parentIds: [${orphans}]. These parentIds do not exist in the items array.`);
64379
+ }
64380
+ if (countNodes(rootItems) < Object.keys(lookup).length) {
64381
+ throw new Error('The items array contains nodes with a circular parent/child relationship.');
64382
+ }
64383
+ return rootItems;
64384
+ }
64385
+ /**
64386
+ * Returns the number of nodes in a tree in a recursive way
64387
+ * @param tree An array of nodes (tree items), each having a field `childrenField` that contains an array of nodes
64388
+ * @returns Number of nodes in the tree
64389
+ */
64390
+ function countNodes(tree) {
64391
+ return tree.reduce((sum, n) => sum + 1 + (n.subRows && countNodes(n.subRows)), 0);
64392
+ }
64393
+
64394
+ /**
64395
+ * Manages data hierarchy within the table, including converting between a flat list of
64396
+ * data and hierarchical data.
64397
+ */
64398
+ class DataHierarchyManager {
64399
+ constructor(records, idFieldName, parentIdFieldName) {
64400
+ this.isDataFlat = false;
64401
+ if (typeof idFieldName === 'string'
64402
+ && typeof parentIdFieldName === 'string') {
64403
+ try {
64404
+ this._hierarchicalData = arrayToTree(records, {
64405
+ id: idFieldName,
64406
+ parentId: parentIdFieldName
64407
+ });
64408
+ this.isDataFlat = false;
64409
+ this._parentIdConfigurationValid = true;
64410
+ }
64411
+ catch {
64412
+ this.isDataFlat = true;
64413
+ this._hierarchicalData = records.map((record, index) => ({
64414
+ clientRecord: { ...record },
64415
+ originalIndex: index
64416
+ }));
64417
+ this._parentIdConfigurationValid = false;
64418
+ }
64419
+ }
64420
+ else {
64421
+ this.isDataFlat = true;
64422
+ this._hierarchicalData = records.map((record, index) => ({
64423
+ clientRecord: { ...record },
64424
+ originalIndex: index
64425
+ }));
64426
+ this._parentIdConfigurationValid = true;
64427
+ }
64428
+ }
64429
+ get hierarchicalData() {
64430
+ return this._hierarchicalData;
64431
+ }
64432
+ get parentIdConfigurationValid() {
64433
+ return this._parentIdConfigurationValid;
64434
+ }
64435
+ getAllRecords(sort = false) {
64436
+ const allNodes = [];
64437
+ this.getAllNodes(this._hierarchicalData, allNodes);
64438
+ if (this.isDataFlat || !sort) {
64439
+ // If the data is flat, then it was never reordered to support hierarchy.
64440
+ // Therefore, there is no need to sort the nodes.
64441
+ return allNodes.map(x => x.clientRecord);
64442
+ }
64443
+ return allNodes
64444
+ .sort((a, b) => a.originalIndex - b.originalIndex)
64445
+ .map(x => x.clientRecord);
64446
+ }
64447
+ getAllNodes(parentNodes, allNodes) {
64448
+ for (const parentNode of parentNodes) {
64449
+ allNodes.push(parentNode);
64450
+ if (parentNode.subRows) {
64451
+ this.getAllNodes(parentNode.subRows, allNodes);
64452
+ }
64453
+ }
64454
+ }
64455
+ }
64456
+
64457
+ /**
64458
+ * Manages the expanded/collapsed state of rows in the table.
64459
+ *
64460
+ * We must track the expansion state separately from TanStack because:
64461
+ * 1. TanStack does not support having a different initial expansion state per row unless explicitly
64462
+ * specified for each row by ID. This causes problems in the nimble-table because we could have
64463
+ * a different initial expansion state for group rows, parent rows, and parent rows with lazy
64464
+ * loaded children.
64465
+ * 2. TanStack does not remove entries from its expanded state when those rows are no longer present
64466
+ * in the data. This is not ideal because the object maintaining the expansion state can grow unbounded.
64467
+ */
64468
+ class ExpansionManager {
64469
+ constructor(tanStackTable) {
64470
+ this.tanStackTable = tanStackTable;
64471
+ // This field represents whether or not the expanded state of **all** rows is in the default expanded
64472
+ // state or not. Note that the default expanded state for a particular row type (group vs parent) can
64473
+ // potentially be different (e.g. expanded for groups and collapsed for parent rows).
64474
+ this.isInDefaultState = true;
64475
+ this.collapsedRows = new Set();
64476
+ }
64477
+ isRowExpanded(row) {
64478
+ if (!this.isRowExpandable(row)) {
64479
+ return false;
64480
+ }
64481
+ return this.isInDefaultState || !this.collapsedRows.has(row.id);
64482
+ }
64483
+ toggleRowExpansion(row) {
64484
+ if (!this.isRowExpandable(row)) {
64485
+ return;
64486
+ }
64487
+ const wasExpanded = this.isRowExpanded(row);
64488
+ this.isInDefaultState = false;
64489
+ if (wasExpanded) {
64490
+ this.collapsedRows.add(row.id);
64491
+ }
64492
+ else {
64493
+ this.collapsedRows.delete(row.id);
64494
+ }
64495
+ row.toggleExpanded();
64496
+ }
64497
+ collapseAll() {
64498
+ this.reset();
64499
+ this.isInDefaultState = false;
64500
+ const rows = this.tanStackTable.getRowModel().flatRows;
64501
+ for (const row of rows) {
64502
+ if (this.isRowExpandable(row)) {
64503
+ this.collapsedRows.add(row.id);
64504
+ }
64505
+ }
64506
+ this.tanStackTable.toggleAllRowsExpanded(false);
64507
+ }
64508
+ reset() {
64509
+ this.collapsedRows.clear();
64510
+ this.isInDefaultState = true;
64511
+ }
64512
+ processDataUpdate(rows) {
64513
+ if (this.isInDefaultState) {
64514
+ return;
64515
+ }
64516
+ const updatedCollapsedRows = new Set();
64517
+ for (const row of rows) {
64518
+ const rowId = row.id;
64519
+ if (this.collapsedRows.has(rowId)) {
64520
+ updatedCollapsedRows.add(rowId);
64521
+ }
64522
+ }
64523
+ this.collapsedRows = updatedCollapsedRows;
64524
+ }
64525
+ isRowExpandable(row) {
64526
+ return row.getIsGrouped() || row.subRows.length > 0;
64527
+ }
64528
+ }
64529
+
64268
64530
  /**
64269
64531
  * A nimble-styled table.
64270
64532
  */
@@ -64323,7 +64585,6 @@ img.ProseMirror-separator {
64323
64585
  this.tableUpdateTracker = new TableUpdateTracker(this);
64324
64586
  this.columnNotifiers = [];
64325
64587
  this.isInitialized = false;
64326
- this.collapsedRows = new Set();
64327
64588
  // Programmatically updating the selection state of a checkbox fires the 'change' event.
64328
64589
  // Therefore, selection change events that occur due to programmatically updating
64329
64590
  // the selection checkbox 'checked' value should be ingored.
@@ -64343,17 +64604,7 @@ img.ProseMirror-separator {
64343
64604
  }
64344
64605
  };
64345
64606
  this.getIsRowExpanded = (row) => {
64346
- if (!row.getIsGrouped()) {
64347
- return false;
64348
- }
64349
- const expandedState = this.table.options.state.expanded;
64350
- if (expandedState === true) {
64351
- return true;
64352
- }
64353
- if (Object.keys(expandedState ?? {}).includes(row.id)) {
64354
- return expandedState[row.id];
64355
- }
64356
- return !this.collapsedRows.has(row.id);
64607
+ return this.expansionManager.isRowExpanded(row);
64357
64608
  };
64358
64609
  this.handleRowSelectionChange = (updaterOrValue) => {
64359
64610
  const rowSelectionState = updaterOrValue instanceof Function
@@ -64385,6 +64636,7 @@ img.ProseMirror-separator {
64385
64636
  getGroupedRowModel: getGroupedRowModel(),
64386
64637
  getExpandedRowModel: getExpandedRowModel(),
64387
64638
  getIsRowExpanded: this.getIsRowExpanded,
64639
+ getSubRows: r => r.subRows,
64388
64640
  columns: [],
64389
64641
  state: {
64390
64642
  rowSelection: {},
@@ -64405,6 +64657,7 @@ img.ProseMirror-separator {
64405
64657
  this.layoutManagerNotifier = Observable.getNotifier(this.layoutManager);
64406
64658
  this.layoutManagerNotifier.subscribe(this, 'isColumnBeingSized');
64407
64659
  this.selectionManager = new InteractiveSelectionManager(this.table, this.selectionMode);
64660
+ this.expansionManager = new ExpansionManager(this.table);
64408
64661
  }
64409
64662
  get validity() {
64410
64663
  return this.tableValidator.getValidity();
@@ -64418,36 +64671,12 @@ img.ProseMirror-separator {
64418
64671
  }
64419
64672
  async setData(newData) {
64420
64673
  await this.processPendingUpdates();
64421
- const data = newData.map(record => {
64422
- return { clientRecord: { ...record } };
64423
- });
64424
- const tanStackUpdates = {
64425
- data
64426
- };
64427
- this.validateWithData(data);
64428
- if (this.tableValidator.areRecordIdsValid()) {
64429
- // Update the selection state to remove previously selected records that no longer exist in the
64430
- // data set while maintaining the selection state of records that still exist in the data set.
64431
- const previousSelection = await this.getSelectedRecordIds();
64432
- tanStackUpdates.state = {
64433
- rowSelection: this.calculateTanStackSelectionState(previousSelection)
64434
- };
64435
- }
64436
- this.updateTableOptions(tanStackUpdates);
64674
+ const tanstackUpdates = this.calculateTanStackData(newData);
64675
+ this.updateTableOptions(tanstackUpdates);
64437
64676
  }
64438
64677
  async getSelectedRecordIds() {
64439
64678
  await this.processPendingUpdates();
64440
- const tanStackSelectionState = this.options.state.rowSelection;
64441
- if (!tanStackSelectionState) {
64442
- return [];
64443
- }
64444
- const selectedRecordIds = [];
64445
- Object.entries(tanStackSelectionState).forEach(([recordId, isSelected]) => {
64446
- if (isSelected) {
64447
- selectedRecordIds.push(recordId);
64448
- }
64449
- });
64450
- return selectedRecordIds;
64679
+ return this.selectionManager.getCurrentSelectedRecordIds();
64451
64680
  }
64452
64681
  async setSelectedRecordIds(recordIds) {
64453
64682
  await this.processPendingUpdates();
@@ -64542,13 +64771,8 @@ img.ProseMirror-separator {
64542
64771
  void this.handleRowActionMenuToggleEvent(event);
64543
64772
  }
64544
64773
  /** @internal */
64545
- handleCollapseAllGroupRows() {
64546
- this.collapsedRows.clear();
64547
- this.table
64548
- .getRowModel()
64549
- .flatRows.filter(row => row.getIsGrouped())
64550
- .forEach(row => this.collapsedRows.add(row.id));
64551
- this.table.toggleAllRowsExpanded(false);
64774
+ handleCollapseAllRows() {
64775
+ this.expansionManager.collapseAll();
64552
64776
  }
64553
64777
  /** @internal */
64554
64778
  onRightDividerMouseDown(event, columnIndex) {
@@ -64564,9 +64788,13 @@ img.ProseMirror-separator {
64564
64788
  }
64565
64789
  /** @internal */
64566
64790
  handleGroupRowExpanded(rowIndex, event) {
64567
- this.toggleGroupExpanded(rowIndex);
64791
+ this.toggleRowExpanded(rowIndex);
64568
64792
  event.stopPropagation();
64569
64793
  }
64794
+ /** @internal */
64795
+ handleRowExpanded(rowIndex) {
64796
+ this.toggleRowExpanded(rowIndex);
64797
+ }
64570
64798
  /**
64571
64799
  * @internal
64572
64800
  */
@@ -64624,9 +64852,6 @@ img.ProseMirror-separator {
64624
64852
  this.rowGridColumns = this.layoutManager.getGridTemplateColumns();
64625
64853
  this.visibleColumns = this.columns.filter(column => !column.columnHidden);
64626
64854
  }
64627
- if (this.tableUpdateTracker.updateGroupRows) {
64628
- this.showCollapseAll = this.getColumnsParticipatingInGrouping().length > 0;
64629
- }
64630
64855
  }
64631
64856
  get ariaMultiSelectable() {
64632
64857
  switch (this.selectionMode) {
@@ -64644,6 +64869,31 @@ img.ProseMirror-separator {
64644
64869
  getHeaderContainerElements() {
64645
64870
  return this.columnHeadersContainer.querySelectorAll('.header-container');
64646
64871
  }
64872
+ /**
64873
+ * @internal
64874
+ */
64875
+ calculateTanStackData(data) {
64876
+ this.dataHierarchyManager = new DataHierarchyManager(data, this.idFieldName, this.parentIdFieldName);
64877
+ const tableNodes = this.dataHierarchyManager.hierarchicalData;
64878
+ this.tableValidator.setParentIdConfigurationValidity(this.dataHierarchyManager.parentIdConfigurationValid);
64879
+ const tanStackUpdates = {
64880
+ data: tableNodes
64881
+ };
64882
+ this.validateWithData(data);
64883
+ if (this.tableValidator.areRecordIdsValid()) {
64884
+ // Update the selection state to remove previously selected records that no longer exist in the
64885
+ // data set while maintaining the selection state of records that still exist in the data set.
64886
+ const previousSelection = this.selectionManager.getCurrentSelectedRecordIds();
64887
+ tanStackUpdates.state = {
64888
+ rowSelection: this.calculateTanStackSelectionState(previousSelection),
64889
+ // Reset the TanStack expanded state to "true" to clear TanStack's cache of expanded state that may include
64890
+ // rows that no longer exist in the table. The nimble table's expansion manager tracks the expanded
64891
+ // state of each row, so no state is lost by resetting TanStack.
64892
+ expanded: true
64893
+ };
64894
+ }
64895
+ return tanStackUpdates;
64896
+ }
64647
64897
  selectionModeChanged(_prev, _next) {
64648
64898
  if (!this.$fastController.isConnected) {
64649
64899
  return;
@@ -64656,6 +64906,12 @@ img.ProseMirror-separator {
64656
64906
  }
64657
64907
  this.tableUpdateTracker.trackIdFieldNameChanged();
64658
64908
  }
64909
+ parentIdFieldNameChanged(_prev, _next) {
64910
+ if (!this.$fastController.isConnected) {
64911
+ return;
64912
+ }
64913
+ this.tableUpdateTracker.trackParentIdFieldNameChanged();
64914
+ }
64659
64915
  columnsChanged(_prev, _next) {
64660
64916
  if (!this.$fastController.isConnected) {
64661
64917
  return;
@@ -64760,14 +65016,30 @@ img.ProseMirror-separator {
64760
65016
  updatedOptions.state.rowSelection = {};
64761
65017
  this.selectionManager.handleSelectionModeChanged(this.selectionMode);
64762
65018
  }
64763
- if (this.tableUpdateTracker.requiresTanStackDataReset) {
64764
- // Perform a shallow copy of the data to trigger tanstack to regenerate the row models and columns.
64765
- updatedOptions.data = [...this.table.options.data];
65019
+ if (this.dataHierarchyManager
65020
+ && this.tableUpdateTracker.requiresTanStackDataReset) {
65021
+ if (!this.parentIdFieldName
65022
+ && !this.tableUpdateTracker.updateRowParentIds) {
65023
+ // Perform a shallow copy of the data to trigger tanstack to regenerate the row models and columns.
65024
+ updatedOptions.data = [...this.table.options.data];
65025
+ }
65026
+ else {
65027
+ const orderedRecords = this.dataHierarchyManager.getAllRecords(true);
65028
+ const tanstackUpdates = this.calculateTanStackData(orderedRecords);
65029
+ if (tanstackUpdates.state) {
65030
+ updatedOptions.state.rowSelection = tanstackUpdates.state.rowSelection;
65031
+ }
65032
+ updatedOptions.data = tanstackUpdates.data;
65033
+ }
64766
65034
  }
64767
65035
  if (this.tableUpdateTracker.updateGroupRows) {
64768
65036
  updatedOptions.state.grouping = this.calculateTanStackGroupingState();
65037
+ }
65038
+ if (this.tableUpdateTracker.updateRowIds
65039
+ || this.tableUpdateTracker.updateRowParentIds
65040
+ || this.tableUpdateTracker.updateGroupRows) {
64769
65041
  updatedOptions.state.expanded = true;
64770
- this.collapsedRows.clear();
65042
+ this.expansionManager.reset();
64771
65043
  }
64772
65044
  this.updateTableOptions(updatedOptions);
64773
65045
  }
@@ -64781,12 +65053,14 @@ img.ProseMirror-separator {
64781
65053
  this.actionMenuSlots = Array.from(slots);
64782
65054
  }
64783
65055
  validate() {
64784
- this.tableValidator.validateSelectionMode(this.selectionMode, this.idFieldName);
65056
+ this.tableValidator.validateIdFieldConfiguration(this.selectionMode, this.idFieldName, this.parentIdFieldName);
64785
65057
  this.tableValidator.validateColumnIds(this.columns.map(x => x.columnId));
64786
65058
  this.tableValidator.validateColumnSortIndices(this.getColumnsParticipatingInSorting().map(x => x.columnInternals.currentSortIndex));
64787
65059
  this.tableValidator.validateColumnGroupIndices(this.getColumnsParticipatingInGrouping().map(x => x.columnInternals.groupIndex));
64788
65060
  this.tableValidator.validateColumnConfigurations(this.columns);
64789
- this.validateWithData(this.table.options.data);
65061
+ if (this.dataHierarchyManager) {
65062
+ this.validateWithData(this.dataHierarchyManager.getAllRecords());
65063
+ }
64790
65064
  }
64791
65065
  validateWithData(data) {
64792
65066
  this.tableValidator.validateRecordIds(data, this.idFieldName);
@@ -64829,27 +65103,41 @@ img.ProseMirror-separator {
64829
65103
  }
64830
65104
  refreshRows() {
64831
65105
  this.selectionState = this.getTableSelectionState();
65106
+ let hasDataHierarchy = false;
64832
65107
  const rows = this.table.getRowModel().rows;
64833
65108
  this.tableData = rows.map(row => {
64834
- const isGrouped = row.getIsGrouped();
65109
+ const isGroupRow = row.getIsGrouped();
65110
+ const hasParentRow = isGroupRow ? false : row.getParentRow();
65111
+ // we check row.original.subRows below because row.subRows is populated for group rows
65112
+ // which we don't want to include
65113
+ const isParent = row.original.subRows !== undefined
65114
+ && row.original.subRows.length > 0;
65115
+ const isChildOfGroupRowWithNoHierarchy = !isGroupRow
65116
+ && !isParent
65117
+ && !hasParentRow
65118
+ && row.depth > 0
65119
+ && !this.parentIdFieldName;
64835
65120
  const rowState = {
64836
65121
  record: row.original.clientRecord,
64837
65122
  id: row.id,
64838
65123
  selectionState: this.getRowSelectionState(row),
64839
- isGrouped,
65124
+ isGroupRow,
64840
65125
  isExpanded: row.getIsExpanded(),
64841
- groupRowValue: isGrouped
65126
+ groupRowValue: isGroupRow
64842
65127
  ? row.getValue(row.groupingColumnId)
64843
65128
  : undefined,
64844
- nestingLevel: !isGrouped && row.depth > 0 ? row.depth - 1 : row.depth,
64845
- leafItemCount: row
64846
- .getLeafRows()
64847
- .filter(leafRow => leafRow.getLeafRows().length === 0)
64848
- .length,
65129
+ nestingLevel: isChildOfGroupRowWithNoHierarchy
65130
+ ? row.depth - 1
65131
+ : row.depth,
65132
+ isParentRow: isParent,
65133
+ immediateChildCount: row.subRows.length,
64849
65134
  groupColumn: this.getGroupRowColumn(row)
64850
65135
  };
65136
+ hasDataHierarchy = hasDataHierarchy || isParent;
64851
65137
  return rowState;
64852
65138
  });
65139
+ this.showCollapseAll = hasDataHierarchy
65140
+ || this.getColumnsParticipatingInGrouping().length > 0;
64853
65141
  this.virtualizer.dataChanged();
64854
65142
  }
64855
65143
  getTableSelectionState() {
@@ -64870,10 +65158,10 @@ img.ProseMirror-separator {
64870
65158
  : TableRowSelectionState.notSelected;
64871
65159
  }
64872
65160
  getGroupedRowSelectionState(groupedRow) {
64873
- const subRows = groupedRow.subRows ?? [];
65161
+ const leafRows = groupedRow.getLeafRows() ?? [];
64874
65162
  let foundSelectedRow = false;
64875
65163
  let foundNotSelectedRow = false;
64876
- for (const row of subRows) {
65164
+ for (const row of leafRows) {
64877
65165
  if (row.getIsGrouped()) {
64878
65166
  const subGroupRowSelectionState = this.getGroupedRowSelectionState(row);
64879
65167
  switch (subGroupRowSelectionState) {
@@ -64915,19 +65203,16 @@ img.ProseMirror-separator {
64915
65203
  state: { ...this.options.state, ...updatedOptions.state }
64916
65204
  };
64917
65205
  this.table.setOptions(this.options);
65206
+ if (updatedOptions.data) {
65207
+ const rows = this.table.getRowModel().flatRows;
65208
+ this.expansionManager.processDataUpdate(rows);
65209
+ }
64918
65210
  this.refreshRows();
64919
65211
  }
64920
- toggleGroupExpanded(rowIndex) {
64921
- const row = this.table.getRowModel().rows[rowIndex];
64922
- const wasExpanded = row.getIsExpanded();
64923
- // must update the collapsedRows before toggling expanded state
64924
- if (wasExpanded) {
64925
- this.collapsedRows.add(row.id);
64926
- }
64927
- else {
64928
- this.collapsedRows.delete(row.id);
64929
- }
64930
- row.toggleExpanded();
65212
+ toggleRowExpanded(rowIndex) {
65213
+ const rows = this.table.getRowModel().rows;
65214
+ const row = rows[rowIndex];
65215
+ this.expansionManager.toggleRowExpansion(row);
64931
65216
  }
64932
65217
  calculateTanStackSortState() {
64933
65218
  const sortedColumns = this.getColumnsParticipatingInSorting().sort((x, y) => x.columnInternals.currentSortIndex
@@ -64987,6 +65272,9 @@ img.ProseMirror-separator {
64987
65272
  __decorate$1([
64988
65273
  attr({ attribute: 'id-field-name' })
64989
65274
  ], Table.prototype, "idFieldName", void 0);
65275
+ __decorate$1([
65276
+ attr({ attribute: 'parent-id-field-name' })
65277
+ ], Table.prototype, "parentIdFieldName", void 0);
64990
65278
  __decorate$1([
64991
65279
  attr({ attribute: 'selection-mode' })
64992
65280
  ], Table.prototype, "selectionMode", void 0);