lightning-base-components 1.18.1-alpha → 1.18.2-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 (215) hide show
  1. package/metadata/raptor.json +5 -0
  2. package/package.json +43 -1
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/src/lightning/accordion/accordion.css +12 -0
  5. package/src/lightning/accordion/accordion.html +3 -1
  6. package/src/lightning/accordion/accordion.js +4 -2
  7. package/src/lightning/accordion/accordion.slds.css +671 -0
  8. package/src/lightning/accordionSection/accordion-section.slds.css +647 -0
  9. package/src/lightning/accordionSection/accordionSection.css +14 -0
  10. package/src/lightning/accordionSection/accordionSection.html +23 -19
  11. package/src/lightning/accordionSection/accordionSection.js +29 -2
  12. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +21 -9
  13. package/src/lightning/ariaObserver/ariaObserver.js +185 -154
  14. package/src/lightning/ariaObserver/polyfill.js +639 -0
  15. package/src/lightning/avatar/avatar.css +2 -0
  16. package/src/lightning/avatar/avatar.html +2 -0
  17. package/src/lightning/avatar/avatar.js +18 -15
  18. package/src/lightning/avatar/avatar.slds.css +272 -0
  19. package/src/lightning/baseCombobox/base-combobox.slds.css +1585 -0
  20. package/src/lightning/baseCombobox/baseCombobox.css +11 -1
  21. package/src/lightning/baseCombobox/baseCombobox.html +154 -146
  22. package/src/lightning/baseCombobox/baseCombobox.js +82 -46
  23. package/src/lightning/baseCombobox/spinner.slds.css +438 -0
  24. package/src/lightning/baseComboboxItem/baseComboboxItem.js +4 -2
  25. package/src/lightning/baseComboboxItem/inline.css +2 -0
  26. package/src/lightning/breadcrumb/breadcrumb.css +2 -2
  27. package/src/lightning/breadcrumb/breadcrumb.js +4 -2
  28. package/src/lightning/breadcrumb/breadcrumb.slds.css +2 -7
  29. package/src/lightning/breadcrumbs/breadcrumbs.css +2 -2
  30. package/src/lightning/breadcrumbs/breadcrumbs.js +3 -2
  31. package/src/lightning/breadcrumbs/breadcrumbs.slds.css +7 -1
  32. package/src/lightning/button/__examples__/inverse/inverse.css +8 -0
  33. package/src/lightning/button/__examples__/inverse/inverse.html +3 -2
  34. package/src/lightning/button/button.css +2 -0
  35. package/src/lightning/button/button.html +4 -2
  36. package/src/lightning/button/button.js +21 -0
  37. package/src/lightning/button/button.slds.css +527 -0
  38. package/src/lightning/buttonGroup/buttonGroup.css +2 -2
  39. package/src/lightning/buttonGroup/buttonGroup.js +3 -2
  40. package/src/lightning/buttonIcon/button-icon.slds.css +215 -453
  41. package/src/lightning/buttonIcon/buttonIcon.css +2 -2
  42. package/src/lightning/buttonIcon/buttonIcon.js +4 -0
  43. package/src/lightning/buttonIconStateful/button-icon-stateful.slds.css +215 -453
  44. package/src/lightning/buttonIconStateful/buttonIconStateful.css +2 -2
  45. package/src/lightning/buttonMenu/{dropdown.slds.css → button-menu.slds.css} +853 -217
  46. package/src/lightning/buttonMenu/buttonMenu.css +2 -2
  47. package/src/lightning/buttonMenu/buttonMenu.html +2 -2
  48. package/src/lightning/buttonMenu/buttonMenu.js +10 -14
  49. package/src/lightning/buttonStateful/button-stateful.slds.css +225 -457
  50. package/src/lightning/buttonStateful/buttonStateful.css +2 -2
  51. package/src/lightning/buttonStateful/buttonStateful.js +3 -2
  52. package/src/lightning/calendar/__examples__/basic/basic.html +7 -0
  53. package/src/lightning/calendar/__examples__/basic/basic.js +3 -0
  54. package/src/lightning/calendar/calendar.css +3 -0
  55. package/src/lightning/calendar/calendar.html +12 -9
  56. package/src/lightning/calendar/calendar.js +15 -1
  57. package/src/lightning/calendar/calendar.slds.css +2048 -0
  58. package/src/lightning/card/card.css +2 -2
  59. package/src/lightning/card/card.js +3 -2
  60. package/src/lightning/card/card.slds.css +141 -88
  61. package/src/lightning/colorPickerCustom/colorPickerCustom.css +2 -2
  62. package/src/lightning/colorPickerCustom/colorPickerCustom.js +3 -2
  63. package/src/lightning/colorPickerPanel/color-picker-panel.slds.css +11 -38
  64. package/src/lightning/colorPickerPanel/colorPickerPanel.css +3 -2
  65. package/src/lightning/colorPickerPanel/colorPickerPanel.js +4 -2
  66. package/src/lightning/colorPickerPanel/popover.slds.css +121 -0
  67. package/src/lightning/combobox/combobox.css +4 -0
  68. package/src/lightning/combobox/combobox.html +31 -29
  69. package/src/lightning/combobox/combobox.js +21 -4
  70. package/src/lightning/combobox/combobox.slds.css +13 -0
  71. package/src/lightning/combobox/form-element.slds.css +281 -0
  72. package/src/lightning/configProvider/defaultConfig.js +2 -1
  73. package/src/lightning/datepicker/datepicker.css +3 -0
  74. package/src/lightning/datepicker/datepicker.html +7 -4
  75. package/src/lightning/datepicker/datepicker.js +73 -19
  76. package/src/lightning/datepicker/form-element.slds.css +281 -0
  77. package/src/lightning/datepicker/input-text.slds.css +398 -0
  78. package/src/lightning/datetimepicker/datetimepicker.css +3 -0
  79. package/src/lightning/datetimepicker/datetimepicker.html +9 -3
  80. package/src/lightning/datetimepicker/datetimepicker.js +39 -35
  81. package/src/lightning/datetimepicker/form-element.slds.css +281 -0
  82. package/src/lightning/datetimepicker/input-text.slds.css +398 -0
  83. package/src/lightning/dualListbox/dualListbox.css +2 -2
  84. package/src/lightning/dualListbox/dualListbox.html +3 -3
  85. package/src/lightning/dualListbox/dualListbox.js +31 -6
  86. package/src/lightning/dualListbox/form-element.slds.css +83 -34
  87. package/src/lightning/dualListbox/keyboard.js +20 -1
  88. package/src/lightning/dynamicIcon/dynamicIcon.js +3 -2
  89. package/src/lightning/dynamicIcon/ellie.css +1 -1
  90. package/src/lightning/dynamicIcon/eq.css +1 -1
  91. package/src/lightning/dynamicIcon/score.css +1 -1
  92. package/src/lightning/dynamicIcon/strength.css +1 -1
  93. package/src/lightning/dynamicIcon/trend.css +1 -1
  94. package/src/lightning/dynamicIcon/waffle.css +1 -1
  95. package/src/lightning/formattedRichText/linkify.js +2 -2
  96. package/src/lightning/helptext/form-element.slds.css +83 -34
  97. package/src/lightning/helptext/help-text.slds.css +215 -453
  98. package/src/lightning/helptext/helptext.css +2 -2
  99. package/src/lightning/helptext/helptext.js +3 -2
  100. package/src/lightning/i18nCldrOptions/README.md +5 -0
  101. package/src/lightning/i18nService/README.md +5 -0
  102. package/src/lightning/icon/icon.css +2 -2
  103. package/src/lightning/icon/icon.js +16 -2
  104. package/src/lightning/icon/icon.slds.css +29 -17
  105. package/src/lightning/icon/iconColors.js +1 -0
  106. package/src/lightning/input/__examples__/text/text.html +0 -1
  107. package/src/lightning/input/form-element.slds.css +281 -0
  108. package/src/lightning/input/input-checkbox.slds.css +3 -12
  109. package/src/lightning/input/input-text.slds.css +239 -128
  110. package/src/lightning/input/input.css +2 -1
  111. package/src/lightning/input/input.html +8 -8
  112. package/src/lightning/input/input.js +107 -73
  113. package/src/lightning/internationalizationLibrary/README.md +24 -0
  114. package/src/lightning/internationalizationLibrary/utils.js +4 -1
  115. package/src/lightning/layout/__docs__/layout.md +1 -1
  116. package/src/lightning/layout/__examples__/simple/simple.css +1 -1
  117. package/src/lightning/layout/layout.css +5 -1
  118. package/src/lightning/layout/layout.js +4 -2
  119. package/src/lightning/layoutItem/__examples__/alignmentBump/alignmentBump.css +1 -1
  120. package/src/lightning/layoutItem/__examples__/sizePerDevice/sizePerDevice.css +0 -1
  121. package/src/lightning/layoutItem/layoutItem.css +5 -0
  122. package/src/lightning/layoutItem/layoutItem.js +4 -2
  123. package/src/lightning/menuDivider/menu-divider.slds.css +15 -0
  124. package/src/lightning/menuDivider/menuDivider.css +3 -0
  125. package/src/lightning/menuDivider/menuDivider.html +1 -1
  126. package/src/lightning/menuDivider/menuDivider.js +4 -2
  127. package/src/lightning/menuItem/menu-item.slds.css +140 -0
  128. package/src/lightning/menuItem/menuItem.css +3 -0
  129. package/src/lightning/menuItem/menuItem.html +43 -41
  130. package/src/lightning/menuItem/menuItem.js +4 -4
  131. package/src/lightning/menuSubheader/menu-subheader.slds.css +22 -0
  132. package/src/lightning/menuSubheader/menuSubheader.css +3 -0
  133. package/src/lightning/menuSubheader/menuSubheader.html +3 -1
  134. package/src/lightning/menuSubheader/menuSubheader.js +4 -6
  135. package/src/lightning/modal/__docs__/modal.md +3 -1
  136. package/src/lightning/modal/__modalUtils__/modalContainerTestConstants.js +267 -0
  137. package/src/lightning/modal/__modalUtils__/modalContainerTestMethods.js +1165 -0
  138. package/src/lightning/modal/__modalUtils__/modalContainerTestMockData.js +131 -0
  139. package/src/lightning/modal/modal.js +1 -1
  140. package/src/lightning/pill/avatar.slds.css +272 -0
  141. package/src/lightning/pill/link.css +3 -0
  142. package/src/lightning/pill/link.html +1 -1
  143. package/src/lightning/pill/pill.js +29 -9
  144. package/src/lightning/pill/pill.slds.css +168 -0
  145. package/src/lightning/pill/plain.css +3 -0
  146. package/src/lightning/pill/plain.html +1 -1
  147. package/src/lightning/pill/plainLink.css +3 -0
  148. package/src/lightning/pill/plainLink.html +1 -1
  149. package/src/lightning/pillContainer/barePillContainer.css +3 -0
  150. package/src/lightning/pillContainer/barePillContainer.html +1 -2
  151. package/src/lightning/pillContainer/listbox.slds.css +267 -0
  152. package/src/lightning/pillContainer/pill-container.slds.css +22 -0
  153. package/src/lightning/pillContainer/pill.slds.css +168 -0
  154. package/src/lightning/pillContainer/pillContainer.js +7 -3
  155. package/src/lightning/pillContainer/standardPillContainer.css +4 -0
  156. package/src/lightning/pillContainer/standardPillContainer.html +2 -2
  157. package/src/lightning/popup/popover.slds.css +119 -119
  158. package/src/lightning/popup/popup.css +1 -2
  159. package/src/lightning/popup/popup.js +3 -2
  160. package/src/lightning/positionLibrary/elementProxy.js +7 -2
  161. package/src/lightning/positionLibrary/util.js +8 -0
  162. package/src/lightning/primitiveBubble/primitiveBubble.css +2 -2
  163. package/src/lightning/primitiveBubble/primitiveBubble.js +4 -2
  164. package/src/lightning/primitiveButton/primitiveButton.js +5 -4
  165. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +29 -21
  166. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +4 -0
  167. package/src/lightning/primitiveColorpickerButton/color-picker-button.slds.css +31 -19
  168. package/src/lightning/primitiveColorpickerButton/primitiveColorpickerButton.css +2 -2
  169. package/src/lightning/primitiveColorpickerButton/primitiveColorpickerButton.js +5 -3
  170. package/src/lightning/primitiveIcon/icon.slds.css +209 -0
  171. package/src/lightning/primitiveIcon/primitiveIcon.css +2 -1
  172. package/src/lightning/primitiveIcon/primitiveIcon.html +1 -1
  173. package/src/lightning/primitiveIcon/primitiveIcon.js +18 -11
  174. package/src/lightning/progressStep/progressStep.js +10 -13
  175. package/src/lightning/radioGroup/radioGroup.css +2 -1
  176. package/src/lightning/radioGroup/radioGroup.js +4 -2
  177. package/src/lightning/select/form-element.slds.css +83 -34
  178. package/src/lightning/select/select.css +2 -2
  179. package/src/lightning/select/select.js +4 -2
  180. package/src/lightning/select/select.slds.css +86 -34
  181. package/src/lightning/sldsCommon/sldsCommon.css +135 -75
  182. package/src/lightning/spinner/spinner.css +2 -2
  183. package/src/lightning/spinner/spinner.js +4 -2
  184. package/src/lightning/tabBar/tab-bar.slds.css +334 -0
  185. package/src/lightning/tabBar/tabBar.css +2 -0
  186. package/src/lightning/tabBar/tabBar.html +4 -3
  187. package/src/lightning/tabBar/tabBar.js +30 -3
  188. package/src/lightning/tabset/tabset.html +5 -4
  189. package/src/lightning/tabset/tabset.js +29 -11
  190. package/src/lightning/timepicker/form-element.slds.css +281 -0
  191. package/src/lightning/timepicker/timepicker.css +3 -0
  192. package/src/lightning/timepicker/timepicker.html +5 -1
  193. package/src/lightning/timepicker/timepicker.js +18 -15
  194. package/src/lightning/timepicker/timepicker.slds.css +18 -0
  195. package/src/lightning/tooltipLibrary/tooltipLibrary.js +21 -19
  196. package/src/lightning/utilsPrivate/browser.js +5 -3
  197. package/src/lightning/utilsPrivate/os.js +6 -4
  198. package/src/lightning/utilsPrivate/ssr.js +4 -0
  199. package/src/lightning/utilsPrivate/utilsPrivate.js +2 -0
  200. package/src/lightning/verticalNavigation/verticalNavigation.css +2 -1
  201. package/src/lightning/verticalNavigation/verticalNavigation.js +3 -2
  202. package/src/lightning/verticalNavigationSection/verticalNavigationSection.css +2 -1
  203. package/src/lightning/verticalNavigationSection/verticalNavigationSection.js +3 -2
  204. package/src/lightning/accordion/__perf__DISABLED/accordion-perf-utils.js +0 -76
  205. package/src/lightning/accordion/__perf__DISABLED/accordion10Multiple25SectionEach.perf.js +0 -57
  206. package/src/lightning/accordion/__perf__DISABLED/accordion10Simple25SectionEach.perf.js +0 -37
  207. package/src/lightning/accordion/__perf__DISABLED/accordionMultiple50Section.perf.js +0 -45
  208. package/src/lightning/accordion/__perf__DISABLED/accordionSimple50Section.perf.js +0 -35
  209. package/src/lightning/accordion/__perf__DISABLED/container/container.html +0 -15
  210. package/src/lightning/accordion/__perf__DISABLED/container/container.js +0 -7
  211. package/src/lightning/positionLibrary/__component__/positionLibraryBounding.spec.js +0 -319
  212. package/src/lightning/positionLibrary/__component__/x/bounding/bounding.css +0 -16
  213. package/src/lightning/positionLibrary/__component__/x/bounding/bounding.html +0 -36
  214. package/src/lightning/positionLibrary/__component__/x/bounding/bounding.js +0 -122
  215. /package/src/lightning/{baseCombobox → baseComboboxItem}/listbox.slds.css +0 -0
@@ -0,0 +1,639 @@
1
+ // borrowed from bootstrap
2
+ const screenReaderOnlyStyles = `
3
+ position: absolute;
4
+ width: 1px;
5
+ height: 1px;
6
+ padding: 0;
7
+ margin: -1px;
8
+ overflow: hidden;
9
+ clip: rect(0,0,0,0);
10
+ border: 0;
11
+ `;
12
+
13
+ let microtaskQueued = false;
14
+ const queue = [];
15
+
16
+ function flushQueue () {
17
+ const sortedQueue = [...queue].sort((a, b) => a.priority - b.priority);
18
+ queue.length = 0;
19
+ microtaskQueued = false;
20
+ sortedQueue.forEach(({ callback }) => callback());
21
+ }
22
+
23
+ // Queue a microtask, but execute with the given priority (lower priority runs first)
24
+ function queueMicrotaskWithPriority (priority, callback) {
25
+ queue.push({ callback, priority });
26
+ if (microtaskQueued) {
27
+ return
28
+ }
29
+ microtaskQueued = true;
30
+ Promise.resolve().then(flushQueue);
31
+ }
32
+
33
+ // borrowed from https://github.com/salesforce/kagekiri/blob/cfd0699/src/index.js#L18-L31
34
+ function getChildNodesIgnoringShadowRoot (node) {
35
+ if (node.shadowRoot) {
36
+ // shadow host
37
+ return node.shadowRoot.childNodes
38
+ } else if (typeof node.assignedNodes === 'function') {
39
+ // slot
40
+ // If the slot has assigned elements, then those
41
+ // should be shown. Otherwise the (default) children should be shown.
42
+ const assigned = node.assignedNodes();
43
+ return assigned.length ? assigned : node.childNodes
44
+ }
45
+ // regular element
46
+ return node.childNodes
47
+ }
48
+
49
+ // borrowed from https://github.com/salesforce/kagekiri/blob/cfd0699/src/index.js#L72-L87
50
+ function getParentIgnoringShadowRoot (element) {
51
+ // If an element is slotted, ignore the "real" parent and use the shadow DOM parent.
52
+ // Unless the slot is also slotted; just return the parent element in this case.
53
+ if (
54
+ typeof element.assignedNodes !== 'function' &&
55
+ element.assignedSlot &&
56
+ element.assignedSlot.parentElement
57
+ ) {
58
+ return element.assignedSlot.parentElement
59
+ }
60
+ if (element.parentElement) {
61
+ return element.parentElement
62
+ }
63
+ // if an element is inside the shadow DOM, break outside of it
64
+ const rootNode = element.getRootNode();
65
+ /* istanbul ignore else */
66
+ if (rootNode !== document) {
67
+ return rootNode.host
68
+ }
69
+ return null
70
+ }
71
+
72
+ function isAncestor (node, possibleAncestor) {
73
+ let ancestor = node;
74
+ while (ancestor !== null && ancestor !== undefined) {
75
+ ancestor = getParentIgnoringShadowRoot(ancestor);
76
+ if (ancestor === possibleAncestor) {
77
+ return true
78
+ }
79
+ }
80
+ return false
81
+ }
82
+
83
+ // MutationObserver that deeply observes open shadow roots
84
+
85
+ class DeepMutationObserver {
86
+ constructor (rootNode) {
87
+ this._observers = [];
88
+ this._callbacks = [];
89
+
90
+ const observedNodes = [];
91
+
92
+ // Avoid adding a mutation observer to a node when its ancestor is already being observed
93
+ // When we cross shadow boundaries, Node.contains() will automatically return false because
94
+ // it's not an ancestor-descendant relationship in the same shadow root
95
+ const alreadyObserved = (node) => {
96
+ return observedNodes.some((otherNode) => otherNode.contains(node))
97
+ };
98
+
99
+ const observe = (node) => {
100
+ if (!alreadyObserved(node)) {
101
+ observedNodes.push(node);
102
+ const observer = new MutationObserver(() =>
103
+ this._mutationCallback()
104
+ );
105
+ observer.observe(node, {
106
+ subtree: true,
107
+ attributes: true,
108
+ childList: true,
109
+ characterData: true
110
+ });
111
+ this._observers.push(observer);
112
+ }
113
+ getChildNodesIgnoringShadowRoot(node).forEach((child) =>
114
+ observe(child)
115
+ );
116
+ };
117
+ observe(rootNode);
118
+ }
119
+
120
+ onMutation (callback) {
121
+ this._callbacks.push(callback);
122
+ }
123
+
124
+ _mutationCallback () {
125
+ queueMicrotaskWithPriority(/* priority */ 0, () =>
126
+ this._callbacks.forEach((callback) => callback())
127
+ );
128
+ }
129
+
130
+ disconnect () {
131
+ this._observers.forEach((observer) => observer.disconnect());
132
+ this._observers = undefined;
133
+ this._callbacks = undefined;
134
+ }
135
+ }
136
+
137
+ // Figure out what tasks we actually need to do, based on the minimal
138
+
139
+ function collateTasks (tasks) {
140
+ const mapOfFromRootsToTasks = new Map();
141
+ tasks.forEach((task) => {
142
+ // TODO: fast path if both nodes have the same shadow root
143
+ const { fromNode } = task;
144
+ const fromRoot = fromNode.getRootNode();
145
+ let collatedTask = mapOfFromRootsToTasks.get(fromRoot);
146
+ if (!collatedTask) {
147
+ collatedTask = {
148
+ relationships: [],
149
+ redundantChildNodes: new Set()
150
+ };
151
+ mapOfFromRootsToTasks.set(fromRoot, collatedTask);
152
+ }
153
+ collatedTask.relationships.push(task);
154
+ });
155
+
156
+ // find the common ancestor for all toNodes
157
+ mapOfFromRootsToTasks.forEach((value) => {
158
+ const { relationships, redundantChildNodes } = value;
159
+
160
+ const allToNodes = relationships
161
+ .map((relationship) => relationship.toNodes)
162
+ .flat();
163
+
164
+ for (let i = 0; i < allToNodes.length; i++) {
165
+ for (let j = i + 1; j < allToNodes.length; j++) {
166
+ const toNodeA = allToNodes[i];
167
+ const toNodeB = allToNodes[j];
168
+
169
+ if (toNodeA && toNodeB && i !== j) {
170
+ if (isAncestor(toNodeA, toNodeB)) {
171
+ // B is an ancestor of A
172
+ redundantChildNodes.add(toNodeA);
173
+ } else if (isAncestor(toNodeB, toNodeA)) {
174
+ // A is an ancestor of B
175
+ redundantChildNodes.add(toNodeB);
176
+ }
177
+ }
178
+ }
179
+ }
180
+ });
181
+
182
+ return mapOfFromRootsToTasks
183
+ }
184
+
185
+ function getAttributes (node) {
186
+ const res = {};
187
+ const { attributes } = node;
188
+ for (let i = 0; i < attributes.length; i++) {
189
+ const attribute = attributes[i];
190
+ res[attribute.name] = attribute.value;
191
+ }
192
+ return res
193
+ }
194
+
195
+ const stableIds = new WeakMap();
196
+
197
+ function generateId () {
198
+ return 'shadow-aria-' + Math.floor(Math.random() * 1000000000).toString(16)
199
+ }
200
+
201
+ function getStableId (referenceNode) {
202
+ let id = stableIds.get(referenceNode);
203
+ if (!id) {
204
+ id = generateId();
205
+ stableIds.set(referenceNode, id);
206
+ }
207
+ return id
208
+ }
209
+
210
+ // Loosely based on https://github.com/focus-trap/tabbable/blob/67452d0/src/index.js#L1-L13
211
+ // We don't actually need to support the full list; only the things that actually get mirrored (e.g. tag name)
212
+ // Also we are fine with false positives.
213
+
214
+ const TABBABLE_TAG_NAMES = new Set([
215
+ 'a',
216
+ 'audio',
217
+ 'button',
218
+ 'details',
219
+ 'input',
220
+ 'select',
221
+ 'summary',
222
+ 'textarea',
223
+ 'video'
224
+ ]);
225
+
226
+ function redirectEvents (fromNode, toNode) {
227
+ if (fromNode && toNode) {
228
+ redirectFocusEvent(fromNode, toNode);
229
+ }
230
+ }
231
+
232
+ function redirectFocusEvent (fromNode, toNode) {
233
+ fromNode.addEventListener('focus', (event) => {
234
+ event.preventDefault();
235
+ event.stopImmediatePropagation();
236
+ toNode.dispatchEvent(new event.constructor(event.type, event));
237
+ toNode.focus();
238
+ });
239
+ }
240
+
241
+ // These styles have an impact on accessibility (e.g. accessible name calculation, DOM hierarchy calculation),
242
+ // so they must be mirrored
243
+ const STYLE_PROPS_TO_MIRROR = ['display', 'visibility'];
244
+
245
+ function mirrorNode (node, existingNode) {
246
+ if (node.nodeType === Node.TEXT_NODE) {
247
+ if (existingNode && existingNode.nodeType === Node.TEXT_NODE) {
248
+ if (existingNode.textContent !== node.textContent) {
249
+ existingNode.textContent = node.textContent;
250
+ }
251
+ return existingNode
252
+ }
253
+ return node.cloneNode()
254
+ }
255
+ if (node.nodeType !== Node.ELEMENT_NODE) {
256
+ // comment or other unsupported node
257
+ return document.createComment('shadow-aria-deleted')
258
+ }
259
+
260
+ let { tagName } = node;
261
+ if (['style', 'link', 'script'].includes(tagName.toLowerCase())) {
262
+ // semantically useless
263
+ return document.createComment('shadow-aria-deleted')
264
+ }
265
+ // For custom elements and slots, just render a <div> The problem with custom elements
266
+ // is that they may bring their own shadow DOM, which we don't want. The problem
267
+ // with slots is that they will try to render slot content since we're inside a shadow root.
268
+ if (tagName.includes('-') || tagName.toLowerCase() === 'slot') {
269
+ tagName = 'div';
270
+ }
271
+ let oldAttributes;
272
+ let mirroredNode;
273
+ if (
274
+ existingNode &&
275
+ existingNode.nodeType === Node.ELEMENT_NODE &&
276
+ existingNode.tagName.toLowerCase() === tagName.toLowerCase()
277
+ ) {
278
+ // reuse existing node
279
+ mirroredNode = existingNode;
280
+ oldAttributes = getAttributes(mirroredNode);
281
+ } else {
282
+ // create a brand-new node
283
+ mirroredNode = document.createElement(tagName);
284
+ }
285
+ const newAttributes = Object.fromEntries(
286
+ [...Object.entries(getAttributes(node))].filter(
287
+ ([name]) =>
288
+ name.toLowerCase().startsWith('aria-') ||
289
+ name.toLowerCase() === 'role'
290
+ )
291
+ );
292
+
293
+ const computedStyle = getComputedStyle(node);
294
+
295
+ let newStyle = '';
296
+
297
+ STYLE_PROPS_TO_MIRROR.forEach(
298
+ (prop) => (newStyle += `${prop}:${computedStyle[prop]};`)
299
+ );
300
+
301
+ if (computedStyle?.display !== 'contents') {
302
+ // Firefox gets confused by IDs on elements with `display:contents`
303
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1762999
304
+ newAttributes.id = getStableId(node);
305
+ }
306
+
307
+ newAttributes.style = newStyle;
308
+
309
+ if (TABBABLE_TAG_NAMES.has(tagName.toLowerCase())) {
310
+ newAttributes.tabindex = '-1';
311
+ }
312
+
313
+ Object.entries(newAttributes).forEach(([name, value]) => {
314
+ if (!oldAttributes || oldAttributes[name] !== value) {
315
+ mirroredNode.setAttribute(name, value);
316
+ }
317
+ });
318
+
319
+ if (oldAttributes) {
320
+ Object.keys(oldAttributes).forEach((name) => {
321
+ if (!(name in newAttributes)) {
322
+ mirroredNode.removeAttribute(name);
323
+ }
324
+ });
325
+ }
326
+
327
+ redirectEvents(mirroredNode, node);
328
+
329
+ return mirroredNode
330
+ }
331
+
332
+ function patchMirrorDomTree (root, existingRoot, trackedNodes) {
333
+ const trackedNodesToMirroredNodes = new Map();
334
+
335
+ const mirrorNodeRecursive = (node, existingNode) => {
336
+ const mirroredNode = mirrorNode(node, existingNode);
337
+ if (mirroredNode.nodeType === Node.COMMENT_NODE) {
338
+ // ignore child nodes of comments; we don't care
339
+ return mirroredNode
340
+ }
341
+ if (trackedNodes.has(node)) {
342
+ trackedNodesToMirroredNodes.set(node, mirroredNode);
343
+ }
344
+ const childNodes = getChildNodesIgnoringShadowRoot(node);
345
+ if (
346
+ existingNode &&
347
+ existingNode.childNodes.length === childNodes.length
348
+ ) {
349
+ // patch
350
+ for (let i = 0; i < childNodes.length; i++) {
351
+ const existingChild = existingNode.childNodes[i];
352
+ const child = childNodes[i];
353
+ const newChild = mirrorNodeRecursive(child, existingChild);
354
+ if (newChild !== existingChild) {
355
+ existingNode.replaceChild(newChild, existingChild);
356
+ }
357
+ }
358
+ } else {
359
+ // clear and overwrite
360
+ if (existingNode) {
361
+ while (existingNode.childNodes.length) {
362
+ existingNode.childNodes[
363
+ existingNode.childNodes.length - 1
364
+ ].remove();
365
+ }
366
+ }
367
+ childNodes.forEach((childNode) => {
368
+ const mirrorChild = mirrorNodeRecursive(childNode, null);
369
+ mirroredNode.appendChild(mirrorChild);
370
+ });
371
+ }
372
+ return mirroredNode
373
+ };
374
+
375
+ const mirroredDomTree = mirrorNodeRecursive(root, existingRoot);
376
+
377
+ return {
378
+ mirroredNode: mirroredDomTree,
379
+ trackedNodesToMirroredNodes
380
+ }
381
+ }
382
+
383
+ function updateAttribute (fromNode, toNodes, relationship) {
384
+ const newIds = toNodes.map((toNode) => toNode.getAttribute('id'));
385
+ const existingIds = splitIds(fromNode.getAttribute(relationship));
386
+ const linkedNodeIds = newIds.filter(
387
+ (newId) => !existingIds.includes(newId)
388
+ );
389
+ const unlinkedNodeIds = existingIds.filter(
390
+ (existingId) => !newIds.includes(existingId)
391
+ );
392
+
393
+ fromNode.setAttribute(relationship, newIds.join(' '));
394
+ return { linkedNodeIds, unlinkedNodeIds }
395
+ }
396
+
397
+ function splitIds (ids) {
398
+ if (!ids) {
399
+ return []
400
+ }
401
+ return ids.trim().split(/\s+/)
402
+ }
403
+
404
+ // Certain ARIA relationships only support one target, not multiple.
405
+ const SINGLE_TARGET_ARIA_RELATIONSHIPS = [
406
+ 'aria-activedescendant',
407
+ 'aria-errormessage'
408
+ ];
409
+
410
+ const mirroredEnvironments = new WeakMap();
411
+ const taskQueue = [];
412
+
413
+ function processTask (fromRoot, task) {
414
+ const mirroredEnvironment = getMirroredEnvironment(fromRoot);
415
+
416
+ mirrorNodes(mirroredEnvironment, task);
417
+ linkAndObserveNodes(mirroredEnvironment, task);
418
+ }
419
+
420
+ function getMirroredEnvironment (fromRoot) {
421
+ let mirroredEnvironment = mirroredEnvironments.get(fromRoot);
422
+ if (!mirroredEnvironment) {
423
+ const mirrorRoot = document.createElement('div');
424
+ mirrorRoot.setAttribute('class', 'aria-element-reflection-mirror');
425
+ mirrorRoot.setAttribute('style', screenReaderOnlyStyles);
426
+ const fromAnchorRoot = fromRoot.body || fromRoot; // for document, append to body
427
+ fromAnchorRoot.appendChild(mirrorRoot);
428
+ mirroredEnvironment = {
429
+ redundantChildNodes: new Set(),
430
+ nodesToMirroredNodes: new Map(),
431
+ mirrorRoot
432
+ };
433
+ mirroredEnvironments.set(fromRoot, mirroredEnvironment);
434
+ }
435
+ return mirroredEnvironment
436
+ }
437
+
438
+ function mirrorNodes (mirroredEnvironment, task) {
439
+ const { redundantChildNodes, nodesToMirroredNodes, mirrorRoot } =
440
+ mirroredEnvironment;
441
+
442
+ task.redundantChildNodes.forEach((node) => redundantChildNodes.add(node));
443
+
444
+ const rootToNodes = new Set(
445
+ task.relationships
446
+ .map(({ toNodes }) => toNodes)
447
+ .flat()
448
+ .filter(Boolean) // skip nulls
449
+ .filter((_) => !redundantChildNodes.has(_)) // skip redundant child nodes
450
+ );
451
+
452
+ rootToNodes.forEach((toNode) => {
453
+ const trackedNodes = new Set([...redundantChildNodes, toNode]);
454
+ const { node: existingMirroredNode = null, usage = 0 } =
455
+ nodesToMirroredNodes.get(toNode) || {};
456
+ const { mirroredNode, trackedNodesToMirroredNodes } =
457
+ patchMirrorDomTree(toNode, existingMirroredNode, trackedNodes);
458
+ if (mirroredNode !== existingMirroredNode) {
459
+ // The following line should never happen, but I feel safer having it in
460
+ /* istanbul ignore if */
461
+ if (existingMirroredNode) {
462
+ mirrorRoot.removeChild(existingMirroredNode);
463
+ }
464
+ mirrorRoot.appendChild(mirroredNode);
465
+ }
466
+
467
+ trackedNodesToMirroredNodes.forEach(
468
+ (trackedMirroredNode, trackedNode) =>
469
+ nodesToMirroredNodes.set(trackedNode, {
470
+ node: trackedMirroredNode,
471
+ usage
472
+ })
473
+ );
474
+ });
475
+ }
476
+
477
+ function linkAndObserveNodes (mirroredEnvironment, task) {
478
+ const { nodesToMirroredNodes } = mirroredEnvironment;
479
+
480
+ task.relationships.forEach((collatedRelationship) => {
481
+ const { fromNode, toNodes, relationship, track, signal, mirrorOnly } =
482
+ collatedRelationship;
483
+
484
+ if (toNodes?.length) {
485
+ if (!mirrorOnly) {
486
+ const mirroredNodes = toNodes.map(
487
+ (toNode) => nodesToMirroredNodes.get(toNode).node
488
+ );
489
+ const { linkedNodeIds, unlinkedNodeIds } = updateAttribute(
490
+ fromNode,
491
+ mirroredNodes,
492
+ relationship
493
+ );
494
+
495
+ updateNodeUsage(
496
+ mirroredEnvironment,
497
+ unlinkedNodeIds,
498
+ linkedNodeIds
499
+ );
500
+
501
+ if (track) {
502
+ observeNode(fromNode, toNodes, signal);
503
+ }
504
+ }
505
+ } else {
506
+ // toNodes not existing indicates the relationship is severed
507
+ const { unlinkedNodeIds } = updateAttribute(
508
+ fromNode,
509
+ [],
510
+ relationship
511
+ );
512
+ updateNodeUsage(mirroredEnvironment, unlinkedNodeIds);
513
+ fromNode.removeAttribute(relationship);
514
+ }
515
+ });
516
+ }
517
+
518
+ function updateNodeUsage (
519
+ mirroredEnvironment,
520
+ unlinkedNodes,
521
+ linkedNodes = []
522
+ ) {
523
+ const { nodesToMirroredNodes, mirrorRoot } = mirroredEnvironment;
524
+
525
+ nodesToMirroredNodes.forEach((mirroredNode, node) => {
526
+ if (linkedNodes.includes(mirroredNode.node.id)) {
527
+ mirroredNode.usage++;
528
+ } else if (
529
+ unlinkedNodes.includes(mirroredNode.node.id) &&
530
+ --mirroredNode.usage <= 0
531
+ ) {
532
+ // If mirrored node is no longer used, remove it from the mirrored environment
533
+ nodesToMirroredNodes.delete(node);
534
+ // If the mirrored node's parent is not the root, then its parent node
535
+ // is being mirrored. Only that parent node can be removed at the top level
536
+ if (mirroredNode.node.parentElement === mirrorRoot) {
537
+ mirrorRoot.removeChild(mirroredNode.node);
538
+ }
539
+ }
540
+ });
541
+ }
542
+
543
+ function observeNode (fromNode, toNodes, signal) {
544
+ toNodes.forEach((toNode) => {
545
+ const mutationObserver = new DeepMutationObserver(toNode);
546
+ mutationObserver.onMutation(() => {
547
+ updateAriaRelationship(fromNode, [toNode]);
548
+ });
549
+ if (signal) {
550
+ signal.addEventListener('abort', () => {
551
+ mutationObserver.disconnect();
552
+ });
553
+ }
554
+ });
555
+ }
556
+
557
+ function processQueue () {
558
+ // Process multiple tasks together so we can collate
559
+ const mapOfFromRootsToTasks = collateTasks(taskQueue);
560
+ taskQueue.length = 0;
561
+ mapOfFromRootsToTasks.forEach((task, fromRoot) =>
562
+ processTask(fromRoot, task)
563
+ );
564
+ }
565
+
566
+ function updateAriaRelationship (fromNode, toNodes) {
567
+ queueTask({ fromNode, toNodes, mirrorOnly: true });
568
+ }
569
+
570
+ function queueTask (task) {
571
+ taskQueue.push(task);
572
+ queueMicrotaskWithPriority(/* priority */ 1, processQueue);
573
+ }
574
+
575
+ // We accept an Element, null, or an Array of Elements
576
+ function massageToNodes (toNodes, relationship) {
577
+ if (!Array.isArray(toNodes)) {
578
+ toNodes = [toNodes];
579
+ }
580
+
581
+ toNodes = toNodes.filter(Boolean); // remove falsy values
582
+
583
+ if (toNodes.length > 1 && SINGLE_TARGET_ARIA_RELATIONSHIPS.includes(relationship)) {
584
+ // Certain ARIA relationships only support one target, not multiple. For those, we should warn
585
+ // when someone tries to set multiple, and only take the first element.
586
+ // See: https://w3c.github.io/aria/#ARIAMixin
587
+ console.warn(`Multiple targets passed to aria relationship "${relationship}". ` +
588
+ 'This API only accepts a single target. Ignoring elements beyond the first one.');
589
+ toNodes = toNodes.slice(0, 1);
590
+ }
591
+ return toNodes
592
+ }
593
+
594
+ function setAriaRelationship (
595
+ fromNode,
596
+ toNodes,
597
+ relationship,
598
+ options = {}
599
+ ) {
600
+ const { track, signal } = options;
601
+ toNodes = massageToNodes(toNodes, relationship);
602
+ queueTask({
603
+ fromNode,
604
+ toNodes,
605
+ relationship,
606
+ track,
607
+ signal
608
+ });
609
+ }
610
+
611
+ // via https://wicg.github.io/aom/spec/aria-reflection.html#attribute-reflection
612
+ // limited to just those that define an idref relationship
613
+
614
+ function setAriaActiveDescendant (fromNode, toNodes, options) {
615
+ setAriaRelationship(fromNode, toNodes, 'aria-activedescendant', options);
616
+ }
617
+ function setAriaControls (fromNode, toNodes, options) {
618
+ setAriaRelationship(fromNode, toNodes, 'aria-controls', options);
619
+ }
620
+ function setAriaDescribedBy (fromNode, toNodes, options) {
621
+ setAriaRelationship(fromNode, toNodes, 'aria-describedby', options);
622
+ }
623
+ function setAriaDetails (fromNode, toNodes, options) {
624
+ setAriaRelationship(fromNode, toNodes, 'aria-details', options);
625
+ }
626
+ function setAriaErrorMessage (fromNode, toNodes, options) {
627
+ setAriaRelationship(fromNode, toNodes, 'aria-errormessage', options);
628
+ }
629
+ function setAriaFlowTo (fromNode, toNodes, options) {
630
+ setAriaRelationship(fromNode, toNodes, 'aria-flowto', options);
631
+ }
632
+ function setAriaLabelledBy (fromNode, toNodes, options) {
633
+ setAriaRelationship(fromNode, toNodes, 'aria-labelledby', options);
634
+ }
635
+ function setAriaOwns (fromNode, toNodes, options) {
636
+ setAriaRelationship(fromNode, toNodes, 'aria-owns', options);
637
+ }
638
+
639
+ export { setAriaActiveDescendant, setAriaControls, setAriaDescribedBy, setAriaDetails, setAriaErrorMessage, setAriaFlowTo, setAriaLabelledBy, setAriaOwns };
@@ -0,0 +1,2 @@
1
+ @import 'lightning/sldsCommon';
2
+ @import './avatar.slds.css';
@@ -1,7 +1,9 @@
1
1
  <template>
2
+ <span class={computedClass} part="avatar">
2
3
  <img if:true={_src} src={_src} onerror={handleImageError} alt={alternativeText} title={alternativeText}>
3
4
 
4
5
  <abbr if:true={showInitials} class={computedInitialsClass} title={alternativeText}>{initials}</abbr>
5
6
 
6
7
  <lightning-icon if:true={showIcon} icon-name={fallbackIconName} alternative-text={alternativeText} title={alternativeText}></lightning-icon>
8
+ </span>
7
9
  </template>