lightning-base-components 1.13.6-alpha → 1.14.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 (191) hide show
  1. package/metadata/raptor.json +104 -2
  2. package/package.json +37 -1
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningAlert.defaultLabel.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningConfirm.defaultLabel.js +1 -0
  6. package/scopedImports/@salesforce-label-LightningDateTimePicker.selectDateFor.js +1 -0
  7. package/scopedImports/@salesforce-label-LightningInteractiveDialogBase.cancel.js +1 -0
  8. package/scopedImports/@salesforce-label-LightningInteractiveDialogBase.ok.js +1 -0
  9. package/scopedImports/@salesforce-label-LightningLookup.recentItems.js +1 -0
  10. package/scopedImports/@salesforce-label-LightningModalBase.close.js +1 -0
  11. package/scopedImports/@salesforce-label-LightningModalBase.waitstate.js +1 -0
  12. package/scopedImports/@salesforce-label-LightningPrompt.defaultLabel.js +1 -0
  13. package/src/lightning/baseCombobox/baseCombobox.html +50 -24
  14. package/src/lightning/baseCombobox/baseCombobox.js +53 -28
  15. package/src/lightning/card/card.html +7 -1
  16. package/src/lightning/card/card.js +30 -2
  17. package/src/lightning/card/utils.js +14 -0
  18. package/src/lightning/combobox/combobox.css +12 -0
  19. package/src/lightning/combobox/combobox.html +1 -0
  20. package/src/lightning/datatable/__docs__/datatable.md +40 -13
  21. package/src/lightning/datatable/columnWidthManager.js +8 -4
  22. package/src/lightning/datatable/columns-shared.js +8 -7
  23. package/src/lightning/datatable/columns.js +38 -4
  24. package/src/lightning/datatable/datatable.js +932 -727
  25. package/src/lightning/datatable/datatableResizeObserver.js +1 -1
  26. package/src/lightning/datatable/inlineEdit.js +15 -3
  27. package/src/lightning/datatable/keyboard.js +1078 -935
  28. package/src/lightning/datatable/resizer.js +92 -109
  29. package/src/lightning/datatable/rows.js +245 -59
  30. package/src/lightning/datatable/sort.js +83 -28
  31. package/src/lightning/datatable/{normalizer.js → state.js} +16 -28
  32. package/src/lightning/datatable/templates/div/div.css +53 -0
  33. package/src/lightning/datatable/templates/div/div.html +272 -0
  34. package/src/lightning/datatable/{datatable.css → templates/table/table.css} +0 -0
  35. package/src/lightning/datatable/templates/table/table.html +260 -0
  36. package/src/lightning/datatable/widthManagerShared.js +1 -1
  37. package/src/lightning/datepicker/datepicker.html +3 -3
  38. package/src/lightning/datepicker/datepicker.js +6 -2
  39. package/src/lightning/datetimepicker/datetimepicker.html +3 -4
  40. package/src/lightning/datetimepicker/datetimepicker.js +0 -2
  41. package/src/lightning/formattedRichText/__docs__/formattedRichText.md +1 -0
  42. package/src/lightning/helptext/helptext.js +8 -0
  43. package/src/lightning/iconSvgTemplates/buildTemplates/standard/asset_audit.html +7 -0
  44. package/src/lightning/iconSvgTemplates/buildTemplates/standard/attach.html +7 -0
  45. package/src/lightning/iconSvgTemplates/buildTemplates/standard/contract_payment.html +10 -0
  46. package/src/lightning/iconSvgTemplates/buildTemplates/standard/field_sales.html +8 -0
  47. package/src/lightning/iconSvgTemplates/buildTemplates/standard/historical_adherence.html +9 -0
  48. package/src/lightning/iconSvgTemplates/buildTemplates/standard/med_rec_recommendation.html +8 -0
  49. package/src/lightning/iconSvgTemplates/buildTemplates/standard/med_rec_statement_recommendation.html +7 -0
  50. package/src/lightning/iconSvgTemplates/buildTemplates/standard/medication_dispense.html +11 -0
  51. package/src/lightning/iconSvgTemplates/buildTemplates/standard/medication_reconciliation.html +7 -0
  52. package/src/lightning/iconSvgTemplates/buildTemplates/standard/report_type.html +9 -0
  53. package/src/lightning/iconSvgTemplates/buildTemplates/standard/story.html +2 -4
  54. package/src/lightning/iconSvgTemplates/buildTemplates/standard/tour.html +9 -0
  55. package/src/lightning/iconSvgTemplates/buildTemplates/standard/tour_check.html +8 -0
  56. package/src/lightning/iconSvgTemplates/buildTemplates/standard/travel_mode.html +2 -2
  57. package/src/lightning/iconSvgTemplates/buildTemplates/standard/unified_health_score.html +7 -0
  58. package/src/lightning/iconSvgTemplates/buildTemplates/standard/workforce_engagement.html +8 -0
  59. package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +26 -1
  60. package/src/lightning/iconSvgTemplates/buildTemplates/utility/asset_audit.html +9 -0
  61. package/src/lightning/iconSvgTemplates/buildTemplates/utility/collection_alt.html +8 -0
  62. package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_doc.html +8 -0
  63. package/src/lightning/iconSvgTemplates/buildTemplates/utility/contract_payment.html +10 -0
  64. package/src/lightning/iconSvgTemplates/buildTemplates/utility/einstein.html +2 -1
  65. package/src/lightning/iconSvgTemplates/buildTemplates/utility/entitlement.html +7 -0
  66. package/src/lightning/iconSvgTemplates/buildTemplates/utility/field_sales.html +8 -0
  67. package/src/lightning/iconSvgTemplates/buildTemplates/utility/signature.html +9 -0
  68. package/src/lightning/iconSvgTemplates/buildTemplates/utility/tour.html +9 -0
  69. package/src/lightning/iconSvgTemplates/buildTemplates/utility/tour_check.html +8 -0
  70. package/src/lightning/iconSvgTemplates/buildTemplates/utility/truck.html +10 -0
  71. package/src/lightning/iconSvgTemplates/buildTemplates/utility/workforce_engagement.html +8 -0
  72. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/asset_audit.html +7 -0
  73. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/attach.html +7 -0
  74. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/contract_payment.html +10 -0
  75. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/field_sales.html +8 -0
  76. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/historical_adherence.html +9 -0
  77. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/med_rec_recommendation.html +8 -0
  78. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/med_rec_statement_recommendation.html +7 -0
  79. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/medication_dispense.html +11 -0
  80. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/medication_reconciliation.html +7 -0
  81. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/report_type.html +9 -0
  82. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/story.html +2 -4
  83. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/tour.html +9 -0
  84. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/tour_check.html +8 -0
  85. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/travel_mode.html +2 -2
  86. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/unified_health_score.html +7 -0
  87. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/workforce_engagement.html +8 -0
  88. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +26 -1
  89. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/asset_audit.html +9 -0
  90. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/collection_alt.html +8 -0
  91. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_doc.html +8 -0
  92. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/contract_payment.html +10 -0
  93. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/einstein.html +2 -1
  94. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/entitlement.html +7 -0
  95. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/field_sales.html +8 -0
  96. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/signature.html +9 -0
  97. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/tour.html +9 -0
  98. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/tour_check.html +8 -0
  99. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/truck.html +10 -0
  100. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/workforce_engagement.html +8 -0
  101. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/asset_audit.html +7 -0
  102. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/attach.html +7 -0
  103. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/contract_payment.html +10 -0
  104. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/field_sales.html +8 -0
  105. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/historical_adherence.html +9 -0
  106. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/med_rec_recommendation.html +8 -0
  107. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/med_rec_statement_recommendation.html +7 -0
  108. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/medication_dispense.html +11 -0
  109. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/medication_reconciliation.html +7 -0
  110. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/report_type.html +9 -0
  111. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/story.html +2 -4
  112. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/tour.html +9 -0
  113. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/tour_check.html +8 -0
  114. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/travel_mode.html +2 -2
  115. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/unified_health_score.html +7 -0
  116. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/workforce_engagement.html +8 -0
  117. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/templates.js +15 -1
  118. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/asset_audit.html +7 -0
  119. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/attach.html +7 -0
  120. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/contract_payment.html +10 -0
  121. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/field_sales.html +8 -0
  122. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/historical_adherence.html +9 -0
  123. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/med_rec_recommendation.html +8 -0
  124. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/med_rec_statement_recommendation.html +7 -0
  125. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/medication_dispense.html +11 -0
  126. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/medication_reconciliation.html +7 -0
  127. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/report_type.html +9 -0
  128. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/story.html +2 -4
  129. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/tour.html +9 -0
  130. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/tour_check.html +8 -0
  131. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/travel_mode.html +2 -2
  132. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/unified_health_score.html +7 -0
  133. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/workforce_engagement.html +8 -0
  134. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/templates.js +15 -1
  135. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +12 -1
  136. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/asset_audit.html +9 -0
  137. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/collection_alt.html +8 -0
  138. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_doc.html +8 -0
  139. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/contract_payment.html +10 -0
  140. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/einstein.html +2 -1
  141. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/entitlement.html +7 -0
  142. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/field_sales.html +8 -0
  143. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/signature.html +9 -0
  144. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/tour.html +9 -0
  145. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/tour_check.html +8 -0
  146. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/truck.html +10 -0
  147. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/workforce_engagement.html +8 -0
  148. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +12 -1
  149. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/asset_audit.html +9 -0
  150. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/collection_alt.html +8 -0
  151. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_doc.html +8 -0
  152. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/contract_payment.html +10 -0
  153. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/einstein.html +2 -1
  154. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/entitlement.html +7 -0
  155. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/field_sales.html +8 -0
  156. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/signature.html +9 -0
  157. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/tour.html +9 -0
  158. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/tour_check.html +8 -0
  159. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/truck.html +10 -0
  160. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/workforce_engagement.html +8 -0
  161. package/src/lightning/input/__docs__/input.md +4 -0
  162. package/src/lightning/input/input.html +0 -1
  163. package/src/lightning/input/input.js +31 -30
  164. package/src/lightning/pill/link.html +1 -1
  165. package/src/lightning/pill/pill.js +18 -0
  166. package/src/lightning/pill/plainLink.html +2 -0
  167. package/src/lightning/pillContainer/barePillContainer.html +5 -5
  168. package/src/lightning/pillContainer/pillContainer.js +5 -4
  169. package/src/lightning/pillContainer/standardPillContainer.html +5 -5
  170. package/src/lightning/positionLibrary/__component__/positionLibraryBounding.spec.js +8 -6
  171. package/src/lightning/positionLibrary/__component__/x/bounding/bounding.html +1 -1
  172. package/src/lightning/positionLibrary/__component__/x/bounding/bounding.js +6 -9
  173. package/src/lightning/positionLibrary/direction.js +17 -5
  174. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +20 -0
  175. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +10 -0
  176. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +17 -3
  177. package/src/lightning/primitiveDatatableTooltip/primitiveDatatableTooltip.js +1 -0
  178. package/src/lightning/primitiveHeaderFactory/nonsortableHeader.html +5 -4
  179. package/src/lightning/primitiveHeaderFactory/primitiveHeaderFactory.js +255 -94
  180. package/src/lightning/primitiveHeaderFactory/selectableHeader.html +25 -23
  181. package/src/lightning/primitiveHeaderFactory/sortableHeader.html +13 -9
  182. package/src/lightning/progressIndicator/progressIndicator.js +30 -9
  183. package/src/lightning/progressRing/progressRing.html +6 -0
  184. package/src/lightning/progressRing/progressRing.js +98 -3
  185. package/src/lightning/progressStep/progressStep.js +6 -3
  186. package/src/lightning/timepicker/timepicker.html +1 -0
  187. package/src/lightning/utilsPrivate/aria.js +30 -0
  188. package/src/lightning/utilsPrivate/utilsPrivate.js +12 -2
  189. package/src/lightning/datatable/datatable.html +0 -237
  190. package/src/lightning/datatable/keys.js +0 -32
  191. package/src/lightning/utilsPrivate/contentMutation.js +0 -273
@@ -1,5 +1,6 @@
1
1
  import { LightningElement, api, track, unwrap } from 'lwc';
2
- import datatableTpl from './datatable.html';
2
+ import tableTemplate from './templates/table/table.html';
3
+ import divTemplate from './templates/div/div.html';
3
4
  import { classSet } from 'lightning/utils';
4
5
  import {
5
6
  normalizeBoolean,
@@ -10,7 +11,8 @@ import {
10
11
  } from 'lightning/utilsPrivate';
11
12
  import { LightningDatatableResizeObserver } from './datatableResizeObserver';
12
13
  import { ColumnWidthManager } from './columnWidthManager';
13
- import { generateHeaderIndexes, getDefaultState } from './normalizer';
14
+ import { getDefaultState } from './state';
15
+ import { getColumns, normalizeColumns, generateHeaderIndexes } from './columns';
14
16
  import {
15
17
  setData,
16
18
  getData,
@@ -18,6 +20,8 @@ import {
18
20
  setKeyField,
19
21
  getKeyField,
20
22
  hasValidKeyField,
23
+ updateCellClassForRoleBasedMode,
24
+ recomputeCellStyles,
21
25
  } from './rows';
22
26
  import {
23
27
  isResizeColumnDisabled,
@@ -31,7 +35,7 @@ import {
31
35
  getColumnsWidths,
32
36
  resizeColumnWithDelta,
33
37
  getCustomerColumnWidths,
34
- getTableWidthStyle,
38
+ getCSSWidthStyleOfTable,
35
39
  updateColumnWidthsMetadata,
36
40
  getResizerDefaultState,
37
41
  } from './resizer';
@@ -52,7 +56,7 @@ import {
52
56
  } from './selector';
53
57
  import {
54
58
  syncActiveCell,
55
- handleCellKeydown,
59
+ handleKeydownOnCell,
56
60
  updateActiveCell,
57
61
  setBlurActiveCell,
58
62
  setFocusActiveCell,
@@ -63,7 +67,7 @@ import {
63
67
  updateTabIndexActiveRow,
64
68
  unsetRowNavigationMode,
65
69
  updateRowNavigationMode,
66
- handleDatatableLosedFocus,
70
+ handleDatatableFocusOut,
67
71
  handleDatatableFocusIn,
68
72
  updateTabIndexRow,
69
73
  getIndexesActiveCell,
@@ -73,7 +77,7 @@ import {
73
77
  resetCellToFocusFromPrev,
74
78
  datatableHasFocus,
75
79
  setCellClickedForFocus,
76
- handleKeyDown,
80
+ handleKeydownOnTable,
77
81
  addFocusStylesToActiveCell,
78
82
  refocusCellElement,
79
83
  } from './keyboard';
@@ -83,7 +87,6 @@ import {
83
87
  hasRowNumberColumn,
84
88
  setShowRowNumberColumn,
85
89
  } from './rowNumber';
86
- import { getColumns, normalizeColumns } from './columns';
87
90
  import {
88
91
  handleLoadMoreCheck,
89
92
  isInfiniteLoadingEnabled,
@@ -142,8 +145,6 @@ import DatatableTypes from './types';
142
145
  import labelAriaLiveNavigationMode from '@salesforce/label/LightningDatatable.ariaLiveNavigationMode';
143
146
  import labelAriaLiveActionMode from '@salesforce/label/LightningDatatable.ariaLiveActionMode';
144
147
 
145
- const typesMap = new WeakMap();
146
-
147
148
  const i18n = {
148
149
  ariaLiveNavigationMode: labelAriaLiveNavigationMode,
149
150
  ariaLiveActionMode: labelAriaLiveActionMode,
@@ -153,53 +154,87 @@ const i18n = {
153
154
  * A table that displays rows and columns of data.
154
155
  */
155
156
  export default class LightningDatatable extends LightningElement {
156
- privateSuppressBottomBar = false;
157
+ /**
158
+ * Usage of State Object vs Private Variable:
159
+ * This is by no means a definitive set of rules and we should add/modify these
160
+ * guidelines with time as we work on the datatable and find specific reasons.
161
+ *
162
+ * In general, the main reason for using the `state` object is to take advantage
163
+ * of LWC's reactivity. In the `state` object we store properties that are required
164
+ * to trigger an update/re-render of the datatable.
165
+ *
166
+ * There are no observable perf implications of using the state object vs
167
+ * private variables. See W-10006095 for details.
168
+ *
169
+ * Guidelines:
170
+ * 1. If possible, avoid adding properties to the state object if it does not
171
+ * trigger an update or re-render of the datatable.
172
+ * 2. You may look to add properties to the state object if that property is
173
+ * required in different areas of the datatable and/or if not adding to the
174
+ * state will significantly add to the complexity of the component.
175
+ * 3. Goal: Breakdown the 'state' object - Right now the state object contains a
176
+ * lot of metadata and we pass around the monolith everywhere even when most
177
+ * of the information is not required. If you have the opportunity, look to
178
+ * separate or break down the state object even if that means adding a new
179
+ * tracked object. This will help us logically separate various modules over
180
+ * time and pass around only necessary information leading to cleaner,
181
+ * more readable and organized code.
182
+ */
183
+
184
+ // Tracked Objects
157
185
  @track state = getDefaultState();
186
+ @track widthsData = getResizerDefaultState();
158
187
 
188
+ // Private Variables
189
+ _actionsMinHeightStyle = ''; // Min height required while actions menu is opened
159
190
  _columns = [];
160
- _hideCheckboxColumn = false;
161
- _draftValues = [];
162
- customerSelectedRows = null;
163
- privateDatatableId = generateUniqueId('lgt-datatable');
164
191
  _columnWidthsMode = 'fixed';
165
- @track widthsData = getResizerDefaultState();
192
+ _customerSelectedRows = null;
193
+ _datatableId = generateUniqueId('lgt-datatable');
194
+ _draftValues = [];
195
+ _isResizing = false; // Whether resizing is in progress
196
+ _privateTypes = {};
197
+ _privateWidthObserver = null; // Instance of LightningDatatableResizeObserver
198
+ _renderMode = 'table';
166
199
  _renderedRowCount = 0;
200
+ _suppressBottomBar = false;
201
+
202
+ /************************* PUBLIC PROPERTIES *************************/
167
203
 
168
204
  /**
169
205
  * Public property for passing `aria-label` down to the child table element.
170
206
  */
171
- @track ariaLabel = null;
207
+ ariaLabel = null;
172
208
 
173
209
  /**
174
210
  * Public property for passing `aria-labelledby` down to the child table element.
175
211
  */
176
- @track ariaLabelledBy = null;
177
-
178
- // Whether resizing is in progress
179
- _isResizing = false;
180
-
181
- constructor() {
182
- super();
183
- if (!typesMap.has(this.constructor)) {
184
- const privateTypes = new DatatableTypes(
185
- this.constructor.customTypes
186
- );
187
- typesMap.set(this.constructor, privateTypes);
188
- }
189
- this._columnWidthManager = new ColumnWidthManager(this.widthsData);
190
- this.updateRowsAndCellIndexes = updateRowsAndCellIndexes.bind(this);
212
+ ariaLabelledBy = null;
191
213
 
192
- this._renderManager = new RenderManager();
214
+ /**
215
+ * Specifies how column widths are calculated. Set to 'fixed' for columns with equal widths.
216
+ * Set to 'auto' for column widths that are based on the width of the column content and the table width. The default is 'fixed'.
217
+ * @type {string}
218
+ * @default fixed
219
+ */
220
+ @api
221
+ get columnWidthsMode() {
222
+ return this.widthsData.columnWidthsMode;
193
223
  }
194
224
 
195
- get privateTypes() {
196
- return typesMap.get(this.constructor);
197
- }
225
+ set columnWidthsMode(value) {
226
+ const normalizedValue = normalizeString(value, {
227
+ fallbackValue: 'fixed',
228
+ validValues: ['fixed', 'auto'],
229
+ });
230
+ this._columnWidthManager.columnWidthMode = normalizedValue;
198
231
 
199
- set columns(value) {
200
- this._columns = Array.isArray(value) ? value : [];
201
- this.updateColumns(this._columns);
202
- this._columnWidthManager.handleColumnsChange(getColumns(this.state));
232
+ const { state, widthsData } = this;
233
+ if (widthsData.columnWidthsMode !== normalizedValue) {
234
+ this._columnWidthManager.handleWidthModeChange(getColumns(state));
235
+ }
236
+
237
+ widthsData.columnWidthsMode = normalizedValue;
203
238
  }
204
239
 
205
240
  /**
@@ -213,22 +248,20 @@ export default class LightningDatatable extends LightningElement {
213
248
  return this._columns;
214
249
  }
215
250
 
251
+ set columns(value) {
252
+ this._columns = Array.isArray(value) ? value : [];
253
+ this.updateColumns(this._columns);
254
+ this._columnWidthManager.handleColumnsChange(getColumns(this.state));
255
+ }
256
+
216
257
  /**
217
- * This value specifies the number of lines after which the
218
- * content will be cut off and hidden. It must be at least 1 or more.
219
- * The text in the last line is truncated and shown with an ellipsis.
220
- * @type {integer}
258
+ * The array of data to be displayed.
259
+ * @type {array}
221
260
  */
222
261
  @api
223
- get wrapTextMaxLines() {
224
- return this.state.wrapTextMaxLines;
225
- }
226
-
227
- set wrapTextMaxLines(value) {
228
- const { state } = this;
229
- setWrapTextMaxLines(state, value);
230
- this._columnWidthManager.wrapTextMaxLines = state.wrapTextMaxLines;
231
- this.updateRowsAndCellIndexes(this.state);
262
+ // eslint-disable-next-line @lwc/lwc/valid-api
263
+ get data() {
264
+ return getData(this.state);
232
265
  }
233
266
 
234
267
  set data(value) {
@@ -245,52 +278,75 @@ export default class LightningDatatable extends LightningElement {
245
278
  if (hasValidKeyField(this.state)) {
246
279
  this.updateRowsState();
247
280
  }
248
- if (this.customerSelectedRows) {
249
- this.setSelectedRows(this.customerSelectedRows);
281
+ if (this._customerSelectedRows) {
282
+ this.setSelectedRows(this._customerSelectedRows);
250
283
  }
251
284
  }
252
285
 
253
286
  /**
254
- * The array of data to be displayed.
255
- * @type {array}
287
+ * Specifies the default sorting direction on an unsorted column.
288
+ * Valid options include 'asc' and 'desc'.
289
+ * The default is 'asc' for sorting in ascending order.
290
+ * @type {string}
291
+ * @default asc
256
292
  */
257
293
  @api
258
- // eslint-disable-next-line @lwc/lwc/valid-api
259
- get data() {
260
- return getData(this.state);
294
+ get defaultSortDirection() {
295
+ return getDefaultSortDirection(this.state);
261
296
  }
262
297
 
263
- set keyField(value) {
264
- setKeyField(this.state, value);
265
- setDirtyValues(this.state, this._draftValues);
266
- this.updateRowsState();
298
+ set defaultSortDirection(value) {
299
+ setDefaultSortDirection(this.state, value);
300
+ updateSorting(this.state);
267
301
  }
268
302
 
269
303
  /**
270
- * Required for better performance.
271
- * Associates each row with a unique ID.
272
- * @type {string}
273
- * @required
304
+ * The current values per row that are provided during inline edit.
305
+ * @type {object}
274
306
  */
275
307
  @api
276
- get keyField() {
277
- return getKeyField(this.state);
308
+ get draftValues() {
309
+ return getDirtyValues(this.state);
278
310
  }
279
311
 
280
- set hideCheckboxColumn(value) {
281
- const { state } = this;
282
- const normalizedValue = normalizeBoolean(value);
283
- this._hideCheckboxColumn = normalizedValue;
312
+ set draftValues(value) {
313
+ this._draftValues = value;
314
+ setDirtyValues(this.state, value);
284
315
 
285
- this._columnWidthManager.handleCheckboxColumnChange(
286
- state.hideCheckboxColumn,
287
- normalizedValue,
288
- getColumns(state)
289
- );
316
+ if (hasValidKeyField(this.state)) {
317
+ this.updateRowsAndCellIndexes(this.state);
318
+ }
319
+ }
290
320
 
291
- this.state.hideCheckboxColumn = normalizeBoolean(value);
292
- // update the columns metadata again to update the status.
293
- this.updateColumns(this._columns);
321
+ /**
322
+ * If present, you can load a subset of data and then display more
323
+ * when users scroll to the end of the table.
324
+ * Use with the onloadmore event handler to retrieve more data.
325
+ * @type {boolean}
326
+ * @default false
327
+ */
328
+ @api
329
+ get enableInfiniteLoading() {
330
+ return isInfiniteLoadingEnabled(this.state);
331
+ }
332
+
333
+ set enableInfiniteLoading(value) {
334
+ setInfiniteLoading(this.state, value);
335
+ }
336
+
337
+ /**
338
+ * Specifies an object containing information about cell level, row level, and table level errors.
339
+ * When it's set, error messages are displayed on the table accordingly.
340
+ * @type {object}
341
+ */
342
+ @api
343
+ get errors() {
344
+ return getErrors(this.state);
345
+ }
346
+
347
+ set errors(value) {
348
+ setErrors(this.state, value);
349
+ this.updateRowsState();
294
350
  }
295
351
 
296
352
  /**
@@ -300,85 +356,83 @@ export default class LightningDatatable extends LightningElement {
300
356
  */
301
357
  @api
302
358
  get hideCheckboxColumn() {
303
- return this._hideCheckboxColumn;
359
+ return this.state.hideCheckboxColumn;
304
360
  }
305
361
 
306
- set showRowNumberColumn(value) {
362
+ set hideCheckboxColumn(value) {
307
363
  const { state } = this;
364
+ const normalizedValue = normalizeBoolean(value);
308
365
 
309
- this._columnWidthManager.handleRowNumberColumnChange(
310
- getRowNumberOffset(state),
311
- value,
366
+ this._columnWidthManager.handleCheckboxColumnChange(
367
+ state.hideCheckboxColumn,
368
+ normalizedValue,
312
369
  getColumns(state)
313
370
  );
314
371
 
315
- setShowRowNumberColumn(state, value);
372
+ this.state.hideCheckboxColumn = normalizedValue;
373
+ // update the columns metadata again to update the status.
316
374
  this.updateColumns(this._columns);
317
375
  }
318
376
 
319
377
  /**
320
- * If present, the row numbers are shown in the first column.
378
+ * If present, the table header is hidden.
321
379
  * @type {boolean}
322
380
  * @default false
323
381
  */
324
382
  @api
325
- get showRowNumberColumn() {
326
- return hasRowNumberColumn(this.state);
383
+ get hideTableHeader() {
384
+ return this.state.hideTableHeader;
327
385
  }
328
386
 
329
- set rowNumberOffset(value) {
330
- const { state, widthsData } = this;
331
- setRowNumberOffset(state, value);
332
-
333
- this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
387
+ set hideTableHeader(value) {
388
+ this.state.hideTableHeader = normalizeBoolean(value);
334
389
  }
335
390
 
336
391
  /**
337
- * Determines where to start counting the row number.
338
- * The default is 0.
339
- * @type {number}
340
- * @default 0
392
+ * If present, a spinner is shown to indicate that more data is loading.
393
+ * @type {boolean}
394
+ * @default false
341
395
  */
342
396
  @api
343
- get rowNumberOffset() {
344
- return getRowNumberOffset(this.state);
397
+ get isLoading() {
398
+ return isLoading(this.state);
345
399
  }
346
400
 
347
- set resizeColumnDisabled(value) {
348
- setResizeColumnDisabled(this.widthsData, value);
401
+ set isLoading(value) {
402
+ setLoading(this.state, value);
349
403
  }
350
404
 
351
405
  /**
352
- * If present, column resizing is disabled.
353
- * @type {boolean}
354
- * @default false
406
+ * Required for better performance.
407
+ * Associates each row with a unique ID.
408
+ * @type {string}
409
+ * @required
355
410
  */
356
411
  @api
357
- get resizeColumnDisabled() {
358
- return isResizeColumnDisabled(this.widthsData);
412
+ get keyField() {
413
+ return getKeyField(this.state);
359
414
  }
360
415
 
361
- set minColumnWidth(value) {
362
- const { state, widthsData } = this;
363
- setMinColumnWidth(state, widthsData, value);
364
- this._columnWidthManager.minColumnWidth = this.minColumnWidth;
416
+ set keyField(value) {
417
+ setKeyField(this.state, value);
418
+ setDirtyValues(this.state, this._draftValues);
419
+ this.updateRowsState();
365
420
  }
366
421
 
367
422
  /**
368
- * The minimum width for all columns.
369
- * The default is 50px.
423
+ * Determines when to trigger infinite loading based on
424
+ * how many pixels the table's scroll position is from the bottom of the table.
425
+ * The default is 20.
370
426
  * @type {number}
371
- * @default 50px
427
+ * @default 20
372
428
  */
373
429
  @api
374
- get minColumnWidth() {
375
- return getMinColumnWidth(this.widthsData);
430
+ get loadMoreOffset() {
431
+ return getLoadMoreOffset(this.state);
376
432
  }
377
433
 
378
- set maxColumnWidth(value) {
379
- const { state, widthsData } = this;
380
- setMaxColumnWidth(state, widthsData, value);
381
- this._columnWidthManager.maxColumnWidth = this.maxColumnWidth;
434
+ set loadMoreOffset(value) {
435
+ setLoadMoreOffset(this.state, value);
382
436
  }
383
437
 
384
438
  /**
@@ -392,175 +446,144 @@ export default class LightningDatatable extends LightningElement {
392
446
  return getMaxColumnWidth(this.widthsData);
393
447
  }
394
448
 
395
- set resizeStep(value) {
396
- setResizeStep(this.widthsData, value);
449
+ set maxColumnWidth(value) {
450
+ const { state, widthsData } = this;
451
+ setMaxColumnWidth(getColumns(state), widthsData, value);
452
+ this._columnWidthManager.maxColumnWidth = this.maxColumnWidth;
397
453
  }
398
454
 
399
455
  /**
400
- * The width to resize the column when a user presses left or right arrow.
401
- * The default is 10px.
456
+ * The maximum number of rows that can be selected.
457
+ * Checkboxes are used for selection by default,
458
+ * and radio buttons are used when maxRowSelection is 1.
402
459
  * @type {number}
403
- * @default 10px
404
460
  */
405
461
  @api
406
- get resizeStep() {
407
- return getResizeStep(this.widthsData);
462
+ get maxRowSelection() {
463
+ return getMaxRowSelection(this.state);
408
464
  }
409
465
 
410
- set sortedBy(value) {
411
- setSortedBy(this.state, value);
412
- updateSorting(this.state);
466
+ set maxRowSelection(value) {
467
+ const previousSelectionLength = getCurrentSelectionLength(this.state);
468
+ setMaxRowSelection(this.state, value);
469
+ if (previousSelectionLength > 0) {
470
+ this.fireSelectedRowsChange(this.getSelectedRows());
471
+ }
413
472
  }
414
473
 
415
474
  /**
416
- * The column key or fieldName that controls the sorting order.
417
- * Sort the data using the onsort event handler.
418
- * @type {string}
475
+ * The minimum width for all columns.
476
+ * The default is 50px.
477
+ * @type {number}
478
+ * @default 50px
419
479
  */
420
480
  @api
421
- get sortedBy() {
422
- return getSortedBy(this.state);
423
- }
424
-
425
- set sortedDirection(value) {
426
- setSortedDirection(this.state, value);
427
- updateSorting(this.state);
481
+ get minColumnWidth() {
482
+ return getMinColumnWidth(this.widthsData);
428
483
  }
429
484
 
430
- /**
431
- * Specifies the sorting direction.
432
- * Sort the data using the onsort event handler.
433
- * Valid options include 'asc' and 'desc'.
434
- * @type {string}
435
- */
436
- @api
437
- get sortedDirection() {
438
- return getSortedDirection(this.state);
439
- }
440
-
441
- set defaultSortDirection(value) {
442
- setDefaultSortDirection(this.state, value);
443
- updateSorting(this.state);
485
+ set minColumnWidth(value) {
486
+ const { state, widthsData } = this;
487
+ setMinColumnWidth(getColumns(state), widthsData, value);
488
+ this._columnWidthManager.minColumnWidth = this.minColumnWidth;
444
489
  }
445
490
 
446
491
  /**
447
- * Specifies the default sorting direction on an unsorted column.
448
- * Valid options include 'asc' and 'desc'.
449
- * The default is 'asc' for sorting in ascending order.
450
- * @type {string}
451
- * @default asc
492
+ * @typedef RenderManagerConfig
493
+ * @type {object}
494
+ * @property {boolean} viewportRendering - Specifies whether to defer rendering of rows outside the viewport until the user begins scrolling. To use this feature, create a fixed-height container element for lightning-datatable.
495
+ * @property {number} rowHeight - Specifies the height of a row, in px
452
496
  */
453
- @api
454
- get defaultSortDirection() {
455
- return getDefaultSortDirection(this.state);
456
- }
457
-
458
- set enableInfiniteLoading(value) {
459
- setInfiniteLoading(this.state, value);
460
- }
461
497
 
462
498
  /**
463
- * If present, you can load a subset of data and then display more
464
- * when users scroll to the end of the table.
465
- * Use with the onloadmore event handler to retrieve more data.
466
- * @type {boolean}
467
- * @default false
499
+ * Reserved for internal use.
500
+ * Enables and configures advanced rendering modes.
501
+ *
502
+ * @type {RenderManagerConfig} value - config object for datatable rendering
468
503
  */
469
504
  @api
470
- get enableInfiniteLoading() {
471
- return isInfiniteLoadingEnabled(this.state);
505
+ get renderConfig() {
506
+ return this._renderConfig;
472
507
  }
473
508
 
474
- set loadMoreOffset(value) {
475
- setLoadMoreOffset(this.state, value);
509
+ set renderConfig(value) {
510
+ if (typeof value === 'object' && !isIE11) {
511
+ const enableViewportRendering = value.viewportRendering;
512
+ setViewportRendering(this.state, enableViewportRendering);
513
+
514
+ this._renderManager.configure(this, value);
515
+ this._renderConfig = value;
516
+ }
476
517
  }
477
518
 
478
519
  /**
479
- * Determines when to trigger infinite loading based on
480
- * how many pixels the table's scroll position is from the bottom of the table.
481
- * The default is 20.
482
- * @type {number}
483
- * @default 20
520
+ * Allows developer to opt-in to a role-based table.
521
+ * Allowed options - "role-based" or "default"
522
+ * `role-based` -> Renders <div>
523
+ * `default` -> Renders <table>
524
+ */
525
+ /**
526
+ * Reserved for internal use.
484
527
  */
485
528
  @api
486
- get loadMoreOffset() {
487
- return getLoadMoreOffset(this.state);
529
+ get renderMode() {
530
+ return this._renderMode;
488
531
  }
489
532
 
490
- set isLoading(value) {
491
- setLoading(this.state, value);
533
+ set renderMode(value) {
534
+ this._renderMode = normalizeString(value, {
535
+ fallbackValue: 'default',
536
+ validValues: ['default', 'role-based'],
537
+ });
538
+ this.state.renderModeRoleBased = this._renderMode === 'role-based';
539
+ updateCellClassForRoleBasedMode(this.state);
492
540
  }
493
541
 
494
542
  /**
495
- * If present, a spinner is shown to indicate that more data is loading.
543
+ * If present, column resizing is disabled.
496
544
  * @type {boolean}
497
545
  * @default false
498
546
  */
499
547
  @api
500
- get isLoading() {
501
- return isLoading(this.state);
548
+ get resizeColumnDisabled() {
549
+ return isResizeColumnDisabled(this.widthsData);
502
550
  }
503
551
 
504
- set renderConfig(value) {
505
- if (typeof value === 'object' && !isIE11) {
506
- const enableViewportRendering = value.viewportRendering;
507
- setViewportRendering(this.state, enableViewportRendering);
508
-
509
- this._renderManager.configure(this, value);
510
- this._renderConfig = value;
511
- }
552
+ set resizeColumnDisabled(value) {
553
+ setResizeColumnDisabled(this.widthsData, value);
512
554
  }
513
555
 
514
556
  /**
515
- * @typedef RenderManagerConfig
516
- * @type {object}
517
- * @property {boolean} viewportRendering - Specifies whether to defer rendering of rows outside the viewport until the user begins scrolling. To use this feature, create a fixed-height container element for lightning-datatable.
518
- * @property {number} rowHeight - Specifies the height of a row, in px
519
- */
520
-
521
- /**
522
- * Reserved for internal use.
523
- * Enables and configures advanced rendering modes.
524
- *
525
- * @type {RenderManagerConfig} value - config object for datatable rendering
557
+ * The width to resize the column when a user presses left or right arrow.
558
+ * The default is 10px.
559
+ * @type {number}
560
+ * @default 10px
526
561
  */
527
562
  @api
528
- get renderConfig() {
529
- return this._renderConfig;
530
- }
531
-
532
- get viewportRendering() {
533
- return isViewportRenderingEnabled(this.state);
534
- }
535
-
536
- get renderedRows() {
537
- if (this.viewportRendering && !isIE11) {
538
- return this.state.rows.slice(0, this._renderedRowCount);
539
- }
540
- return this.state.rows;
563
+ get resizeStep() {
564
+ return getResizeStep(this.widthsData);
541
565
  }
542
566
 
543
- set maxRowSelection(value) {
544
- const previousSelectionLenght = getCurrentSelectionLength(this.state);
545
- setMaxRowSelection(this.state, value);
546
- if (previousSelectionLenght > 0) {
547
- this.fireSelectedRowsChange(this.getSelectedRows());
548
- }
567
+ set resizeStep(value) {
568
+ setResizeStep(this.widthsData, value);
549
569
  }
550
570
 
551
571
  /**
552
- * The maximum number of rows that can be selected.
553
- * Checkboxes are used for selection by default,
554
- * and radio buttons are used when maxRowSelection is 1.
572
+ * Determines where to start counting the row number.
573
+ * The default is 0.
555
574
  * @type {number}
575
+ * @default 0
556
576
  */
557
577
  @api
558
- get maxRowSelection() {
559
- return getMaxRowSelection(this.state);
578
+ get rowNumberOffset() {
579
+ return getRowNumberOffset(this.state);
560
580
  }
561
- set selectedRows(value) {
562
- this.customerSelectedRows = value;
563
- this.setSelectedRows(value);
581
+
582
+ set rowNumberOffset(value) {
583
+ const { state, widthsData } = this;
584
+ setRowNumberOffset(state, value);
585
+
586
+ this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
564
587
  }
565
588
 
566
589
  /**
@@ -572,66 +595,63 @@ export default class LightningDatatable extends LightningElement {
572
595
  return getSelectedRowsKeys(this.state);
573
596
  }
574
597
 
575
- set errors(value) {
576
- setErrors(this.state, value);
577
- this.updateRowsState();
598
+ set selectedRows(value) {
599
+ this._customerSelectedRows = value;
600
+ this.setSelectedRows(value);
578
601
  }
579
602
 
580
603
  /**
581
- * Specifies an object containing information about cell level, row level, and table level errors.
582
- * When it's set, error messages are displayed on the table accordingly.
583
- * @type {object}
604
+ * If present, the row numbers are shown in the first column.
605
+ * @type {boolean}
606
+ * @default false
584
607
  */
585
608
  @api
586
- get errors() {
587
- return getErrors(this.state);
609
+ get showRowNumberColumn() {
610
+ return hasRowNumberColumn(this.state);
588
611
  }
589
612
 
590
- /**
591
- * The current values per row that are provided during inline edit.
592
- * @type {object}
593
- */
594
- @api
595
- get draftValues() {
596
- return getDirtyValues(this.state);
597
- }
613
+ set showRowNumberColumn(value) {
614
+ const { state, _columns } = this;
598
615
 
599
- set draftValues(value) {
600
- this._draftValues = value;
601
- setDirtyValues(this.state, value);
616
+ this._columnWidthManager.handleRowNumberColumnChange(
617
+ getRowNumberOffset(state),
618
+ value,
619
+ getColumns(state)
620
+ );
602
621
 
603
- if (hasValidKeyField(this.state)) {
604
- this.updateRowsAndCellIndexes(this.state);
605
- }
622
+ setShowRowNumberColumn(state, value);
623
+ this.updateColumns(_columns);
606
624
  }
607
625
 
608
626
  /**
609
- * If present, the table header is hidden.
610
- * @type {boolean}
611
- * @default false
627
+ * The column key or fieldName that controls the sorting order.
628
+ * Sort the data using the onsort event handler.
629
+ * @type {string}
612
630
  */
613
631
  @api
614
- get hideTableHeader() {
615
- return this.state.hideTableHeader;
632
+ get sortedBy() {
633
+ return getSortedBy(this.state);
616
634
  }
617
635
 
618
- set hideTableHeader(value) {
619
- this.state.hideTableHeader = !!value;
636
+ set sortedBy(value) {
637
+ setSortedBy(this.state, value);
638
+ updateSorting(this.state);
620
639
  }
621
640
 
622
- get hasValidKeyField() {
623
- if (hasValidKeyField(this.state)) {
624
- return true;
625
- }
626
- // eslint-disable-next-line no-console
627
- console.error(
628
- `The "keyField" is a required attribute of lightning:datatable.`
629
- );
630
- return false;
641
+ /**
642
+ * Specifies the sorting direction.
643
+ * Sort the data using the onsort event handler.
644
+ * Valid options include 'asc' and 'desc'.
645
+ * @type {string}
646
+ */
647
+ @api
648
+ get sortedDirection() {
649
+ return getSortedDirection(this.state);
631
650
  }
632
651
 
633
- get showSelectAllCheckbox() {
634
- return !getHideSelectAllCheckbox(this.state);
652
+ set sortedDirection(value) {
653
+ setSortedDirection(this.state, value);
654
+ updateSorting(this.state);
635
655
  }
636
656
 
637
657
  /**
@@ -641,50 +661,298 @@ export default class LightningDatatable extends LightningElement {
641
661
  */
642
662
  @api
643
663
  get suppressBottomBar() {
644
- return this.privateSuppressBottomBar;
664
+ return this._suppressBottomBar;
645
665
  }
646
666
 
647
667
  set suppressBottomBar(value) {
648
- this.privateSuppressBottomBar = !!value;
649
- }
650
-
651
- set columnWidthsMode(value) {
652
- const normalizedValue = normalizeString(value, {
653
- fallbackValue: 'fixed',
654
- validValues: ['fixed', 'auto'],
655
- });
656
- this._columnWidthManager.columnWidthMode = normalizedValue;
657
-
658
- const { state, widthsData } = this;
659
- if (widthsData.columnWidthsMode !== normalizedValue) {
660
- this._columnWidthManager.handleWidthModeChange(getColumns(state));
661
- }
662
-
663
- widthsData.columnWidthsMode = normalizedValue;
668
+ this._suppressBottomBar = normalizeBoolean(value);
664
669
  }
665
670
 
666
671
  /**
667
- * Specifies how column widths are calculated. Set to 'fixed' for columns with equal widths.
668
- * Set to 'auto' for column widths that are based on the width of the column content and the table width. The default is 'fixed'.
669
- * @type {string}
670
- * @default fixed
672
+ * This value specifies the number of lines after which the
673
+ * content will be cut off and hidden. It must be at least 1 or more.
674
+ * The text in the last line is truncated and shown with an ellipsis.
675
+ * @type {integer}
671
676
  */
672
677
  @api
673
- get columnWidthsMode() {
674
- return this.widthsData.columnWidthsMode;
678
+ get wrapTextMaxLines() {
679
+ return this.state.wrapTextMaxLines;
675
680
  }
676
681
 
677
- connectedCallback() {
678
- const {
679
- handleResizeColumn,
680
- handleUpdateColumnSort,
681
- handleCellFocusByClick,
682
- handleFalseCellBlur,
683
- handleSelectionCellClick,
684
- } = this;
682
+ set wrapTextMaxLines(value) {
683
+ const { state } = this;
684
+ setWrapTextMaxLines(state, value);
685
+ this._columnWidthManager.wrapTextMaxLines = state.wrapTextMaxLines;
686
+ this.updateRowsAndCellIndexes(state);
687
+ }
685
688
 
686
- this.template.addEventListener(
687
- 'selectallrows',
689
+ /************************** PUBLIC METHODS ***************************/
690
+
691
+ /**
692
+ * Returns data in each selected row.
693
+ * @returns {array} An array of data in each selected row.
694
+ */
695
+ @api
696
+ getSelectedRows() {
697
+ const data = unwrap(getData(this.state));
698
+ return this.state.rows.reduce((prev, row, index) => {
699
+ if (row.isSelected) {
700
+ prev.push(data[index]);
701
+ }
702
+ return prev;
703
+ }, []);
704
+ }
705
+
706
+ /**
707
+ * Opens the inline edit panel for the datatable's currently active cell. If the active cell is not
708
+ * editable, then the panel is instead opened for the first editable cell in the table. Given two
709
+ * distinct cells, C_x and C_y, C_x is considered "first" in the cell ordering if the following condition
710
+ * evaluates to true:
711
+ *
712
+ * (C_x.rowIndex < C_y.rowIndex) || (C_x.rowIndex === C_y.rowIndex && C_x.columnIndex < C_y.columnIndex)
713
+ *
714
+ * If there is no data in the table or there are no editable cells in the table then calling this function
715
+ * results in a no-op.
716
+ */
717
+ @api
718
+ openInlineEdit() {
719
+ openInlineEditOnActiveCell(this);
720
+ }
721
+
722
+ /************************** PRIVATE GETTERS **************************/
723
+
724
+ /**
725
+ * Retrieves the grid container:
726
+ * 1. For a table-based table, it will retrieve the <table role="grid"> element
727
+ * 2. For a role-based table, it will retrieve the <div role="grid"> element
728
+ * 3. If it is a tree grid, it will retrieve the <table role="treegrid"> element
729
+ */
730
+ get gridContainer() {
731
+ return (
732
+ this.template.querySelector('[role="grid"]') ||
733
+ this.template.querySelector('[role="treegrid"]')
734
+ );
735
+ }
736
+
737
+ get computedTableContainerClass() {
738
+ return classSet({
739
+ 'slds-table_header-fixed_container': !this.hideTableHeader,
740
+ 'slds-scrollable_x': !this._isResizing,
741
+ }).toString();
742
+ }
743
+
744
+ get computedTableClass() {
745
+ const headerType = this.hideTableHeader ? 'hidden' : 'fixed';
746
+ return classSet(
747
+ `slds-table slds-table_header-${headerType} slds-table_bordered slds-table_edit`
748
+ )
749
+ .add({
750
+ 'slds-table_resizable-cols': this.hasResizebleColumns,
751
+ 'slds-tree slds-table_tree': hasTreeDataType(this.state),
752
+ })
753
+ .toString();
754
+ }
755
+
756
+ get computedTableRole() {
757
+ return hasTreeDataType(this.state) ? 'treegrid' : 'grid';
758
+ }
759
+
760
+ get computedTableStyle() {
761
+ if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
762
+ return ['table-layout:auto'].join(';');
763
+ }
764
+ return [
765
+ 'table-layout:fixed',
766
+ getCSSWidthStyleOfTable(this.widthsData),
767
+ ].join(';');
768
+ }
769
+
770
+ get computedTbodyStyle() {
771
+ if (
772
+ hasRowNumberColumn(this.state) &&
773
+ getRowNumberOffset(this.state) >= 0
774
+ ) {
775
+ return (
776
+ 'counter-reset: row-number ' + getRowNumberOffset(this.state)
777
+ );
778
+ }
779
+ return '';
780
+ }
781
+
782
+ /**
783
+ * Sets the min height and the table width on the container
784
+ * that wraps [role="grid"].
785
+ * 1. Min Height is required for the case when the header actions
786
+ * dropdown is opened and there are no rows present. The dropdown
787
+ * would be cut off. To prevent that, we set a min height on the table.
788
+ * 2. The table width is required for horizontal scroll. If the table
789
+ * is overflowing horizontally, we need to set the width in order
790
+ * to be able to view the remaining columns on scroll.
791
+ */
792
+ get computedScrollerStyle() {
793
+ const minHeight = this._actionsMinHeightStyle
794
+ ? `${this._actionsMinHeightStyle};`
795
+ : '';
796
+ if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
797
+ return `${minHeight}overflow-x:auto`;
798
+ }
799
+ return `${minHeight}${getCSSWidthStyleOfTable(this.widthsData)}`;
800
+ }
801
+
802
+ get scrollerXStyles() {
803
+ const styles = {
804
+ height: '100%',
805
+ };
806
+
807
+ if (this.showStatusBar) {
808
+ styles['padding-bottom'] = '3rem';
809
+ }
810
+
811
+ if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
812
+ styles['overflow-x'] = 'auto';
813
+ }
814
+
815
+ return Object.entries(styles)
816
+ .map(([key, value]) => key + ':' + value)
817
+ .join(';');
818
+ }
819
+
820
+ /**
821
+ * Private method to get datatable header element
822
+ * used when getting checkboxColumnHeaderId for
823
+ * aria-labelledby in checkbox column
824
+ * @returns {(HTMLElement|null)}
825
+ */
826
+ get checkboxColumnHeaderElement() {
827
+ return this.template.querySelector(
828
+ 'lightning-primitive-header-factory'
829
+ );
830
+ }
831
+
832
+ /**
833
+ * Private method to get computedCheckboxColumnHeaderId
834
+ * from checkboxColumnHeaderElement for
835
+ * aria-labelledby in checkbox column
836
+ */
837
+ get computedCheckboxColumnHeaderId() {
838
+ return this.checkboxColumnHeaderElement
839
+ ? this.checkboxColumnHeaderElement.computedColumnHeaderId
840
+ : null;
841
+ }
842
+
843
+ get computedAriaLiveClassForNavMode() {
844
+ const keyboardMode = this.state.keyboardMode;
845
+ return classSet()
846
+ .add({
847
+ 'slds-hide': keyboardMode !== 'NAVIGATION',
848
+ 'slds-assistive-text': keyboardMode === 'NAVIGATION',
849
+ })
850
+ .toString();
851
+ }
852
+
853
+ get computedAriaLiveClassForActionMode() {
854
+ const keyboardMode = this.state.keyboardMode;
855
+ return classSet()
856
+ .add({
857
+ 'slds-hide': keyboardMode !== 'ACTION',
858
+ 'slds-assistive-text': keyboardMode === 'ACTION',
859
+ })
860
+ .toString();
861
+ }
862
+
863
+ /**
864
+ * aria-rowcount is the count of TRs in the table
865
+ * It includes the # of rows of data + column header row (since this is also a TR)
866
+ * A table with no rows of data still has an aria-rowcount of 1
867
+ */
868
+ get ariaRowCount() {
869
+ return this.state.rows.length + 1;
870
+ }
871
+
872
+ get hasValidKeyField() {
873
+ if (hasValidKeyField(this.state)) {
874
+ return true;
875
+ }
876
+ // eslint-disable-next-line no-console
877
+ console.error(
878
+ `The "keyField" is a required attribute of lightning:datatable.`
879
+ );
880
+ return false;
881
+ }
882
+
883
+ get numberOfColumns() {
884
+ return getColumns(this.state).length;
885
+ }
886
+
887
+ get hasResizebleColumns() {
888
+ return !isResizeColumnDisabled(this.widthsData);
889
+ }
890
+
891
+ get privateTypes() {
892
+ return this._privateTypes;
893
+ }
894
+
895
+ get viewportRendering() {
896
+ return isViewportRenderingEnabled(this.state);
897
+ }
898
+
899
+ get renderedRows() {
900
+ if (this.viewportRendering && !isIE11) {
901
+ return this.state.rows.slice(0, this._renderedRowCount);
902
+ }
903
+ return this.state.rows;
904
+ }
905
+
906
+ get showSelectAllCheckbox() {
907
+ return !getHideSelectAllCheckbox(this.state);
908
+ }
909
+
910
+ get showStatusBar() {
911
+ return isInlineEditTriggered(this.state) && !this.suppressBottomBar;
912
+ }
913
+
914
+ get tableError() {
915
+ return getTableError(this.state);
916
+ }
917
+
918
+ get i18n() {
919
+ return i18n;
920
+ }
921
+
922
+ /************************** LIFECYCLE HOOKS **************************/
923
+
924
+ /**
925
+ * Initialize the following:
926
+ * 1. DatatableTypes
927
+ * 2. ColumnWidthManager
928
+ * 3. RenderManager
929
+ */
930
+ constructor() {
931
+ super();
932
+
933
+ this._privateTypes = new DatatableTypes(this.constructor.customTypes);
934
+
935
+ this._columnWidthManager = new ColumnWidthManager(this.widthsData);
936
+ this.updateRowsAndCellIndexes = updateRowsAndCellIndexes.bind(this);
937
+
938
+ this._renderManager = new RenderManager();
939
+ }
940
+
941
+ /**
942
+ * Attach event handlers for various events on `lightning-datatable`
943
+ */
944
+ connectedCallback() {
945
+ const {
946
+ handleResizeColumn,
947
+ handleUpdateColumnSort,
948
+ handleCellFocusByClick,
949
+ handleFalseCellBlur,
950
+ handleSelectionCellClick,
951
+ } = this;
952
+
953
+ // Row Selection and De-selection
954
+ this.template.addEventListener(
955
+ 'selectallrows',
688
956
  handleSelectionCellClick.bind(this)
689
957
  );
690
958
  this.template.addEventListener(
@@ -705,18 +973,22 @@ export default class LightningDatatable extends LightningElement {
705
973
  handleRowSelectionChange.bind(this)
706
974
  );
707
975
 
976
+ // Column Resizing
708
977
  this.template.addEventListener(
709
978
  'resizecol',
710
979
  handleResizeColumn.bind(this)
711
980
  );
981
+
982
+ // Column Sorting
712
983
  this.template.addEventListener(
713
984
  'privateupdatecolsort',
714
985
  handleUpdateColumnSort.bind(this)
715
986
  );
716
987
 
988
+ // Cell Interaction
717
989
  this.template.addEventListener(
718
990
  'privatecellkeydown',
719
- handleCellKeydown.bind(this)
991
+ handleKeydownOnCell.bind(this)
720
992
  );
721
993
 
722
994
  this.template.addEventListener(
@@ -728,7 +1000,7 @@ export default class LightningDatatable extends LightningElement {
728
1000
  handleFalseCellBlur.bind(this)
729
1001
  );
730
1002
 
731
- // row-level-actions
1003
+ // Row Level Actions
732
1004
  this.template.addEventListener(
733
1005
  'privatecellactiontriggered',
734
1006
  handleRowActionTriggered.bind(this)
@@ -742,7 +1014,7 @@ export default class LightningDatatable extends LightningElement {
742
1014
  handleCellButtonClick.bind(this)
743
1015
  );
744
1016
 
745
- // header-actions
1017
+ // Header Actions
746
1018
  this.template.addEventListener(
747
1019
  'privatecellheaderactionmenuopening',
748
1020
  handleHeaderActionMenuOpening.bind(this)
@@ -756,75 +1028,41 @@ export default class LightningDatatable extends LightningElement {
756
1028
  handleHeaderActionTriggered.bind(this)
757
1029
  );
758
1030
 
759
- // inline-edit
1031
+ // Inline Edit
760
1032
  this.template.addEventListener(
761
1033
  'privateeditcell',
762
1034
  handleEditCell.bind(this)
763
1035
  );
764
1036
  }
765
1037
 
1038
+ /**
1039
+ * Renders the appropriate template - div.html or table.html,
1040
+ * based on the `render-mode` value passed in.
1041
+ * By default, table.html is rendered
1042
+ */
766
1043
  render() {
767
- return datatableTpl;
1044
+ return this.state.renderModeRoleBased ? divTemplate : tableTemplate;
768
1045
  }
769
1046
 
770
- handleTrRowKeyDown(event) {
771
- // we probably should not be doing this unless we actually are interested in it
772
- if (
773
- this.state.keyboardMode === 'NAVIGATION' &&
774
- this.state.rowMode === true
775
- ) {
776
- event.stopPropagation();
777
-
778
- const tr = event.currentTarget;
779
- const rowKeyValue = tr.getAttribute('data-row-key-value');
780
- const keyCode = event.keyCode;
781
- const rowHasChildren = !!tr.getAttribute('aria-expanded');
782
- const rowExpanded = tr.getAttribute('aria-expanded') === 'true';
783
- const rowLevel = tr.getAttribute('aria-level');
1047
+ renderedCallback() {
1048
+ const { state, template, widthsData } = this;
784
1049
 
785
- const evt = {
786
- target: tr,
787
- detail: {
788
- rowKeyValue,
789
- keyCode,
790
- rowHasChildren,
791
- rowExpanded,
792
- rowLevel,
793
- keyEvent: event,
794
- },
795
- };
1050
+ // This keeps underlying table element up to date if the aria-* properties on this element is dynamically changed.
1051
+ // It does the work of removing and adding the attribute if the value is empty(ish) or a normal string.
1052
+ synchronizeAttrs(this.gridContainer, {
1053
+ 'aria-label': this.ariaLabel,
1054
+ 'aria-labelledby': this.ariaLabelledBy,
1055
+ });
796
1056
 
797
- reactToKeyboardOnRow(this, this.state, evt);
798
- }
799
- }
800
-
801
- disconnectedCallback() {
802
- if (this.privateWidthObserver) {
803
- this.privateWidthObserver.disconnect();
804
- }
805
-
806
- this._renderManager.disconnectResizeObserver();
807
- }
808
-
809
- renderedCallback() {
810
- const { state, template, widthsData } = this;
811
-
812
- // This keeps underlying table element up to date if the aria-* properties on this element is dynamically changed.
813
- // It does the work of removing and adding the attribute if the value is empty(ish) or a normal string.
814
- synchronizeAttrs(this.template.querySelector('table'), {
815
- 'aria-label': this.ariaLabel,
816
- 'aria-labelledby': this.ariaLabelledBy,
817
- });
818
-
819
- if (!this.privateWidthObserver) {
820
- this.privateWidthObserver = new LightningDatatableResizeObserver(
1057
+ if (!this._privateWidthObserver) {
1058
+ this._privateWidthObserver = new LightningDatatableResizeObserver(
821
1059
  template,
822
1060
  state,
823
1061
  widthsData,
824
1062
  this._columnWidthManager
825
1063
  );
826
- } else if (!this.privateWidthObserver.isConnected()) {
827
- this.privateWidthObserver.observe(template);
1064
+ } else if (!this._privateWidthObserver.isConnected()) {
1065
+ this._privateWidthObserver.observe(template);
828
1066
  }
829
1067
 
830
1068
  if (this._columnWidthManager.isResizingUpdateQueued()) {
@@ -845,9 +1083,15 @@ export default class LightningDatatable extends LightningElement {
845
1083
  }
846
1084
  }
847
1085
 
1086
+ // Managing the cell widths is only required for the role-based table
1087
+ if (state.renderModeRoleBased) {
1088
+ // TODO: Look to further optimize - Do this only when required
1089
+ recomputeCellStyles(this.privateTypes, state);
1090
+ }
1091
+
848
1092
  handlePrefetch.call(this, template, state);
849
1093
  // customerSelectedRows is only valid till render, after it, the one used should be the one from the state.
850
- this.customerSelectedRows = null;
1094
+ this._customerSelectedRows = null;
851
1095
  // set the previous focused cell to null after render is done
852
1096
  resetCellToFocusFromPrev(state);
853
1097
 
@@ -866,256 +1110,77 @@ export default class LightningDatatable extends LightningElement {
866
1110
  }
867
1111
  }
868
1112
 
869
- setSelectedRows(value) {
870
- setSelectedRowsKeys(this.state, value);
871
- handleRowSelectionChange.call(this);
872
- }
873
-
874
- updateRowsState() {
875
- const { state, widthsData, template } = this;
876
- // calculate cell to focus next before indexes are updated
877
- setCellToFocusFromPrev(state, template);
878
-
879
- this.updateRowsAndCellIndexes(state);
880
-
881
- if (this.viewportRendering) {
882
- this._renderManager.updateViewportRendering(this);
883
- }
884
-
885
- this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
886
- // update celltofocus next to null if the row still exists after indexes calculation
887
- updateCellToFocusFromPrev(state);
888
- syncSelectedRowsKeys(state, this.getSelectedRows()).ifChanged(() => {
889
- // Only trigger row selection event once after all the setters have executed
890
- // Otherwise, event can be fired with stale data if not all setters have been triggered
891
- if (!this._rowSelectionEventPending) {
892
- this._rowSelectionEventPending = true;
893
- Promise.resolve().then(() => {
894
- if (this._rowSelectionEventPending) {
895
- this.fireSelectedRowsChange(this.getSelectedRows());
896
- this._rowSelectionEventPending = false;
897
- }
898
- });
899
- }
900
- });
901
- syncActiveCell(state);
902
-
903
- if (state.keyboardMode === 'NAVIGATION') {
904
- updateTabIndexActiveCell(state);
905
- updateTabIndexActiveRow(state);
906
- }
907
- // if there is previously focused cell which was deleted set focus from celltofocus next
908
- if (state.cellToFocusNext && state.activeCell) {
909
- setFocusActiveCell(this.template, this.state);
910
- }
911
- }
912
-
913
- updateColumns(columns) {
914
- const { state, widthsData, template } = this;
915
- const hadTreeDataTypePreviously = hasTreeDataType(state);
916
- // calculate cell to focus next before indexes are updated
917
- setCellToFocusFromPrev(state, template);
918
- normalizeColumns(state, columns, this.privateTypes);
919
- setDirtyValues(state, this._draftValues);
920
- updateRowNavigationMode(hadTreeDataTypePreviously, state);
921
- state.headerIndexes = generateHeaderIndexes(getColumns(state));
922
- // Updates state.wrapText and when isWrapableType, sets internal header actions
923
- updateHeaderActions(state);
924
- this.updateRowsAndCellIndexes(state);
925
- updateSelectionState(state);
926
- this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
927
- updateColumnWidthsMetadata(state, widthsData);
928
- // set the celltofocus next to null if the column still exists after indexes calculation
929
- updateCellToFocusFromPrev(state);
930
-
931
- if (getColumns(state).length !== getColumnsWidths(widthsData).length) {
932
- if (getData(state).length > 0) {
933
- // when there are column changes, update the active cell
934
- syncActiveCell(state);
935
- }
936
- }
937
- if (state.keyboardMode === 'NAVIGATION') {
938
- updateTabIndexActiveCell(state);
939
- updateTabIndexActiveRow(state);
940
- }
941
- // if there is previously focused cell which was deleted set focus from celltofocus next
942
- if (state.cellToFocusNext && state.activeCell) {
943
- setFocusActiveCell(this.template, this.state);
944
- }
945
- }
946
-
947
- get computedTableHeaderClass() {
948
- if (this.state.hideTableHeader) {
949
- return 'slds-assistive-text';
950
- }
951
- return undefined;
952
- }
953
-
954
- // Min height required while actions menu is opened
955
- _actionsMinHeightStyle = '';
956
-
957
- get computedScrollerStyle() {
958
- const minHeight = this._actionsMinHeightStyle
959
- ? `${this._actionsMinHeightStyle};`
960
- : '';
961
- if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
962
- return `${minHeight}overflow-x:auto`;
963
- }
964
- return `${minHeight}${getTableWidthStyle(this.widthsData)}`;
965
- }
966
-
967
- get computedTableContainerClass() {
968
- return classSet({
969
- 'slds-table_header-fixed_container': !this.hideTableHeader,
970
- })
971
- .add({ 'slds-scrollable_x': !this._isResizing })
972
- .toString();
973
- }
974
-
975
- get computedTableClass() {
976
- const headerType = this.hideTableHeader ? 'hidden' : 'fixed';
977
- return classSet(
978
- `slds-table slds-table_header-${headerType} slds-table_bordered slds-table_edit`
979
- )
980
- .add({ 'slds-table_resizable-cols': this.hasResizebleColumns })
981
- .add({ 'slds-tree slds-table_tree': hasTreeDataType(this.state) })
982
- .toString();
983
- }
984
-
985
- get computedTableRole() {
986
- return hasTreeDataType(this.state) ? 'treegrid' : 'grid';
987
- }
988
-
989
- get computedTableStyle() {
990
- if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
991
- return ['table-layout:auto'].join(';');
992
- }
993
- return ['table-layout:fixed', getTableWidthStyle(this.widthsData)].join(
994
- ';'
995
- );
996
- }
997
-
998
- get computedAriaLiveClassForNavMode() {
999
- return classSet()
1000
- .add({ 'slds-hide': this.state.keyboardMode !== 'NAVIGATION' })
1001
- .add({
1002
- 'slds-assistive-text': this.state.keyboardMode === 'NAVIGATION',
1003
- })
1004
- .toString();
1005
- }
1006
-
1007
- get computedAriaLiveClassForActionMode() {
1008
- return classSet()
1009
- .add({ 'slds-hide': this.state.keyboardMode !== 'ACTION' })
1010
- .add({
1011
- 'slds-assistive-text': this.state.keyboardMode === 'ACTION',
1012
- })
1013
- .toString();
1014
- }
1015
-
1016
- get ariaLiveNavigationModeText() {
1017
- return `${i18n.ariaLiveNavigationMode}`;
1018
- }
1019
-
1020
- get ariaLiveActionModeText() {
1021
- return `${i18n.ariaLiveActionMode}`;
1022
- }
1023
-
1024
- get ariaRowCount() {
1025
- // aria-rowcount is the count of TRs in the table
1026
- // It includes the # of rows of data + column header row (since this is also a TR)
1027
- // In this case, a table with no rows of data still has an aria-rowcount of 1
1028
- return this.state.rows.length + 1;
1029
- }
1030
-
1031
- get computedTbodyStyle() {
1032
- if (
1033
- hasRowNumberColumn(this.state) &&
1034
- getRowNumberOffset(this.state) >= 0
1035
- ) {
1036
- return (
1037
- 'counter-reset: row-number ' + getRowNumberOffset(this.state)
1038
- );
1113
+ disconnectedCallback() {
1114
+ if (this._privateWidthObserver) {
1115
+ this._privateWidthObserver.disconnect();
1039
1116
  }
1040
- return '';
1041
- }
1042
1117
 
1043
- get hasSelectableRows() {
1044
- return !this.state.hideCheckboxColumn;
1118
+ this._renderManager.disconnectResizeObserver();
1045
1119
  }
1046
1120
 
1047
- get hasResizebleColumns() {
1048
- return !isResizeColumnDisabled(this.widthsData);
1049
- }
1121
+ /************************** EVENT HANDLERS ***************************/
1050
1122
 
1051
1123
  /**
1052
- * Private method to get datatable header element
1053
- * used when getting checkboxColumnHeaderId for
1054
- * aria-labelledby in checkbox column
1055
- * @returns {(HTMLElement|null)}
1124
+ * Handles the `keydown` event on <table> and the
1125
+ * corresponding <div> on the role-based table
1126
+ *
1127
+ * @param {KeyboardEvent} event - `keydown`
1056
1128
  */
1057
- get checkboxColumnHeaderElement() {
1058
- return this.template.querySelector(
1059
- 'lightning-primitive-header-factory'
1060
- );
1129
+ handleTableKeydown(event) {
1130
+ handleKeydownOnTable.call(this, event);
1061
1131
  }
1062
1132
 
1063
1133
  /**
1064
- * Private method to get computedCheckboxColumnHeaderId
1065
- * from checkboxColumnHeaderElement for
1066
- * aria-labelledby in checkbox column
1134
+ * Handles the `keydown` event on data row <tr> (table-based) and div[role="row"] (role-based)
1135
+ *
1136
+ * @param {KeyboardEvent} event - `keydown`
1067
1137
  */
1068
- get computedCheckboxColumnHeaderId() {
1069
- return this.checkboxColumnHeaderElement
1070
- ? this.checkboxColumnHeaderElement.computedColumnHeaderId
1071
- : null;
1072
- }
1073
-
1074
- get numberOfColumns() {
1075
- return getColumns(this.state).length;
1076
- }
1077
-
1078
- get showLoadingIndicator() {
1079
- return isLoading(this.state);
1080
- }
1138
+ handleKeydownOnDataRow(event) {
1139
+ // we probably should not be doing this unless we actually are interested in it
1140
+ if (
1141
+ this.state.keyboardMode === 'NAVIGATION' &&
1142
+ this.state.rowMode === true
1143
+ ) {
1144
+ event.stopPropagation();
1081
1145
 
1082
- get scrollerXStyles() {
1083
- const styles = {
1084
- height: '100%',
1085
- };
1146
+ const tr = event.currentTarget;
1147
+ const rowKeyValue = tr.getAttribute('data-row-key-value');
1148
+ const keyCode = event.keyCode;
1149
+ const rowHasChildren = !!tr.getAttribute('aria-expanded');
1150
+ const rowExpanded = tr.getAttribute('aria-expanded') === 'true';
1151
+ const rowLevel = tr.getAttribute('aria-level');
1086
1152
 
1087
- if (this.showStatusBar) {
1088
- styles['padding-bottom'] = '3rem';
1089
- }
1153
+ const evt = {
1154
+ target: tr,
1155
+ detail: {
1156
+ rowKeyValue,
1157
+ keyCode,
1158
+ rowHasChildren,
1159
+ rowExpanded,
1160
+ rowLevel,
1161
+ keyEvent: event,
1162
+ },
1163
+ };
1090
1164
 
1091
- if (this._columnWidthManager.isAutoResizingUpdateQueued()) {
1092
- styles['overflow-x'] = 'auto';
1165
+ reactToKeyboardOnRow(this, this.state, evt);
1093
1166
  }
1094
-
1095
- return Object.entries(styles)
1096
- .map(([key, value]) => key + ':' + value)
1097
- .join(';');
1098
- }
1099
-
1100
- get showStatusBar() {
1101
- return isInlineEditTriggered(this.state) && !this.suppressBottomBar;
1102
- }
1103
-
1104
- get tableError() {
1105
- return getTableError(this.state);
1106
- }
1107
-
1108
- handleUpdateColumnSort(event) {
1109
- event.stopPropagation();
1110
- const { fieldName, columnKey, sortDirection } = event.detail;
1111
-
1112
- this.fireSortedColumnChange(fieldName, columnKey, sortDirection);
1113
1167
  }
1114
1168
 
1169
+ /**
1170
+ * Handles the `scroll` event on the table container
1171
+ *
1172
+ * @param {Event} event - `scroll`
1173
+ */
1115
1174
  handleHorizontalScroll(event) {
1116
1175
  handleInlineEditPanelScroll.call(this, event);
1117
1176
  }
1118
1177
 
1178
+ /**
1179
+ * Handles the `scroll` event on the child of the
1180
+ * table container at div.slds-scrollable_y
1181
+ *
1182
+ * @param {Event} event - `scroll`
1183
+ */
1119
1184
  handleVerticalScroll(event) {
1120
1185
  if (this.enableInfiniteLoading) {
1121
1186
  handleLoadMoreCheck.call(this, event);
@@ -1128,64 +1193,101 @@ export default class LightningDatatable extends LightningElement {
1128
1193
  }
1129
1194
  }
1130
1195
 
1131
- fireSelectedRowsChange(selectedRows) {
1132
- const event = new CustomEvent('rowselection', {
1133
- detail: { selectedRows },
1134
- });
1196
+ /**
1197
+ * Handles the `click` event on the <table> element and
1198
+ * the corresponding <div> in the role-based table
1199
+ *
1200
+ * @param {MouseEvent} event - `click`
1201
+ */
1202
+ handleCellClick(event) {
1203
+ // handles the case when clicking on the margin/pading of the td/th
1204
+ const targetTagName = event.target.tagName.toLowerCase();
1135
1205
 
1136
- this.dispatchEvent(event);
1137
- }
1206
+ if (targetTagName === 'td' || targetTagName === 'th') {
1207
+ // get the row/col key value from the primitive cell.
1208
+ const { rowKeyValue, colKeyValue } =
1209
+ event.target.querySelector(':first-child');
1138
1210
 
1139
- fireSortedColumnChange(fieldName, columnKey, sortDirection) {
1140
- const event = new CustomEvent('sort', {
1141
- detail: { fieldName, columnKey, sortDirection },
1142
- });
1143
- this.dispatchEvent(event);
1144
- }
1211
+ const { state, template } = this;
1212
+ if (
1213
+ state.rowMode ||
1214
+ !isActiveCell(state, rowKeyValue, colKeyValue)
1215
+ ) {
1216
+ if (state.rowMode && state.activeCell) {
1217
+ unsetRowNavigationMode(state);
1218
+ const { rowIndex } = getIndexesActiveCell(state);
1219
+ updateTabIndexRow(state, rowIndex, -1);
1220
+ }
1221
+ this.setActiveCell(rowKeyValue, colKeyValue);
1222
+ }
1145
1223
 
1146
- fireOnResize(isUserTriggered) {
1147
- const { state, widthsData } = this;
1148
- const event = new CustomEvent('resize', {
1149
- detail: {
1150
- columnWidths: getCustomerColumnWidths(state, widthsData),
1151
- isUserTriggered: !!isUserTriggered,
1152
- },
1153
- });
1154
- this.dispatchEvent(event);
1224
+ if (!datatableHasFocus(state, template)) {
1225
+ setCellClickedForFocus(state);
1226
+ }
1227
+ }
1155
1228
  }
1156
1229
 
1157
- safariHeaderFix() {
1158
- // W-6363867, W-7143375 Safari Refresh Bug
1159
- if (isSafari) {
1160
- const thead = this.template.querySelector('thead');
1161
-
1162
- if (thead) {
1163
- /* Safari hack: hide and show the table head to force a browser repaint */
1164
- thead.style.display = 'none';
1230
+ /**
1231
+ * Handles the `privateupdatecolsort` event on lightning-datatable
1232
+ *
1233
+ * @param {CustomEvent} event - `privateupdatecolsort`
1234
+ */
1235
+ handleUpdateColumnSort(event) {
1236
+ event.stopPropagation();
1237
+ const { fieldName, columnKey, sortDirection } = event.detail;
1165
1238
 
1166
- // eslint-disable-next-line @lwc/lwc/no-async-operation
1167
- requestAnimationFrame(() => {
1168
- thead.style.display = '';
1169
- });
1170
- }
1171
- }
1239
+ this.fireSortedColumnChange(fieldName, columnKey, sortDirection);
1172
1240
  }
1173
1241
 
1242
+ /**
1243
+ * Handles the `resizecol` event on lightning-datatable
1244
+ *
1245
+ * @param {CustomEvent} event - `resizecol`
1246
+ */
1174
1247
  handleResizeColumn(event) {
1175
1248
  event.stopPropagation();
1176
1249
  const { state, widthsData } = this;
1177
1250
  const { colIndex, widthDelta } = event.detail;
1178
1251
  if (widthDelta !== 0) {
1179
- resizeColumnWithDelta(state, widthsData, colIndex, widthDelta);
1252
+ resizeColumnWithDelta(
1253
+ getColumns(state),
1254
+ widthsData,
1255
+ colIndex,
1256
+ widthDelta
1257
+ );
1180
1258
  this.fireOnResize(true);
1181
1259
  this.safariHeaderFix();
1182
1260
  }
1183
1261
  }
1184
1262
 
1185
- get tableTabIndex() {
1186
- return this.state.focusIsInside ? '-1' : '0';
1263
+ /**
1264
+ * Handles the `privateresizestart` event on the <tr> and the corresponding
1265
+ * <div> in the role-based table on the column header row
1266
+ *
1267
+ * @param {CustomEvent} event - `privateresizestart`
1268
+ */
1269
+ handleResizeStart(event) {
1270
+ event.stopPropagation();
1271
+ this._isResizing = true;
1187
1272
  }
1188
1273
 
1274
+ /**
1275
+ * Handles the `privateresizeend` event on the <tr> and the corresponding
1276
+ * <div> in the role-based table on the column header row
1277
+ *
1278
+ * @param {CustomEvent} event - `privateresizeend`
1279
+ */
1280
+ handleResizeEnd(event) {
1281
+ event.stopPropagation();
1282
+ this._isResizing = false;
1283
+ }
1284
+
1285
+ /**
1286
+ * Handles the `selectallrows`, `deselectallrows`, `selectrow`, `deselectrow`
1287
+ * events on lightning-datatable
1288
+ *
1289
+ * @param {CustomEvent} event - `selectallrows`, `deselectallrows`, `selectrow`, `deselectrow`
1290
+ */
1189
1291
  handleSelectionCellClick(event) {
1190
1292
  this.handleCellFocusByClick(event);
1191
1293
 
@@ -1200,6 +1302,11 @@ export default class LightningDatatable extends LightningElement {
1200
1302
  }
1201
1303
  }
1202
1304
 
1305
+ /**
1306
+ * Handles the `privatecellfocusedbyclick` event on lightning-datatable
1307
+ *
1308
+ * @param {CustomEvent} event - `privatecellfocusedbyclick`
1309
+ */
1203
1310
  handleCellFocusByClick(event) {
1204
1311
  event.stopPropagation();
1205
1312
  const { rowKeyValue, colKeyValue, needsRefocusOnCellElement } =
@@ -1216,47 +1323,11 @@ export default class LightningDatatable extends LightningElement {
1216
1323
  }
1217
1324
  }
1218
1325
 
1219
- handleCellClick(event) {
1220
- // handles the case when clicking on the margin/pading of the td/th
1221
- const targetTagName = event.target.tagName.toLowerCase();
1222
-
1223
- if (targetTagName === 'td' || targetTagName === 'th') {
1224
- // get the row/col key value from the primitive cell.
1225
- const { rowKeyValue, colKeyValue } =
1226
- event.target.querySelector(':first-child');
1227
-
1228
- const { state, template } = this;
1229
- if (
1230
- state.rowMode ||
1231
- !isActiveCell(state, rowKeyValue, colKeyValue)
1232
- ) {
1233
- if (state.rowMode && state.activeCell) {
1234
- unsetRowNavigationMode(state);
1235
- const { rowIndex } = getIndexesActiveCell(state);
1236
- updateTabIndexRow(state, rowIndex, -1);
1237
- }
1238
- this.setActiveCell(rowKeyValue, colKeyValue);
1239
- }
1240
-
1241
- if (!datatableHasFocus(state, template)) {
1242
- setCellClickedForFocus(state);
1243
- }
1244
- }
1245
- }
1246
-
1247
- setActiveCell(rowKeyValue, colKeyValue) {
1248
- const { template, state } = this;
1249
- const { rowIndex, colIndex } = getIndexesByKeys(
1250
- state,
1251
- rowKeyValue,
1252
- colKeyValue
1253
- );
1254
- setBlurActiveCell(template, state);
1255
- updateActiveCell(state, rowKeyValue, colKeyValue);
1256
- addFocusStylesToActiveCell(template, state);
1257
- updateTabIndex(state, rowIndex, colIndex, 0);
1258
- }
1259
-
1326
+ /**
1327
+ * Handles the `privatecellfalseblurred` event on lightning-datatable
1328
+ *
1329
+ * @param {CustomEvent} event - `privatecellfalseblurred`
1330
+ */
1260
1331
  handleFalseCellBlur(event) {
1261
1332
  event.stopPropagation();
1262
1333
  const { template, state } = this;
@@ -1268,71 +1339,52 @@ export default class LightningDatatable extends LightningElement {
1268
1339
  }
1269
1340
 
1270
1341
  /**
1271
- * Returns data in each selected row.
1272
- * @returns {array} An array of data in each selected row.
1273
- */
1274
- @api
1275
- getSelectedRows() {
1276
- const data = unwrap(getData(this.state));
1277
- return this.state.rows.reduce((prev, row, index) => {
1278
- if (row.isSelected) {
1279
- prev.push(data[index]);
1280
- }
1281
- return prev;
1282
- }, []);
1283
- }
1284
-
1285
- /**
1286
- * Opens the inline edit panel for the datatable's currently active cell. If the active cell is not
1287
- * editable, then the panel is instead opened for the first editable cell in the table. Given two
1288
- * distinct cells, C_x and C_y, C_x is considered "first" in the cell ordering if the following condition
1289
- * evaluates to true:
1342
+ * Handles the `focusin` event on <table> and the corresponding
1343
+ * <div> on the role-based table
1290
1344
  *
1291
- * (C_x.rowIndex < C_y.rowIndex) || (C_x.rowIndex === C_y.rowIndex && C_x.columnIndex < C_y.columnIndex)
1292
- *
1293
- * If there is no data in the table or there are no editable cells in the table then calling this function
1294
- * results in a no-op.
1345
+ * @param {FocusEvent} event - `focusin`
1295
1346
  */
1296
- @api
1297
- openInlineEdit() {
1298
- openInlineEditOnActiveCell(this);
1299
- }
1300
-
1301
1347
  handleTableFocusIn(event) {
1302
1348
  handleDatatableFocusIn.call(this, event);
1303
1349
  }
1304
1350
 
1351
+ /**
1352
+ * Handles the `focusout` event on <table> and the corresponding
1353
+ * <div> on the role-based table
1354
+ *
1355
+ * @param {FocusEvent} event - `focusout`
1356
+ */
1305
1357
  handleTableFocusOut(event) {
1306
- handleDatatableLosedFocus.call(this, event);
1358
+ handleDatatableFocusOut.call(this, event);
1307
1359
  }
1308
1360
 
1309
1361
  /**
1310
- * @return {Object} containing the visible dimensions of the table { left, right, top, bottom, }
1362
+ * Handles the `ieditfinished` event on the inline edit panel -
1363
+ * `lightning-primitive-datatable-iedit-panel`
1364
+ *
1365
+ * @param {CustomEvent} event - `ieditfinished`
1311
1366
  */
1312
- getViewableRect() {
1313
- const scrollerX = this.template
1314
- .querySelector('.slds-scrollable_x')
1315
- .getBoundingClientRect();
1316
- const scrollerY = this.template
1317
- .querySelector('.slds-scrollable_y')
1318
- .getBoundingClientRect();
1319
-
1320
- return {
1321
- left: scrollerX.left,
1322
- right: scrollerX.right,
1323
- top: scrollerY.top,
1324
- bottom: scrollerY.bottom,
1325
- };
1326
- }
1327
-
1328
1367
  handleInlineEditFinish(event) {
1329
1368
  handleInlineEditFinish.call(this, event);
1330
1369
  }
1331
1370
 
1371
+ /**
1372
+ * Handles the `masscheckboxchange` event on the inline edit panel -
1373
+ * `lightning-primitive-datatable-iedit-panel`
1374
+ *
1375
+ * @param {CustomEvent} event - `masscheckboxchange`
1376
+ */
1332
1377
  handleMassCheckboxChange(event) {
1333
1378
  handleMassCheckboxChange.call(this, event);
1334
1379
  }
1335
1380
 
1381
+ /**
1382
+ * Handles the `privatesave` event on the status bar -
1383
+ * `lightning-primitive-datatable-status-bar` and
1384
+ * fires the `save` custom event
1385
+ *
1386
+ * @param {CustomEvent} event - `privatesave`
1387
+ */
1336
1388
  handleInlineEditSave(event) {
1337
1389
  event.stopPropagation();
1338
1390
  event.preventDefault();
@@ -1349,6 +1401,13 @@ export default class LightningDatatable extends LightningElement {
1349
1401
  );
1350
1402
  }
1351
1403
 
1404
+ /**
1405
+ * Handles the `privatecancel` event on the status bar -
1406
+ * `lightning-primitive-datatable-status-bar` and
1407
+ * fires the `cancel` custom event
1408
+ *
1409
+ * @param {CustomEvent} event - `privatecancel`
1410
+ */
1352
1411
  handleInlineEditCancel(event) {
1353
1412
  event.stopPropagation();
1354
1413
  event.preventDefault();
@@ -1365,17 +1424,163 @@ export default class LightningDatatable extends LightningElement {
1365
1424
  }
1366
1425
  }
1367
1426
 
1368
- handleTableKeydown(event) {
1369
- handleKeyDown.call(this, event);
1427
+ /************************ EVENT DISPATCHERS **************************/
1428
+
1429
+ fireSelectedRowsChange(selectedRows) {
1430
+ const event = new CustomEvent('rowselection', {
1431
+ detail: { selectedRows },
1432
+ });
1433
+
1434
+ this.dispatchEvent(event);
1370
1435
  }
1371
1436
 
1372
- handleResizeStart(event) {
1373
- event.stopPropagation();
1374
- this._isResizing = true;
1437
+ fireSortedColumnChange(fieldName, columnKey, sortDirection) {
1438
+ const event = new CustomEvent('sort', {
1439
+ detail: { fieldName, columnKey, sortDirection },
1440
+ });
1441
+ this.dispatchEvent(event);
1375
1442
  }
1376
1443
 
1377
- handleResizeEnd(event) {
1378
- event.stopPropagation();
1379
- this._isResizing = false;
1444
+ fireOnResize(isUserTriggered) {
1445
+ const { state, widthsData } = this;
1446
+ const event = new CustomEvent('resize', {
1447
+ detail: {
1448
+ columnWidths: getCustomerColumnWidths(
1449
+ getColumns(state),
1450
+ widthsData
1451
+ ),
1452
+ isUserTriggered: !!isUserTriggered,
1453
+ },
1454
+ });
1455
+ this.dispatchEvent(event);
1456
+ }
1457
+
1458
+ /************************* HELPER FUNCTIONS **************************/
1459
+
1460
+ updateRowsState() {
1461
+ const { state, widthsData, template } = this;
1462
+ // calculate cell to focus next before indexes are updated
1463
+ setCellToFocusFromPrev(state, template);
1464
+
1465
+ this.updateRowsAndCellIndexes(state);
1466
+
1467
+ if (this.viewportRendering) {
1468
+ this._renderManager.updateViewportRendering(this);
1469
+ }
1470
+
1471
+ this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
1472
+ // update celltofocus next to null if the row still exists after indexes calculation
1473
+ updateCellToFocusFromPrev(state);
1474
+ syncSelectedRowsKeys(state, this.getSelectedRows()).ifChanged(() => {
1475
+ // Only trigger row selection event once after all the setters have executed
1476
+ // Otherwise, event can be fired with stale data if not all setters have been triggered
1477
+ if (!this._rowSelectionEventPending) {
1478
+ this._rowSelectionEventPending = true;
1479
+ Promise.resolve().then(() => {
1480
+ if (this._rowSelectionEventPending) {
1481
+ this.fireSelectedRowsChange(this.getSelectedRows());
1482
+ this._rowSelectionEventPending = false;
1483
+ }
1484
+ });
1485
+ }
1486
+ });
1487
+ syncActiveCell(state);
1488
+
1489
+ if (state.keyboardMode === 'NAVIGATION') {
1490
+ updateTabIndexActiveCell(state);
1491
+ updateTabIndexActiveRow(state);
1492
+ }
1493
+ // if there is previously focused cell which was deleted set focus from celltofocus next
1494
+ if (state.cellToFocusNext && state.activeCell) {
1495
+ setFocusActiveCell(this.template, this.state);
1496
+ }
1497
+ }
1498
+
1499
+ updateColumns(columns) {
1500
+ const { state, widthsData, template } = this;
1501
+ const hadTreeDataTypePreviously = hasTreeDataType(state);
1502
+ // calculate cell to focus next before indexes are updated
1503
+ setCellToFocusFromPrev(state, template);
1504
+ normalizeColumns(state, columns, this.privateTypes);
1505
+ setDirtyValues(state, this._draftValues);
1506
+ updateRowNavigationMode(hadTreeDataTypePreviously, state);
1507
+ state.headerIndexes = generateHeaderIndexes(getColumns(state));
1508
+ // Updates state.wrapText and when isWrapableType, sets internal header actions
1509
+ updateHeaderActions(state);
1510
+ this.updateRowsAndCellIndexes(state);
1511
+ updateSelectionState(state);
1512
+ this._columnWidthManager.handleRowNumberOffsetChange(state, widthsData);
1513
+ updateColumnWidthsMetadata(getColumns(state), widthsData);
1514
+ // set the celltofocus next to null if the column still exists after indexes calculation
1515
+ updateCellToFocusFromPrev(state);
1516
+
1517
+ if (getColumns(state).length !== getColumnsWidths(widthsData).length) {
1518
+ if (getData(state).length > 0) {
1519
+ // when there are column changes, update the active cell
1520
+ syncActiveCell(state);
1521
+ }
1522
+ }
1523
+ if (state.keyboardMode === 'NAVIGATION') {
1524
+ updateTabIndexActiveCell(state);
1525
+ updateTabIndexActiveRow(state);
1526
+ }
1527
+ // if there is previously focused cell which was deleted set focus from celltofocus next
1528
+ if (state.cellToFocusNext && state.activeCell) {
1529
+ setFocusActiveCell(this.template, this.state);
1530
+ }
1531
+ }
1532
+
1533
+ setSelectedRows(value) {
1534
+ setSelectedRowsKeys(this.state, value);
1535
+ handleRowSelectionChange.call(this);
1536
+ }
1537
+
1538
+ setActiveCell(rowKeyValue, colKeyValue) {
1539
+ const { template, state } = this;
1540
+ const { rowIndex, colIndex } = getIndexesByKeys(
1541
+ state,
1542
+ rowKeyValue,
1543
+ colKeyValue
1544
+ );
1545
+ setBlurActiveCell(template, state);
1546
+ updateActiveCell(state, rowKeyValue, colKeyValue);
1547
+ addFocusStylesToActiveCell(template, state);
1548
+ updateTabIndex(state, rowIndex, colIndex, 0);
1549
+ }
1550
+
1551
+ /**
1552
+ * @return {Object} containing the visible dimensions of the table { left, right, top, bottom, }
1553
+ */
1554
+ getViewableRect() {
1555
+ const scrollerX = this.template
1556
+ .querySelector('.slds-scrollable_x')
1557
+ .getBoundingClientRect();
1558
+ const scrollerY = this.template
1559
+ .querySelector('.slds-scrollable_y')
1560
+ .getBoundingClientRect();
1561
+
1562
+ return {
1563
+ left: scrollerX.left,
1564
+ right: scrollerX.right,
1565
+ top: scrollerY.top,
1566
+ bottom: scrollerY.bottom,
1567
+ };
1568
+ }
1569
+
1570
+ // W-6363867, W-7143375 Safari Refresh Bug
1571
+ safariHeaderFix() {
1572
+ if (isSafari) {
1573
+ const thead = this.template.querySelector('thead');
1574
+
1575
+ if (thead) {
1576
+ /* Safari hack: hide and show the table head to force a browser repaint */
1577
+ thead.style.display = 'none';
1578
+
1579
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
1580
+ requestAnimationFrame(() => {
1581
+ thead.style.display = '';
1582
+ });
1583
+ }
1584
+ }
1380
1585
  }
1381
1586
  }