lightning-base-components 1.18.1-alpha → 1.18.3-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.
- package/metadata/raptor.json +9 -0
- package/package.json +57 -1
- package/src/lightning/accordion/__docs__/accordion.md +2 -2
- package/src/lightning/accordion/accordion.css +12 -0
- package/src/lightning/accordion/accordion.html +3 -1
- package/src/lightning/accordion/accordion.js +4 -2
- package/src/lightning/accordion/accordion.slds.css +671 -0
- package/src/lightning/accordionSection/accordion-section.slds.css +647 -0
- package/src/lightning/accordionSection/accordionSection.css +14 -0
- package/src/lightning/accordionSection/accordionSection.html +23 -19
- package/src/lightning/accordionSection/accordionSection.js +29 -2
- package/src/lightning/ariaObserver/__docs__/ariaObserver.md +21 -9
- package/src/lightning/ariaObserver/ariaObserver.js +185 -154
- package/src/lightning/ariaObserver/polyfill.js +639 -0
- package/src/lightning/avatar/__docs__/avatar.md +7 -7
- package/src/lightning/avatar/avatar.css +2 -0
- package/src/lightning/avatar/avatar.html +2 -0
- package/src/lightning/avatar/avatar.js +18 -15
- package/src/lightning/avatar/avatar.slds.css +272 -0
- package/src/lightning/badge/__docs__/badge.md +2 -2
- package/src/lightning/baseCombobox/base-combobox.slds.css +1585 -0
- package/src/lightning/baseCombobox/baseCombobox.css +11 -1
- package/src/lightning/baseCombobox/baseCombobox.html +154 -146
- package/src/lightning/baseCombobox/baseCombobox.js +122 -46
- package/src/lightning/baseCombobox/spinner.slds.css +438 -0
- package/src/lightning/baseComboboxItem/baseComboboxItem.js +4 -2
- package/src/lightning/baseComboboxItem/inline.css +2 -0
- package/src/lightning/breadcrumb/breadcrumb.css +2 -2
- package/src/lightning/breadcrumb/breadcrumb.js +4 -2
- package/src/lightning/breadcrumb/breadcrumb.slds.css +2 -7
- package/src/lightning/breadcrumbs/__docs__/breadcrumbs.md +3 -3
- package/src/lightning/breadcrumbs/breadcrumbs.css +2 -2
- package/src/lightning/breadcrumbs/breadcrumbs.js +3 -2
- package/src/lightning/breadcrumbs/breadcrumbs.slds.css +7 -1
- package/src/lightning/button/__docs__/button.md +15 -15
- package/src/lightning/button/__examples__/inverse/inverse.css +8 -0
- package/src/lightning/button/__examples__/inverse/inverse.html +3 -2
- package/src/lightning/button/button.css +2 -0
- package/src/lightning/button/button.html +4 -2
- package/src/lightning/button/button.js +21 -0
- package/src/lightning/button/button.slds.css +527 -0
- package/src/lightning/buttonGroup/buttonGroup.css +2 -2
- package/src/lightning/buttonGroup/buttonGroup.js +3 -2
- package/src/lightning/buttonIcon/__docs__/buttonIcon.md +9 -9
- package/src/lightning/buttonIcon/button-icon.slds.css +215 -453
- package/src/lightning/buttonIcon/buttonIcon.css +2 -2
- package/src/lightning/buttonIcon/buttonIcon.js +4 -0
- package/src/lightning/buttonIconStateful/__docs__/buttonIconStateful.md +9 -9
- package/src/lightning/buttonIconStateful/button-icon-stateful.slds.css +215 -453
- package/src/lightning/buttonIconStateful/buttonIconStateful.css +2 -2
- package/src/lightning/buttonMenu/__docs__/buttonMenu.md +8 -8
- package/src/lightning/buttonMenu/{dropdown.slds.css → button-menu.slds.css} +853 -217
- package/src/lightning/buttonMenu/buttonMenu.css +2 -2
- package/src/lightning/buttonMenu/buttonMenu.html +2 -2
- package/src/lightning/buttonMenu/buttonMenu.js +10 -14
- package/src/lightning/buttonStateful/__docs__/buttonStateful.md +12 -12
- package/src/lightning/buttonStateful/button-stateful.slds.css +225 -457
- package/src/lightning/buttonStateful/buttonStateful.css +2 -2
- package/src/lightning/buttonStateful/buttonStateful.js +3 -2
- package/src/lightning/calendar/__examples__/basic/basic.html +7 -0
- package/src/lightning/calendar/__examples__/basic/basic.js +3 -0
- package/src/lightning/calendar/calendar.css +3 -0
- package/src/lightning/calendar/calendar.html +12 -9
- package/src/lightning/calendar/calendar.js +18 -2
- package/src/lightning/calendar/calendar.slds.css +2048 -0
- package/src/lightning/card/__docs__/card.md +3 -3
- package/src/lightning/card/card.css +2 -2
- package/src/lightning/card/card.js +3 -2
- package/src/lightning/card/card.slds.css +141 -88
- package/src/lightning/checkboxGroup/__docs__/checkboxGroup.md +2 -2
- package/src/lightning/colorPickerCustom/colorPickerCustom.css +2 -2
- package/src/lightning/colorPickerCustom/colorPickerCustom.js +3 -2
- package/src/lightning/colorPickerPanel/color-picker-panel.slds.css +11 -38
- package/src/lightning/colorPickerPanel/colorPickerPanel.css +3 -2
- package/src/lightning/colorPickerPanel/colorPickerPanel.js +4 -2
- package/src/lightning/colorPickerPanel/popover.slds.css +121 -0
- package/src/lightning/combobox/combobox.css +4 -0
- package/src/lightning/combobox/combobox.html +31 -29
- package/src/lightning/combobox/combobox.js +21 -4
- package/src/lightning/combobox/combobox.slds.css +13 -0
- package/src/lightning/combobox/form-element.slds.css +281 -0
- package/src/lightning/configProvider/defaultConfig.js +2 -1
- package/src/lightning/datatable/__docs__/datatable.md +45 -35
- package/src/lightning/datatable/autoWidthStrategy.js +3 -0
- package/src/lightning/datatable/columnWidthManager.js +1 -1
- package/src/lightning/datatable/datatable.js +8 -7
- package/src/lightning/datatable/rowSelection.js +7 -4
- package/src/lightning/datatable/templates/table/table.html +1 -0
- package/src/lightning/datepicker/datepicker.css +3 -0
- package/src/lightning/datepicker/datepicker.html +7 -4
- package/src/lightning/datepicker/datepicker.js +76 -20
- package/src/lightning/datepicker/form-element.slds.css +281 -0
- package/src/lightning/datepicker/input-text.slds.css +398 -0
- package/src/lightning/datetimepicker/datetimepicker.css +3 -0
- package/src/lightning/datetimepicker/datetimepicker.html +9 -3
- package/src/lightning/datetimepicker/datetimepicker.js +42 -36
- package/src/lightning/datetimepicker/form-element.slds.css +281 -0
- package/src/lightning/datetimepicker/input-text.slds.css +398 -0
- package/src/lightning/dualListbox/dualListbox.css +2 -2
- package/src/lightning/dualListbox/dualListbox.html +3 -3
- package/src/lightning/dualListbox/dualListbox.js +47 -13
- package/src/lightning/dualListbox/form-element.slds.css +83 -34
- package/src/lightning/dualListbox/keyboard.js +20 -1
- package/src/lightning/dynamicIcon/dynamicIcon.js +3 -2
- package/src/lightning/dynamicIcon/ellie.css +1 -1
- package/src/lightning/dynamicIcon/eq.css +1 -1
- package/src/lightning/dynamicIcon/score.css +1 -1
- package/src/lightning/dynamicIcon/strength.css +1 -1
- package/src/lightning/dynamicIcon/trend.css +1 -1
- package/src/lightning/dynamicIcon/waffle.css +1 -1
- package/src/lightning/formattedRichText/formatted-rich-text.slds.css +230 -0
- package/src/lightning/formattedRichText/formattedRichText.css +2 -0
- package/src/lightning/formattedRichText/formattedRichText.js +4 -2
- package/src/lightning/formattedRichText/linkify.js +2 -2
- package/src/lightning/formattedText/formattedText.css +1 -0
- package/src/lightning/formattedText/formattedText.js +3 -2
- package/src/lightning/helptext/__docs__/helptext.md +2 -2
- package/src/lightning/helptext/form-element.slds.css +83 -34
- package/src/lightning/helptext/help-text.slds.css +215 -453
- package/src/lightning/helptext/helptext.css +2 -2
- package/src/lightning/helptext/helptext.js +3 -2
- package/src/lightning/i18nCldrOptions/README.md +5 -0
- package/src/lightning/i18nService/README.md +5 -0
- package/src/lightning/icon/__docs__/icon.md +5 -5
- package/src/lightning/icon/icon.css +2 -2
- package/src/lightning/icon/icon.js +21 -2
- package/src/lightning/icon/icon.slds.css +29 -17
- package/src/lightning/icon/iconColors.js +1 -0
- package/src/lightning/iconUtils/iconUtils.js +0 -12
- package/src/lightning/iconUtils/polyfill.js +5 -90
- package/src/lightning/input/__docs__/input.md +7 -7
- package/src/lightning/input/__examples__/checkboxbutton/checkboxbutton.css +6 -0
- package/src/lightning/input/__examples__/checkboxbutton/checkboxbutton.html +2 -1
- package/src/lightning/input/__examples__/checkboxbutton/checkboxbutton.js +1 -1
- package/src/lightning/input/__examples__/number/number.html +0 -5
- package/src/lightning/input/__examples__/text/text.html +0 -1
- package/src/lightning/input/form-element.slds.css +281 -0
- package/src/lightning/input/input.css +2 -3
- package/src/lightning/input/input.html +154 -244
- package/src/lightning/input/input.js +306 -595
- package/src/lightning/inputAddress/__docs__/inputAddress.md +3 -3
- package/src/lightning/inputUtils/inputUtils.js +15 -20
- package/src/lightning/inputUtils/normalize.js +7 -0
- package/src/lightning/{input/numberUtil.js → inputUtils/number.js} +1 -1
- package/src/lightning/inputUtils/utils.js +18 -0
- package/src/lightning/internationalizationLibrary/README.md +24 -0
- package/src/lightning/internationalizationLibrary/utils.js +4 -1
- package/src/lightning/layout/__docs__/layout.md +1 -1
- package/src/lightning/layout/__examples__/simple/simple.css +1 -1
- package/src/lightning/layout/layout.css +5 -1
- package/src/lightning/layout/layout.js +4 -2
- package/src/lightning/layoutItem/__examples__/alignmentBump/alignmentBump.css +1 -1
- package/src/lightning/layoutItem/__examples__/sizePerDevice/sizePerDevice.css +0 -1
- package/src/lightning/layoutItem/layoutItem.css +5 -0
- package/src/lightning/layoutItem/layoutItem.js +4 -2
- package/src/lightning/menuDivider/menu-divider.slds.css +15 -0
- package/src/lightning/menuDivider/menuDivider.css +3 -0
- package/src/lightning/menuDivider/menuDivider.html +1 -1
- package/src/lightning/menuDivider/menuDivider.js +4 -2
- package/src/lightning/menuItem/menu-item.slds.css +140 -0
- package/src/lightning/menuItem/menuItem.css +3 -0
- package/src/lightning/menuItem/menuItem.html +43 -41
- package/src/lightning/menuItem/menuItem.js +4 -4
- package/src/lightning/menuSubheader/menu-subheader.slds.css +22 -0
- package/src/lightning/menuSubheader/menuSubheader.css +3 -0
- package/src/lightning/menuSubheader/menuSubheader.html +3 -1
- package/src/lightning/menuSubheader/menuSubheader.js +4 -6
- package/src/lightning/modal/__docs__/modal.md +3 -1
- package/src/lightning/modal/__modalUtils__/modalContainerTestConstants.js +267 -0
- package/src/lightning/modal/__modalUtils__/modalContainerTestMethods.js +1165 -0
- package/src/lightning/modal/__modalUtils__/modalContainerTestMockData.js +131 -0
- package/src/lightning/modal/modal.js +1 -1
- package/src/lightning/modalBody/__docs__/modalBody.md +9 -9
- package/src/lightning/modalFooter/__docs__/modalFooter.md +9 -9
- package/src/lightning/modalHeader/__docs__/modalHeader.md +9 -9
- package/src/lightning/overlayContainer/overlayContainer.js +4 -2
- package/src/lightning/pill/__docs__/pill.md +3 -3
- package/src/lightning/pill/avatar.slds.css +272 -0
- package/src/lightning/pill/link.css +3 -0
- package/src/lightning/pill/link.html +1 -1
- package/src/lightning/pill/pill.js +29 -9
- package/src/lightning/pill/pill.slds.css +168 -0
- package/src/lightning/pill/plain.css +3 -0
- package/src/lightning/pill/plain.html +1 -1
- package/src/lightning/pill/plainLink.css +3 -0
- package/src/lightning/pill/plainLink.html +1 -1
- package/src/lightning/pillContainer/__docs__/pillContainer.md +14 -14
- package/src/lightning/pillContainer/barePillContainer.css +3 -0
- package/src/lightning/pillContainer/barePillContainer.html +1 -2
- package/src/lightning/pillContainer/listbox.slds.css +267 -0
- package/src/lightning/pillContainer/pill-container.slds.css +22 -0
- package/src/lightning/pillContainer/pill.slds.css +168 -0
- package/src/lightning/pillContainer/pillContainer.js +7 -3
- package/src/lightning/pillContainer/standardPillContainer.css +4 -0
- package/src/lightning/pillContainer/standardPillContainer.html +2 -2
- package/src/lightning/popup/popover.slds.css +119 -119
- package/src/lightning/popup/popup.css +1 -2
- package/src/lightning/popup/popup.js +3 -2
- package/src/lightning/positionLibrary/elementProxy.js +7 -2
- package/src/lightning/positionLibrary/util.js +8 -0
- package/src/lightning/primitiveBubble/primitiveBubble.css +2 -2
- package/src/lightning/primitiveBubble/primitiveBubble.js +4 -2
- package/src/lightning/primitiveButton/primitiveButton.js +5 -4
- package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +29 -21
- package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +4 -0
- package/src/lightning/primitiveColorpickerButton/color-picker-button.slds.css +31 -19
- package/src/lightning/primitiveColorpickerButton/primitiveColorpickerButton.css +2 -2
- package/src/lightning/primitiveColorpickerButton/primitiveColorpickerButton.js +5 -3
- package/src/lightning/primitiveIcon/icon.slds.css +209 -0
- package/src/lightning/primitiveIcon/primitiveIcon.css +2 -1
- package/src/lightning/primitiveIcon/primitiveIcon.html +1 -1
- package/src/lightning/primitiveIcon/primitiveIcon.js +26 -16
- package/src/lightning/progressStep/progressStep.js +10 -13
- package/src/lightning/radioGroup/__docs__/radioGroup.md +4 -4
- package/src/lightning/radioGroup/radioGroup.css +2 -1
- package/src/lightning/radioGroup/radioGroup.js +4 -2
- package/src/lightning/select/__docs__/select.md +2 -2
- package/src/lightning/select/form-element.slds.css +83 -34
- package/src/lightning/select/select.css +2 -2
- package/src/lightning/select/select.js +4 -2
- package/src/lightning/select/select.slds.css +86 -34
- package/src/lightning/shadowBaseClassPrivate/shadowBaseClassPrivate.js +1 -1
- package/src/lightning/sldsCommon/sldsCommon.css +251 -89
- package/src/lightning/sldsUtilsAlignment/sldsUtilsAlignment.css +10 -0
- package/src/lightning/sldsUtilsAlignment/sldsUtilsAlignment.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsBorders/sldsUtilsBorders.css +18 -0
- package/src/lightning/sldsUtilsBorders/sldsUtilsBorders.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsBox/sldsUtilsBox.css +24 -0
- package/src/lightning/sldsUtilsBox/sldsUtilsBox.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsFloats/sldsUtilsFloats.css +20 -0
- package/src/lightning/sldsUtilsFloats/sldsUtilsFloats.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsGrid/sldsUtilsGrid.css +259 -0
- package/src/lightning/sldsUtilsGrid/sldsUtilsGrid.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsHyphenation/sldsUtilsHyphenation.css +8 -0
- package/src/lightning/sldsUtilsHyphenation/sldsUtilsHyphenation.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsLineClamp/sldsUtilsLineClamp.css +57 -0
- package/src/lightning/sldsUtilsLineClamp/sldsUtilsLineClamp.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsMargin/sldsUtilsMargin.css +313 -0
- package/src/lightning/sldsUtilsMargin/sldsUtilsMargin.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsPadding/sldsUtilsPadding.css +308 -0
- package/src/lightning/sldsUtilsPadding/sldsUtilsPadding.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsPosition/sldsUtilsPosition.css +18 -0
- package/src/lightning/sldsUtilsPosition/sldsUtilsPosition.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsSizing/sldsUtilsSizing.css +1408 -0
- package/src/lightning/sldsUtilsSizing/sldsUtilsSizing.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsThemes/sldsUtilsThemes.css +295 -0
- package/src/lightning/sldsUtilsThemes/sldsUtilsThemes.js-meta.xml +4 -0
- package/src/lightning/sldsUtilsTruncation/sldsUtilsTruncation.css +14 -0
- package/src/lightning/sldsUtilsTruncation/sldsUtilsTruncation.js-meta.xml +4 -0
- package/src/lightning/slider/__docs__/slider.md +2 -2
- package/src/lightning/spinner/spinner.css +2 -2
- package/src/lightning/spinner/spinner.js +4 -2
- package/src/lightning/tabBar/tab-bar.slds.css +334 -0
- package/src/lightning/tabBar/tabBar.css +2 -0
- package/src/lightning/tabBar/tabBar.html +4 -3
- package/src/lightning/tabBar/tabBar.js +30 -3
- package/src/lightning/tabset/__docs__/tabset.md +2 -2
- package/src/lightning/tabset/tabset.html +5 -4
- package/src/lightning/tabset/tabset.js +29 -11
- package/src/lightning/textarea/__docs__/textarea.md +2 -2
- package/src/lightning/timepicker/form-element.slds.css +281 -0
- package/src/lightning/timepicker/timepicker.css +3 -0
- package/src/lightning/timepicker/timepicker.html +5 -1
- package/src/lightning/timepicker/timepicker.js +22 -17
- package/src/lightning/timepicker/timepicker.slds.css +18 -0
- package/src/lightning/toast/toast.js-meta.xml +2 -0
- package/src/lightning/toastContainer/__docs__/toastContainer.md +14 -34
- package/src/lightning/toastContainer/toastContainer.js +10 -15
- package/src/lightning/tooltipLibrary/tooltipLibrary.js +32 -23
- package/src/lightning/utilsPrivate/browser.js +5 -3
- package/src/lightning/utilsPrivate/os.js +6 -4
- package/src/lightning/utilsPrivate/ssr.js +4 -0
- package/src/lightning/utilsPrivate/utilsPrivate.js +2 -0
- package/src/lightning/verticalNavigation/verticalNavigation.css +2 -1
- package/src/lightning/verticalNavigation/verticalNavigation.js +3 -2
- package/src/lightning/verticalNavigationSection/verticalNavigationSection.css +2 -1
- package/src/lightning/verticalNavigationSection/verticalNavigationSection.js +3 -2
- package/src/lightning/accordion/__perf__DISABLED/accordion-perf-utils.js +0 -76
- package/src/lightning/accordion/__perf__DISABLED/accordion10Multiple25SectionEach.perf.js +0 -57
- package/src/lightning/accordion/__perf__DISABLED/accordion10Simple25SectionEach.perf.js +0 -37
- package/src/lightning/accordion/__perf__DISABLED/accordionMultiple50Section.perf.js +0 -45
- package/src/lightning/accordion/__perf__DISABLED/accordionSimple50Section.perf.js +0 -35
- package/src/lightning/accordion/__perf__DISABLED/container/container.html +0 -15
- package/src/lightning/accordion/__perf__DISABLED/container/container.js +0 -7
- package/src/lightning/iconUtils/isIframeInEdge.js +0 -7
- package/src/lightning/iconUtils/supportsSvg.js +0 -16
- package/src/lightning/input/input-checkbox.slds.css +0 -404
- package/src/lightning/input/input-text.slds.css +0 -287
- package/src/lightning/input/normalize.js +0 -6
- package/src/lightning/input/selection.js +0 -131
- package/src/lightning/positionLibrary/__component__/positionLibraryBounding.spec.js +0 -319
- package/src/lightning/positionLibrary/__component__/x/bounding/bounding.css +0 -16
- package/src/lightning/positionLibrary/__component__/x/bounding/bounding.html +0 -36
- package/src/lightning/positionLibrary/__component__/x/bounding/bounding.js +0 -122
- /package/src/lightning/{baseCombobox → baseComboboxItem}/listbox.slds.css +0 -0
- /package/src/lightning/formattedRichText/{__examples__disabled → __examples__}/basic/basic.html +0 -0
- /package/src/lightning/formattedRichText/{__examples__disabled → __examples__}/basic/basic.js +0 -0
- /package/src/lightning/{input/emailUtil.js → inputUtils/email.js} +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 };
|
|
@@ -127,22 +127,22 @@ The fallback icon is displayed if the image path is invalid. To apply custom sty
|
|
|
127
127
|
</lightning-avatar>
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
Use SLDS styling hooks to customize the component's styles. For example, specify the background color on the fallback icon using the `--
|
|
130
|
+
Use SLDS styling hooks to customize the component's styles. For example, specify the background color on the fallback icon using the `--slds-c-icon-color-background` custom property.
|
|
131
131
|
|
|
132
132
|
```css
|
|
133
133
|
.my-fallback-color {
|
|
134
|
-
--
|
|
134
|
+
--slds-c-icon-color-background: orange;
|
|
135
135
|
}
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
`lightning-avatar` contains the same customizable elements as `lightning-icon`, which supports the `--
|
|
138
|
+
`lightning-avatar` contains the same customizable elements as `lightning-icon`, which supports the `--slds-c-icon-*` custom properties. Consider the following guidelines when working with SLDS styling hooks.
|
|
139
139
|
|
|
140
140
|
| CSS Custom Property | `lightning-avatar` Guideline |
|
|
141
141
|
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
-
| `--
|
|
143
|
-
| `--
|
|
144
|
-
| `--
|
|
145
|
-
| `--
|
|
142
|
+
| `--slds-c-avatar-text-color` | Use with the `initials` attribute and when you specify the `fallback-icon-name` with a standard, utility, doctype, or action icon only. |
|
|
143
|
+
| `--slds-c-avatar-radius-border` | Adds a rounded border. Alternatively, to create an avatar on a circular background, use the `circle` variant. |
|
|
144
|
+
| `--slds-c-icon-color-background` | Use with the `fallback-icon-name` attribute. |
|
|
145
|
+
| `--slds-c-icon-color-foreground` | Use with the `fallback-icon-name` attribute for standard, custom, and action icons only. |
|
|
146
146
|
|
|
147
147
|
See [Styling Hooks Overview](https://www.lightningdesignsystem.com/components/avatar/#Styling-Hooks-Overview) for a list of CSS custom properties.
|
|
148
148
|
|
|
@@ -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>
|