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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/metadata/raptor.json +5 -3
  2. package/package.json +8 -1
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningProgressBar.progressBar.js +1 -0
  5. package/src/lightning/alert/__docs__/alert.md +101 -0
  6. package/src/lightning/alert/__examples__disabled/basic/basic.css +7 -0
  7. package/src/lightning/alert/__examples__disabled/basic/basic.html +8 -0
  8. package/src/lightning/alert/__examples__disabled/basic/basic.js +14 -0
  9. package/src/lightning/alert/alert.html +3 -0
  10. package/src/lightning/alert/alert.js +78 -0
  11. package/src/lightning/alert/alert.js-meta.xml +6 -0
  12. package/src/lightning/button/__wdio__/utam/utam.html +3 -0
  13. package/src/lightning/button/__wdio__/utam/utam.js +3 -0
  14. package/src/lightning/button/__wdio__/utam/utam.spec.js +20 -0
  15. package/src/lightning/buttonMenu/buttonMenu.js +12 -0
  16. package/src/lightning/confirm/__docs__/confirm.md +100 -0
  17. package/src/lightning/confirm/__examples__disabled/basic/basic.css +7 -0
  18. package/src/lightning/confirm/__examples__disabled/basic/basic.html +8 -0
  19. package/src/lightning/confirm/__examples__disabled/basic/basic.js +14 -0
  20. package/src/lightning/confirm/confirm.html +3 -0
  21. package/src/lightning/confirm/confirm.js +80 -0
  22. package/src/lightning/confirm/confirm.js-meta.xml +6 -0
  23. package/src/lightning/datatable/__wdio__/utam/utam.html +17 -0
  24. package/src/lightning/datatable/__wdio__/utam/utam.js +91 -0
  25. package/src/lightning/datatable/__wdio__/utam/utam.spec.js +189 -0
  26. package/src/lightning/datatable/columns.js +166 -71
  27. package/src/lightning/datatable/datatable.js +98 -20
  28. package/src/lightning/datatable/headerActions.js +2 -2
  29. package/src/lightning/datatable/keyboard.js +10 -10
  30. package/src/lightning/datatable/renderManager.js +45 -13
  31. package/src/lightning/datatable/resizeSensor.js +11 -3
  32. package/src/lightning/datatable/rowSelection.js +1 -1
  33. package/src/lightning/datatable/rowSelectionShared.js +33 -20
  34. package/src/lightning/datatable/rows.js +3 -2
  35. package/src/lightning/datatable/sort.js +8 -8
  36. package/src/lightning/datatable/state.js +8 -1
  37. package/src/lightning/datatable/templates/div/div.html +6 -2
  38. package/src/lightning/datatable/templates/table/table.html +5 -2
  39. package/src/lightning/datatable/tree.js +25 -0
  40. package/src/lightning/datatable/types.js +77 -9
  41. package/src/lightning/datatable/utils.js +51 -24
  42. package/src/lightning/datatable/virtualization.js +319 -0
  43. package/src/lightning/datatable/wrapText.js +53 -16
  44. package/src/lightning/datepicker/__perf__DISABLED/datepickerWithCalendarOpen.perf.js +55 -0
  45. package/src/lightning/iconSvgTemplates/buildTemplates/standard/dashboard_component.html +7 -0
  46. package/src/lightning/iconSvgTemplates/buildTemplates/standard/slack.html +7 -0
  47. package/src/lightning/iconSvgTemplates/buildTemplates/standard/tableau.html +7 -0
  48. package/src/lightning/iconSvgTemplates/buildTemplates/standard/travel_mode.html +2 -2
  49. package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +8 -1
  50. package/src/lightning/iconSvgTemplates/buildTemplates/utility/data_model.html +7 -0
  51. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product.html +1 -1
  52. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product_transaction.html +2 -1
  53. package/src/lightning/iconSvgTemplates/buildTemplates/utility/slack.html +7 -0
  54. package/src/lightning/iconSvgTemplates/buildTemplates/utility/tableau.html +7 -0
  55. package/src/lightning/iconSvgTemplates/buildTemplates/utility/video_off.html +7 -0
  56. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/dashboard_component.html +7 -0
  57. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/slack.html +7 -0
  58. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/tableau.html +7 -0
  59. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/travel_mode.html +2 -2
  60. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +8 -1
  61. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/data_model.html +7 -0
  62. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product.html +1 -1
  63. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  64. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/slack.html +7 -0
  65. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/tableau.html +7 -0
  66. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/video_off.html +7 -0
  67. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/dashboard_component.html +7 -0
  68. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/slack.html +7 -0
  69. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/tableau.html +7 -0
  70. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/travel_mode.html +2 -2
  71. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/templates.js +4 -1
  72. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/dashboard_component.html +7 -0
  73. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/slack.html +7 -0
  74. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/tableau.html +7 -0
  75. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/travel_mode.html +2 -2
  76. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/templates.js +4 -1
  77. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +5 -1
  78. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/data_model.html +7 -0
  79. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product.html +1 -1
  80. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product_transaction.html +2 -1
  81. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/slack.html +7 -0
  82. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/tableau.html +7 -0
  83. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/video_off.html +7 -0
  84. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +5 -1
  85. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/data_model.html +7 -0
  86. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product.html +1 -1
  87. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  88. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/slack.html +7 -0
  89. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/tableau.html +7 -0
  90. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/video_off.html +7 -0
  91. package/src/lightning/input/__docs__/input.md +2 -0
  92. package/src/lightning/interactiveDialogBase/interactiveDialogBase.css +494 -0
  93. package/src/lightning/interactiveDialogBase/interactiveDialogBase.html +63 -0
  94. package/src/lightning/interactiveDialogBase/interactiveDialogBase.js +200 -0
  95. package/src/lightning/menuItem/menuItem.js +4 -1
  96. package/src/lightning/modalBase/modalBase.css +20 -0
  97. package/src/lightning/modalBase/modalBase.html +54 -0
  98. package/src/lightning/modalBase/modalBase.js +1039 -0
  99. package/src/lightning/overlay/__docs__/overlay.md +90 -0
  100. package/src/lightning/overlay/__examples__/alert/alert.html +27 -0
  101. package/src/lightning/overlay/__examples__/alert/alert.js +33 -0
  102. package/src/lightning/overlay/__examples__/basic/basic.css +7 -0
  103. package/src/lightning/overlay/__examples__/basic/basic.html +18 -0
  104. package/src/lightning/overlay/__examples__/basic/basic.js +61 -0
  105. package/src/lightning/overlay/__examples__/demo/demo.html +29 -0
  106. package/src/lightning/overlay/__examples__/demo/demo.js +40 -0
  107. package/src/lightning/overlay/__examples__/panel/panel.html +17 -0
  108. package/src/lightning/overlay/__examples__/panel/panel.js +21 -0
  109. package/src/lightning/overlay/overlay.html +3 -0
  110. package/src/lightning/overlay/overlay.js +45 -0
  111. package/src/lightning/overlayContainer/__docs__/overlayContainer.md +0 -0
  112. package/src/lightning/overlayContainer/overlayContainer.html +3 -0
  113. package/src/lightning/overlayContainer/overlayContainer.js +138 -0
  114. package/src/lightning/overlayManager/overlayManager.js +54 -0
  115. package/src/lightning/overlayUtils/overlayUtils.js +17 -0
  116. package/src/lightning/progressBar/progressBar.html +2 -1
  117. package/src/lightning/progressBar/progressBar.js +18 -1
  118. package/src/lightning/prompt/__docs__/prompt.md +102 -0
  119. package/src/lightning/prompt/__examples__disabled/basic/basic.css +7 -0
  120. package/src/lightning/prompt/__examples__disabled/basic/basic.html +8 -0
  121. package/src/lightning/prompt/__examples__disabled/basic/basic.js +15 -0
  122. package/src/lightning/prompt/prompt.css +81 -0
  123. package/src/lightning/prompt/prompt.html +8 -0
  124. package/src/lightning/prompt/prompt.js +92 -0
  125. package/src/lightning/prompt/prompt.js-meta.xml +6 -0
@@ -35,7 +35,10 @@
35
35
  aria-label={ariaLabel}
36
36
  aria-labelledby={ariaLabelledBy}
37
37
  aria-rowcount={ariaRowCount}
38
- aria-colcount={ariaColCount}>
38
+ aria-colcount={ariaColCount}
39
+ data-num-rows={data.length}
40
+ data-num-selected-rows={selectedRows.length}
41
+ data-last-rendered-row={_lastRenderedRow}>
39
42
  <template if:false={hasValidKeyField}>
40
43
  <!-- empty since keyField wasn't provided -->
41
44
  </template>
@@ -128,7 +131,8 @@
128
131
  aria-posinset={row.posInSet}
129
132
  aria-rowindex={row.ariaRowIndex}
130
133
  style={row.style}
131
- tabindex={row.tabIndex}>
134
+ tabindex={row.tabIndex}
135
+ data-row-number={row.rowNumber}>
132
136
  <template for:each={row.cells} for:item="cell">
133
137
  <template if:true={cell.isCheckbox}>
134
138
  <!-- Checkbox Cell -->
@@ -33,7 +33,9 @@
33
33
  onfocusout={handleTableFocusOut}
34
34
  aria-label={ariaLabel}
35
35
  aria-labelledby={ariaLabelledBy}
36
- aria-rowcount={ariaRowCount}>
36
+ data-num-rows={data.length}
37
+ data-num-selected-rows={selectedRows.length}
38
+ data-last-rendered-row={_lastRenderedRow}>
37
39
  <template if:false={hasValidKeyField}>
38
40
  <!-- empty since keyField wasn't provided TODO: Why is this here? Test and remove! -->
39
41
  </template>
@@ -111,7 +113,8 @@
111
113
  aria-expanded={row.isExpanded}
112
114
  aria-setsize={row.setSize}
113
115
  aria-posinset={row.posInSet}
114
- tabindex={row.tabIndex}>
116
+ tabindex={row.tabIndex}
117
+ data-row-number={row.rowNumber}>
115
118
  <template for:each={row.cells} for:item="cell">
116
119
  <template if:true={cell.isCheckbox}>
117
120
  <!-- Checkbox Cell -->
@@ -1,6 +1,13 @@
1
1
  import { getColumns } from './columns';
2
2
  import { isTreeType } from './types';
3
3
 
4
+ /**
5
+ * Retrieves the default values for the tree state indicator field names.
6
+ * These values are used to make logic decisions later and may be updated
7
+ * during normal operation.
8
+ *
9
+ * @returns {Object} The default tree state indicator field names
10
+ */
4
11
  export function getTreeStateIndicatorFieldNames() {
5
12
  return {
6
13
  children: 'hasChildren',
@@ -11,6 +18,12 @@ export function getTreeStateIndicatorFieldNames() {
11
18
  };
12
19
  }
13
20
 
21
+ /**
22
+ * Determines if any of the columns in the datatable are of a tree-type.
23
+ *
24
+ * @param {Object} state The datatable state
25
+ * @returns {Boolean} Whether any of the columns are of a tree-type
26
+ */
14
27
  export function hasTreeDataType(state) {
15
28
  const columns = getColumns(state);
16
29
  return columns.some((column) => {
@@ -18,6 +31,12 @@ export function hasTreeDataType(state) {
18
31
  });
19
32
  }
20
33
 
34
+ /**
35
+ * Retrieves the first tree-type column from the state.
36
+ *
37
+ * @param {Object} state The datatable state
38
+ * @returns {Object} The first tree-type column, else `null`
39
+ */
21
40
  export function getStateTreeColumn(state) {
22
41
  const columns = getColumns(state);
23
42
  for (let i = 0; i < columns.length; i++) {
@@ -28,6 +47,12 @@ export function getStateTreeColumn(state) {
28
47
  return null;
29
48
  }
30
49
 
50
+ /**
51
+ * Dispatches an event when a row is toggled to be expanded or collapsed.
52
+ *
53
+ * @param {String} rowKeyValue The row key being acted upon
54
+ * @param {Boolean} expanded The current expand/collapse state of the row
55
+ */
31
56
  export function fireRowToggleEvent(rowKeyValue, expanded) {
32
57
  const customEvent = new CustomEvent('privatetogglecell', {
33
58
  bubbles: true,
@@ -102,35 +102,66 @@ const EDITABLE_STANDARD_TYPES = {
102
102
  date: true,
103
103
  };
104
104
 
105
+ /**
106
+ * Determines if a supplied type is a valid datatable type.
107
+ *
108
+ * @param {String} typeName The type to validate
109
+ * @returns {Boolean} Whether the supplied type is valid
110
+ */
105
111
  export function isValidType(typeName) {
106
112
  return !!STANDARD_TYPES[typeName];
107
113
  }
108
114
 
115
+ /**
116
+ * Determines if a supplied type is a tree type.
117
+ *
118
+ * @param {String} typeName The type to validate
119
+ * @returns {Boolean} Whether the supplied type is a tree type
120
+ */
109
121
  export function isTreeType(typeName) {
110
122
  return typeName === 'tree';
111
123
  }
112
124
 
125
+ /**
126
+ * Determines if a supplied type is valid for a tree type datatable.
127
+ *
128
+ * @param {String} typeName The type to validate
129
+ * @returns {Boolean} Whether the supplied type is valid for a tree
130
+ */
131
+ export function isValidTypeForTree(typeName) {
132
+ return !!TREE_SUPPORTED_TYPES[typeName];
133
+ }
134
+
135
+ /**
136
+ * Retrieves the attributes for a given type. Additionally, verifies
137
+ * the supplied type is valid.
138
+ *
139
+ * @param {String} typeName The type to get the attributes for
140
+ * @returns {Array} An array of attributes for the supplied type
141
+ */
113
142
  export function getAttributesNames(typeName) {
114
143
  assert(
115
144
  isValidType(typeName),
116
- `your are trying to access an invalid type (${typeName})`
145
+ `You are trying to access an invalid type (${typeName})`
117
146
  );
118
-
119
- return Array.isArray(STANDARD_TYPES[typeName])
120
- ? STANDARD_TYPES[typeName]
121
- : [];
122
- }
123
-
124
- export function isValidTypeForTree(dataType) {
125
- return !!TREE_SUPPORTED_TYPES[dataType];
147
+ return getStandardTypeAttributesNames(typeName);
126
148
  }
127
149
 
150
+ /**
151
+ * Retrieves the attributes for a given type.
152
+ *
153
+ * @param {String} typeName The type to get the attributes for
154
+ * @returns {Array} An array of attributes for the supplied type
155
+ */
128
156
  function getStandardTypeAttributesNames(typeName) {
129
157
  return Array.isArray(STANDARD_TYPES[typeName])
130
158
  ? STANDARD_TYPES[typeName]
131
159
  : [];
132
160
  }
133
161
 
162
+ /**
163
+ * A class for handling valid datatable types.
164
+ */
134
165
  export default class DatatableTypes {
135
166
  privateCustomTypes = {};
136
167
  isValidTypeForTree = isValidTypeForTree;
@@ -156,6 +187,13 @@ export default class DatatableTypes {
156
187
  }
157
188
  }
158
189
 
190
+ /**
191
+ * Retrieves a type. If the specified type is not a custom type,
192
+ * lookup the type in our standard types. Otherwise, return undefined.
193
+ *
194
+ * @param {String} typeName The type to retrieve
195
+ * @returns {Object | Undefined} The type metadata
196
+ */
159
197
  getType(typeName) {
160
198
  if (this.privateCustomTypes[typeName]) {
161
199
  return this.privateCustomTypes[typeName];
@@ -169,6 +207,12 @@ export default class DatatableTypes {
169
207
  return undefined;
170
208
  }
171
209
 
210
+ /**
211
+ * Retrieves a custom type's edit template if it exists.
212
+ *
213
+ * @param {String} typeName The custom type to retrieve
214
+ * @returns {Object | Undefined} The custom type's edit template
215
+ */
172
216
  getCustomTypeEditTemplate(typeName) {
173
217
  if (this.privateCustomTypes[typeName]) {
174
218
  return this.privateCustomTypes[typeName].editTemplate;
@@ -176,10 +220,22 @@ export default class DatatableTypes {
176
220
  return undefined;
177
221
  }
178
222
 
223
+ /**
224
+ * Determines if a type is a valid custom or standard type.
225
+ *
226
+ * @param {String} typeName The type to validate
227
+ * @returns {Boolean} Whether the type is valid
228
+ */
179
229
  isValidType(typeName) {
180
230
  return !!this.getType(typeName);
181
231
  }
182
232
 
233
+ /**
234
+ * Determines if a given type is editable.
235
+ *
236
+ * @param {String} typeName The type to test
237
+ * @returns {Boolean} Whether the type is editable
238
+ */
183
239
  isEditableType(typeName) {
184
240
  return (
185
241
  !!EDITABLE_STANDARD_TYPES[typeName] ||
@@ -187,12 +243,24 @@ export default class DatatableTypes {
187
243
  );
188
244
  }
189
245
 
246
+ /**
247
+ * Determines if a given type is a non-standard type.
248
+ *
249
+ * @param {String} typeName The type to test
250
+ * @returns {Boolean} Whether the type is a non-standard type
251
+ */
190
252
  isCustomType(typeName) {
191
253
  return (
192
254
  this.getType(typeName) && this.getType(typeName).type === 'custom'
193
255
  );
194
256
  }
195
257
 
258
+ /**
259
+ * Determines whether or not a given custom type is using a standard cell layout.
260
+ *
261
+ * @param {String} typeName The custom type to test
262
+ * @returns {Boolean} Whether the custom type is using a standard cell layout
263
+ */
196
264
  isStandardCellLayoutForCustomType(typeName) {
197
265
  return (
198
266
  this.isCustomType(typeName) &&
@@ -1,8 +1,4 @@
1
- export const isObjectLike = function (value) {
2
- return typeof value === 'object' && value !== null;
3
- };
4
-
5
- const proto = {
1
+ const CLASSSET_PROTOTYPE = {
6
2
  add(className) {
7
3
  if (typeof className === 'string') {
8
4
  this[className] = true;
@@ -24,32 +20,59 @@ const proto = {
24
20
  },
25
21
  };
26
22
 
23
+ /**
24
+ * Determines if a given value is object-like.
25
+ *
26
+ * @param {*} value Any value to check for object-like status
27
+ * @returns {Boolean} Whether the value is object-like
28
+ */
29
+ export const isObjectLike = function (value) {
30
+ return typeof value === 'object' && value !== null;
31
+ };
32
+
33
+ /**
34
+ * Creates an object of CSS class names based on a given config.
35
+ * Then, attaches an interface for managing the classes.
36
+ *
37
+ * @param {String | Object} config The initial class configuration
38
+ * @returns An interface, as defined in the `proto` method.
39
+ */
27
40
  export const classSet = function (config) {
28
41
  if (typeof config === 'string') {
29
42
  const key = config;
30
43
  config = {};
31
44
  config[key] = true;
32
45
  }
33
- return Object.assign(Object.create(proto), config);
46
+ return Object.assign(Object.create(CLASSSET_PROTOTYPE), config);
34
47
  };
35
48
 
49
+ /**
50
+ * Clamps a value between a minimum and maximum value
51
+ *
52
+ * @param {Number} num The input number
53
+ * @param {Number} min The minimum value the number can be
54
+ * @param {Number} max The maximum value the number can be
55
+ * @returns The clamped number
56
+ */
36
57
  export const clamp = function (num, min, max) {
37
58
  return num <= min ? min : num >= max ? max : num;
38
59
  };
39
60
 
40
61
  /**
41
- * Tests if the value passed in is a value greater than 0
42
- * @param {Integer} value - value to test
43
- * @returns {Boolean} - true if value is > 0
62
+ * Tests if the value passed in is a value greater than 0.
63
+ *
64
+ * @param {Integer} value Value to test
65
+ * @returns {Boolean} Whether the value is greater than 0
44
66
  */
45
67
  export const isPositiveInteger = function (value) {
46
68
  return /^[0-9]*[1-9][0-9]*$/.test(value);
47
69
  };
48
70
 
49
71
  /**
50
- * Tests if the value passed in is 0 or a number greater than 0
51
- * @param {Integer} value - value to test
52
- * @returns {Boolean} - true if value is >= 0
72
+ * Tests if the value passed in is 0 or a number greater than 0.
73
+ *
74
+ * @param {Integer} value Value to test
75
+ * @returns {Boolean} Whether the value is greater than or equal to 0
53
76
  */
54
77
  export const isNonNegativeInteger = function (value) {
55
78
  return /^\d+$/.test(value);
@@ -62,11 +85,11 @@ export const isNonNegativeInteger = function (value) {
62
85
  * b. numberType - non-negative: if value >= 0
63
86
  * If the value fails the test, the fallback value is returned
64
87
  *
65
- * @param {String} attrName - name of attribute to normalize
66
- * @param {Integer/String} value - value to normalize
67
- * @param {String} numberType - number type to validate against: positive / non-negative
68
- * @param {Integer} fallback - value to return if validation fails
69
- * @returns {Integer} - Returns normalized value if validation passes; else returns fallback
88
+ * @param {String} attrName Name of attribute to normalize
89
+ * @param {Integer | String} value Value to normalize
90
+ * @param {String} numberType Number type to validate against: positive / non-negative
91
+ * @param {Integer} fallback Value to return if validation fails
92
+ * @returns {Integer} Returns normalized value if validation passes; else returns fallback
70
93
  */
71
94
  export function normalizeNumberAttribute(
72
95
  attrName,
@@ -98,11 +121,14 @@ export function normalizeNumberAttribute(
98
121
  return fallback;
99
122
  }
100
123
 
101
- // TODO: move into scroller-specific utility when more scroll-related functionality
102
- // needs to be shared between libraries
103
124
  /**
104
- * Utility for calculating the scroll offset
105
- * @param {HTMLElement} el - target element of the scroll
125
+ * Utility for calculating the scroll offset.
126
+ *
127
+ * TODO: move into scroller-specific utility when more scroll-related functionality
128
+ * needs to be shared between libraries.
129
+ *
130
+ * @param {HTMLElement} el Target element of the scroll
131
+ * @returns {Number} The scroll offset from the table's end
106
132
  */
107
133
  export function getScrollOffsetFromTableEnd(el) {
108
134
  return (
@@ -111,9 +137,10 @@ export function getScrollOffsetFromTableEnd(el) {
111
137
  }
112
138
 
113
139
  /**
114
- * Utility for converting arrays and plain objects to style strings
115
- * @param {Array|Object} style
116
- * @returns {string} representing array/object as a string
140
+ * Utility for converting arrays and plain objects to style strings.
141
+ *
142
+ * @param {Array | Object} style The CSS style array/object
143
+ * @returns {String} Representing array/object as a string
117
144
  */
118
145
  export function styleToString(style) {
119
146
  if (!Array.isArray(style)) {
@@ -0,0 +1,319 @@
1
+ import { styleToString } from './utils';
2
+
3
+ /**
4
+ * sets an initial table height in the datatable state
5
+ *
6
+ * @param {Object} state - datatable state
7
+ */
8
+ export function resetTableHeight(state) {
9
+ state.tableHeight = state.rowHeight * state.rows.length;
10
+ }
11
+
12
+ /**
13
+ * resets state properties relevant to virtualization
14
+ * rowHeights when fixedHeight is false
15
+ *
16
+ * @param {Object} state - datatable state
17
+ */
18
+ export function resetRowHeights(state) {
19
+ state.heightCache = {};
20
+ state.offsets = [0];
21
+ state.offsetRanges = [];
22
+ if (state.virtualize && state.rows.length) {
23
+ resetTableHeight(state);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * updates state properties relevant to virtualization
29
+ * rowHeights when fixedHeight is false
30
+ *
31
+ * @param {Node} template - the custom element root `this.template` from datatable.js
32
+ * @param {Object} state - datatable state
33
+ * @param {Array} renderedRows - array of rows currently being rendered
34
+ */
35
+ export function handleVariableRowHeights(template, state, renderedRows) {
36
+ const currentRange = {
37
+ start: renderedRows[0].rowIndex,
38
+ end: renderedRows[renderedRows.length - 1].rowIndex + 1,
39
+ };
40
+ let adjustFromIndex;
41
+ let adjustmentValue = 0;
42
+ let offsetRangeIndex = findOffsetRangeIndex(
43
+ state.offsetRanges,
44
+ currentRange.start
45
+ );
46
+
47
+ renderedRows.forEach((row) => {
48
+ if (!state.heightCache[row.key]) {
49
+ // need to get row actual element so we can find its height
50
+ const rowElement = template.querySelector(
51
+ `[data-row-key-value="${row.key}"]`
52
+ );
53
+ if (rowElement) {
54
+ // first rendered row needs height increased by 1 to account for missing border
55
+ let height = rowElement.getBoundingClientRect().height;
56
+ if (row.rowIndex === currentRange.start) {
57
+ height++;
58
+ }
59
+ state.heightCache[row.key] = height;
60
+
61
+ // calculate estimate of row offset
62
+ setOffset(state, row, offsetRangeIndex, height);
63
+
64
+ // update variables used to adjust later row offsets
65
+ adjustmentValue += height - state.rowHeight;
66
+ adjustFromIndex = row.rowIndex + 2;
67
+ }
68
+ }
69
+ });
70
+ state.tableHeight += adjustmentValue;
71
+ updateOffsetRanges(
72
+ state,
73
+ offsetRangeIndex,
74
+ currentRange,
75
+ adjustFromIndex,
76
+ adjustmentValue
77
+ );
78
+ updateVirtualizeStyles(template, state, renderedRows);
79
+ }
80
+
81
+ /**
82
+ * uses binary search with offsets and offsetRange
83
+ * to determine what the first visible index should be
84
+ * for a given scrollTop value
85
+ */
86
+ export function findFirstVisibleIndex(state, scrollTop) {
87
+ const { offsetRanges, offsets, rowHeight, fixedHeight, virtualize } = state;
88
+ if (virtualize && fixedHeight) {
89
+ return scrollTop / rowHeight;
90
+ }
91
+ let start = 0;
92
+ let end = offsetRanges.length - 1;
93
+
94
+ while (start <= end) {
95
+ let mid = Math.floor((start + end) / 2);
96
+ const prevRange = offsetRanges[mid];
97
+ const nextRange = offsetRanges[mid + 1];
98
+ const startOffset = offsets[prevRange.start];
99
+ const endOffset = offsets[prevRange.end];
100
+
101
+ const scrollTopAfterPrevStart = startOffset <= scrollTop;
102
+ const scrollTopInPrevRange =
103
+ scrollTopAfterPrevStart && scrollTop <= endOffset;
104
+ const scrollTopBeforeNextRange =
105
+ !nextRange || scrollTop < offsets[nextRange.start];
106
+ const scrollTopBetweenRanges =
107
+ scrollTopAfterPrevStart && scrollTopBeforeNextRange;
108
+
109
+ // check if scrollTop is in prevAdj offset values
110
+ // or between prevAdj and nextAdj offsetValues
111
+ if (scrollTopInPrevRange) {
112
+ // find offset in prevRange to use for firstVisibleIndex
113
+ return searchForOffset(state, prevRange, scrollTop);
114
+ } else if (scrollTopBetweenRanges) {
115
+ // use scrollTop and rowHeight to calculate firstVisibleIndex
116
+ const diff = scrollTop - endOffset;
117
+ const extraRows = Math.floor(diff / rowHeight);
118
+ state._firstRowOffset = diff % rowHeight;
119
+ return prevRange.end + extraRows;
120
+ }
121
+ // update start or end for next round of binary search
122
+ if (scrollTop < startOffset) {
123
+ end = mid - 1;
124
+ } else {
125
+ start = mid + 1;
126
+ }
127
+ }
128
+ return -1;
129
+ }
130
+
131
+ /**
132
+ * determines the rowIndex for given scrollTop
133
+ * within a provided offset range using binary search
134
+ * also sets firstRowOffset to correct value
135
+ *
136
+ * @param {object} range - object with start and end index for binary search
137
+ * @param {number} scrollTop - value to find offset rowIndex for
138
+ * @returns {number} representing firstVisibleIndex for given scrollTop
139
+ */
140
+ function searchForOffset(state, range, scrollTop) {
141
+ let { start, end } = range;
142
+ const offsets = state.offsets;
143
+
144
+ while (start <= end) {
145
+ const mid = Math.floor((start + end) / 2);
146
+ const currentOffsetUnderScrollTop = offsets[mid] <= scrollTop;
147
+ const nextOffsetUnderScrollTop =
148
+ offsets[mid + 1] && offsets[mid + 1] <= scrollTop;
149
+
150
+ if (currentOffsetUnderScrollTop && !nextOffsetUnderScrollTop) {
151
+ // store how many pixels scrollTop is from top of row
152
+ state.firstRowOffset = scrollTop - offsets[mid];
153
+ return mid;
154
+ } else if (currentOffsetUnderScrollTop) {
155
+ start = mid + 1;
156
+ } else {
157
+ end = mid - 1;
158
+ }
159
+ }
160
+ return -1;
161
+ }
162
+
163
+ /**
164
+ * uses a binary search to find the offsetRange index that
165
+ * encompasses the provided row index, or the one immediately
166
+ * before if no offsetRange encompasses the provided rowIndex
167
+ */
168
+ function findOffsetRangeIndex(offsetRanges, rowIndex) {
169
+ let start = 0;
170
+ let end = offsetRanges.length - 1;
171
+ while (start <= end) {
172
+ let mid = Math.floor((start + end) / 2);
173
+ const currentRangeStartsBeforeRowIndex =
174
+ offsetRanges[mid].start <= rowIndex;
175
+ const nextRangeStartsBeforeRowIndex =
176
+ offsetRanges[mid + 1] && offsetRanges[mid + 1].start <= rowIndex;
177
+
178
+ // check if rowIndex is between start of range at "mid"
179
+ // and start of range at "mid + 1" (or if there is no "mid + 1")
180
+ if (
181
+ currentRangeStartsBeforeRowIndex &&
182
+ !nextRangeStartsBeforeRowIndex
183
+ ) {
184
+ return mid;
185
+ } else if (currentRangeStartsBeforeRowIndex) {
186
+ // look for earlier offsetRanges
187
+ start = mid + 1;
188
+ } else {
189
+ // look for later offsetRanges
190
+ end = mid - 1;
191
+ }
192
+ }
193
+ return -1;
194
+ }
195
+
196
+ /**
197
+ * sets the offset value for a given row and the next
198
+ * based on the closest set offset value, the default
199
+ * row height and the height of the current row
200
+ *
201
+ * @param {object} state - datatable state
202
+ * @param {object} row - specific row object from state
203
+ * @param {number} offsetRangeIndex - index of offset range to use for row
204
+ * @param {number} height - height of row's node
205
+ */
206
+ function setOffset(state, row, offsetRangeIndex, height) {
207
+ let currentRange = state.offsetRanges[offsetRangeIndex];
208
+ let currentOffset = state.offsets[row.rowIndex];
209
+ // if no offset is set for current row, estimate it
210
+ // based on most recent offset and default rowHeight
211
+ if (!currentOffset) {
212
+ currentOffset = 0;
213
+ if (currentRange) {
214
+ const baseOffset = state.offsets[currentRange.end];
215
+ const estimatedRowOffsets =
216
+ (row.rowIndex - currentRange.end) * state.rowHeight;
217
+ currentOffset = baseOffset + estimatedRowOffsets;
218
+ state.offsets[row.rowIndex] = currentOffset;
219
+ }
220
+ }
221
+ // set next offset based on current offset and height
222
+ state.offsets[row.rowIndex + 1] = currentOffset + height;
223
+ }
224
+
225
+ /**
226
+ * merges or adds new offset range and
227
+ * updates offsets and range values as needed
228
+ *
229
+ * @param {number} offsetRangeIndex
230
+ * @param {*} state - datatable state
231
+ * @param {object} currentRange - range for current rendered rows
232
+ * @param {number} adjustFromIndex - first index to increase by rangeValue
233
+ * @param {number} rangeValue - amount to increase offsets after adjustFromIndex
234
+ */
235
+ function updateOffsetRanges(
236
+ state,
237
+ offsetRangeIndex,
238
+ currentRange,
239
+ adjustFromIndex,
240
+ adjustmentValue
241
+ ) {
242
+ const { offsets, offsetRanges } = state;
243
+ let prevRange = offsetRanges[offsetRangeIndex];
244
+ let nextRange = offsetRanges[offsetRangeIndex + 1];
245
+ const overlapsWithPrevRange =
246
+ prevRange && checkOverlap(prevRange, currentRange);
247
+ const overlapsWithNextRange =
248
+ nextRange && checkOverlap(currentRange, nextRange);
249
+
250
+ // update remaining values in next range by
251
+ // adjustment value if we're overlapping it
252
+ if (overlapsWithNextRange && adjustmentValue) {
253
+ for (let i = adjustFromIndex; i <= nextRange.end; i++) {
254
+ offsets[i] = offsets[i] + adjustmentValue;
255
+ }
256
+ }
257
+
258
+ // update state.offsetRanges
259
+ if (overlapsWithPrevRange && overlapsWithNextRange) {
260
+ nextRange.start = prevRange.start;
261
+ nextRange.end = Math.max(currentRange.end, nextRange.end);
262
+ state.offsetRanges.splice(offsetRangeIndex, 1); // removes prevRange
263
+ } else if (overlapsWithPrevRange) {
264
+ prevRange.end = Math.max(prevRange.end, currentRange.end);
265
+ } else if (overlapsWithNextRange) {
266
+ nextRange.start = currentRange.start;
267
+ nextRange.end = Math.max(currentRange.end, nextRange.end);
268
+ // increase offsetRangeIndex; since it's values have already been updated
269
+ // we want to skip it when adjusting offsets in later ranges
270
+ offsetRangeIndex = offsetRangeIndex + 1;
271
+ } else {
272
+ state.offsetRanges.splice(offsetRangeIndex + 1, 0, currentRange);
273
+ // need to increase offsetRangeIndex so we don't
274
+ // unnecessarily add adjustmentValue to currentRange
275
+ offsetRangeIndex = offsetRangeIndex + 1;
276
+ }
277
+
278
+ // loop through every offset range after the current one
279
+ // and increase the offset for each index by adjustmentValue
280
+ if (offsetRangeIndex >= 0) {
281
+ for (let i = offsetRangeIndex + 1; i < offsetRanges.length; i++) {
282
+ const { start: rangeStart, end: rangeEnd } = offsetRanges[i];
283
+ for (let j = rangeStart; j <= rangeEnd; j++) {
284
+ offsets[j] = offsets[j] + adjustmentValue;
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * compares start/end of two ranges to see if the values overlap
292
+ * used to determine if renderedRows are part of an existent offsetRange
293
+ * or if a new offsetRange will need to be added
294
+ */
295
+ function checkOverlap(range1, range2) {
296
+ return (
297
+ (range1.start <= range2.start && range2.start <= range1.end) ||
298
+ (range1.start <= range2.end && range2.end <= range1.end)
299
+ );
300
+ }
301
+
302
+ /**
303
+ * updates scrollTop and top values for rows when
304
+ * using virtualization with fixedHeight of false
305
+ */
306
+ function updateVirtualizeStyles(template, state, renderedRows) {
307
+ // update scrollTop so firstVisibleIndex is correctly placed in viewport
308
+ const scrollerY = template.querySelector('.slds-scrollable_y');
309
+ scrollerY.scrollTop =
310
+ state.offsets[state.firstVisibleIndex] + state.firstRowOffset;
311
+
312
+ // update top of rendered rows based on offsets
313
+ renderedRows.forEach((row) => {
314
+ row.style = styleToString({
315
+ position: 'absolute',
316
+ top: `${state.offsets[row.rowIndex]}px`,
317
+ });
318
+ });
319
+ }