lightning-base-components 1.14.6-alpha → 1.15.1-alpha

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 (125) hide show
  1. package/metadata/raptor.json +5 -3
  2. package/package.json +8 -1
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningProgressBar.progressBar.js +1 -0
  5. package/src/lightning/alert/__docs__/alert.md +101 -0
  6. package/src/lightning/alert/__examples__disabled/basic/basic.css +7 -0
  7. package/src/lightning/alert/__examples__disabled/basic/basic.html +8 -0
  8. package/src/lightning/alert/__examples__disabled/basic/basic.js +14 -0
  9. package/src/lightning/alert/alert.html +3 -0
  10. package/src/lightning/alert/alert.js +78 -0
  11. package/src/lightning/alert/alert.js-meta.xml +6 -0
  12. package/src/lightning/button/__wdio__/utam/utam.html +3 -0
  13. package/src/lightning/button/__wdio__/utam/utam.js +3 -0
  14. package/src/lightning/button/__wdio__/utam/utam.spec.js +20 -0
  15. package/src/lightning/buttonMenu/buttonMenu.js +12 -0
  16. package/src/lightning/confirm/__docs__/confirm.md +100 -0
  17. package/src/lightning/confirm/__examples__disabled/basic/basic.css +7 -0
  18. package/src/lightning/confirm/__examples__disabled/basic/basic.html +8 -0
  19. package/src/lightning/confirm/__examples__disabled/basic/basic.js +14 -0
  20. package/src/lightning/confirm/confirm.html +3 -0
  21. package/src/lightning/confirm/confirm.js +80 -0
  22. package/src/lightning/confirm/confirm.js-meta.xml +6 -0
  23. package/src/lightning/datatable/__wdio__/utam/utam.html +17 -0
  24. package/src/lightning/datatable/__wdio__/utam/utam.js +91 -0
  25. package/src/lightning/datatable/__wdio__/utam/utam.spec.js +189 -0
  26. package/src/lightning/datatable/columns.js +166 -71
  27. package/src/lightning/datatable/datatable.js +98 -20
  28. package/src/lightning/datatable/headerActions.js +2 -2
  29. package/src/lightning/datatable/keyboard.js +10 -10
  30. package/src/lightning/datatable/renderManager.js +45 -13
  31. package/src/lightning/datatable/resizeSensor.js +11 -3
  32. package/src/lightning/datatable/rowSelection.js +1 -1
  33. package/src/lightning/datatable/rowSelectionShared.js +33 -20
  34. package/src/lightning/datatable/rows.js +3 -2
  35. package/src/lightning/datatable/sort.js +8 -8
  36. package/src/lightning/datatable/state.js +8 -1
  37. package/src/lightning/datatable/templates/div/div.html +6 -2
  38. package/src/lightning/datatable/templates/table/table.html +5 -2
  39. package/src/lightning/datatable/tree.js +25 -0
  40. package/src/lightning/datatable/types.js +77 -9
  41. package/src/lightning/datatable/utils.js +51 -24
  42. package/src/lightning/datatable/virtualization.js +319 -0
  43. package/src/lightning/datatable/wrapText.js +53 -16
  44. package/src/lightning/datepicker/__perf__DISABLED/datepickerWithCalendarOpen.perf.js +55 -0
  45. package/src/lightning/iconSvgTemplates/buildTemplates/standard/dashboard_component.html +7 -0
  46. package/src/lightning/iconSvgTemplates/buildTemplates/standard/slack.html +7 -0
  47. package/src/lightning/iconSvgTemplates/buildTemplates/standard/tableau.html +7 -0
  48. package/src/lightning/iconSvgTemplates/buildTemplates/standard/travel_mode.html +2 -2
  49. package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +8 -1
  50. package/src/lightning/iconSvgTemplates/buildTemplates/utility/data_model.html +7 -0
  51. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product.html +1 -1
  52. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product_transaction.html +2 -1
  53. package/src/lightning/iconSvgTemplates/buildTemplates/utility/slack.html +7 -0
  54. package/src/lightning/iconSvgTemplates/buildTemplates/utility/tableau.html +7 -0
  55. package/src/lightning/iconSvgTemplates/buildTemplates/utility/video_off.html +7 -0
  56. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/dashboard_component.html +7 -0
  57. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/slack.html +7 -0
  58. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/tableau.html +7 -0
  59. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/travel_mode.html +2 -2
  60. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +8 -1
  61. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/data_model.html +7 -0
  62. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product.html +1 -1
  63. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  64. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/slack.html +7 -0
  65. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/tableau.html +7 -0
  66. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/video_off.html +7 -0
  67. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/dashboard_component.html +7 -0
  68. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/slack.html +7 -0
  69. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/tableau.html +7 -0
  70. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/travel_mode.html +2 -2
  71. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/templates.js +4 -1
  72. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/dashboard_component.html +7 -0
  73. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/slack.html +7 -0
  74. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/tableau.html +7 -0
  75. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/travel_mode.html +2 -2
  76. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/templates.js +4 -1
  77. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +5 -1
  78. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/data_model.html +7 -0
  79. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product.html +1 -1
  80. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product_transaction.html +2 -1
  81. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/slack.html +7 -0
  82. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/tableau.html +7 -0
  83. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/video_off.html +7 -0
  84. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +5 -1
  85. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/data_model.html +7 -0
  86. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product.html +1 -1
  87. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  88. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/slack.html +7 -0
  89. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/tableau.html +7 -0
  90. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/video_off.html +7 -0
  91. package/src/lightning/input/__docs__/input.md +2 -0
  92. package/src/lightning/interactiveDialogBase/interactiveDialogBase.css +494 -0
  93. package/src/lightning/interactiveDialogBase/interactiveDialogBase.html +63 -0
  94. package/src/lightning/interactiveDialogBase/interactiveDialogBase.js +200 -0
  95. package/src/lightning/menuItem/menuItem.js +4 -1
  96. package/src/lightning/modalBase/modalBase.css +20 -0
  97. package/src/lightning/modalBase/modalBase.html +54 -0
  98. package/src/lightning/modalBase/modalBase.js +1039 -0
  99. package/src/lightning/overlay/__docs__/overlay.md +90 -0
  100. package/src/lightning/overlay/__examples__/alert/alert.html +27 -0
  101. package/src/lightning/overlay/__examples__/alert/alert.js +33 -0
  102. package/src/lightning/overlay/__examples__/basic/basic.css +7 -0
  103. package/src/lightning/overlay/__examples__/basic/basic.html +18 -0
  104. package/src/lightning/overlay/__examples__/basic/basic.js +61 -0
  105. package/src/lightning/overlay/__examples__/demo/demo.html +29 -0
  106. package/src/lightning/overlay/__examples__/demo/demo.js +40 -0
  107. package/src/lightning/overlay/__examples__/panel/panel.html +17 -0
  108. package/src/lightning/overlay/__examples__/panel/panel.js +21 -0
  109. package/src/lightning/overlay/overlay.html +3 -0
  110. package/src/lightning/overlay/overlay.js +45 -0
  111. package/src/lightning/overlayContainer/__docs__/overlayContainer.md +0 -0
  112. package/src/lightning/overlayContainer/overlayContainer.html +3 -0
  113. package/src/lightning/overlayContainer/overlayContainer.js +138 -0
  114. package/src/lightning/overlayManager/overlayManager.js +54 -0
  115. package/src/lightning/overlayUtils/overlayUtils.js +17 -0
  116. package/src/lightning/progressBar/progressBar.html +2 -1
  117. package/src/lightning/progressBar/progressBar.js +18 -1
  118. package/src/lightning/prompt/__docs__/prompt.md +102 -0
  119. package/src/lightning/prompt/__examples__disabled/basic/basic.css +7 -0
  120. package/src/lightning/prompt/__examples__disabled/basic/basic.html +8 -0
  121. package/src/lightning/prompt/__examples__disabled/basic/basic.js +15 -0
  122. package/src/lightning/prompt/prompt.css +81 -0
  123. package/src/lightning/prompt/prompt.html +8 -0
  124. package/src/lightning/prompt/prompt.js +92 -0
  125. package/src/lightning/prompt/prompt.js-meta.xml +6 -0
@@ -82,6 +82,7 @@ import {
82
82
  refocusCellElement,
83
83
  isCellElement,
84
84
  getActiveCellElement,
85
+ getDataRow,
85
86
  FOCUS_CLASS,
86
87
  } from './keyboard';
87
88
  import {
@@ -138,10 +139,15 @@ import {
138
139
  isViewportRenderingEnabled,
139
140
  setViewportRendering,
140
141
  getDTWrapperHeight,
141
- setFirstVisibleIndex,
142
142
  setVirtualize,
143
143
  RenderManager,
144
144
  } from './renderManager';
145
+ import {
146
+ handleVariableRowHeights,
147
+ resetRowHeights,
148
+ resetTableHeight,
149
+ findFirstVisibleIndex,
150
+ } from './virtualization';
145
151
 
146
152
  import { hasTreeDataType } from './tree';
147
153
  import { setErrors, getTableError, getErrors } from './errors';
@@ -200,9 +206,12 @@ export default class LightningDatatable extends LightningElement {
200
206
  _datatableId = generateUniqueId('lgt-datatable');
201
207
  _draftValues = [];
202
208
  _isResizing = false; // Whether resizing is in progress
209
+ _lastRenderedRow = null; // last rendered row, used for UTAM
203
210
  _privateTypes = {};
204
211
  _privateWidthObserver = null; // Instance of LightningDatatableResizeObserver
205
212
  _renderMode = 'table';
213
+ _shouldResetFocus = false; // used to ensure focus isn't lost from changes in renderedRows
214
+ _shouldResetHeights = false;
206
215
  _suppressBottomBar = false;
207
216
 
208
217
  /************************* PUBLIC PROPERTIES *************************/
@@ -283,6 +292,7 @@ export default class LightningDatatable extends LightningElement {
283
292
  // do necessary updates since rows have changed
284
293
  if (hasValidKeyField(this.state)) {
285
294
  this.updateRowsState();
295
+ resetTableHeight(this.state);
286
296
  }
287
297
  if (this._customerSelectedRows) {
288
298
  this.setSelectedRows(this._customerSelectedRows);
@@ -797,8 +807,8 @@ export default class LightningDatatable extends LightningElement {
797
807
  * virtualization is enabled
798
808
  */
799
809
  get computedTbodyStyle() {
800
- const style = [];
801
- const { firstVisibleIndex, bufferSize, virtualize, rows, rowHeight } =
810
+ const style = {};
811
+ const { firstVisibleIndex, bufferSize, virtualize, tableHeight } =
802
812
  this.state;
803
813
  if (
804
814
  hasRowNumberColumn(this.state) &&
@@ -809,11 +819,11 @@ export default class LightningDatatable extends LightningElement {
809
819
  0
810
820
  );
811
821
  const rowNumber = firstRenderedRow + getRowNumberOffset(this.state);
812
- style.push(`counter-reset: row-number ${rowNumber}`);
822
+ style['counter-reset'] = `row-number ${rowNumber}`;
813
823
  }
814
824
  if (virtualize) {
815
- const length = rows.length;
816
- style.push('position: relative', `height:${length * rowHeight}px`);
825
+ style.position = 'relative';
826
+ style.height = `${tableHeight}px`;
817
827
  }
818
828
  return styleToString(style);
819
829
  }
@@ -938,11 +948,16 @@ export default class LightningDatatable extends LightningElement {
938
948
  if (virtualize) {
939
949
  const { firstIndex, lastIndex } =
940
950
  this._renderManager.getRenderedRange(this.state);
951
+ this._lastRenderedRow = lastIndex + 1; // UTAM rows are 1-indexed
952
+ // we shouldn't lose focus from re-renders caused by a change in renderedRows
953
+ this._shouldResetFocus = true;
941
954
  return rows.slice(firstIndex, lastIndex);
942
955
  }
943
956
  if (this.viewportRendering && !isIE11) {
957
+ this._lastRenderedRow = renderedRowCount;
944
958
  return rows.slice(0, renderedRowCount);
945
959
  }
960
+ this._lastRenderedRow = rows.length;
946
961
  return rows;
947
962
  }
948
963
 
@@ -1124,10 +1139,7 @@ export default class LightningDatatable extends LightningElement {
1124
1139
  if (fireResizeEvent) {
1125
1140
  this.fireOnResize(false);
1126
1141
  }
1127
-
1128
- const role = '[role="' + this.computedTableRole + '"]';
1129
-
1130
- this.template.querySelector(role).style = this.computedTableStyle;
1142
+ this.updateTableAndScrollerStyleOnRender();
1131
1143
  }
1132
1144
 
1133
1145
  // Managing the cell widths is only required for the role-based table
@@ -1142,17 +1154,23 @@ export default class LightningDatatable extends LightningElement {
1142
1154
  // set the previous focused cell to null after render is done
1143
1155
  resetCellToFocusFromPrev(state);
1144
1156
  // reset focus styles on re-render
1145
- if (state.activeCell && state.activeCell.focused) {
1146
- const cellElement = getActiveCellElement(template, state);
1147
- if (
1148
- cellElement &&
1149
- cellElement.parentElement &&
1150
- !cellElement.parentElement.classList.contains(FOCUS_CLASS)
1151
- ) {
1152
- setFocusActiveCell(template, state, null, null, false);
1157
+ if (this._shouldResetFocus) {
1158
+ // since focus is now getting reset, can change this back to false
1159
+ this._shouldResetFocus = false;
1160
+ if (state.activeCell && state.activeCell.focused) {
1161
+ const cellElement = getActiveCellElement(template, state);
1162
+ if (
1163
+ cellElement &&
1164
+ cellElement.parentElement &&
1165
+ !cellElement.parentElement.classList.contains(FOCUS_CLASS)
1166
+ ) {
1167
+ setFocusActiveCell(template, state, null, null, false);
1168
+ }
1153
1169
  }
1154
1170
  }
1155
1171
 
1172
+ this.updateVirtualizedRowHeights();
1173
+
1156
1174
  if (this.viewportRendering || state.virtualize) {
1157
1175
  const resizeTarget = this.template.querySelector(
1158
1176
  'div.dt-outer-container'
@@ -1175,6 +1193,18 @@ export default class LightningDatatable extends LightningElement {
1175
1193
  }
1176
1194
  }
1177
1195
 
1196
+ updateTableAndScrollerStyleOnRender() {
1197
+ const role = '[role="' + this.computedTableRole + '"]';
1198
+ const tableElement = this.template.querySelector(role);
1199
+ const scrollYEle = this.template.querySelector('.slds-scrollable_y');
1200
+ if (tableElement) {
1201
+ tableElement.style = this.computedTableStyle;
1202
+ }
1203
+ if (scrollYEle) {
1204
+ scrollYEle.style = this.computedScrollerStyle;
1205
+ }
1206
+ }
1207
+
1178
1208
  disconnectedCallback() {
1179
1209
  if (this._privateWidthObserver) {
1180
1210
  this._privateWidthObserver.disconnect();
@@ -1253,7 +1283,10 @@ export default class LightningDatatable extends LightningElement {
1253
1283
 
1254
1284
  handleInlineEditPanelScroll.call(this, event);
1255
1285
  if (this.state.virtualize) {
1256
- setFirstVisibleIndex(this.state, event.target.scrollTop);
1286
+ this.state.firstVisibleIndex = findFirstVisibleIndex(
1287
+ this.state,
1288
+ event.target.scrollTop
1289
+ );
1257
1290
  } else if (this.viewportRendering) {
1258
1291
  this._renderManager.handleScroll(this.state, event);
1259
1292
  }
@@ -1347,6 +1380,7 @@ export default class LightningDatatable extends LightningElement {
1347
1380
  handleResizeEnd(event) {
1348
1381
  event.stopPropagation();
1349
1382
  this._isResizing = false;
1383
+ this._shouldResetHeights = true;
1350
1384
  }
1351
1385
 
1352
1386
  /**
@@ -1419,10 +1453,19 @@ export default class LightningDatatable extends LightningElement {
1419
1453
  * Handles the `focusout` event on <table> and the corresponding
1420
1454
  * <div> on the role-based table
1421
1455
  *
1456
+ * This gets called both when we expect the table to lose focus
1457
+ * and when the active cell loses focus after renderedRows changes
1458
+ * on a virtualized table, in which case we don't want to lose focus.
1459
+ *
1460
+ * We account for this by setting activeCell.focused to the value of
1461
+ * _shouldResetFocus, which will be true if and only if focus was
1462
+ * lost due to a renderedRows change for a virtualized table.
1463
+ *
1422
1464
  * @param {FocusEvent} event - `focusout`
1423
1465
  */
1424
1466
  handleTableFocusOut(event) {
1425
1467
  handleDatatableFocusOut.call(this, event);
1468
+ this.state.activeCell.focused = this._shouldResetFocus;
1426
1469
  }
1427
1470
 
1428
1471
  /**
@@ -1601,6 +1644,41 @@ export default class LightningDatatable extends LightningElement {
1601
1644
  }
1602
1645
  }
1603
1646
 
1647
+ updateVirtualizedRowHeights() {
1648
+ const state = this.state;
1649
+ const virtualizedRows = state.virtualize && this.renderedRows.length;
1650
+
1651
+ // no need to handle other virtualization/row height logic
1652
+ // if heights need to be reset
1653
+ if (this._shouldResetHeights) {
1654
+ resetRowHeights(state);
1655
+ this._shouldResetHeights = false;
1656
+ } else if (virtualizedRows && !state.fixedHeight) {
1657
+ // if row heights aren't fixed, we need to update items
1658
+ // in state to know where rows should be positioned
1659
+ handleVariableRowHeights(this.template, state, this.renderedRows);
1660
+ } else if (virtualizedRows && state.fixedHeight) {
1661
+ // if heights are fixed, we only need to check height of first row
1662
+ const rowElement = this.template.querySelector(
1663
+ getDataRow(this.renderedRows[0].key)
1664
+ );
1665
+ // increase height by 1 since first rendered row is missing an extra 1px border
1666
+ if (rowElement) {
1667
+ const height = rowElement.getBoundingClientRect().height + 1;
1668
+ if (state.rowHeight !== height) {
1669
+ state.rowHeight = height;
1670
+ resetTableHeight(state);
1671
+ state.rows.forEach((row) => {
1672
+ row.style = styleToString({
1673
+ position: 'absolute',
1674
+ top: `${row.rowIndex * height}px`,
1675
+ });
1676
+ });
1677
+ }
1678
+ }
1679
+ }
1680
+ }
1681
+
1604
1682
  setSelectedRows(value) {
1605
1683
  setSelectedRowsKeys(this.state, value);
1606
1684
  handleRowSelectionChange.call(this);
@@ -1620,7 +1698,7 @@ export default class LightningDatatable extends LightningElement {
1620
1698
  }
1621
1699
 
1622
1700
  /**
1623
- * @return {Object} containing the visible dimensions of the table { left, right, top, bottom, }
1701
+ * @returns {Object} containing the visible dimensions of the table { left, right, top, bottom, }
1624
1702
  */
1625
1703
  getViewableRect() {
1626
1704
  const scrollerX = this.template
@@ -16,7 +16,7 @@ const DIVIDER_REM_HEIGHT = 1.0625;
16
16
  *
17
17
  * @param {Object} state The state of the datatable
18
18
  * @param {Object} columnDefinition The column definition to extract internal actions from
19
- * @return {Array} All wrapText internal actions
19
+ * @returns {Array} All wrapText internal actions
20
20
  */
21
21
  export function getInternalActions(state, columnDefinition) {
22
22
  return [...getActions(state, columnDefinition)];
@@ -113,7 +113,7 @@ function dispatchHeaderActionEvent(dt, action, colKeyValue) {
113
113
  *
114
114
  * @param {Array} columns Array of all the columns
115
115
  * @param {Integer} index The current column index to check
116
- * @return {String} The computed alignment
116
+ * @returns {String} The computed alignment
117
117
  */
118
118
  function getMenuAlignment(columns, index) {
119
119
  const isLastColumn = index === columns.length - 1;
@@ -10,7 +10,7 @@ import {
10
10
  } from './tree';
11
11
  import { isCellEditable, getRowByKey } from './rows';
12
12
  import { isRTL, getShadowActiveElements } from 'lightning/utilsPrivate';
13
- import { setFirstVisibleIndex } from './renderManager';
13
+ import { findFirstVisibleIndex } from './virtualization';
14
14
 
15
15
  // Indicator/flag for a header row
16
16
  const HEADER_ROW = 'HEADER';
@@ -88,7 +88,7 @@ const SELECTORS = {
88
88
  * user keys down on a cell that contains actionable items (ex. edit button, links,
89
89
  * email, buttons).
90
90
  *
91
- * @param {Event} event - Custom DOM event (privatecellkeydown) sent by the cell
91
+ * @param {Event} event Custom DOM event (privatecellkeydown) sent by the cell
92
92
  */
93
93
  export function handleKeydownOnCell(event) {
94
94
  event.stopPropagation();
@@ -110,7 +110,7 @@ export function handleKeydownOnCell(event) {
110
110
  * Those events are handled by `handleKeydownOnCell()` and the remaining are
111
111
  * handled by this function.
112
112
  *
113
- * @param {*} event
113
+ * @param {Event} event
114
114
  */
115
115
  export function handleKeydownOnTable(event) {
116
116
  const targetTagName = event.target.tagName.toLowerCase();
@@ -127,10 +127,10 @@ export function handleKeydownOnTable(event) {
127
127
  * Changes the datatable state based on the keyboard event sent from the cell component.
128
128
  * The result of those changes may trigger a re-render on the table
129
129
  *
130
- * @param {node} template - the custom element root `this.template`
131
- * @param {object} state - datatable state
132
- * @param {event} event - custom DOM event sent by the cell
133
- * @returns {object} - mutated state
130
+ * @param {Node} template The custom element root `this.template`
131
+ * @param {Object} state Datatable state
132
+ * @param {Event} event Custom DOM event sent by the cell
133
+ * @returns {Object} Mutated state
134
134
  */
135
135
  function reactToKeyboardInActionMode(template, state, event) {
136
136
  switch (event.detail.keyCode) {
@@ -1313,7 +1313,7 @@ function isHeaderRow(rowIndex) {
1313
1313
  return rowIndex === -1;
1314
1314
  }
1315
1315
 
1316
- function getDataRow(rowKeyValue) {
1316
+ export function getDataRow(rowKeyValue) {
1317
1317
  return `[data-row-key-value="${rowKeyValue}"]`;
1318
1318
  }
1319
1319
 
@@ -1365,7 +1365,7 @@ function updateScrollTop(state, template, element) {
1365
1365
  } else if (findMeRect.bottom > parentRect.bottom - BOTTOM_MARGIN) {
1366
1366
  scrollableY.scrollTop += SCROLL_OFFSET;
1367
1367
  }
1368
- setFirstVisibleIndex(state, scrollableY.scrollTop);
1368
+ findFirstVisibleIndex(state, scrollableY.scrollTop);
1369
1369
  }
1370
1370
 
1371
1371
  function scrollToCell(state, template, rowIndex) {
@@ -1380,7 +1380,7 @@ function scrollToCell(state, template, rowIndex) {
1380
1380
 
1381
1381
  const scrollableY = template.querySelector('.slds-scrollable_y');
1382
1382
  scrollableY.scrollTop = scrollTop;
1383
- setFirstVisibleIndex(state, scrollTop);
1383
+ findFirstVisibleIndex(state, scrollTop);
1384
1384
  }
1385
1385
 
1386
1386
  export function isActiveCellEditable(state) {
@@ -1,6 +1,7 @@
1
1
  import { getScrollOffsetFromTableEnd, normalizeNumberAttribute } from './utils';
2
2
  import { LightningResizeObserver } from 'lightning/resizeObserver';
3
3
  import { normalizeBoolean, normalizeString } from 'lightning/utilsPrivate';
4
+ import { resetRowHeights } from './virtualization';
4
5
 
5
6
  export const DEFAULT_ROW_HEIGHT = 30.5;
6
7
  const DEFAULT_BUFFER_SIZE = 5;
@@ -31,17 +32,6 @@ export function getDTWrapperHeight() {
31
32
  return this.template.querySelector('.slds-scrollable_x').offsetHeight;
32
33
  }
33
34
 
34
- /**
35
- * Sets the first visible index in the datatable state based on
36
- * a given scrollTop value and the current rowHeight
37
- *
38
- * @param {Object} state - datatable state
39
- * @param {Number} scrollTop - scroll top to calculate first visible index for
40
- */
41
- export function setFirstVisibleIndex(state, scrollTop) {
42
- state.firstVisibleIndex = Math.floor(scrollTop / state.rowHeight);
43
- }
44
-
45
35
  function getDefaultPreviousInfo() {
46
36
  return {
47
37
  renderedRowCount: 0,
@@ -81,7 +71,13 @@ export class RenderManager {
81
71
  * @param {RenderManagerConfig} config - set of properties used for render management
82
72
  */
83
73
  configure(state, getWrapperHeight, config) {
84
- const { viewportRendering, rowHeight, virtualize, bufferSize } = config;
74
+ const {
75
+ viewportRendering,
76
+ rowHeight,
77
+ virtualize,
78
+ bufferSize,
79
+ fixedHeight,
80
+ } = config;
85
81
  setVirtualize(state, virtualize);
86
82
 
87
83
  if (normalizeBoolean(viewportRendering) || state.virtualize) {
@@ -93,6 +89,7 @@ export class RenderManager {
93
89
  'non-negative',
94
90
  DEFAULT_BUFFER_SIZE
95
91
  );
92
+ state.fixedHeight = normalizeBoolean(fixedHeight);
96
93
  if (typeof rowHeight === 'number') {
97
94
  state.rowHeight = rowHeight;
98
95
  this.threshold = ROW_THRESHOLD * state.rowHeight;
@@ -157,9 +154,42 @@ export class RenderManager {
157
154
  * @returns {Object} object with firstIndex and lastIndex of rendered range of rows
158
155
  */
159
156
  getRenderedRange(state) {
160
- const { firstVisibleIndex, bufferSize, renderedRowCount, rows } = state;
157
+ const {
158
+ firstVisibleIndex,
159
+ bufferSize,
160
+ renderedRowCount,
161
+ rows,
162
+ fixedHeight,
163
+ rowHeight,
164
+ heightCache,
165
+ } = state;
166
+
161
167
  const firstIndex = Math.max(firstVisibleIndex - bufferSize, 0);
162
168
  let lastIndex = firstVisibleIndex - bufferSize + renderedRowCount;
169
+ const firstVisibleKey =
170
+ rows[firstVisibleIndex] && rows[firstVisibleIndex].key;
171
+
172
+ if (!fixedHeight && heightCache[firstVisibleKey]) {
173
+ let i = firstVisibleIndex;
174
+ let currentHeight = 0;
175
+ const knownRowHeight = rows[i] && heightCache[rows[i].key];
176
+ // loop through rows until we find last row based on wrapper height
177
+ while (knownRowHeight && currentHeight < this.wrapperHeight) {
178
+ currentHeight += knownRowHeight;
179
+ i = i + 1;
180
+ }
181
+ if (currentHeight >= this.wrapperHeight) {
182
+ // row heights were measured; i represents last visible index
183
+ lastIndex = i + bufferSize - 1;
184
+ } else {
185
+ // row heights not yet measured, i is last visible index with known height
186
+ // guess lastIndex based on default row height
187
+ const extraRowEstimate =
188
+ (this.wrapperHeight - currentHeight) / rowHeight;
189
+ lastIndex = i + extraRowEstimate + bufferSize;
190
+ }
191
+ }
192
+
163
193
  // without this, we may render too few rows when there
164
194
  // aren't enough rows for virtualization to be needed
165
195
  if (
@@ -224,6 +254,8 @@ export class RenderManager {
224
254
  const rowCountWithBuffer =
225
255
  this.getRowCountWithBuffer(state);
226
256
 
257
+ resetRowHeights(state);
258
+
227
259
  if (
228
260
  rowCountWithBuffer > state.renderedRowCount ||
229
261
  state.virtualize
@@ -40,7 +40,8 @@ class EventQueue {
40
40
 
41
41
  /**
42
42
  * Get element size
43
- * @param {HTMLElement} element - element to return the size.
43
+ *
44
+ * @param {HTMLElement} element Element to return the size.
44
45
  * @returns {Object} {width, height}
45
46
  */
46
47
  function getElementSize(element) {
@@ -73,9 +74,10 @@ function createResizeSensor() {
73
74
  }
74
75
 
75
76
  /**
77
+ * Attaches the resize event to an element
76
78
  *
77
- * @param {HTMLElement} element - element to listen resize.
78
- * @param {Function} resizeListener - resize event listener.
79
+ * @param {HTMLElement} element Element to listen resize
80
+ * @param {Function} resizeListener Resize event listener
79
81
  */
80
82
  function attachResizeEvent(element, resizeListener) {
81
83
  if (!element) {
@@ -192,6 +194,12 @@ function attachResizeEvent(element, resizeListener) {
192
194
  requestAnimationFrame(reset);
193
195
  }
194
196
 
197
+ /**
198
+ * Removes a resize event from an element.
199
+ *
200
+ * @param {HTMLElement} element Element to remove resize listener from
201
+ * @param {Event} event The event to remove
202
+ */
195
203
  function detach(elem, ev) {
196
204
  if (!elem) {
197
205
  return;
@@ -497,7 +497,7 @@ export function resetSelectedRowsKeys(state) {
497
497
  * Returns undefined if the row key value is invalid
498
498
  *
499
499
  * @param {Object} state - the datatable state.
500
- * @return {String | undefined } the row key or undefined.
500
+ * @returns {String | undefined } the row key or undefined.
501
501
  */
502
502
  function getLastRowSelection(state) {
503
503
  const lastSelectedRowKey = state.lastSelectedRowKey;
@@ -9,31 +9,27 @@
9
9
  */
10
10
 
11
11
  /**
12
- * Returns whether or not the row is selected using the state and the rowKeyValue
13
- * The state maintains the list of selected rows from which
14
- * this value can be retrieved.
12
+ * Returns whether or not the row is selected using the state and the `rowKeyValue`.
13
+ * The state maintains the list of selected rows from which this value can be retrieved.
15
14
  *
16
- * @param {Object} state - datatable state object
17
- * @param {String} rowKeyValue
18
- * @returns
15
+ * @param {Object} state Datatable state object
16
+ * @param {String} rowKeyValue The row key value to lookup
17
+ * @returns {Boolean} Whether the row is currently selected
19
18
  */
20
19
  export function isSelectedRow(state, rowKeyValue) {
21
20
  return !!state.selectedRowsKeys[rowKeyValue];
22
21
  }
23
22
 
24
23
  /**
25
- * Returns whether the row (whose row key value is specified)
26
- * should be disabled or not.
27
- * Should not disable if the row is selected.
28
- * If the particular row is not selected, the row should be disabled
29
- * when max-row-selection > 1 and the selection limit is reached
24
+ * Returns whether the row (whose row key value is specified) should be disabled or not.
25
+ * Should not disable if the row is selected. If the particular row is not selected, the
26
+ * row should be disabled when `max-row-selection` > 1 and the selection limit is reached.
30
27
  *
31
- * Note: Do not disable selection when max-row-selection = 1 and
32
- * a row has been selected.
28
+ * NOTE: Do not disable selection when `max-row-selection` is 1 and a row has been selected.
33
29
  *
34
- * @param {Object} state
35
- * @param {String} rowKeyValue
36
- * @returns {Boolean} whether the row should be disabled or not
30
+ * @param {Object} state Datatable state object
31
+ * @param {String} rowKeyValue The row key value to lookup
32
+ * @returns {Boolean} Whether the row should be disabled or not
37
33
  */
38
34
  export function isDisabledRow(state, rowKeyValue) {
39
35
  if (!isSelectedRow(state, rowKeyValue)) {
@@ -51,11 +47,10 @@ export function isDisabledRow(state, rowKeyValue) {
51
47
 
52
48
  /**
53
49
  * Returns which input type to use for row selection.
54
- * If max-row-selection = 1, use radio buttons,
55
- * otherwise, use checkboxes.
50
+ * If `max-row-selection` is 1, use radio buttons. Otherwise, use checkboxes.
56
51
  *
57
- * @param {Object} state - datatable's state object
58
- * @returns
52
+ * @param {Object} state Datatable state object
53
+ * @returns {String} `radio` is only one row is allowed to be selected, otherwise `checkbox`
59
54
  */
60
55
  export function getRowSelectionInputType(state) {
61
56
  if (getMaxRowSelection(state) === 1) {
@@ -64,14 +59,32 @@ export function getRowSelectionInputType(state) {
64
59
  return 'checkbox';
65
60
  }
66
61
 
62
+ /**
63
+ * Retrieves the maximum number of rows that can be selected from state.
64
+ *
65
+ * @param {Object} state Datatable state object
66
+ * @returns {Integer} The maximum rows that can be selected
67
+ */
67
68
  export function getMaxRowSelection(state) {
68
69
  return state.maxRowSelection;
69
70
  }
70
71
 
72
+ /**
73
+ * Determines the number of rows currently selected.
74
+ *
75
+ * @param {Object} state Datatable state object
76
+ * @returns {Integer} The number of currently selected rows
77
+ */
71
78
  export function getCurrentSelectionLength(state) {
72
79
  return getSelectedRowsKeys(state).length;
73
80
  }
74
81
 
82
+ /**
83
+ * Retrieves the row keys that are currently selected.
84
+ *
85
+ * @param {Object} state Datatable state object
86
+ * @returns {Array} An array of the row keys that are currently selected
87
+ */
75
88
  export function getSelectedRowsKeys(state) {
76
89
  return Object.keys(state.selectedRowsKeys).filter(
77
90
  (key) => state.selectedRowsKeys[key]
@@ -76,8 +76,7 @@ export function resolveRowClassNames(row) {
76
76
  * @param {object} state - data table state
77
77
  * @param {string} rowKeyValue - computed id for the row
78
78
  * @param {string} colKeyValue - computed id for the column
79
- *
80
- * @return {object} The user row that its related to the action.
79
+ * @returns {object} The user row that its related to the action.
81
80
  */
82
81
  export function getUserRowByCellKeys(state, rowKeyValue, colKeyValue) {
83
82
  const rowIndex = state.indexes[rowKeyValue][colKeyValue][0];
@@ -135,6 +134,8 @@ export function updateRowsAndCellIndexes() {
135
134
  state.indexes[row.key] = { rowIndex };
136
135
 
137
136
  row.rowIndex = rowIndex;
137
+ row.rowNumber = rowIndex + 1; // for UTAM since methods are base-1
138
+ row.ariaRowIndex = rowIndex + 2; // aria attrs are base-1 and also count header as a row
138
139
  row.inputType = getRowSelectionInputType(state);
139
140
  row.isSelected = isSelectedRow(state, row.key);
140
141
  row.ariaSelected = row.isSelected ? 'true' : false;
@@ -10,8 +10,8 @@ const VALID_SORT_DIRECTIONS = {
10
10
  * Determines if the supplied sort direction is valid.
11
11
  * Refer to `VALID_SORT_DIRECTIONS` for valid solid directions.
12
12
  *
13
- * @param {string} value The sort direction to validate
14
- * @return {boolean} Whether the supplied sort direction is valid
13
+ * @param {String} value The sort direction to validate
14
+ * @returns {Boolean} Whether the supplied sort direction is valid
15
15
  */
16
16
  export function isValidSortDirection(value) {
17
17
  return !!VALID_SORT_DIRECTIONS[value];
@@ -22,7 +22,7 @@ export function isValidSortDirection(value) {
22
22
  * the default sort direction is applied first. Clicking again reverses it
23
23
  *
24
24
  * @param {Object} state The current datatable state
25
- * @return {string} The default sort direction
25
+ * @returns {String} The default sort direction
26
26
  */
27
27
  export function getDefaultSortDirection(state) {
28
28
  return state.defaultSortDirection;
@@ -33,7 +33,7 @@ export function getDefaultSortDirection(state) {
33
33
  * the default sort direction is applied first. Clicking again reverses it
34
34
  *
35
35
  * @param {Object} state The current datatable state
36
- * @param {string} value The value to update the default sort direction to
36
+ * @param {String} value The value to update the default sort direction to
37
37
  */
38
38
  export function setDefaultSortDirection(state, value) {
39
39
  assert(
@@ -51,7 +51,7 @@ export function setDefaultSortDirection(state, value) {
51
51
  * Gets the current sort direction
52
52
  *
53
53
  * @param {Object} state The current datatable state
54
- * @return {string} The current sort direction
54
+ * @returns {String} The current sort direction
55
55
  */
56
56
  export function getSortedDirection(state) {
57
57
  return state.sortedDirection;
@@ -64,7 +64,7 @@ export function getSortedDirection(state) {
64
64
  * `sortedDirection` will be `undefined`
65
65
  *
66
66
  * @param {Object} state The current datatable state
67
- * @param {string} value The value to update the sort direction to
67
+ * @param {String} value The value to update the sort direction to
68
68
  */
69
69
  export function setSortedDirection(state, value) {
70
70
  assert(
@@ -81,7 +81,7 @@ export function setSortedDirection(state, value) {
81
81
  * of a given column in the datatable
82
82
  *
83
83
  * @param {Object} state The current datatable state
84
- * @return {string} The current sort value
84
+ * @returns {String} The current sort value
85
85
  */
86
86
  export function getSortedBy(state) {
87
87
  return state.sortedBy;
@@ -94,7 +94,7 @@ export function getSortedBy(state) {
94
94
  * to `undefined`
95
95
  *
96
96
  * @param {Object} state The current datatable state
97
- * @param {string} value The value to update the sort state to
97
+ * @param {String} value The value to update the sort state to
98
98
  */
99
99
  export function setSortedBy(state, value) {
100
100
  if (typeof value === 'string') {
@@ -3,7 +3,8 @@
3
3
  * The state object is further manipulated while it is passed around by datatable
4
4
  *
5
5
  * TODO: Check to see if there are other items that need to be added to the default state
6
- * @returns {Object} - intial state of the datatable
6
+ *
7
+ * @returns {Object} Intial state of the datatable
7
8
  */
8
9
  export const getDefaultState = function () {
9
10
  return {
@@ -62,6 +63,12 @@ export const getDefaultState = function () {
62
63
  rowHeight: 30.5,
63
64
  renderedRowCount: 0,
64
65
  firstVisibleIndex: 0, // first row that should be visible in viewport
66
+ fixedHeight: false, // by default, assume that not all rows are same height
67
+ heightCache: {}, // cache of row heights
68
+ offsets: [0],
69
+ offsetRanges: [],
70
+ firstRowOffset: 0, // how many pixels scrollTop is from top of first visible row
71
+ tableHeight: 0,
65
72
 
66
73
  // inline edit
67
74
  inlineEdit: {