lightning-base-components 1.13.10-alpha → 1.14.4-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 (69) hide show
  1. package/metadata/raptor.json +24 -0
  2. package/package.json +20 -4
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js +1 -0
  6. package/scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js +1 -0
  7. package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
  8. package/scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js +1 -0
  9. package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +112 -0
  10. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
  11. package/src/lightning/{utilsPrivate/contentMutation.js → ariaObserver/ariaObserver.js} +60 -98
  12. package/src/lightning/buttonMenu/keyboard.js +0 -10
  13. package/src/lightning/card/card.html +6 -0
  14. package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
  15. package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
  16. package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
  17. package/src/lightning/datatable/__docs__/datatable.md +55 -0
  18. package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
  19. package/src/lightning/datatable/columns-shared.js +1 -1
  20. package/src/lightning/datatable/datatable.js +98 -30
  21. package/src/lightning/datatable/errors.js +20 -9
  22. package/src/lightning/datatable/headerActions.js +77 -49
  23. package/src/lightning/datatable/infiniteLoading.js +100 -28
  24. package/src/lightning/datatable/inlineEdit.js +505 -379
  25. package/src/lightning/datatable/inlineEditShared.js +24 -0
  26. package/src/lightning/datatable/keyboard.js +162 -127
  27. package/src/lightning/datatable/renderManager.js +201 -133
  28. package/src/lightning/datatable/rowLevelActions.js +17 -13
  29. package/src/lightning/datatable/rowNumber.js +54 -20
  30. package/src/lightning/datatable/rowSelection.js +760 -0
  31. package/src/lightning/datatable/rowSelectionShared.js +79 -0
  32. package/src/lightning/datatable/rows.js +17 -6
  33. package/src/lightning/datatable/state.js +16 -2
  34. package/src/lightning/datatable/templates/div/div.css +4 -0
  35. package/src/lightning/datatable/templates/div/div.html +6 -0
  36. package/src/lightning/datatable/templates/table/table.html +5 -0
  37. package/src/lightning/datatable/utils.js +14 -0
  38. package/src/lightning/datatable/wrapText.js +77 -47
  39. package/src/lightning/dualListbox/dualListbox.html +1 -1
  40. package/src/lightning/dualListbox/dualListbox.js +42 -0
  41. package/src/lightning/formattedDateTime/__docs__/formattedDateTime.md +36 -3
  42. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.html +2 -2
  43. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.js +3 -1
  44. package/src/lightning/formattedDateTime/__examples__/time/time.html +1 -1
  45. package/src/lightning/formattedDateTime/__examples__/time/time.js +3 -1
  46. package/src/lightning/formattedDateTime/formattedDateTime.js +1 -0
  47. package/src/lightning/input/input.html +1 -5
  48. package/src/lightning/input/input.js +69 -48
  49. package/src/lightning/inputUtils/validity.js +12 -1
  50. package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
  51. package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
  52. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
  53. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
  54. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
  55. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +167 -98
  56. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +94 -69
  57. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
  58. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
  59. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
  60. package/src/lightning/progressIndicator/progressIndicator.js +1 -1
  61. package/src/lightning/progressStep/progressStep.js +30 -22
  62. package/src/lightning/staticMap/staticMap.html +1 -0
  63. package/src/lightning/staticMap/staticMap.js +39 -2
  64. package/src/lightning/utils/classSet.js +4 -1
  65. package/src/lightning/utilsPrivate/utilsPrivate.js +12 -1
  66. package/scopedImports/@salesforce-label-LightningModalBase.close.js +0 -1
  67. package/src/lightning/datatable/inlineEdit-shared.js +0 -14
  68. package/src/lightning/datatable/selector-shared.js +0 -38
  69. package/src/lightning/datatable/selector.js +0 -527
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Retrieves the dirty/unsaved value of a cell that resulted from an inline
3
+ * edit change. If no change was made on the cell, this function
4
+ * returns `undefined`.
5
+ *
6
+ * @param {Object} state - datatable's state object
7
+ * @param {String} rowKeyValue - computed id for the row
8
+ * @param {String} colKeyValue - computed id for the column
9
+ * @returns {String} The dirty/unsaved value of the cell.
10
+ * If no change was made, this returns `undefined`
11
+ */
12
+ export function getDirtyValueFromCell(state, rowKeyValue, colKeyValue) {
13
+ const dirtyValues = state.inlineEdit.dirtyValues;
14
+
15
+ if (
16
+ dirtyValues &&
17
+ dirtyValues[rowKeyValue] &&
18
+ dirtyValues[rowKeyValue][colKeyValue]
19
+ ) {
20
+ return dirtyValues[rowKeyValue][colKeyValue];
21
+ }
22
+
23
+ return undefined;
24
+ }
@@ -1,16 +1,22 @@
1
- import { isCustomerColumn, generateColKeyValue } from './columns';
1
+ import {
2
+ isCustomerColumn,
3
+ generateColKeyValue,
4
+ getStateColumnIndex,
5
+ } from './columns';
2
6
  import {
3
7
  hasTreeDataType,
4
8
  getStateTreeColumn,
5
9
  fireRowToggleEvent,
6
10
  } from './tree';
11
+ import { isCellEditable, getRowByKey } from './rows';
7
12
  import { isRTL, getShadowActiveElements } from 'lightning/utilsPrivate';
13
+ import { setFirstVisibleIndex } from './renderManager';
8
14
 
9
15
  // Indicator/flag for a header row
10
16
  const HEADER_ROW = 'HEADER';
11
17
 
12
18
  // SLDS Class for Focus
13
- const FOCUS_CLASS = 'slds-has-focus';
19
+ export const FOCUS_CLASS = 'slds-has-focus';
14
20
 
15
21
  // Keyboard Navigation Modes
16
22
  const NAVIGATION_MODE = 'NAVIGATION';
@@ -121,28 +127,28 @@ export function handleKeydownOnTable(event) {
121
127
  * Changes the datatable state based on the keyboard event sent from the cell component.
122
128
  * The result of those changes may trigger a re-render on the table
123
129
  *
124
- * @param {node} element - the custom element root `this.template`
130
+ * @param {node} template - the custom element root `this.template`
125
131
  * @param {object} state - datatable state
126
132
  * @param {event} event - custom DOM event sent by the cell
127
133
  * @returns {object} - mutated state
128
134
  */
129
- function reactToKeyboardInActionMode(element, state, event) {
135
+ function reactToKeyboardInActionMode(template, state, event) {
130
136
  switch (event.detail.keyCode) {
131
137
  case ARROW_LEFT:
132
- return reactToArrowLeft(element, state, event);
138
+ return reactToArrowLeft(template, state, event);
133
139
  case ARROW_RIGHT:
134
- return reactToArrowRight(element, state, event);
140
+ return reactToArrowRight(template, state, event);
135
141
  case ARROW_UP:
136
- return reactToArrowUp(element, state, event);
142
+ return reactToArrowUp(template, state, event);
137
143
  case ARROW_DOWN:
138
- return reactToArrowDown(element, state, event);
144
+ return reactToArrowDown(template, state, event);
139
145
  case ENTER:
140
146
  case SPACE:
141
- return reactToEnter(element, state, event);
147
+ return reactToEnter(template, state, event);
142
148
  case ESCAPE:
143
- return reactToEscape(element, state, event);
149
+ return reactToEscape(template, state, event);
144
150
  case TAB:
145
- return reactToTab(element, state, event);
151
+ return reactToTab(template, state, event);
146
152
  default:
147
153
  return state;
148
154
  }
@@ -642,12 +648,15 @@ function setDefaultActiveCell(state) {
642
648
  * Given a datatable template and state, returns an LWC component reference that represents
643
649
  * the currently active cell in the table.
644
650
  *
645
- * @param {Object} element - A reference to the datatable's template
651
+ * @param {Object} template - A reference to the datatable's template
646
652
  * @param {Object} state - A reference to the datatable's state
647
653
  */
648
- export function getActiveCellElement(element, state) {
649
- const { rowIndex, colIndex } = getIndexesActiveCell(state);
650
- return getCellElementByIndexes(element, rowIndex, colIndex, state);
654
+ export function getActiveCellElement(template, state) {
655
+ if (state.activeCell) {
656
+ const { rowKeyValue, colKeyValue } = state.activeCell;
657
+ return getCellElementByKeys(template, rowKeyValue, colKeyValue);
658
+ }
659
+ return null;
651
660
  }
652
661
 
653
662
  /**
@@ -805,45 +814,59 @@ function stillValidActiveCell(state) {
805
814
  * - update the tabindex of the activeCell
806
815
  * - set the current keyboard mode
807
816
  * - set the focus to the cell
808
- * @param {node} element - the custom element template `this.template`
817
+ * @param {node} template - the custom element template `this.template`
809
818
  * @param {object} state - datatable state
810
819
  * @param {int} direction - direction (-1 left, 1 right and 0 for no direction) its used to know which actionable element to activate.
811
- * @param {object} info - extra information when setting the cell mode.
820
+ * @param {object} info - extra information when setting the cell mode; currently only set when pressing tab
821
+ * @param {boolean} shouldScroll - true if scrollTop should be adjusted when setting focus
812
822
  */
813
- export function setFocusActiveCell(element, state, direction, info) {
823
+ export function setFocusActiveCell(
824
+ template,
825
+ state,
826
+ direction,
827
+ info,
828
+ shouldScroll = true
829
+ ) {
814
830
  const { keyboardMode } = state;
815
831
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
816
832
 
833
+ // if pressing tab on editable cell, focus will go to inline edit panel instead of cell
834
+ state.activeCell.focused =
835
+ !(info && isActiveCellEditable(state)) && isActiveCellValid(state);
817
836
  updateTabIndex(state, rowIndex, colIndex);
837
+
838
+ let cellElement = getActiveCellElement(template, state);
839
+ // if the cell wasn't found, but does exist in the table, scroll to where it should be
840
+ if (!cellElement && isActiveCellValid(state) && shouldScroll) {
841
+ scrollToCell(state, template, rowIndex);
842
+ }
843
+
818
844
  return new Promise((resolve) => {
819
845
  // eslint-disable-next-line @lwc/lwc/no-async-operation
820
846
  setTimeout(() => {
821
- const cellElement = getCellElementByIndexes(
822
- element,
823
- rowIndex,
824
- colIndex,
825
- state
826
- );
847
+ // reset cell element if falsy or no longer valid
848
+ if (
849
+ !cellElement ||
850
+ !isValidCell(
851
+ state,
852
+ cellElement.rowKeyValue,
853
+ cellElement.colKeyValue
854
+ )
855
+ ) {
856
+ cellElement = getActiveCellElement(template, state);
857
+ }
827
858
  if (cellElement) {
828
859
  if (direction) {
829
860
  cellElement.resetCurrentInputIndex(direction, keyboardMode);
830
861
  }
831
862
  cellElement.addFocusStyles();
832
863
  cellElement.parentElement.classList.add(FOCUS_CLASS);
833
- cellElement.parentElement.focus();
864
+ cellElement.parentElement.focus({
865
+ preventScroll: !shouldScroll,
866
+ });
834
867
  cellElement.setMode(keyboardMode, info);
835
-
836
- const scrollableY = element.querySelector('.slds-scrollable_y');
837
- const scrollingParent = scrollableY.parentElement;
838
- const parentRect = scrollingParent.getBoundingClientRect();
839
- const findMeRect = cellElement.getBoundingClientRect();
840
- if (findMeRect.top < parentRect.top + TOP_MARGIN) {
841
- scrollableY.scrollTop -= SCROLL_OFFSET;
842
- } else if (
843
- findMeRect.bottom >
844
- parentRect.bottom - BOTTOM_MARGIN
845
- ) {
846
- scrollableY.scrollTop += SCROLL_OFFSET;
868
+ if (shouldScroll) {
869
+ updateScrollTop(state, template, cellElement);
847
870
  }
848
871
  }
849
872
  resolve();
@@ -855,22 +878,27 @@ export function setFocusActiveCell(element, state, direction, info) {
855
878
  * It blur to the current activeCell, this operation imply multiple changes
856
879
  * - blur the activeCell
857
880
  * - update the tabindex to -1
858
- * @param {node} element - the custom element root `this.template`
881
+ * @param {node} template - the custom element root `this.template`
859
882
  * @param {object} state - datatable state
860
883
  */
861
- export function setBlurActiveCell(element, state) {
884
+ export function setBlurActiveCell(template, state) {
862
885
  if (state.activeCell) {
863
886
  const { rowIndex, colIndex } = getIndexesActiveCell(state);
887
+ let cellElement = getActiveCellElement(template, state);
888
+ state.activeCell.focused = false;
864
889
  // eslint-disable-next-line @lwc/lwc/no-async-operation
865
890
  setTimeout(() => {
866
- const cellElement = getCellElementByIndexes(
867
- element,
868
- rowIndex,
869
- colIndex,
870
- state
871
- );
872
- // we need to check because of the tree,
873
- // at this point it may remove/change the rows/keys because opening or closing a row.
891
+ // check cellElement; value may have changed
892
+ if (
893
+ !cellElement ||
894
+ !isValidCell(
895
+ state,
896
+ cellElement.rowKeyValue,
897
+ cellElement.colKeyValue
898
+ )
899
+ ) {
900
+ cellElement = getActiveCellElement(template, state);
901
+ }
874
902
  if (cellElement) {
875
903
  if (document.activeElement === cellElement) {
876
904
  cellElement.blur();
@@ -940,18 +968,12 @@ export function resetCellToFocusFromPrev(state) {
940
968
  /**
941
969
  * It adds and the focus classes to the th/td or div[role=gridcell/rowheader].
942
970
  *
943
- * @param {node} element - the custom element template `this.template`
971
+ * @param {node} template - the custom element template `this.template`
944
972
  * @param {object} state - datatable state
945
973
  */
946
- export function addFocusStylesToActiveCell(element, state) {
947
- const { rowIndex, colIndex } = getIndexesActiveCell(state);
948
-
949
- const cellElement = getCellElementByIndexes(
950
- element,
951
- rowIndex,
952
- colIndex,
953
- state
954
- );
974
+ export function addFocusStylesToActiveCell(template, state) {
975
+ const cellElement = getActiveCellElement(template, state);
976
+ state.activeCell.focused = true;
955
977
 
956
978
  if (cellElement) {
957
979
  cellElement.parentElement.classList.add(FOCUS_CLASS);
@@ -959,31 +981,22 @@ export function addFocusStylesToActiveCell(element, state) {
959
981
  }
960
982
 
961
983
  /**
962
- * It set the focus to the current activeCell, this operation imply multiple changes
984
+ * It set the focus to the row of current activeCell, this operation implies multiple changes
963
985
  * - update the tabindex of the activeCell
964
986
  * - set the current keyboard mode
965
- * - set the focus to the cell
966
- * @param {node} element - the custom element root `this.template`
987
+ * - set the focus to the row
988
+ * @param {node} template - the custom element root `this.template`
967
989
  * @param {object} state - datatable state
968
990
  */
969
- function setFocusActiveRow(element, state) {
991
+ function setFocusActiveRow(template, state) {
970
992
  const { rowIndex } = getIndexesActiveCell(state);
993
+ const row = getActiveCellRow(template, state);
971
994
 
972
995
  updateTabIndexRow(state, rowIndex);
973
996
  // eslint-disable-next-line @lwc/lwc/no-async-operation
974
997
  setTimeout(() => {
975
- const row = getRowElementByIndexes(element, rowIndex, state);
976
- row.focus();
977
-
978
- const scrollableY = element.querySelector('.slds-scrollable_y');
979
- const scrollingParent = scrollableY.parentElement;
980
- const parentRect = scrollingParent.getBoundingClientRect();
981
- const findMeRect = row.getBoundingClientRect();
982
- if (findMeRect.top < parentRect.top + TOP_MARGIN) {
983
- scrollableY.scrollTop -= SCROLL_OFFSET;
984
- } else if (findMeRect.bottom > parentRect.bottom - BOTTOM_MARGIN) {
985
- scrollableY.scrollTop += SCROLL_OFFSET;
986
- }
998
+ row.focus({ preventScroll: true });
999
+ updateScrollTop(state, template, row);
987
1000
  }, 0);
988
1001
  }
989
1002
 
@@ -991,15 +1004,15 @@ function setFocusActiveRow(element, state) {
991
1004
  * It blurs the active row, this operation implies multiple changes
992
1005
  * - blur the active row
993
1006
  * - update the tabindex to -1
994
- * @param {node} element - the custom element root `this.template`
1007
+ * @param {node} template - the custom element root `this.template`
995
1008
  * @param {object} state - datatable state
996
1009
  */
997
- function setBlurActiveRow(element, state) {
1010
+ function setBlurActiveRow(template, state) {
998
1011
  if (state.activeCell) {
999
1012
  const { rowIndex } = getIndexesActiveCell(state);
1000
1013
  // eslint-disable-next-line @lwc/lwc/no-async-operation
1001
1014
  setTimeout(() => {
1002
- const row = getRowElementByIndexes(element, rowIndex, state);
1015
+ const row = getActiveCellRow(template, state);
1003
1016
  if (document.activeElement === row) {
1004
1017
  row.blur();
1005
1018
  }
@@ -1017,13 +1030,7 @@ function setBlurActiveRow(element, state) {
1017
1030
  */
1018
1031
  export function refocusCellElement(template, state, needsRefocusOnCellElement) {
1019
1032
  if (needsRefocusOnCellElement) {
1020
- const { rowIndex, colIndex } = getIndexesActiveCell(state);
1021
- const cellElement = getCellElementByIndexes(
1022
- template,
1023
- rowIndex,
1024
- colIndex,
1025
- state
1026
- );
1033
+ const cellElement = getActiveCellElement(template, state);
1027
1034
  if (cellElement) {
1028
1035
  cellElement.parentElement.focus();
1029
1036
  }
@@ -1056,13 +1063,8 @@ export function handleDatatableFocusIn(event) {
1056
1063
  // workaround for delegatesFocus issue that focusin is called when not supposed to W-6220418
1057
1064
  if (isFocusInside(event.currentTarget)) {
1058
1065
  if (!state.rowMode && state.activeCell) {
1059
- const { rowIndex, colIndex } = getIndexesActiveCell(state);
1060
- const cellElement = getCellElementByIndexes(
1061
- this.template,
1062
- rowIndex,
1063
- colIndex,
1064
- state
1065
- );
1066
+ state.activeCell.focused = true;
1067
+ const cellElement = getActiveCellElement(this.template, state);
1066
1068
  // we need to check because of the tree,
1067
1069
  // at this point it may remove/change the rows/keys because opening or closing a row.
1068
1070
  if (cellElement) {
@@ -1090,13 +1092,7 @@ export function handleDatatableFocusOut(event) {
1090
1092
  state.isExitingActionMode)
1091
1093
  ) {
1092
1094
  if (state.activeCell && !state.rowMode) {
1093
- const { rowIndex, colIndex } = getIndexesActiveCell(state);
1094
- const cellElement = getCellElementByIndexes(
1095
- this.template,
1096
- rowIndex,
1097
- colIndex,
1098
- state
1099
- );
1095
+ const cellElement = getActiveCellElement(this.template, state);
1100
1096
  // we need to check because of the tree,
1101
1097
  // at this point it may remove/change the rows/keys because opening or closing a row.
1102
1098
  if (cellElement) {
@@ -1306,7 +1302,7 @@ export function updateRowNavigationMode(hadTreeDataTypePreviously, state) {
1306
1302
 
1307
1303
  /***************************** HELPER FUNCTIONS *****************************/
1308
1304
 
1309
- function isCellElement(tagName, role) {
1305
+ export function isCellElement(tagName, role) {
1310
1306
  return (
1311
1307
  SELECTORS.cell.default.includes(tagName) ||
1312
1308
  SELECTORS.cell.roleBased.includes(role)
@@ -1317,40 +1313,24 @@ function isHeaderRow(rowIndex) {
1317
1313
  return rowIndex === -1;
1318
1314
  }
1319
1315
 
1320
- function getHeaderRow(isRenderModeRoleBased) {
1321
- const selectors = SELECTORS.headerRow;
1322
- return isRenderModeRoleBased ? selectors.roleBased : selectors.default;
1316
+ function getDataRow(rowKeyValue) {
1317
+ return `[data-row-key-value="${rowKeyValue}"]`;
1323
1318
  }
1324
1319
 
1325
- function getDataRow(rowIndex, isRenderModeRoleBased) {
1326
- const dataRowRowGroupSelector = isRenderModeRoleBased
1327
- ? SELECTORS.dataRowRowGroup.roleBased
1328
- : SELECTORS.dataRowRowGroup.default;
1329
- return `${dataRowRowGroupSelector} > :nth-child(${rowIndex + 1})`;
1320
+ export function getCellElementByKeys(template, rowKeyValue, colKeyValue) {
1321
+ const selector = `${getDataRow(
1322
+ rowKeyValue
1323
+ )} [data-col-key-value="${colKeyValue}"] > :first-child`;
1324
+ return template.querySelector(selector);
1330
1325
  }
1331
1326
 
1332
- export function getCellElementByIndexes(element, rowIndex, colIndex, state) {
1333
- const isRenderModeRoleBased = state.renderModeRoleBased;
1334
- let selector = '';
1335
-
1336
- if (isHeaderRow(rowIndex)) {
1337
- selector = `${getHeaderRow(isRenderModeRoleBased)}
1338
- > :nth-child(${colIndex + 1}) > :first-child`;
1339
- return element.querySelector(selector);
1340
- }
1341
-
1342
- selector = `${getDataRow(rowIndex, isRenderModeRoleBased)}
1343
- > :nth-child(${colIndex + 1}) > :first-child`;
1344
- return element.querySelector(selector);
1345
- }
1346
-
1347
- function getRowElementByIndexes(element, rowIndex, state) {
1348
- const isRenderModeRoleBased = state.renderModeRoleBased;
1349
- if (isHeaderRow(rowIndex)) {
1350
- return element.querySelector(getHeaderRow(isRenderModeRoleBased));
1327
+ function getActiveCellRow(template, state) {
1328
+ if (state.activeCell) {
1329
+ const { rowKeyValue } = state.activeCell;
1330
+ const selector = getDataRow(rowKeyValue);
1331
+ return template.querySelector(selector);
1351
1332
  }
1352
-
1353
- return element.querySelector(getDataRow(rowIndex, isRenderModeRoleBased));
1333
+ return null;
1354
1334
  }
1355
1335
 
1356
1336
  export function getRowParent(state, rowLevel, rowIndex) {
@@ -1374,3 +1354,58 @@ function getCellFromIndexes(state, rowIndex, colIndex) {
1374
1354
  }
1375
1355
  return undefined;
1376
1356
  }
1357
+
1358
+ function updateScrollTop(state, template, element) {
1359
+ const scrollableY = template.querySelector('.slds-scrollable_y');
1360
+ const scrollingParent = scrollableY.parentElement;
1361
+ const parentRect = scrollingParent.getBoundingClientRect();
1362
+ const findMeRect = element.getBoundingClientRect();
1363
+ if (findMeRect.top < parentRect.top + TOP_MARGIN) {
1364
+ scrollableY.scrollTop -= SCROLL_OFFSET;
1365
+ } else if (findMeRect.bottom > parentRect.bottom - BOTTOM_MARGIN) {
1366
+ scrollableY.scrollTop += SCROLL_OFFSET;
1367
+ }
1368
+ setFirstVisibleIndex(state, scrollableY.scrollTop);
1369
+ }
1370
+
1371
+ function scrollToCell(state, template, rowIndex) {
1372
+ const { firstVisibleIndex, bufferSize, renderedRowCount, rowHeight } =
1373
+ state;
1374
+
1375
+ let scrollTop = rowIndex * rowHeight;
1376
+ if (firstVisibleIndex > rowIndex) {
1377
+ const rowsInViewport = renderedRowCount - 2 * bufferSize;
1378
+ scrollTop = Math.max(scrollTop - rowsInViewport * rowHeight, 0);
1379
+ }
1380
+
1381
+ const scrollableY = template.querySelector('.slds-scrollable_y');
1382
+ scrollableY.scrollTop = scrollTop;
1383
+ setFirstVisibleIndex(state, scrollTop);
1384
+ }
1385
+
1386
+ export function isActiveCellEditable(state) {
1387
+ const { activeCell, rows, columns } = state;
1388
+ if (activeCell) {
1389
+ const { rowIndex, colIndex } = getIndexesActiveCell(state);
1390
+ return isCellEditable(rows[rowIndex], columns[colIndex]);
1391
+ }
1392
+ return false;
1393
+ }
1394
+
1395
+ export function isValidCell(state, rowKeyValue, colKeyValue) {
1396
+ if (rowKeyValue === HEADER_ROW) {
1397
+ return state.headerIndexes[colKeyValue] !== undefined;
1398
+ }
1399
+ const row = getRowByKey(state, rowKeyValue);
1400
+ const colIndex = getStateColumnIndex(state, colKeyValue);
1401
+
1402
+ return row && row.cells[colIndex];
1403
+ }
1404
+
1405
+ function isActiveCellValid(state) {
1406
+ if (state.activeCell) {
1407
+ const { rowKeyValue, colKeyValue } = state.activeCell;
1408
+ return isValidCell(state, rowKeyValue, colKeyValue);
1409
+ }
1410
+ return false;
1411
+ }