@ni/nimble-components 29.6.0 → 29.7.1

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 (70) hide show
  1. package/dist/all-components-bundle.js +1334 -68
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +4076 -3811
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/combobox/index.d.ts +3 -6
  6. package/dist/esm/combobox/index.js +4 -12
  7. package/dist/esm/combobox/index.js.map +1 -1
  8. package/dist/esm/combobox/template.js +1 -0
  9. package/dist/esm/combobox/template.js.map +1 -1
  10. package/dist/esm/patterns/dropdown/styles.js +22 -2
  11. package/dist/esm/patterns/dropdown/styles.js.map +1 -1
  12. package/dist/esm/select/index.d.ts +2 -4
  13. package/dist/esm/select/index.js +4 -13
  14. package/dist/esm/select/index.js.map +1 -1
  15. package/dist/esm/select/template.js +1 -0
  16. package/dist/esm/select/template.js.map +1 -1
  17. package/dist/esm/table/components/cell/index.d.ts +9 -0
  18. package/dist/esm/table/components/cell/index.js +20 -0
  19. package/dist/esm/table/components/cell/index.js.map +1 -1
  20. package/dist/esm/table/components/cell/styles.js +17 -1
  21. package/dist/esm/table/components/cell/styles.js.map +1 -1
  22. package/dist/esm/table/components/cell/template.js +10 -2
  23. package/dist/esm/table/components/cell/template.js.map +1 -1
  24. package/dist/esm/table/components/group-row/index.d.ts +11 -3
  25. package/dist/esm/table/components/group-row/index.js +13 -1
  26. package/dist/esm/table/components/group-row/index.js.map +1 -1
  27. package/dist/esm/table/components/group-row/styles.js +7 -1
  28. package/dist/esm/table/components/group-row/styles.js.map +1 -1
  29. package/dist/esm/table/components/group-row/template.js +1 -1
  30. package/dist/esm/table/components/group-row/template.js.map +1 -1
  31. package/dist/esm/table/components/header/styles.js +7 -1
  32. package/dist/esm/table/components/header/styles.js.map +1 -1
  33. package/dist/esm/table/components/row/index.d.ts +16 -4
  34. package/dist/esm/table/components/row/index.js +34 -6
  35. package/dist/esm/table/components/row/index.js.map +1 -1
  36. package/dist/esm/table/components/row/styles.js +39 -1
  37. package/dist/esm/table/components/row/styles.js.map +1 -1
  38. package/dist/esm/table/components/row/template.js +4 -2
  39. package/dist/esm/table/components/row/template.js.map +1 -1
  40. package/dist/esm/table/index.d.ts +22 -2
  41. package/dist/esm/table/index.js +58 -1
  42. package/dist/esm/table/index.js.map +1 -1
  43. package/dist/esm/table/models/keyboard-navigation-manager.d.ts +96 -0
  44. package/dist/esm/table/models/keyboard-navigation-manager.js +1015 -0
  45. package/dist/esm/table/models/keyboard-navigation-manager.js.map +1 -0
  46. package/dist/esm/table/models/table-update-tracker.d.ts +2 -1
  47. package/dist/esm/table/models/table-update-tracker.js +20 -3
  48. package/dist/esm/table/models/table-update-tracker.js.map +1 -1
  49. package/dist/esm/table/models/virtualizer.d.ts +6 -2
  50. package/dist/esm/table/models/virtualizer.js +16 -22
  51. package/dist/esm/table/models/virtualizer.js.map +1 -1
  52. package/dist/esm/table/styles.js +21 -0
  53. package/dist/esm/table/styles.js.map +1 -1
  54. package/dist/esm/table/template.js +21 -3
  55. package/dist/esm/table/template.js.map +1 -1
  56. package/dist/esm/table/testing/table.pageobject.d.ts +7 -2
  57. package/dist/esm/table/testing/table.pageobject.js +16 -9
  58. package/dist/esm/table/testing/table.pageobject.js.map +1 -1
  59. package/dist/esm/table/types.d.ts +38 -0
  60. package/dist/esm/table/types.js +14 -0
  61. package/dist/esm/table/types.js.map +1 -1
  62. package/dist/esm/table-column/anchor/cell-view/index.d.ts +3 -0
  63. package/dist/esm/table-column/anchor/cell-view/index.js +13 -0
  64. package/dist/esm/table-column/anchor/cell-view/index.js.map +1 -1
  65. package/dist/esm/table-column/anchor/cell-view/template.js +4 -2
  66. package/dist/esm/table-column/anchor/cell-view/template.js.map +1 -1
  67. package/dist/esm/table-column/base/cell-view/index.d.ts +5 -0
  68. package/dist/esm/table-column/base/cell-view/index.js +7 -0
  69. package/dist/esm/table-column/base/cell-view/index.js.map +1 -1
  70. package/package.json +1 -1
@@ -16280,7 +16280,7 @@
16280
16280
 
16281
16281
  /**
16282
16282
  * Do not edit directly
16283
- * Generated on Thu, 20 Jun 2024 19:54:16 GMT
16283
+ * Generated on Wed, 26 Jun 2024 20:04:52 GMT
16284
16284
  */
16285
16285
 
16286
16286
  const Information100DarkUi = "#a46eff";
@@ -16699,6 +16699,9 @@
16699
16699
  const keyEnter = "Enter";
16700
16700
  const keyEscape = "Escape";
16701
16701
  const keyHome = "Home";
16702
+ const keyFunction2 = "F2";
16703
+ const keyPageDown = "PageDown";
16704
+ const keyPageUp = "PageUp";
16702
16705
  const keyShift = "Shift";
16703
16706
  const keySpace = " ";
16704
16707
  const keyTab = "Tab";
@@ -20793,12 +20796,32 @@
20793
20796
  overflow-y: auto;
20794
20797
  width: 100%;
20795
20798
  --ni-private-listbox-padding: ${smallPadding};
20796
- max-height: calc(var(--ni-private-select-max-height) - ${smallPadding});
20799
+ --ni-private-listbox-filter-height: 0px;
20800
+ --ni-private-listbox-loading-indicator-height: 0px;
20801
+ max-height: min(
20802
+ calc(
20803
+ ${smallPadding} + 2 * ${borderWidth} + ${controlHeight} * 10.5 +
20804
+ var(--ni-private-listbox-filter-height) +
20805
+ var(--ni-private-listbox-loading-indicator-height)
20806
+ ),
20807
+ calc(
20808
+ var(--ni-private-listbox-available-viewport-height) -
20809
+ ${smallPadding}
20810
+ )
20811
+ );
20797
20812
  box-shadow: ${elevation2BoxShadow};
20798
- border: 1px solid ${popupBorderColor};
20813
+ border: ${borderWidth} solid ${popupBorderColor};
20799
20814
  background-color: ${applicationBackgroundColor};
20800
20815
  }
20801
20816
 
20817
+ .listbox:has(.filter-field) {
20818
+ --ni-private-listbox-filter-height: ${controlHeight};
20819
+ }
20820
+
20821
+ .listbox:has(.loading-container) {
20822
+ --ni-private-listbox-loading-indicator-height: ${controlHeight};
20823
+ }
20824
+
20802
20825
  .listbox slot {
20803
20826
  display: block;
20804
20827
  background: transparent;
@@ -21143,6 +21166,7 @@
21143
21166
  part="listbox"
21144
21167
  role="listbox"
21145
21168
  ?disabled="${x => x.disabled}"
21169
+ style="--ni-private-listbox-available-viewport-height: ${x => x.availableViewportHeight}px;"
21146
21170
  ${ref('listbox')}
21147
21171
  >
21148
21172
  <slot name="option"
@@ -21200,11 +21224,11 @@
21200
21224
  */
21201
21225
  this.listboxId = uniqueId('listbox-');
21202
21226
  /**
21203
- * The max height for the listbox when opened.
21227
+ * The space available in the viewport for the listbox when opened.
21204
21228
  *
21205
21229
  * @internal
21206
21230
  */
21207
- this.maxHeight = 0;
21231
+ this.availableViewportHeight = 0;
21208
21232
  this.valueUpdatedByInput = false;
21209
21233
  this._value = '';
21210
21234
  this.filter = '';
@@ -21584,7 +21608,7 @@
21584
21608
  this.positionAttribute = this.forcedPosition
21585
21609
  ? this.positionAttribute
21586
21610
  : this.position;
21587
- this.maxHeight = this.position === SelectPosition.above
21611
+ this.availableViewportHeight = this.position === SelectPosition.above
21588
21612
  ? Math.trunc(currentBox.top)
21589
21613
  : Math.trunc(availableBottom);
21590
21614
  }
@@ -21662,9 +21686,6 @@
21662
21686
  ariaLabelChanged(_oldValue, _newValue) {
21663
21687
  this.updateInputAriaLabel();
21664
21688
  }
21665
- maxHeightChanged() {
21666
- this.updateListboxMaxHeightCssVariable();
21667
- }
21668
21689
  /**
21669
21690
  * Sets the value and to match the first selected option.
21670
21691
  */
@@ -21708,11 +21729,6 @@
21708
21729
  : this.control.value;
21709
21730
  this.updateValue(this.value !== newValue);
21710
21731
  }
21711
- updateListboxMaxHeightCssVariable() {
21712
- if (this.listbox) {
21713
- this.listbox.style.setProperty('--ni-private-select-max-height', `${this.maxHeight}px`);
21714
- }
21715
- }
21716
21732
  updateInputAriaLabel() {
21717
21733
  const inputElement = this.shadowRoot?.querySelector('.selected-value');
21718
21734
  if (this.ariaLabel) {
@@ -21785,7 +21801,7 @@
21785
21801
  ], Combobox.prototype, "hasOverflow", void 0);
21786
21802
  __decorate$1([
21787
21803
  observable
21788
- ], Combobox.prototype, "maxHeight", void 0);
21804
+ ], Combobox.prototype, "availableViewportHeight", void 0);
21789
21805
  const nimbleCombobox = Combobox.compose({
21790
21806
  baseName: 'combobox',
21791
21807
  baseClass: FormAssociatedCombobox,
@@ -59802,6 +59818,7 @@ img.ProseMirror-separator {
59802
59818
  part="listbox"
59803
59819
  role="listbox"
59804
59820
  ?disabled="${x => x.disabled}"
59821
+ style="--ni-private-listbox-available-viewport-height: ${x => x.availableViewportHeight}px;"
59805
59822
  ${ref('listbox')}
59806
59823
  >
59807
59824
  ${when(x => x.filterMode !== FilterMode.none, html `
@@ -59913,11 +59930,11 @@ img.ProseMirror-separator {
59913
59930
  */
59914
59931
  this.filter = '';
59915
59932
  /**
59916
- * The max height for the listbox when opened.
59933
+ * The space available in the viewport for the listbox when opened.
59917
59934
  *
59918
59935
  * @internal
59919
59936
  */
59920
- this.maxHeight = 0;
59937
+ this.availableViewportHeight = 0;
59921
59938
  this._value = '';
59922
59939
  this.forcedPosition = false;
59923
59940
  }
@@ -60613,10 +60630,9 @@ img.ProseMirror-separator {
60613
60630
  this.positionAttribute = this.forcedPosition
60614
60631
  ? this.positionAttribute
60615
60632
  : this.position;
60616
- this.maxHeight = this.position === SelectPosition.above
60633
+ this.availableViewportHeight = this.position === SelectPosition.above
60617
60634
  ? Math.trunc(currentBox.top)
60618
60635
  : Math.trunc(availableBottom);
60619
- this.updateListboxMaxHeightCssVariable();
60620
60636
  }
60621
60637
  updateAdjacentSeparatorState(element) {
60622
60638
  const previousElement = this.getPreviousVisibleOptionOrGroup(element);
@@ -60779,9 +60795,6 @@ img.ProseMirror-separator {
60779
60795
  };
60780
60796
  this.$emit('filter-input', eventDetail, { bubbles: true });
60781
60797
  }
60782
- maxHeightChanged() {
60783
- this.updateListboxMaxHeightCssVariable();
60784
- }
60785
60798
  initializeOpenState() {
60786
60799
  this.setActiveOption(this.selectedIndex);
60787
60800
  this.ariaControls = this.listboxId;
@@ -60789,11 +60802,6 @@ img.ProseMirror-separator {
60789
60802
  this.setPositioning();
60790
60803
  this.focusAndScrollOptionIntoView();
60791
60804
  }
60792
- updateListboxMaxHeightCssVariable() {
60793
- if (this.listbox) {
60794
- this.listbox.style.setProperty('--ni-private-select-max-height', `${this.maxHeight}px`);
60795
- }
60796
- }
60797
60805
  }
60798
60806
  __decorate$1([
60799
60807
  attr
@@ -60848,7 +60856,7 @@ img.ProseMirror-separator {
60848
60856
  ], Select.prototype, "filter", void 0);
60849
60857
  __decorate$1([
60850
60858
  observable
60851
- ], Select.prototype, "maxHeight", void 0);
60859
+ ], Select.prototype, "availableViewportHeight", void 0);
60852
60860
  __decorate$1([
60853
60861
  volatile
60854
60862
  ], Select.prototype, "collapsible", null);
@@ -64433,6 +64441,20 @@ img.ProseMirror-separator {
64433
64441
  selected: 'selected',
64434
64442
  partiallySelected: 'partially-selected'
64435
64443
  };
64444
+ /**
64445
+ * @internal
64446
+ * Table keyboard focus types
64447
+ */
64448
+ const TableFocusType = {
64449
+ none: 'none',
64450
+ columnHeader: 'columnHeader',
64451
+ headerActions: 'headerActions',
64452
+ row: 'row',
64453
+ rowSelectionCheckbox: 'rowSelectionCheckbox',
64454
+ cell: 'cell',
64455
+ cellActionMenu: 'cellActionMenu',
64456
+ cellContent: 'cellContent'
64457
+ };
64436
64458
 
64437
64459
  /**
64438
64460
  * The possible operations to use when sorting a table column.
@@ -64514,6 +64536,13 @@ img.ProseMirror-separator {
64514
64536
  this.delegatedEvents = [];
64515
64537
  this.delegatedEventHandler = () => { };
64516
64538
  }
64539
+ /**
64540
+ * Gets the child elements in this cell view that should be able to be reached via Tab/ Shift-Tab,
64541
+ * if any.
64542
+ */
64543
+ get tabbableChildren() {
64544
+ return [];
64545
+ }
64517
64546
  /**
64518
64547
  * Called if an element inside this cell view has focus, and this row/cell is being recycled.
64519
64548
  * Expected implementation is to commit changes as needed, and blur the focusable element (or close
@@ -64897,6 +64926,15 @@ img.ProseMirror-separator {
64897
64926
  --ni-private-column-divider-padding: 3px;
64898
64927
  }
64899
64928
 
64929
+ :host(${focusVisible}) {
64930
+ ${
64931
+ /* The table can briefly be focused in some keyboard nav cases (e.g. regaining focus and we
64932
+ need to scroll to the previously focused row first). Ensure that we don't get the browser-default
64933
+ focus outline in that case.
64934
+ ) */ ''}
64935
+ outline: none;
64936
+ }
64937
+
64900
64938
  .disable-select {
64901
64939
  ${userSelectNone}
64902
64940
  }
@@ -65027,6 +65065,10 @@ img.ProseMirror-separator {
65027
65065
  position: relative;
65028
65066
  }
65029
65067
 
65068
+ .table-viewport${focusVisible} {
65069
+ outline: none;
65070
+ }
65071
+
65030
65072
  .table-scroll {
65031
65073
  pointer-events: none;
65032
65074
  position: absolute;
@@ -65053,10 +65095,17 @@ img.ProseMirror-separator {
65053
65095
 
65054
65096
  .group-row {
65055
65097
  position: relative;
65098
+ --ni-private-table-cell-focus-offset-multiplier: 0;
65056
65099
  }
65057
65100
 
65058
65101
  .row {
65059
65102
  position: relative;
65103
+ --ni-private-table-cell-focus-offset-multiplier: 0;
65104
+ }
65105
+
65106
+ .collapse-all-visible .row,
65107
+ .collapse-all-visible .group-row {
65108
+ --ni-private-table-cell-focus-offset-multiplier: 1;
65060
65109
  }
65061
65110
 
65062
65111
  .accessibly-hidden {
@@ -65087,6 +65136,11 @@ img.ProseMirror-separator {
65087
65136
  cursor: default;
65088
65137
  }
65089
65138
 
65139
+ :host(${focusVisible}) {
65140
+ outline: calc(2 * ${borderWidth}) solid ${borderHoverColor};
65141
+ outline-offset: calc(-2 * ${borderWidth});
65142
+ }
65143
+
65090
65144
  .sort-indicator,
65091
65145
  .grouped-indicator {
65092
65146
  flex: 0 0 auto;
@@ -65240,6 +65294,11 @@ img.ProseMirror-separator {
65240
65294
  background-color: ${fillHoverSelectedColor};
65241
65295
  }
65242
65296
 
65297
+ :host(${focusVisible}) {
65298
+ outline: calc(2 * ${borderWidth}) solid ${borderHoverColor};
65299
+ outline-offset: calc(-2 * ${borderWidth});
65300
+ }
65301
+
65243
65302
  .expand-collapse-button {
65244
65303
  flex: 0 0 auto;
65245
65304
  margin-left: max(
@@ -65314,6 +65373,34 @@ img.ProseMirror-separator {
65314
65373
  --ni-private-table-cell-action-menu-display: block;
65315
65374
  }
65316
65375
 
65376
+ nimble-table-cell${focusVisible} {
65377
+ --ni-private-table-cell-action-menu-display: block;
65378
+ }
65379
+
65380
+ nimble-table-cell:first-of-type${focusVisible} {
65381
+ margin-left: calc(
65382
+ -1 * (${controlHeight} - ${smallPadding}) * var(--ni-private-table-cell-focus-offset-multiplier)
65383
+ );
65384
+ padding-left: calc(
65385
+ (${controlHeight} - ${mediumPadding}) *
65386
+ var(--ni-private-table-cell-focus-offset-multiplier) +
65387
+ ${mediumPadding}
65388
+ );
65389
+ }
65390
+
65391
+ nimble-table-cell:first-of-type${focusVisible}::before {
65392
+ content: '';
65393
+ display: block;
65394
+ width: calc(
65395
+ (
65396
+ ${controlHeight} *
65397
+ var(--ni-private-table-cell-nesting-level) +
65398
+ ${smallPadding}
65399
+ ) * var(--ni-private-table-cell-focus-offset-multiplier)
65400
+ );
65401
+ height: ${controlHeight};
65402
+ }
65403
+
65317
65404
  :host(:hover) nimble-table-cell {
65318
65405
  --ni-private-table-cell-action-menu-display: block;
65319
65406
  }
@@ -65321,6 +65408,10 @@ img.ProseMirror-separator {
65321
65408
  :host([selected]) nimble-table-cell {
65322
65409
  --ni-private-table-cell-action-menu-display: block;
65323
65410
  }
65411
+
65412
+ :host(${focusVisible}) nimble-table-cell {
65413
+ --ni-private-table-cell-action-menu-display: block;
65414
+ }
65324
65415
  `.withBehaviors(themeBehavior(Theme.color, css `
65325
65416
  :host([selectable]:not([selected]):hover)::before {
65326
65417
  background-color: ${hexToRgbaCssColor(White, 0.05)};
@@ -65351,6 +65442,15 @@ img.ProseMirror-separator {
65351
65442
  --ni-private-table-cell-action-menu-display: block;
65352
65443
  }
65353
65444
 
65445
+ :host(${focusVisible}) {
65446
+ outline: calc(2 * ${borderWidth}) solid ${borderHoverColor};
65447
+ outline-offset: -2px;
65448
+ }
65449
+
65450
+ .cell-view-container {
65451
+ display: contents;
65452
+ }
65453
+
65354
65454
  .cell-view {
65355
65455
  overflow: hidden;
65356
65456
  }
@@ -65363,19 +65463,33 @@ img.ProseMirror-separator {
65363
65463
  height: ${controlSlimHeight};
65364
65464
  align-self: center;
65365
65465
  }
65466
+
65467
+ ${
65468
+ /* This CSS class is applied dynamically by KeyboardNavigationManager */ ''}
65469
+ .action-menu.cell-action-menu-focused {
65470
+ display: block;
65471
+ }
65366
65472
  `;
65367
65473
 
65368
65474
  // prettier-ignore
65369
65475
  const template$h = html `
65370
- <template role="cell" style="--ni-private-table-cell-nesting-level: ${x => x.nestingLevel}">
65371
- ${x => x.cellViewTemplate}
65476
+ <template role="cell" style="--ni-private-table-cell-nesting-level: ${x => x.nestingLevel}"
65477
+ @focusin="${x => x.onCellFocusIn()}"
65478
+ @blur="${x => x.onCellBlur()}"
65479
+ >
65480
+ <div ${ref('cellViewContainer')} class="cell-view-container" @focusin="${x => x.onCellViewFocusIn()}">
65481
+ ${x => x.cellViewTemplate}
65482
+ </div>
65372
65483
  ${when(x => x.hasActionMenu, html `
65373
65484
  <${menuButtonTag} ${ref('actionMenuButton')}
65374
65485
  content-hidden
65375
65486
  appearance="${ButtonAppearance.ghost}"
65487
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
65488
+ tabindex="-1"
65376
65489
  @beforetoggle="${(x, c) => x.onActionMenuBeforeToggle(c.event)}"
65377
65490
  @toggle="${(x, c) => x.onActionMenuToggle(c.event)}"
65378
65491
  @click="${(_, c) => c.event.stopPropagation()}"
65492
+ @blur="${x => x.onActionMenuBlur()}"
65379
65493
  class="action-menu"
65380
65494
  title="${x => (x.actionMenuLabel ? x.actionMenuLabel : tableCellActionMenuLabel.getValueFor(x))}"
65381
65495
  >
@@ -65398,6 +65512,11 @@ img.ProseMirror-separator {
65398
65512
  this.menuOpen = false;
65399
65513
  this.nestingLevel = 0;
65400
65514
  }
65515
+ /** @internal */
65516
+ get cellView() {
65517
+ return this.cellViewContainer
65518
+ .firstElementChild;
65519
+ }
65401
65520
  onActionMenuBeforeToggle(event) {
65402
65521
  this.$emit('cell-action-menu-beforetoggle', event.detail);
65403
65522
  }
@@ -65405,6 +65524,18 @@ img.ProseMirror-separator {
65405
65524
  this.menuOpen = event.detail.newState;
65406
65525
  this.$emit('cell-action-menu-toggle', event.detail);
65407
65526
  }
65527
+ onActionMenuBlur() {
65528
+ this.$emit('cell-action-menu-blur', this);
65529
+ }
65530
+ onCellViewFocusIn() {
65531
+ this.$emit('cell-view-focus-in', this);
65532
+ }
65533
+ onCellFocusIn() {
65534
+ this.$emit('cell-focus-in', this);
65535
+ }
65536
+ onCellBlur() {
65537
+ this.$emit('cell-blur', this);
65538
+ }
65408
65539
  }
65409
65540
  __decorate$1([
65410
65541
  observable
@@ -65430,6 +65561,9 @@ img.ProseMirror-separator {
65430
65561
  __decorate$1([
65431
65562
  observable
65432
65563
  ], TableCell.prototype, "cellViewTemplate", void 0);
65564
+ __decorate$1([
65565
+ observable
65566
+ ], TableCell.prototype, "cellViewContainer", void 0);
65433
65567
  __decorate$1([
65434
65568
  observable
65435
65569
  ], TableCell.prototype, "nestingLevel", void 0);
@@ -65451,11 +65585,13 @@ img.ProseMirror-separator {
65451
65585
  >
65452
65586
  ${when(x => !x.rowOperationGridCellHidden, html `
65453
65587
  <span role="gridcell" class="row-operations-container">
65454
- ${when(x => x.selectable && !x.hideSelection, html `
65588
+ ${when(x => x.showSelectionCheckbox, html `
65455
65589
  <${checkboxTag}
65456
65590
  ${ref('selectionCheckbox')}
65457
65591
  class="selection-checkbox"
65458
- @change="${(x, c) => x.onSelectionChange(c.event)}"
65592
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
65593
+ tabindex="-1"
65594
+ @change="${(x, c) => x.onSelectionCheckboxChange(c.event)}"
65459
65595
  @click="${(_, c) => c.event.stopPropagation()}"
65460
65596
  title="${x => tableRowSelectLabel.getValueFor(x)}"
65461
65597
  aria-label="${x => tableRowSelectLabel.getValueFor(x)}"
@@ -65576,6 +65712,10 @@ img.ProseMirror-separator {
65576
65712
  get isNestedParent() {
65577
65713
  return this.isParentRow && this.nestingLevel > 0;
65578
65714
  }
65715
+ /** @internal */
65716
+ get showSelectionCheckbox() {
65717
+ return this.selectable && !this.hideSelection;
65718
+ }
65579
65719
  get ariaSelected() {
65580
65720
  if (this.selectable) {
65581
65721
  return this.selected ? 'true' : 'false';
@@ -65583,16 +65723,20 @@ img.ProseMirror-separator {
65583
65723
  return null;
65584
65724
  }
65585
65725
  /** @internal */
65586
- onSelectionChange(event) {
65726
+ onSelectionCheckboxChange(event) {
65587
65727
  if (this.ignoreSelectionChangeEvents) {
65588
65728
  return;
65589
65729
  }
65590
65730
  const checkbox = event.target;
65591
65731
  const checked = checkbox.checked;
65592
- this.selected = checked;
65732
+ this.onSelectionChange(!checked, checked);
65733
+ }
65734
+ /** @internal */
65735
+ onSelectionChange(oldState, newState) {
65736
+ this.selected = newState;
65593
65737
  const detail = {
65594
- oldState: !checked,
65595
- newState: checked
65738
+ oldState,
65739
+ newState
65596
65740
  };
65597
65741
  this.$emit('row-selection-toggle', detail);
65598
65742
  }
@@ -65623,6 +65767,20 @@ img.ProseMirror-separator {
65623
65767
  this.updateCellStates();
65624
65768
  }
65625
65769
  }
65770
+ /** @internal */
65771
+ getFocusableElements() {
65772
+ return {
65773
+ selectionCheckbox: this.showSelectionCheckbox
65774
+ ? this.selectionCheckbox
65775
+ : undefined,
65776
+ cells: Array.from(this.cellContainer.querySelectorAll(tableCellTag)).map(cell => ({
65777
+ cell,
65778
+ actionMenuButton: cell.hasActionMenu
65779
+ ? cell.actionMenuButton
65780
+ : undefined
65781
+ }))
65782
+ };
65783
+ }
65626
65784
  onRowExpandToggle(event) {
65627
65785
  const expandEventDetail = {
65628
65786
  oldState: this.expanded,
@@ -65630,7 +65788,7 @@ img.ProseMirror-separator {
65630
65788
  recordId: this.recordId
65631
65789
  };
65632
65790
  this.$emit('row-expand-toggle', expandEventDetail);
65633
- event.stopImmediatePropagation();
65791
+ event?.stopImmediatePropagation();
65634
65792
  // To avoid a visual glitch with improper expand/collapse icons performing an
65635
65793
  // animation (due to visual re-use apparently), we apply a class to the
65636
65794
  // contained expand-collapse button temporarily. We use the 'transitionend' event
@@ -65742,6 +65900,9 @@ img.ProseMirror-separator {
65742
65900
  __decorate$1([
65743
65901
  observable
65744
65902
  ], TableRow.prototype, "nestingLevel", void 0);
65903
+ __decorate$1([
65904
+ observable
65905
+ ], TableRow.prototype, "resolvedRowIndex", void 0);
65745
65906
  __decorate$1([
65746
65907
  attr({ attribute: 'is-parent-row', mode: 'boolean' })
65747
65908
  ], TableRow.prototype, "isParentRow", void 0);
@@ -65772,6 +65933,9 @@ img.ProseMirror-separator {
65772
65933
  __decorate$1([
65773
65934
  volatile
65774
65935
  ], TableRow.prototype, "isNestedParent", null);
65936
+ __decorate$1([
65937
+ volatile
65938
+ ], TableRow.prototype, "showSelectionCheckbox", null);
65775
65939
  __decorate$1([
65776
65940
  volatile
65777
65941
  ], TableRow.prototype, "ariaSelected", null);
@@ -65822,6 +65986,11 @@ img.ProseMirror-separator {
65822
65986
  background-color: ${fillHoverColor};
65823
65987
  }
65824
65988
 
65989
+ :host(${focusVisible}) {
65990
+ outline: calc(2 * ${borderWidth}) solid ${borderHoverColor};
65991
+ outline-offset: calc(-2 * ${borderWidth});
65992
+ }
65993
+
65825
65994
  .expand-collapse-button {
65826
65995
  margin-left: calc(
65827
65996
  ${mediumPadding} + ${standardPadding} * 2 *
@@ -65882,7 +66051,7 @@ img.ProseMirror-separator {
65882
66051
  <${checkboxTag}
65883
66052
  ${ref('selectionCheckbox')}
65884
66053
  class="selection-checkbox"
65885
- @change="${(x, c) => x.onSelectionChange(c.event)}"
66054
+ @change="${(x, c) => x.onSelectionCheckboxChange(c.event)}"
65886
66055
  @click="${(_, c) => c.event.stopPropagation()}"
65887
66056
  title="${x => tableGroupSelectAllLabel.getValueFor(x)}"
65888
66057
  aria-label="${x => tableGroupSelectAllLabel.getValueFor(x)}"
@@ -65948,7 +66117,7 @@ img.ProseMirror-separator {
65948
66117
  this.expandIcon.addEventListener('transitionend', this.removeAnimatingClass);
65949
66118
  }
65950
66119
  /** @internal */
65951
- onSelectionChange(event) {
66120
+ onSelectionCheckboxChange(event) {
65952
66121
  if (this.ignoreSelectionChangeEvents) {
65953
66122
  return;
65954
66123
  }
@@ -65963,6 +66132,15 @@ img.ProseMirror-separator {
65963
66132
  };
65964
66133
  this.$emit('group-selection-toggle', detail);
65965
66134
  }
66135
+ /** @internal */
66136
+ getFocusableElements() {
66137
+ return {
66138
+ selectionCheckbox: this.selectable
66139
+ ? this.selectionCheckbox
66140
+ : undefined,
66141
+ cells: []
66142
+ };
66143
+ }
65966
66144
  selectionStateChanged() {
65967
66145
  this.setSelectionCheckboxState();
65968
66146
  }
@@ -65985,6 +66163,9 @@ img.ProseMirror-separator {
65985
66163
  __decorate$1([
65986
66164
  observable
65987
66165
  ], TableGroupRow.prototype, "nestingLevel", void 0);
66166
+ __decorate$1([
66167
+ observable
66168
+ ], TableGroupRow.prototype, "resolvedRowIndex", void 0);
65988
66169
  __decorate$1([
65989
66170
  observable
65990
66171
  ], TableGroupRow.prototype, "immediateChildCount", void 0);
@@ -66018,6 +66199,8 @@ img.ProseMirror-separator {
66018
66199
  const template$e = html `
66019
66200
  <template
66020
66201
  role="treegrid"
66202
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
66203
+ tabindex="0"
66021
66204
  aria-multiselectable="${x => x.ariaMultiSelectable}"
66022
66205
  ${children$1({ property: 'childItems', filter: elements() })}
66023
66206
  >
@@ -66045,6 +66228,8 @@ img.ProseMirror-separator {
66045
66228
  <span class="checkbox-container">
66046
66229
  <${checkboxTag}
66047
66230
  ${ref('selectionCheckbox')}
66231
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
66232
+ tabindex="-1"
66048
66233
  class="${x => `selection-checkbox ${x.selectionMode ? x.selectionMode : ''}`}"
66049
66234
  @change="${(x, c) => x.onAllRowsSelectionChange(c.event)}"
66050
66235
  title="${x => tableSelectAllLabel.getValueFor(x)}"
@@ -66054,6 +66239,9 @@ img.ProseMirror-separator {
66054
66239
  </span>
66055
66240
  `)}
66056
66241
  <${buttonTag}
66242
+ ${ref('collapseAllButton')}
66243
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
66244
+ tabindex="-1"
66057
66245
  class="collapse-all-button ${x => `${x.showCollapseAll ? 'visible' : ''}`}"
66058
66246
  content-hidden
66059
66247
  appearance="${ButtonAppearance.ghost}"
@@ -66081,9 +66269,11 @@ img.ProseMirror-separator {
66081
66269
  `)}
66082
66270
  <${tableHeaderTag}
66083
66271
  class="header"
66272
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager (if column sorting not disabled) */}
66084
66273
  sort-direction="${x => (typeof x.columnInternals.currentSortIndex === 'number' ? x.columnInternals.currentSortDirection : TableColumnSortDirection.none)}"
66085
66274
  ?first-sorted-column="${(x, c) => x === c.parent.firstSortedColumn}"
66086
66275
  ?indicators-hidden="${x => x.columnInternals.hideHeaderIndicators}"
66276
+ @keydown="${(x, c) => c.parent.onHeaderKeyDown(x, c.event)}"
66087
66277
  @click="${(x, c) => c.parent.toggleColumnSort(x, c.event.shiftKey)}"
66088
66278
  :isGrouped=${x => (typeof x.columnInternals.groupIndex === 'number' && !x.columnInternals.groupingDisabled)}
66089
66279
  >
@@ -66107,15 +66297,17 @@ img.ProseMirror-separator {
66107
66297
  </span>
66108
66298
  </div>
66109
66299
  </div>
66110
- <div class="table-viewport" ${ref('viewport')}>
66300
+ <div class="table-viewport" tabindex="-1" ${ref('viewport')}>
66111
66301
  <div class="table-scroll"></div>
66112
- <div class="table-row-container" ${children$1({ property: 'rowElements', filter: elements(tableRowTag) })}
66302
+ <div class="table-row-container ${x => `${x.showCollapseAll ? 'collapse-all-visible' : ''}`}" ${children$1({ property: 'rowElements', filter: elements(`${tableRowTag}, ${tableGroupRowTag}`) })}
66113
66303
  role="rowgroup">
66114
66304
  ${when(x => x.columns.length > 0 && x.canRenderRows, html `
66115
66305
  ${repeat(x => x.virtualizer.visibleItems, html `
66116
66306
  ${when((x, c) => c.parent.tableData[x.index]?.isGroupRow, html `
66117
66307
  <${tableGroupRowTag}
66118
66308
  class="group-row"
66309
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
66310
+ tabindex="-1"
66119
66311
  :groupRowValue="${(x, c) => c.parent.tableData[x.index]?.groupRowValue}"
66120
66312
  ?expanded="${(x, c) => c.parent.tableData[x.index]?.isExpanded}"
66121
66313
  :nestingLevel="${(x, c) => c.parent.tableData[x.index]?.nestingLevel}"
@@ -66123,6 +66315,9 @@ img.ProseMirror-separator {
66123
66315
  :groupColumn="${(x, c) => c.parent.tableData[x.index]?.groupColumn}"
66124
66316
  ?selectable="${(_, c) => c.parent.selectionMode === TableRowSelectionMode.multiple}"
66125
66317
  selection-state="${(x, c) => c.parent.tableData[x.index]?.selectionState}"
66318
+ :resolvedRowIndex="${x => x.index}"
66319
+ @focusin="${(_, c) => c.parent.onRowFocusIn(c.event)}"
66320
+ @blur="${(_, c) => c.parent.onRowBlur(c.event)}"
66126
66321
  @group-selection-toggle="${(x, c) => c.parent.onRowSelectionToggle(x.index, c.event)}"
66127
66322
  @group-expand-toggle="${(x, c) => c.parent.handleGroupRowExpanded(x.index, c.event)}"
66128
66323
  >
@@ -66131,6 +66326,8 @@ img.ProseMirror-separator {
66131
66326
  ${when((x, c) => !c.parent.tableData[x.index]?.isGroupRow, html `
66132
66327
  <${tableRowTag}
66133
66328
  class="row"
66329
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
66330
+ tabindex="-1"
66134
66331
  record-id="${(x, c) => c.parent.tableData[x.index]?.id}"
66135
66332
  ?selectable="${(_, c) => c.parent.selectionMode !== TableRowSelectionMode.none}"
66136
66333
  ?selected="${(x, c) => c.parent.tableData[x.index]?.selectionState === TableRowSelectionState.selected}"
@@ -66142,12 +66339,14 @@ img.ProseMirror-separator {
66142
66339
  :nestingLevel="${(x, c) => c.parent.tableData[x.index]?.nestingLevel}"
66143
66340
  ?row-operation-grid-cell-hidden="${(_, c) => !c.parent.showRowOperationColumn}"
66144
66341
  ?loading="${(x, c) => c.parent.tableData[x.index]?.isLoadingChildren}"
66342
+ :resolvedRowIndex="${x => x.index}"
66145
66343
  @click="${(x, c) => c.parent.onRowClick(x.index, c.event)}"
66344
+ @focusin="${(_, c) => c.parent.onRowFocusIn(c.event)}"
66345
+ @blur="${(_, c) => c.parent.onRowBlur(c.event)}"
66146
66346
  @row-selection-toggle="${(x, c) => c.parent.onRowSelectionToggle(x.index, c.event)}"
66147
66347
  @row-action-menu-beforetoggle="${(x, c) => c.parent.onRowActionMenuBeforeToggle(x.index, c.event)}"
66148
66348
  @row-action-menu-toggle="${(_, c) => c.parent.onRowActionMenuToggle(c.event)}"
66149
66349
  @row-expand-toggle="${(x, c) => c.parent.handleRowExpanded(x.index)}"
66150
- :dataIndex="${x => x.index}"
66151
66350
  >
66152
66351
  ${when((x, c) => c.parent.openActionMenuRecordId === c.parent.tableData[x.index]?.id, html `
66153
66352
  ${repeat((_, c) => c.parent.actionMenuSlots, html `
@@ -66812,16 +67011,25 @@ img.ProseMirror-separator {
66812
67011
  * @internal
66813
67012
  */
66814
67013
  class Virtualizer {
67014
+ get pageSize() {
67015
+ return this._pageSize;
67016
+ }
67017
+ get rowHeight() {
67018
+ return (parseFloat(controlHeight.getValueFor(this.table))
67019
+ + 2 * parseFloat(borderWidth.getValueFor(this.table)));
67020
+ }
66815
67021
  constructor(table, tanStackTable) {
66816
67022
  this.visibleItems = [];
66817
67023
  this.scrollHeight = 0;
66818
67024
  this.headerContainerMarginRight = 0;
66819
67025
  this.rowContainerYOffset = 0;
67026
+ this._pageSize = 0;
66820
67027
  this.table = table;
66821
67028
  this.tanStackTable = tanStackTable;
66822
67029
  this.viewportResizeObserver = new ResizeObserver(entries => {
66823
67030
  const borderBoxSize = entries[0]?.borderBoxSize[0];
66824
67031
  if (borderBoxSize) {
67032
+ this.updatePageSize();
66825
67033
  // If we have enough rows that a vertical scrollbar is shown, we need to offset the header widths
66826
67034
  // by the same margin so the column headers align with the corresponding rendered cells
66827
67035
  const viewportBoundingWidth = borderBoxSize.inlineSize;
@@ -66842,6 +67050,9 @@ img.ProseMirror-separator {
66842
67050
  this.updateVirtualizer();
66843
67051
  }
66844
67052
  }
67053
+ scrollToIndex(index, options) {
67054
+ this.virtualizer?.scrollToIndex(index, options);
67055
+ }
66845
67056
  updateVirtualizer() {
66846
67057
  const options = this.createVirtualizerOptions();
66847
67058
  if (this.virtualizer) {
@@ -66854,8 +67065,7 @@ img.ProseMirror-separator {
66854
67065
  this.handleVirtualizerChange();
66855
67066
  }
66856
67067
  createVirtualizerOptions() {
66857
- const rowHeight = parseFloat(controlHeight.getValueFor(this.table))
66858
- + 2 * parseFloat(borderWidth.getValueFor(this.table));
67068
+ const rowHeight = this.rowHeight;
66859
67069
  return {
66860
67070
  count: this.tanStackTable.getRowModel().rows.length,
66861
67071
  getScrollElement: () => {
@@ -66872,7 +67082,7 @@ img.ProseMirror-separator {
66872
67082
  };
66873
67083
  }
66874
67084
  handleVirtualizerChange() {
66875
- this.notifyFocusedCellRecycling();
67085
+ this.table.handleFocusedCellRecycling();
66876
67086
  const virtualizer = this.virtualizer;
66877
67087
  this.visibleItems = virtualizer.getVirtualItems();
66878
67088
  this.scrollHeight = virtualizer.getTotalSize();
@@ -66887,24 +67097,8 @@ img.ProseMirror-separator {
66887
67097
  }
66888
67098
  this.rowContainerYOffset = rowContainerYOffset;
66889
67099
  }
66890
- notifyFocusedCellRecycling() {
66891
- let tableFocusedElement = this.table.shadowRoot.activeElement;
66892
- while (tableFocusedElement !== null
66893
- && !(tableFocusedElement instanceof TableCellView)) {
66894
- if (tableFocusedElement.shadowRoot) {
66895
- tableFocusedElement = tableFocusedElement.shadowRoot.activeElement;
66896
- }
66897
- else {
66898
- break;
66899
- }
66900
- }
66901
- if (tableFocusedElement instanceof TableCellView) {
66902
- tableFocusedElement.focusedRecycleCallback();
66903
- }
66904
- if (this.table.openActionMenuRecordId !== undefined) {
66905
- const activeRow = this.table.rowElements.find(row => row.recordId === this.table.openActionMenuRecordId);
66906
- activeRow?.closeOpenActionMenus();
66907
- }
67100
+ updatePageSize() {
67101
+ this._pageSize = Math.round(this.table.viewport.clientHeight / this.rowHeight);
66908
67102
  }
66909
67103
  }
66910
67104
  __decorate$1([
@@ -67264,7 +67458,9 @@ img.ProseMirror-separator {
67264
67458
  'rowParentIds',
67265
67459
  'groupRows',
67266
67460
  'columnIds',
67461
+ 'columnHidden',
67267
67462
  'columnSort',
67463
+ 'columnSortDisabled',
67268
67464
  'columnWidths',
67269
67465
  'columnDefinition',
67270
67466
  'actionMenuSlots',
@@ -67320,6 +67516,13 @@ img.ProseMirror-separator {
67320
67516
  || this.isTracked('columnDefinition')
67321
67517
  || this.isTracked('rowParentIds'));
67322
67518
  }
67519
+ get requiresKeyboardFocusReset() {
67520
+ return (this.isTracked('columnSortDisabled')
67521
+ || this.isTracked('columnDefinition')
67522
+ || this.isTracked('columnHidden')
67523
+ || this.isTracked('selectionMode')
67524
+ || this.isTracked('actionMenuSlots'));
67525
+ }
67323
67526
  trackAllStateChanged() {
67324
67527
  this.trackAll();
67325
67528
  this.queueUpdate();
@@ -67334,13 +67537,20 @@ img.ProseMirror-separator {
67334
67537
  else if (isColumnInternalsProperty(changedColumnProperty, 'operandDataRecordFieldName', 'sortOperation')) {
67335
67538
  this.track('columnDefinition');
67336
67539
  }
67337
- else if (isColumnInternalsProperty(changedColumnProperty, 'sortingDisabled', 'currentSortDirection', 'currentSortIndex')) {
67540
+ else if (isColumnInternalsProperty(changedColumnProperty, 'sortingDisabled')) {
67541
+ this.track('columnSort');
67542
+ this.track('columnSortDisabled');
67543
+ }
67544
+ else if (isColumnInternalsProperty(changedColumnProperty, 'currentSortDirection', 'currentSortIndex')) {
67338
67545
  this.track('columnSort');
67339
67546
  }
67340
- else if (isColumnProperty(changedColumnProperty, 'columnHidden')
67341
- || isColumnInternalsProperty(changedColumnProperty, 'currentFractionalWidth', 'currentPixelWidth', 'minPixelWidth', 'resizingDisabled')) {
67547
+ else if (isColumnInternalsProperty(changedColumnProperty, 'currentFractionalWidth', 'currentPixelWidth', 'minPixelWidth', 'resizingDisabled')) {
67342
67548
  this.track('columnWidths');
67343
67549
  }
67550
+ else if (isColumnProperty(changedColumnProperty, 'columnHidden')) {
67551
+ this.track('columnWidths');
67552
+ this.track('columnHidden');
67553
+ }
67344
67554
  else if (isColumnProperty(changedColumnProperty, 'actionMenuSlot')) {
67345
67555
  this.track('actionMenuSlots');
67346
67556
  }
@@ -67353,6 +67563,7 @@ img.ProseMirror-separator {
67353
67563
  this.track('columnIds');
67354
67564
  this.track('columnDefinition');
67355
67565
  this.track('columnSort');
67566
+ this.track('columnSortDisabled');
67356
67567
  this.track('columnWidths');
67357
67568
  this.track('actionMenuSlots');
67358
67569
  this.track('groupRows');
@@ -67982,6 +68193,992 @@ img.ProseMirror-separator {
67982
68193
  observable
67983
68194
  ], ColumnValidator.prototype, "isColumnValid", void 0);
67984
68195
 
68196
+ /**
68197
+ * Manages the keyboard navigation and focus within the table.
68198
+ * @internal
68199
+ */
68200
+ class KeyboardNavigationManager {
68201
+ get inNavigationMode() {
68202
+ return (this.focusType !== TableFocusType.cellActionMenu
68203
+ && this.focusType !== TableFocusType.cellContent);
68204
+ }
68205
+ constructor(table, virtualizer) {
68206
+ this.table = table;
68207
+ this.virtualizer = virtualizer;
68208
+ this.focusType = TableFocusType.none;
68209
+ this.headerActionIndex = -1;
68210
+ this.rowIndex = -1;
68211
+ this.cellContentIndex = -1;
68212
+ this.columnIndex = -1;
68213
+ this.focusWithinTable = false;
68214
+ this.isCurrentlyFocusingElement = false;
68215
+ this.visibleRowNotifiers = [];
68216
+ this.onTableFocusIn = (event) => {
68217
+ this.focusWithinTable = true;
68218
+ this.updateFocusStateFromActiveElement(false);
68219
+ // Sets initial focus on the appropriate table content
68220
+ const actionMenuOpen = this.table.openActionMenuRecordId !== undefined;
68221
+ if ((event.target === this.table
68222
+ || this.focusType === TableFocusType.none)
68223
+ && !actionMenuOpen) {
68224
+ let focusHeader = true;
68225
+ if (this.hasRowOrCellFocusType()
68226
+ && this.scrollToAndFocusRow(this.rowIndex)) {
68227
+ focusHeader = false;
68228
+ }
68229
+ if (focusHeader && !this.setFocusOnHeader()) {
68230
+ // nothing to focus
68231
+ this.table.blur();
68232
+ }
68233
+ }
68234
+ };
68235
+ this.onTableFocusOut = () => {
68236
+ this.focusWithinTable = false;
68237
+ };
68238
+ this.onCellActionMenuBlur = (event) => {
68239
+ event.stopPropagation();
68240
+ const cell = event.detail;
68241
+ if (cell.actionMenuButton) {
68242
+ // Ensure that action menu buttons get hidden when no longer focused
68243
+ this.setActionMenuButtonFocused(cell.actionMenuButton, false);
68244
+ }
68245
+ };
68246
+ this.onCellViewFocusIn = (event) => {
68247
+ event.stopPropagation();
68248
+ this.updateFocusStateFromActiveElement(false);
68249
+ };
68250
+ this.onCellFocusIn = (event) => {
68251
+ event.stopPropagation();
68252
+ const cell = event.detail;
68253
+ this.updateFocusStateFromActiveElement(true);
68254
+ // Currently, clicking a non-interactive cell only updates the focus state to that row, it
68255
+ // doesn't focus the cell. If we revisit this, we most likely need to set the cells to tabindex=-1
68256
+ // upfront too, so their focusing behavior is consistent whether they've been previously keyboard
68257
+ // focused or not.
68258
+ if (this.focusType === TableFocusType.row
68259
+ && this.getActiveElement() === cell) {
68260
+ this.focusCurrentRow(false);
68261
+ }
68262
+ };
68263
+ this.onCellBlur = (event) => {
68264
+ event.stopPropagation();
68265
+ const cell = event.detail;
68266
+ this.setElementFocusable(cell, false);
68267
+ };
68268
+ this.onCaptureKeyDown = (event) => {
68269
+ let handled = false;
68270
+ if (event.key === keyTab) {
68271
+ handled = this.onTabPressed(event.shiftKey);
68272
+ }
68273
+ else if (this.inNavigationMode) {
68274
+ switch (event.key) {
68275
+ case keyArrowLeft:
68276
+ handled = this.onLeftArrowPressed();
68277
+ break;
68278
+ case keyArrowRight:
68279
+ handled = this.onRightArrowPressed();
68280
+ break;
68281
+ case keyArrowUp:
68282
+ handled = this.onUpArrowPressed();
68283
+ break;
68284
+ case keyArrowDown:
68285
+ handled = this.onDownArrowPressed();
68286
+ break;
68287
+ case keyPageUp:
68288
+ handled = this.onPageUpPressed();
68289
+ break;
68290
+ case keyPageDown:
68291
+ handled = this.onPageDownPressed();
68292
+ break;
68293
+ case keyHome:
68294
+ handled = this.onHomePressed(event.ctrlKey);
68295
+ break;
68296
+ case keyEnd:
68297
+ handled = this.onEndPressed(event.ctrlKey);
68298
+ break;
68299
+ case keyEnter:
68300
+ handled = this.onEnterPressed(event.ctrlKey);
68301
+ break;
68302
+ case keySpace:
68303
+ handled = this.onSpacePressed(event.shiftKey);
68304
+ break;
68305
+ case keyFunction2:
68306
+ handled = this.onF2Pressed();
68307
+ break;
68308
+ }
68309
+ }
68310
+ if (handled) {
68311
+ event.preventDefault();
68312
+ }
68313
+ };
68314
+ this.onKeyDown = (event) => {
68315
+ if (!this.inNavigationMode && !event.defaultPrevented) {
68316
+ if (event.key === keyEscape || event.key === keyFunction2) {
68317
+ const row = this.getCurrentRow();
68318
+ if (row) {
68319
+ this.trySetCellFocus(row.getFocusableElements());
68320
+ }
68321
+ }
68322
+ }
68323
+ };
68324
+ this.onViewportKeyDown = (event) => {
68325
+ let handleEvent = !this.inNavigationMode
68326
+ && (event.key === keyArrowUp || event.key === keyArrowDown);
68327
+ switch (event.key) {
68328
+ case keyPageUp:
68329
+ case keyPageDown:
68330
+ case keyHome:
68331
+ case keyEnd:
68332
+ handleEvent = true;
68333
+ break;
68334
+ }
68335
+ if (handleEvent) {
68336
+ // Swallow key presses that would cause table scrolling, independently of keyboard navigation
68337
+ event.preventDefault();
68338
+ event.stopImmediatePropagation();
68339
+ }
68340
+ };
68341
+ this.tableNotifier = Observable.getNotifier(this.table);
68342
+ this.tableNotifier.subscribe(this, 'rowElements');
68343
+ this.virtualizerNotifier = Observable.getNotifier(this.virtualizer);
68344
+ this.virtualizerNotifier.subscribe(this, 'visibleItems');
68345
+ }
68346
+ resetFocusState() {
68347
+ this.focusType = TableFocusType.none;
68348
+ const activeElement = this.getActiveElement();
68349
+ if (activeElement && this.isInTable(activeElement)) {
68350
+ this.setDefaultFocus();
68351
+ }
68352
+ }
68353
+ get hasActiveRowOrCellFocus() {
68354
+ return this.focusWithinTable && this.hasRowOrCellFocusType();
68355
+ }
68356
+ connect() {
68357
+ this.table.addEventListener('keydown', this.onCaptureKeyDown, { capture: true });
68358
+ this.table.addEventListener('keydown', this.onKeyDown);
68359
+ this.table.addEventListener('focusin', this.onTableFocusIn);
68360
+ this.table.addEventListener('focusout', this.onTableFocusOut);
68361
+ this.table.viewport.addEventListener('keydown', this.onViewportKeyDown);
68362
+ this.table.viewport.addEventListener('cell-action-menu-blur', this.onCellActionMenuBlur);
68363
+ this.table.viewport.addEventListener('cell-view-focus-in', this.onCellViewFocusIn);
68364
+ this.table.viewport.addEventListener('cell-focus-in', this.onCellFocusIn);
68365
+ this.table.viewport.addEventListener('cell-blur', this.onCellBlur);
68366
+ }
68367
+ disconnect() {
68368
+ this.table.removeEventListener('keydown', this.onCaptureKeyDown, { capture: true });
68369
+ this.table.removeEventListener('keydown', this.onKeyDown);
68370
+ this.table.removeEventListener('focusin', this.onTableFocusIn);
68371
+ this.table.removeEventListener('focusout', this.onTableFocusOut);
68372
+ this.table.viewport.removeEventListener('keydown', this.onViewportKeyDown);
68373
+ this.table.viewport.removeEventListener('cell-action-menu-blur', this.onCellActionMenuBlur);
68374
+ this.table.viewport.removeEventListener('cell-view-focus-in', this.onCellViewFocusIn);
68375
+ this.table.viewport.removeEventListener('cell-focus-in', this.onCellFocusIn);
68376
+ this.table.viewport.removeEventListener('cell-blur', this.onCellBlur);
68377
+ }
68378
+ handleChange(source, args) {
68379
+ let focusRowAndCell = false;
68380
+ if (source === this.virtualizer && args === 'visibleItems') {
68381
+ focusRowAndCell = true;
68382
+ }
68383
+ else if (source === this.table && args === 'rowElements') {
68384
+ for (const notifier of this.visibleRowNotifiers) {
68385
+ notifier.unsubscribe(this);
68386
+ }
68387
+ this.visibleRowNotifiers = [];
68388
+ for (const visibleRow of this.table.rowElements) {
68389
+ const rowNotifier = Observable.getNotifier(visibleRow);
68390
+ rowNotifier.subscribe(this, 'resolvedRowIndex');
68391
+ if (visibleRow.resolvedRowIndex === this.rowIndex) {
68392
+ focusRowAndCell = true;
68393
+ }
68394
+ }
68395
+ }
68396
+ else if (args === 'resolvedRowIndex'
68397
+ && this.isResolvedRowType(source)) {
68398
+ if (source.resolvedRowIndex === this.rowIndex) {
68399
+ focusRowAndCell = true;
68400
+ }
68401
+ }
68402
+ if (focusRowAndCell) {
68403
+ // Focusable elements in cells, and action menus, are both blurred on scroll. To maintain our row/cell focus state,
68404
+ // we focus the cell instead here. (We also don't want to refocus the cell content when the focusedRecycleCallback just
68405
+ // blurred it.)
68406
+ if (this.focusType === TableFocusType.cellActionMenu
68407
+ || this.focusType === TableFocusType.cellContent) {
68408
+ this.setCellFocusState(this.columnIndex, this.rowIndex, false);
68409
+ }
68410
+ if (this.inNavigationMode && this.hasRowOrCellFocusType()) {
68411
+ if (this.rowIndex > this.table.tableData.length - 1) {
68412
+ // Focused row index no longer valid, coerce to 1st row if possible
68413
+ if (this.table.tableData.length > 0) {
68414
+ this.rowIndex = 0;
68415
+ }
68416
+ else {
68417
+ if (this.focusWithinTable) {
68418
+ this.setDefaultFocus();
68419
+ }
68420
+ else {
68421
+ this.focusType = TableFocusType.none;
68422
+ }
68423
+ return;
68424
+ }
68425
+ }
68426
+ if (this.focusWithinTable) {
68427
+ this.focusCurrentRow(false);
68428
+ }
68429
+ }
68430
+ }
68431
+ }
68432
+ handleFocusedCellRecycling(hadRowOrCellFocus) {
68433
+ if (hadRowOrCellFocus && !this.focusWithinTable) {
68434
+ this.focusCurrentRow(false);
68435
+ }
68436
+ }
68437
+ onRowFocusIn(event) {
68438
+ if (this.isCurrentlyFocusingElement) {
68439
+ return;
68440
+ }
68441
+ const row = event.target;
68442
+ if (this.isResolvedRowType(row)) {
68443
+ if (this.rowIndex !== row.resolvedRowIndex) {
68444
+ // If user focuses a row some other way (e.g. mouse), update our focus state so future keyboard nav
68445
+ // will start from that row
68446
+ this.setRowFocusState(row.resolvedRowIndex);
68447
+ }
68448
+ }
68449
+ }
68450
+ onRowBlur(event) {
68451
+ const row = event.target;
68452
+ if (this.isResolvedRowType(row)) {
68453
+ this.setElementFocusable(row, false);
68454
+ }
68455
+ }
68456
+ onRowActionMenuToggle(event) {
68457
+ const isOpen = event.detail.newState;
68458
+ if (isOpen) {
68459
+ const row = event.target;
68460
+ const columnIndex = this.table.visibleColumns.findIndex(column => column.columnId === event.detail.columnId);
68461
+ this.setCellActionMenuFocusState(row.resolvedRowIndex, columnIndex, false);
68462
+ }
68463
+ }
68464
+ onEnterPressed(ctrlKey) {
68465
+ let row;
68466
+ let rowElements;
68467
+ if (this.hasRowOrCellFocusType()) {
68468
+ row = this.getCurrentRow();
68469
+ rowElements = row?.getFocusableElements();
68470
+ }
68471
+ if (this.focusType === TableFocusType.row) {
68472
+ if (row instanceof TableGroupRow) {
68473
+ this.toggleRowExpanded(row);
68474
+ return true;
68475
+ }
68476
+ }
68477
+ if (this.focusType === TableFocusType.cell) {
68478
+ if (ctrlKey) {
68479
+ const cell = rowElements?.cells[this.columnIndex];
68480
+ if (cell?.actionMenuButton && !cell.actionMenuButton.open) {
68481
+ cell.actionMenuButton.toggleButton.control.click();
68482
+ return true;
68483
+ }
68484
+ }
68485
+ return this.focusFirstInteractiveElementInCurrentCell(rowElements);
68486
+ }
68487
+ return false;
68488
+ }
68489
+ onF2Pressed() {
68490
+ if (this.focusType === TableFocusType.cell) {
68491
+ const row = this.getCurrentRow();
68492
+ const rowElements = row?.getFocusableElements();
68493
+ return this.focusFirstInteractiveElementInCurrentCell(rowElements);
68494
+ }
68495
+ return false;
68496
+ }
68497
+ onSpacePressed(shiftKey) {
68498
+ if (this.focusType === TableFocusType.row
68499
+ || this.focusType === TableFocusType.cell) {
68500
+ if (this.focusType === TableFocusType.row || shiftKey) {
68501
+ const row = this.getCurrentRow();
68502
+ if (row instanceof TableRow && row.selectable) {
68503
+ row.onSelectionChange(row.selected, !row.selected);
68504
+ }
68505
+ else if (row instanceof TableGroupRow) {
68506
+ this.toggleRowExpanded(row);
68507
+ }
68508
+ }
68509
+ // Default Space behavior scrolls down, which is redundant given the rest of our keyboard nav code, and we'd still try to focus a
68510
+ // row that you scrolled away from. So suppress default Space behavior if a row or cell is selected, regardless of if we're
68511
+ // toggling selection or not.
68512
+ return true;
68513
+ }
68514
+ return false;
68515
+ }
68516
+ onLeftArrowPressed() {
68517
+ let row;
68518
+ let rowElements;
68519
+ let headerElements;
68520
+ if (this.hasRowOrCellFocusType()) {
68521
+ row = this.getCurrentRow();
68522
+ rowElements = row?.getFocusableElements();
68523
+ }
68524
+ else if (this.hasHeaderFocusType()) {
68525
+ headerElements = this.getTableHeaderFocusableElements();
68526
+ }
68527
+ switch (this.focusType) {
68528
+ case TableFocusType.headerActions:
68529
+ return this.trySetHeaderActionFocus(headerElements, this.headerActionIndex - 1);
68530
+ case TableFocusType.columnHeader:
68531
+ return (this.trySetColumnHeaderFocus(headerElements, this.columnIndex - 1)
68532
+ || this.trySetHeaderActionFocus(headerElements, headerElements.headerActions.length - 1));
68533
+ case TableFocusType.row:
68534
+ if (this.isRowExpanded(row) === true) {
68535
+ this.toggleRowExpanded(row);
68536
+ return true;
68537
+ }
68538
+ return false;
68539
+ case TableFocusType.rowSelectionCheckbox:
68540
+ this.setRowFocusState();
68541
+ return this.focusCurrentRow(true);
68542
+ case TableFocusType.cell:
68543
+ if (!this.trySetCellFocus(rowElements, this.columnIndex - 1)
68544
+ && !this.trySetRowSelectionCheckboxFocus(rowElements)) {
68545
+ this.setRowFocusState();
68546
+ this.focusCurrentRow(true);
68547
+ }
68548
+ return true;
68549
+ }
68550
+ return false;
68551
+ }
68552
+ onRightArrowPressed() {
68553
+ let row;
68554
+ let rowElements;
68555
+ let headerElements;
68556
+ if (this.hasRowOrCellFocusType()) {
68557
+ row = this.getCurrentRow();
68558
+ rowElements = row?.getFocusableElements();
68559
+ }
68560
+ else if (this.hasHeaderFocusType()) {
68561
+ headerElements = this.getTableHeaderFocusableElements();
68562
+ }
68563
+ switch (this.focusType) {
68564
+ case TableFocusType.headerActions:
68565
+ return (this.trySetHeaderActionFocus(headerElements, this.headerActionIndex + 1) || this.trySetColumnHeaderFocus(headerElements, 0));
68566
+ case TableFocusType.columnHeader:
68567
+ return this.trySetColumnHeaderFocus(headerElements, this.columnIndex + 1);
68568
+ case TableFocusType.row:
68569
+ if (this.isRowExpanded(row) === false) {
68570
+ this.toggleRowExpanded(row);
68571
+ return true;
68572
+ }
68573
+ return (this.trySetRowSelectionCheckboxFocus(rowElements)
68574
+ || this.trySetCellFocus(rowElements, 0));
68575
+ case TableFocusType.rowSelectionCheckbox:
68576
+ return this.trySetCellFocus(rowElements, 0);
68577
+ case TableFocusType.cell:
68578
+ return this.trySetCellFocus(rowElements, this.columnIndex + 1);
68579
+ }
68580
+ return false;
68581
+ }
68582
+ onUpArrowPressed() {
68583
+ this.onMoveUp(1);
68584
+ // Always prevent default - prevents page scroll, and FireFox changing focus if focus is at table extents
68585
+ return true;
68586
+ }
68587
+ onPageUpPressed() {
68588
+ return this.onMoveUp(this.virtualizer.pageSize);
68589
+ }
68590
+ onHomePressed(ctrlKey) {
68591
+ if (this.handleHomeEndWithinRow(ctrlKey)) {
68592
+ const row = this.getCurrentRow();
68593
+ const rowElements = row?.getFocusableElements();
68594
+ return (this.trySetRowSelectionCheckboxFocus(rowElements)
68595
+ || this.trySetCellFocus(rowElements, 0));
68596
+ }
68597
+ return this.onMoveUp(0, 0);
68598
+ }
68599
+ onDownArrowPressed() {
68600
+ this.onMoveDown(1);
68601
+ // Always prevent default - prevents page scroll, and FireFox changing focus if focus is at table extents
68602
+ return true;
68603
+ }
68604
+ onPageDownPressed() {
68605
+ return this.onMoveDown(this.virtualizer.pageSize);
68606
+ }
68607
+ onEndPressed(ctrlKey) {
68608
+ if (this.handleHomeEndWithinRow(ctrlKey)) {
68609
+ const row = this.getCurrentRow();
68610
+ const rowElements = row?.getFocusableElements();
68611
+ return this.trySetCellFocus(rowElements, this.table.visibleColumns.length - 1);
68612
+ }
68613
+ return this.onMoveDown(0, this.table.tableData.length - 1);
68614
+ }
68615
+ handleHomeEndWithinRow(ctrlKey) {
68616
+ return ((this.focusType === TableFocusType.cell
68617
+ || this.focusType === TableFocusType.rowSelectionCheckbox)
68618
+ && !ctrlKey);
68619
+ }
68620
+ onTabPressed(shiftKeyPressed) {
68621
+ const activeElement = this.getActiveElement();
68622
+ if (activeElement === null || activeElement === this.table) {
68623
+ return false;
68624
+ }
68625
+ const nextFocusState = this.hasRowOrCellFocusType()
68626
+ ? this.getNextRowTabStop(shiftKeyPressed)
68627
+ : this.getNextHeaderTabStop(shiftKeyPressed);
68628
+ if (nextFocusState) {
68629
+ this.focusType = nextFocusState.focusType;
68630
+ this.rowIndex = nextFocusState.rowIndex ?? this.rowIndex;
68631
+ this.columnIndex = nextFocusState.columnIndex ?? this.columnIndex;
68632
+ this.headerActionIndex = nextFocusState.headerActionIndex ?? this.headerActionIndex;
68633
+ this.cellContentIndex = nextFocusState.cellContentIndex ?? this.cellContentIndex;
68634
+ if (this.hasRowOrCellFocusType()) {
68635
+ this.focusCurrentRow(false);
68636
+ }
68637
+ else {
68638
+ this.focusHeaderElement();
68639
+ }
68640
+ return true;
68641
+ }
68642
+ this.blurAfterLastTab(activeElement);
68643
+ return false;
68644
+ }
68645
+ getNextRowTabStop(shiftKeyPressed) {
68646
+ const row = this.getCurrentRow();
68647
+ if (row === undefined) {
68648
+ return undefined;
68649
+ }
68650
+ let startIndex = -1;
68651
+ const focusStates = [];
68652
+ const rowElements = row.getFocusableElements();
68653
+ if (rowElements.selectionCheckbox) {
68654
+ focusStates.push({
68655
+ focusType: TableFocusType.rowSelectionCheckbox
68656
+ });
68657
+ if (this.focusType === TableFocusType.rowSelectionCheckbox) {
68658
+ startIndex = 0;
68659
+ }
68660
+ }
68661
+ let cellIndex = 0;
68662
+ while (cellIndex < rowElements.cells.length) {
68663
+ const firstCellTabbableIndex = focusStates.length;
68664
+ const cellInfo = rowElements.cells[cellIndex];
68665
+ const cellViewTabbableChildren = cellInfo.cell.cellView.tabbableChildren;
68666
+ for (let i = 0; i < cellViewTabbableChildren.length; i++) {
68667
+ focusStates.push({
68668
+ focusType: TableFocusType.cellContent,
68669
+ columnIndex: cellIndex,
68670
+ cellContentIndex: i
68671
+ });
68672
+ if (this.focusType === TableFocusType.cellContent
68673
+ && this.columnIndex === cellIndex
68674
+ && this.cellContentIndex === i) {
68675
+ startIndex = focusStates.length - 1;
68676
+ }
68677
+ }
68678
+ if (cellInfo.actionMenuButton) {
68679
+ focusStates.push({
68680
+ focusType: TableFocusType.cellActionMenu,
68681
+ columnIndex: cellIndex
68682
+ });
68683
+ if (this.focusType === TableFocusType.cellActionMenu
68684
+ && this.columnIndex === cellIndex) {
68685
+ startIndex = focusStates.length - 1;
68686
+ }
68687
+ }
68688
+ const lastCellTabbableIndex = focusStates.length - 1;
68689
+ if (this.focusType === TableFocusType.cell
68690
+ && this.columnIndex === cellIndex) {
68691
+ startIndex = shiftKeyPressed
68692
+ ? lastCellTabbableIndex + 1
68693
+ : firstCellTabbableIndex - 1;
68694
+ }
68695
+ cellIndex += 1;
68696
+ }
68697
+ if (this.focusType === TableFocusType.row) {
68698
+ startIndex = shiftKeyPressed ? focusStates.length : -1;
68699
+ }
68700
+ const direction = shiftKeyPressed ? -1 : 1;
68701
+ return focusStates[startIndex + direction];
68702
+ }
68703
+ getNextHeaderTabStop(shiftKeyPressed) {
68704
+ let startIndex = -1;
68705
+ const focusStates = [];
68706
+ const headerTabbableElements = this.getTableHeaderFocusableElements().headerActions;
68707
+ for (let i = 0; i < headerTabbableElements.length; i++) {
68708
+ focusStates.push({
68709
+ focusType: TableFocusType.headerActions,
68710
+ headerActionIndex: i
68711
+ });
68712
+ }
68713
+ if (this.focusType === TableFocusType.headerActions) {
68714
+ startIndex = this.headerActionIndex;
68715
+ }
68716
+ else {
68717
+ // TableFocusType.columnHeader
68718
+ startIndex = focusStates.length;
68719
+ }
68720
+ const direction = shiftKeyPressed ? -1 : 1;
68721
+ return focusStates[startIndex + direction];
68722
+ }
68723
+ blurAfterLastTab(activeElement) {
68724
+ // In order to get the desired browser-provided Tab/Shift-Tab behavior of focusing the
68725
+ // element before/after the table, the table shouldn't have tabIndex=0 when this event
68726
+ // handling ends. However it needs to be tabIndex=0 so we can re-focus the table the next time
68727
+ // it's tabbed to, so set tabIndex back to 0 after a rAF.
68728
+ // Note: In Chrome this is only needed for Shift-Tab, but in Firefox both Tab and Shift-Tab need this
68729
+ // to work as expected.
68730
+ this.table.tabIndex = -1;
68731
+ window.requestAnimationFrame(() => {
68732
+ this.table.tabIndex = 0;
68733
+ });
68734
+ // Don't explicitly call blur() on activeElement (causes unexpected behavior on Safari / Mac Firefox)
68735
+ this.setElementFocusable(activeElement, false);
68736
+ }
68737
+ onMoveUp(rowDelta, newRowIndex) {
68738
+ const coerceRowIndex = rowDelta > 1;
68739
+ switch (this.focusType) {
68740
+ case TableFocusType.row:
68741
+ case TableFocusType.rowSelectionCheckbox:
68742
+ case TableFocusType.cell: {
68743
+ const scrollOptions = {};
68744
+ let rowIndex = this.rowIndex;
68745
+ if (newRowIndex !== undefined) {
68746
+ rowIndex = newRowIndex;
68747
+ }
68748
+ rowIndex -= rowDelta;
68749
+ if (coerceRowIndex && rowIndex < 0) {
68750
+ rowIndex = 0;
68751
+ }
68752
+ if (rowDelta > 1) {
68753
+ scrollOptions.align = 'start';
68754
+ }
68755
+ if (rowIndex < this.rowIndex && rowIndex >= 0) {
68756
+ return this.scrollToAndFocusRow(rowIndex, scrollOptions);
68757
+ }
68758
+ if (rowIndex === -1) {
68759
+ const headerElements = this.getTableHeaderFocusableElements();
68760
+ if (this.focusType === TableFocusType.row
68761
+ || this.focusType === TableFocusType.rowSelectionCheckbox) {
68762
+ return (this.trySetHeaderActionFocus(headerElements, 0)
68763
+ || this.trySetColumnHeaderFocus(headerElements, 0));
68764
+ }
68765
+ return this.trySetColumnHeaderFocus(headerElements, this.columnIndex);
68766
+ }
68767
+ return false;
68768
+ }
68769
+ }
68770
+ return false;
68771
+ }
68772
+ onMoveDown(rowDelta, newRowIndex) {
68773
+ const coerceRowIndex = rowDelta > 1;
68774
+ switch (this.focusType) {
68775
+ case TableFocusType.headerActions: {
68776
+ this.setRowFocusState(0);
68777
+ return this.scrollToAndFocusRow(0);
68778
+ }
68779
+ case TableFocusType.columnHeader: {
68780
+ this.setCellFocusState(this.columnIndex, 0, false);
68781
+ return this.scrollToAndFocusRow(0);
68782
+ }
68783
+ case TableFocusType.row:
68784
+ case TableFocusType.rowSelectionCheckbox:
68785
+ case TableFocusType.cell: {
68786
+ const scrollOptions = {};
68787
+ let rowIndex = this.rowIndex;
68788
+ if (newRowIndex !== undefined) {
68789
+ rowIndex = newRowIndex;
68790
+ }
68791
+ rowIndex += rowDelta;
68792
+ if (coerceRowIndex && rowIndex >= this.table.tableData.length) {
68793
+ rowIndex = this.table.tableData.length - 1;
68794
+ }
68795
+ if (rowDelta > 1) {
68796
+ scrollOptions.align = 'end';
68797
+ }
68798
+ if (rowIndex > this.rowIndex
68799
+ && rowIndex < this.table.tableData.length) {
68800
+ return this.scrollToAndFocusRow(rowIndex, scrollOptions);
68801
+ }
68802
+ return false;
68803
+ }
68804
+ }
68805
+ return false;
68806
+ }
68807
+ updateFocusStateFromActiveElement(setRowFocus) {
68808
+ // If the user is interacting with the table with non-keyboard methods (like mouse), we need to
68809
+ // update our focus state based on the current active/focused element
68810
+ const activeElement = this.getActiveElement();
68811
+ if (activeElement) {
68812
+ const row = this.getContainingRow(activeElement);
68813
+ if (row) {
68814
+ if (!(row instanceof TableGroupRow)) {
68815
+ const cell = this.getContainingCell(activeElement);
68816
+ if (cell) {
68817
+ const columnIndex = this.table.visibleColumns.indexOf(cell.column);
68818
+ if (cell.actionMenuButton === activeElement) {
68819
+ this.setCellActionMenuFocusState(row.resolvedRowIndex, columnIndex, false);
68820
+ return;
68821
+ }
68822
+ const contentIndex = cell.cellView.tabbableChildren.indexOf(activeElement);
68823
+ if (contentIndex > -1) {
68824
+ this.setCellContentFocusState(contentIndex, row.resolvedRowIndex, columnIndex, false);
68825
+ return;
68826
+ }
68827
+ }
68828
+ }
68829
+ if (setRowFocus
68830
+ && this.hasRowOrCellFocusType()
68831
+ && this.rowIndex !== row.resolvedRowIndex) {
68832
+ this.setRowFocusState(row.resolvedRowIndex);
68833
+ }
68834
+ }
68835
+ }
68836
+ }
68837
+ focusElement(element, focusOptions) {
68838
+ const previousActiveElement = this.getActiveElement();
68839
+ if (previousActiveElement !== element) {
68840
+ this.setElementFocusable(element, true);
68841
+ this.isCurrentlyFocusingElement = true;
68842
+ element.focus(focusOptions);
68843
+ this.isCurrentlyFocusingElement = false;
68844
+ if (previousActiveElement
68845
+ && this.isInTable(previousActiveElement)) {
68846
+ this.setElementFocusable(previousActiveElement, false);
68847
+ }
68848
+ }
68849
+ }
68850
+ setElementFocusable(element, focusable) {
68851
+ if (element === this.table) {
68852
+ return;
68853
+ }
68854
+ element.tabIndex = focusable ? 0 : -1;
68855
+ }
68856
+ setActionMenuButtonFocused(menuButton, focused) {
68857
+ // The action MenuButton needs to be visible in order to be focused, so this CSS class styling
68858
+ // handles that (see cell/styles.ts).
68859
+ if (focused) {
68860
+ menuButton.classList.add('cell-action-menu-focused');
68861
+ }
68862
+ else {
68863
+ menuButton.classList.remove('cell-action-menu-focused');
68864
+ }
68865
+ }
68866
+ setFocusOnHeader() {
68867
+ if (this.hasHeaderFocusType()) {
68868
+ return this.focusHeaderElement();
68869
+ }
68870
+ this.setDefaultFocus();
68871
+ return this.focusType !== TableFocusType.none;
68872
+ }
68873
+ setDefaultFocus() {
68874
+ const headerElements = this.getTableHeaderFocusableElements();
68875
+ if (!this.trySetHeaderActionFocus(headerElements, 0)
68876
+ && !this.trySetColumnHeaderFocus(headerElements, 0)
68877
+ && !this.scrollToAndFocusRow(0)) {
68878
+ this.focusType = TableFocusType.none;
68879
+ }
68880
+ }
68881
+ scrollToAndFocusRow(totalRowIndex, scrollOptions) {
68882
+ if (totalRowIndex >= 0 && totalRowIndex < this.table.tableData.length) {
68883
+ switch (this.focusType) {
68884
+ case TableFocusType.none:
68885
+ case TableFocusType.headerActions:
68886
+ case TableFocusType.columnHeader:
68887
+ this.setRowFocusState(totalRowIndex);
68888
+ break;
68889
+ }
68890
+ this.rowIndex = totalRowIndex;
68891
+ this.virtualizer.scrollToIndex(totalRowIndex, scrollOptions);
68892
+ this.focusCurrentRow(true);
68893
+ return true;
68894
+ }
68895
+ return false;
68896
+ }
68897
+ focusCurrentRow(allowScroll) {
68898
+ const visibleRowIndex = this.getCurrentRowVisibleIndex();
68899
+ if (visibleRowIndex < 0) {
68900
+ return false;
68901
+ }
68902
+ const focusedRow = this.table.rowElements[visibleRowIndex];
68903
+ let focusRowOnly = false;
68904
+ switch (this.focusType) {
68905
+ case TableFocusType.row:
68906
+ focusRowOnly = true;
68907
+ break;
68908
+ case TableFocusType.cell:
68909
+ case TableFocusType.cellActionMenu:
68910
+ case TableFocusType.cellContent:
68911
+ focusRowOnly = focusedRow instanceof TableGroupRow;
68912
+ break;
68913
+ }
68914
+ const focusOptions = { preventScroll: !allowScroll };
68915
+ if (focusRowOnly) {
68916
+ this.focusElement(focusedRow, focusOptions);
68917
+ return true;
68918
+ }
68919
+ this.focusRowElement(focusedRow, focusOptions);
68920
+ return true;
68921
+ }
68922
+ focusRowElement(row, focusOptions) {
68923
+ const rowElements = row.getFocusableElements();
68924
+ let focusableElement;
68925
+ switch (this.focusType) {
68926
+ case TableFocusType.rowSelectionCheckbox:
68927
+ focusableElement = rowElements.selectionCheckbox;
68928
+ break;
68929
+ case TableFocusType.cell: {
68930
+ focusableElement = rowElements.cells[this.columnIndex].cell;
68931
+ break;
68932
+ }
68933
+ case TableFocusType.cellActionMenu: {
68934
+ const actionMenuButton = rowElements.cells[this.columnIndex]?.cell.actionMenuButton;
68935
+ if (actionMenuButton) {
68936
+ focusableElement = actionMenuButton;
68937
+ this.setActionMenuButtonFocused(actionMenuButton, true);
68938
+ }
68939
+ break;
68940
+ }
68941
+ case TableFocusType.cellContent: {
68942
+ focusableElement = rowElements.cells[this.columnIndex]?.cell.cellView
68943
+ .tabbableChildren[this.cellContentIndex];
68944
+ break;
68945
+ }
68946
+ }
68947
+ if (focusableElement) {
68948
+ this.focusElement(focusableElement, focusOptions);
68949
+ }
68950
+ }
68951
+ focusHeaderElement() {
68952
+ const headerElements = this.getTableHeaderFocusableElements();
68953
+ let focusableElement;
68954
+ switch (this.focusType) {
68955
+ case TableFocusType.headerActions:
68956
+ focusableElement = headerElements.headerActions[this.headerActionIndex];
68957
+ break;
68958
+ case TableFocusType.columnHeader:
68959
+ focusableElement = headerElements.columnHeaders[this.columnIndex];
68960
+ break;
68961
+ }
68962
+ if (focusableElement) {
68963
+ this.focusElement(focusableElement);
68964
+ return true;
68965
+ }
68966
+ return false;
68967
+ }
68968
+ getCurrentRowVisibleIndex() {
68969
+ return this.table.rowElements.findIndex(row => row.resolvedRowIndex === this.rowIndex);
68970
+ }
68971
+ getTableHeaderFocusableElements() {
68972
+ const headerActions = [];
68973
+ if (this.table.selectionCheckbox?.getRootNode()
68974
+ === this.table.shadowRoot) {
68975
+ headerActions.push(this.table.selectionCheckbox);
68976
+ }
68977
+ if (this.table.showCollapseAll
68978
+ && this.table.collapseAllButton?.getRootNode()
68979
+ === this.table.shadowRoot) {
68980
+ headerActions.push(this.table.collapseAllButton);
68981
+ }
68982
+ const columnHeaders = [];
68983
+ if (this.canFocusColumnHeaders()) {
68984
+ this.table.columnHeadersContainer
68985
+ .querySelectorAll(tableHeaderTag)
68986
+ .forEach(header => columnHeaders.push(header));
68987
+ }
68988
+ return { headerActions, columnHeaders };
68989
+ }
68990
+ canFocusColumnHeaders() {
68991
+ return (this.table.columns.find(c => !c.columnInternals.sortingDisabled)
68992
+ !== undefined);
68993
+ }
68994
+ getCurrentRow() {
68995
+ return this.table.rowElements[this.getCurrentRowVisibleIndex()];
68996
+ }
68997
+ isRowExpanded(row) {
68998
+ if ((row instanceof TableRow && row.isParentRow)
68999
+ || row instanceof TableGroupRow) {
69000
+ return row.expanded;
69001
+ }
69002
+ return undefined;
69003
+ }
69004
+ toggleRowExpanded(row) {
69005
+ if (row instanceof TableGroupRow) {
69006
+ row.onGroupExpandToggle();
69007
+ }
69008
+ else {
69009
+ row.onRowExpandToggle();
69010
+ }
69011
+ this.focusRowElement(row);
69012
+ }
69013
+ getContainingRow(start) {
69014
+ return this.getContainingElement(start, e => this.isResolvedRowType(e));
69015
+ }
69016
+ getContainingCell(start) {
69017
+ return this.getContainingElement(start, e => e instanceof TableCell);
69018
+ }
69019
+ getContainingElement(start, isElementMatch) {
69020
+ let possibleMatch = start;
69021
+ while (possibleMatch && possibleMatch !== this.table) {
69022
+ if (isElementMatch(possibleMatch)) {
69023
+ return possibleMatch;
69024
+ }
69025
+ possibleMatch = possibleMatch.parentElement
69026
+ ?? possibleMatch.parentNode?.host;
69027
+ }
69028
+ return undefined;
69029
+ }
69030
+ isInTable(start) {
69031
+ let possibleMatch = start;
69032
+ while (possibleMatch && possibleMatch !== this.table) {
69033
+ possibleMatch = possibleMatch.parentElement
69034
+ ?? possibleMatch.parentNode?.host;
69035
+ }
69036
+ return possibleMatch === this.table;
69037
+ }
69038
+ getActiveElement() {
69039
+ let activeElement = document.activeElement;
69040
+ while (activeElement?.shadowRoot?.activeElement) {
69041
+ activeElement = activeElement.shadowRoot.activeElement;
69042
+ // In some cases, the active element may be a sub-part of a control (example: MenuButton -> ToggleButton -> a div with tabindex=0). Stop at the outer control boundary, so that
69043
+ // we can more simply check equality against the elements of getTableHeaderFocusableElements() / row.getFocusableElements().
69044
+ // (For rows/cells/cell views, we do need to recurse into them, to get to the appropriate focused controls though)
69045
+ if (activeElement instanceof FoundationElement
69046
+ && !this.isResolvedRowType(activeElement)
69047
+ && !(activeElement instanceof TableCell)
69048
+ && !(activeElement instanceof TableCellView)) {
69049
+ break;
69050
+ }
69051
+ }
69052
+ return activeElement;
69053
+ }
69054
+ focusFirstInteractiveElementInCurrentCell(rowElements) {
69055
+ if (!rowElements) {
69056
+ return false;
69057
+ }
69058
+ return (this.trySetCellContentFocus(rowElements, 0)
69059
+ || this.trySetCellActionMenuFocus(rowElements));
69060
+ }
69061
+ hasRowOrCellFocusType() {
69062
+ switch (this.focusType) {
69063
+ case TableFocusType.cell:
69064
+ case TableFocusType.cellActionMenu:
69065
+ case TableFocusType.cellContent:
69066
+ case TableFocusType.row:
69067
+ case TableFocusType.rowSelectionCheckbox:
69068
+ return true;
69069
+ default:
69070
+ return false;
69071
+ }
69072
+ }
69073
+ hasHeaderFocusType() {
69074
+ switch (this.focusType) {
69075
+ case TableFocusType.headerActions:
69076
+ case TableFocusType.columnHeader:
69077
+ return true;
69078
+ default:
69079
+ return false;
69080
+ }
69081
+ }
69082
+ trySetRowSelectionCheckboxFocus(rowElements) {
69083
+ if (rowElements?.selectionCheckbox) {
69084
+ this.focusType = TableFocusType.rowSelectionCheckbox;
69085
+ this.focusCurrentRow(true);
69086
+ return true;
69087
+ }
69088
+ return false;
69089
+ }
69090
+ trySetColumnHeaderFocus(headerElements, columnIndex) {
69091
+ if (columnIndex >= 0
69092
+ && columnIndex < headerElements.columnHeaders.length) {
69093
+ this.focusType = TableFocusType.columnHeader;
69094
+ this.columnIndex = columnIndex;
69095
+ this.focusHeaderElement();
69096
+ return true;
69097
+ }
69098
+ return false;
69099
+ }
69100
+ trySetHeaderActionFocus(headerElements, headerActionIndex) {
69101
+ if (headerActionIndex >= 0
69102
+ && headerActionIndex < headerElements.headerActions.length) {
69103
+ this.focusType = TableFocusType.headerActions;
69104
+ this.headerActionIndex = headerActionIndex;
69105
+ this.focusHeaderElement();
69106
+ return true;
69107
+ }
69108
+ return false;
69109
+ }
69110
+ trySetCellFocus(rowElements, columnIndex, rowIndex) {
69111
+ if (!rowElements) {
69112
+ return false;
69113
+ }
69114
+ const newColumnIndex = columnIndex ?? this.columnIndex;
69115
+ const newRowIndex = rowIndex ?? this.rowIndex;
69116
+ if (newColumnIndex >= 0 && newColumnIndex < rowElements.cells.length) {
69117
+ this.focusType = TableFocusType.cell;
69118
+ this.setRowCellFocusState(newColumnIndex, newRowIndex, true);
69119
+ return true;
69120
+ }
69121
+ return false;
69122
+ }
69123
+ trySetCellContentFocus(rowElements, cellContentIndex, columnIndex, rowIndex) {
69124
+ if (!rowElements) {
69125
+ return false;
69126
+ }
69127
+ const newColumnIndex = columnIndex ?? this.columnIndex;
69128
+ const newRowIndex = rowIndex ?? this.rowIndex;
69129
+ if (newColumnIndex >= 0
69130
+ && newColumnIndex < rowElements.cells.length
69131
+ && cellContentIndex >= 0
69132
+ && cellContentIndex
69133
+ < rowElements.cells[newColumnIndex].cell.cellView
69134
+ .tabbableChildren.length) {
69135
+ this.setCellContentFocusState(cellContentIndex, newRowIndex, newColumnIndex, true);
69136
+ return true;
69137
+ }
69138
+ return false;
69139
+ }
69140
+ trySetCellActionMenuFocus(rowElements, columnIndex, rowIndex) {
69141
+ const newColumnIndex = columnIndex ?? this.columnIndex;
69142
+ const newRowIndex = rowIndex ?? this.rowIndex;
69143
+ if (newColumnIndex >= 0
69144
+ && newColumnIndex < rowElements.cells.length
69145
+ && rowElements.cells[newColumnIndex].actionMenuButton) {
69146
+ this.setCellActionMenuFocusState(newRowIndex, newColumnIndex, true);
69147
+ return true;
69148
+ }
69149
+ return false;
69150
+ }
69151
+ setCellActionMenuFocusState(rowIndex, columnIndex, focusElement) {
69152
+ this.focusType = TableFocusType.cellActionMenu;
69153
+ this.setRowCellFocusState(columnIndex, rowIndex, focusElement);
69154
+ }
69155
+ setCellContentFocusState(cellContentIndex, rowIndex, columnIndex, focusElement) {
69156
+ this.focusType = TableFocusType.cellContent;
69157
+ this.cellContentIndex = cellContentIndex;
69158
+ this.setRowCellFocusState(columnIndex, rowIndex, focusElement);
69159
+ }
69160
+ setRowFocusState(rowIndex) {
69161
+ this.focusType = TableFocusType.row;
69162
+ if (rowIndex !== undefined) {
69163
+ this.rowIndex = rowIndex;
69164
+ }
69165
+ }
69166
+ setCellFocusState(columnIndex, rowIndex, focusElement) {
69167
+ this.focusType = TableFocusType.cell;
69168
+ this.setRowCellFocusState(columnIndex, rowIndex, focusElement);
69169
+ }
69170
+ setRowCellFocusState(columnIndex, rowIndex, focusElement) {
69171
+ this.rowIndex = rowIndex;
69172
+ this.columnIndex = columnIndex;
69173
+ if (focusElement) {
69174
+ this.focusCurrentRow(true);
69175
+ }
69176
+ }
69177
+ isResolvedRowType(row) {
69178
+ return row instanceof TableRow || row instanceof TableGroupRow;
69179
+ }
69180
+ }
69181
+
67985
69182
  /**
67986
69183
  * A nimble-styled table.
67987
69184
  */
@@ -68122,6 +69319,7 @@ img.ProseMirror-separator {
68122
69319
  };
68123
69320
  this.table = createTable(this.options);
68124
69321
  this.virtualizer = new Virtualizer(this, this.table);
69322
+ this.keyboardNavigationManager = new KeyboardNavigationManager(this, this.virtualizer);
68125
69323
  this.layoutManager = new TableLayoutManager(this);
68126
69324
  this.layoutManagerNotifier = Observable.getNotifier(this.layoutManager);
68127
69325
  this.layoutManagerNotifier.subscribe(this, 'isColumnBeingSized');
@@ -68162,12 +69360,14 @@ img.ProseMirror-separator {
68162
69360
  this.viewport.addEventListener('scroll', this.onViewPortScroll, {
68163
69361
  passive: true
68164
69362
  });
69363
+ this.keyboardNavigationManager.connect();
68165
69364
  document.addEventListener('keydown', this.onKeyDown);
68166
69365
  document.addEventListener('keyup', this.onKeyUp);
68167
69366
  }
68168
69367
  disconnectedCallback() {
68169
69368
  super.disconnectedCallback();
68170
69369
  this.virtualizer.disconnect();
69370
+ this.keyboardNavigationManager.disconnect();
68171
69371
  this.viewport.removeEventListener('scroll', this.onViewPortScroll);
68172
69372
  document.removeEventListener('keydown', this.onKeyDown);
68173
69373
  document.removeEventListener('keyup', this.onKeyUp);
@@ -68217,6 +69417,14 @@ img.ProseMirror-separator {
68217
69417
  return true;
68218
69418
  }
68219
69419
  /** @internal */
69420
+ onRowFocusIn(event) {
69421
+ this.keyboardNavigationManager.onRowFocusIn(event);
69422
+ }
69423
+ /** @internal */
69424
+ onRowBlur(event) {
69425
+ this.keyboardNavigationManager.onRowBlur(event);
69426
+ }
69427
+ /** @internal */
68220
69428
  onAllRowsSelectionChange(event) {
68221
69429
  event.stopPropagation();
68222
69430
  if (this.ignoreSelectionChangeEvents) {
@@ -68310,6 +69518,18 @@ img.ProseMirror-separator {
68310
69518
  }
68311
69519
  this.emitColumnConfigurationChangeEvent();
68312
69520
  }
69521
+ /**
69522
+ * @internal
69523
+ */
69524
+ onHeaderKeyDown(column, event) {
69525
+ const allowMultiSort = event.shiftKey;
69526
+ if (event.key === keyEnter) {
69527
+ this.toggleColumnSort(column, allowMultiSort);
69528
+ }
69529
+ // Return true so that we don't prevent default behavior. Without this, Tab navigation
69530
+ // gets stuck on the column headers.
69531
+ return true;
69532
+ }
68313
69533
  /**
68314
69534
  * @internal
68315
69535
  */
@@ -68325,6 +69545,9 @@ img.ProseMirror-separator {
68325
69545
  this.rowGridColumns = this.layoutManager.getGridTemplateColumns();
68326
69546
  this.visibleColumns = this.columns.filter(column => !column.columnHidden);
68327
69547
  }
69548
+ if (this.tableUpdateTracker.requiresKeyboardFocusReset) {
69549
+ this.keyboardNavigationManager.resetFocusState();
69550
+ }
68328
69551
  }
68329
69552
  get ariaMultiSelectable() {
68330
69553
  switch (this.selectionMode) {
@@ -68367,6 +69590,29 @@ img.ProseMirror-separator {
68367
69590
  }
68368
69591
  return tanStackUpdates;
68369
69592
  }
69593
+ /** @internal */
69594
+ handleFocusedCellRecycling() {
69595
+ const hadActiveRowOrCellFocus = this.keyboardNavigationManager.hasActiveRowOrCellFocus;
69596
+ let tableFocusedElement = this.shadowRoot.activeElement;
69597
+ while (tableFocusedElement !== null
69598
+ && !(tableFocusedElement instanceof TableCellView)) {
69599
+ if (tableFocusedElement.shadowRoot) {
69600
+ tableFocusedElement = tableFocusedElement.shadowRoot.activeElement;
69601
+ }
69602
+ else {
69603
+ break;
69604
+ }
69605
+ }
69606
+ if (tableFocusedElement instanceof TableCellView) {
69607
+ tableFocusedElement.focusedRecycleCallback();
69608
+ }
69609
+ if (this.openActionMenuRecordId !== undefined) {
69610
+ const activeRow = this.rowElements.find(row => row instanceof TableRow
69611
+ && row.recordId === this.openActionMenuRecordId);
69612
+ activeRow?.closeOpenActionMenus();
69613
+ }
69614
+ this.keyboardNavigationManager.handleFocusedCellRecycling(hadActiveRowOrCellFocus);
69615
+ }
68370
69616
  selectionModeChanged(_prev, _next) {
68371
69617
  if (!this.$fastController.isConnected) {
68372
69618
  return;
@@ -68402,6 +69648,7 @@ img.ProseMirror-separator {
68402
69648
  this.$emit('action-menu-beforetoggle', detail);
68403
69649
  }
68404
69650
  async handleRowActionMenuToggleEvent(event) {
69651
+ this.keyboardNavigationManager.onRowActionMenuToggle(event);
68405
69652
  const detail = await this.getActionMenuToggleEventDetail(event);
68406
69653
  this.$emit('action-menu-toggle', detail);
68407
69654
  if (!event.detail.newState) {
@@ -68612,6 +69859,7 @@ img.ProseMirror-separator {
68612
69859
  isParentRow: isParent,
68613
69860
  immediateChildCount: row.subRows.length,
68614
69861
  groupColumn: this.getGroupRowColumn(row),
69862
+ resolvedRowIndex: row.index,
68615
69863
  isLoadingChildren: this.expansionManager.isLoadingChildren(row.id)
68616
69864
  };
68617
69865
  hasDataHierarchy = hasDataHierarchy || isParent;
@@ -68779,6 +70027,9 @@ img.ProseMirror-separator {
68779
70027
  __decorate$1([
68780
70028
  observable
68781
70029
  ], Table.prototype, "selectionCheckbox", void 0);
70030
+ __decorate$1([
70031
+ observable
70032
+ ], Table.prototype, "collapseAllButton", void 0);
68782
70033
  __decorate$1([
68783
70034
  observable
68784
70035
  ], Table.prototype, "showCollapseAll", void 0);
@@ -68932,10 +70183,12 @@ img.ProseMirror-separator {
68932
70183
  }}"
68933
70184
  class="${x => (x.isPlaceholder ? 'placeholder' : '')}"
68934
70185
  >
68935
- ${when(x => typeof x.cellRecord?.href === 'string', html `
70186
+ ${when(x => x.showAnchor, html `
68936
70187
  <${anchorTag}
68937
70188
  ${ref('anchor')}
68938
70189
  ${overflow('hasOverflow')}
70190
+ ${'' /* tabindex managed dynamically by KeyboardNavigationManager */}
70191
+ tabindex="-1"
68939
70192
  href="${x => x.cellRecord?.href}"
68940
70193
  hreflang="${x => x.columnConfig?.hreflang}"
68941
70194
  ping="${x => x.columnConfig?.ping}"
@@ -68950,7 +70203,7 @@ img.ProseMirror-separator {
68950
70203
  >
68951
70204
  ${x => x.text}
68952
70205
  </${anchorTag}>`)}
68953
- ${when(x => typeof x.cellRecord?.href !== 'string', html `
70206
+ ${when(x => !x.showAnchor, html `
68954
70207
  <span
68955
70208
  ${overflow('hasOverflow')}
68956
70209
  title=${x => (x.hasOverflow ? x.text : null)}
@@ -68987,9 +70240,19 @@ img.ProseMirror-separator {
68987
70240
  }
68988
70241
  return '';
68989
70242
  }
70243
+ /** @internal */
70244
+ get showAnchor() {
70245
+ return typeof this.cellRecord?.href === 'string';
70246
+ }
68990
70247
  focusedRecycleCallback() {
68991
70248
  this.anchor?.blur();
68992
70249
  }
70250
+ get tabbableChildren() {
70251
+ if (this.showAnchor) {
70252
+ return [this.anchor];
70253
+ }
70254
+ return [];
70255
+ }
68993
70256
  }
68994
70257
  __decorate$1([
68995
70258
  observable
@@ -69000,6 +70263,9 @@ img.ProseMirror-separator {
69000
70263
  __decorate$1([
69001
70264
  volatile
69002
70265
  ], TableColumnAnchorCellView.prototype, "text", null);
70266
+ __decorate$1([
70267
+ volatile
70268
+ ], TableColumnAnchorCellView.prototype, "showAnchor", null);
69003
70269
  const anchorCellView = TableColumnAnchorCellView.compose({
69004
70270
  baseName: 'table-column-anchor-cell-view',
69005
70271
  template: template$c,