@ui5/webcomponents-base 1.11.0-rc.0 → 1.11.0-rc.2
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/CHANGELOG.md +22 -0
- package/dist/Boot.js +4 -0
- package/dist/Boot.js.map +1 -1
- package/dist/Device.js +109 -34
- package/dist/Device.js.map +1 -1
- package/dist/InitialConfiguration.js +1 -1
- package/dist/InitialConfiguration.js.map +1 -1
- package/dist/asset-registries/Icons.d.ts +1 -1
- package/dist/asset-registries/Icons.js +21 -21
- package/dist/asset-registries/Icons.js.map +1 -1
- package/dist/assets/bundle.esm.2d0cb31f.js +56 -0
- package/dist/assets/test/pages/{i18n.html.a765def3.js → i18n.html.cd6e3cfc.js} +1 -1
- package/dist/assets/test/pages/{i18n_texts.html.9a235f8f.js → i18n_texts.html.7beb72ee.js} +1 -1
- package/dist/assets-meta/IconCollectionsAlias.d.ts +24 -10
- package/dist/assets-meta/IconCollectionsAlias.js +29 -9
- package/dist/assets-meta/IconCollectionsAlias.js.map +1 -1
- package/dist/config/Icons.d.ts +42 -18
- package/dist/config/Icons.js +74 -25
- package/dist/config/Icons.js.map +1 -1
- package/dist/config/RTL.js +3 -0
- package/dist/config/RTL.js.map +1 -1
- package/dist/delegate/ResizeHandler.d.ts +2 -1
- package/dist/delegate/ResizeHandler.js +3 -1
- package/dist/delegate/ResizeHandler.js.map +1 -1
- package/dist/features/F6Navigation.d.ts +3 -0
- package/dist/features/F6Navigation.js +77 -38
- package/dist/features/F6Navigation.js.map +1 -1
- package/dist/generated/VersionInfo.js +3 -3
- package/dist/getSharedResource.js +9 -1
- package/dist/getSharedResource.js.map +1 -1
- package/dist/test/pages/AllTestElements.html +1 -1
- package/dist/test/pages/Configuration.html +1 -1
- package/dist/test/pages/ConfigurationScript.html +1 -1
- package/dist/test/pages/WithComplexTemplate.html +1 -1
- package/dist/test/pages/i18n.html +2 -2
- package/dist/test/pages/i18n_texts.html +2 -2
- package/dist/theming/CustomStyle.js +7 -5
- package/dist/theming/CustomStyle.js.map +1 -1
- package/dist/util/AriaLabelHelper.d.ts +10 -1
- package/dist/util/AriaLabelHelper.js +135 -8
- package/dist/util/AriaLabelHelper.js.map +1 -1
- package/package-scripts.js +6 -2
- package/package.json +3 -3
- package/src/Boot.ts +4 -0
- package/src/Device.ts +113 -34
- package/src/InitialConfiguration.ts +1 -1
- package/src/asset-registries/Icons.ts +26 -27
- package/src/assets-meta/IconCollectionsAlias.ts +32 -9
- package/src/config/Icons.ts +85 -25
- package/src/config/RTL.ts +4 -0
- package/src/delegate/ResizeHandler.ts +7 -2
- package/src/features/F6Navigation.ts +98 -44
- package/src/getSharedResource.ts +10 -1
- package/src/theming/CustomStyle.ts +7 -5
- package/src/util/AriaLabelHelper.ts +167 -12
- package/dist/assets/bundle.esm.38c84565.js +0 -56
|
@@ -3,6 +3,7 @@ import { isF6Next, isF6Previous } from "../Keys.js";
|
|
|
3
3
|
import { instanceOfUI5Element } from "../UI5Element.js";
|
|
4
4
|
import { getFirstFocusableElement } from "../util/FocusableElements.js";
|
|
5
5
|
import getFastNavigationGroups from "../util/getFastNavigationGroups.js";
|
|
6
|
+
import isElementClickable from "../util/isElementClickable.js";
|
|
6
7
|
|
|
7
8
|
class F6Navigation {
|
|
8
9
|
static _instance: F6Navigation;
|
|
@@ -19,56 +20,61 @@ class F6Navigation {
|
|
|
19
20
|
document.addEventListener("keydown", this.keydownHandler);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
async
|
|
23
|
-
|
|
24
|
-
this.updateGroups();
|
|
23
|
+
async groupElementToFocus(nextElement: HTMLElement) {
|
|
24
|
+
const nextElementDomRef = instanceOfUI5Element(nextElement) ? nextElement.getDomRef() : nextElement;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
if (nextElementDomRef) {
|
|
27
|
+
if (isElementClickable(nextElementDomRef)) {
|
|
28
|
+
return nextElementDomRef;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
const elementToFocus = await getFirstFocusableElement(nextElementDomRef);
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (this.selectedGroup) {
|
|
35
|
-
nextIndex = this.groups.indexOf(this.selectedGroup);
|
|
33
|
+
if (elementToFocus) {
|
|
34
|
+
return elementToFocus;
|
|
36
35
|
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async findNextFocusableGroupElement(currentIndex: number) {
|
|
40
|
+
let elementToFocus;
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
/* eslint-disable no-await-in-loop */
|
|
43
|
+
for (let index = 0; index < this.groups.length; index++) {
|
|
44
|
+
let nextElement;
|
|
45
|
+
|
|
46
|
+
if (currentIndex > -1) {
|
|
47
|
+
if (currentIndex + 1 >= this.groups.length) {
|
|
48
|
+
currentIndex = 0;
|
|
49
|
+
nextElement = this.groups[currentIndex];
|
|
41
50
|
} else {
|
|
42
|
-
|
|
51
|
+
currentIndex += 1;
|
|
52
|
+
nextElement = this.groups[currentIndex];
|
|
43
53
|
}
|
|
44
54
|
} else {
|
|
45
|
-
|
|
55
|
+
currentIndex = 0;
|
|
56
|
+
nextElement = this.groups[currentIndex];
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
|
|
59
|
+
elementToFocus = await this.groupElementToFocus(nextElement);
|
|
49
60
|
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
elementToFocus?.focus();
|
|
61
|
+
if (elementToFocus) {
|
|
62
|
+
break;
|
|
53
63
|
}
|
|
54
64
|
}
|
|
65
|
+
/* eslint-enable no-await-in-loop */
|
|
55
66
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (this.groups.length < 1) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
67
|
+
return elementToFocus;
|
|
68
|
+
}
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
async findPreviousFocusableGroupElement(currentIndex: number) {
|
|
71
|
+
let elementToFocus;
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
/* eslint-disable no-await-in-loop */
|
|
74
|
+
for (let index = 0; index < this.groups.length; index++) {
|
|
66
75
|
let nextElement;
|
|
67
|
-
if (this.selectedGroup) {
|
|
68
|
-
nextIndex = this.groups.indexOf(this.selectedGroup);
|
|
69
|
-
}
|
|
70
76
|
|
|
71
|
-
if (
|
|
77
|
+
if (currentIndex > 0) {
|
|
72
78
|
// Handle the situation where the first focusable element of two neighbor groups is the same
|
|
73
79
|
// For example:
|
|
74
80
|
// <ui5-flexible-column-layout>
|
|
@@ -77,22 +83,70 @@ class F6Navigation {
|
|
|
77
83
|
// </ui5-list>
|
|
78
84
|
// </ui5-flexible-column-layout>
|
|
79
85
|
// Here for both FCL & List the firstFoccusableElement is the same (the ui5-li)
|
|
86
|
+
const firstFocusable = await this.groupElementToFocus(this.groups[currentIndex - 1]);
|
|
87
|
+
const shouldSkipParent = firstFocusable === await this.groupElementToFocus(this.groups[currentIndex]);
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
const shouldSkipParent = firstFocusable === await getFirstFocusableElement(this.groups[nextIndex], true);
|
|
89
|
+
currentIndex = shouldSkipParent ? currentIndex - 2 : currentIndex - 1;
|
|
83
90
|
|
|
84
|
-
|
|
91
|
+
if (currentIndex < 0) {
|
|
92
|
+
currentIndex = this.groups.length - 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
nextElement = this.groups[currentIndex];
|
|
85
96
|
} else {
|
|
86
|
-
|
|
97
|
+
currentIndex = this.groups.length - 1;
|
|
98
|
+
nextElement = this.groups[currentIndex];
|
|
87
99
|
}
|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
elementToFocus = await this.groupElementToFocus(nextElement);
|
|
90
102
|
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
elementToFocus?.focus();
|
|
103
|
+
if (elementToFocus) {
|
|
104
|
+
break;
|
|
94
105
|
}
|
|
95
106
|
}
|
|
107
|
+
/* eslint-enable no-await-in-loop */
|
|
108
|
+
|
|
109
|
+
return elementToFocus;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async _keydownHandler(event: KeyboardEvent) {
|
|
113
|
+
const forward = isF6Next(event);
|
|
114
|
+
const backward = isF6Previous(event);
|
|
115
|
+
if (!(forward || backward)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.updateGroups();
|
|
120
|
+
|
|
121
|
+
if (this.groups.length < 1) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
|
|
127
|
+
let elementToFocus;
|
|
128
|
+
|
|
129
|
+
if (this.groups.length === 0) {
|
|
130
|
+
elementToFocus = await this.groupElementToFocus(this.groups[0]);
|
|
131
|
+
|
|
132
|
+
return elementToFocus?.focus();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let currentIndex = -1;
|
|
136
|
+
|
|
137
|
+
if (this.selectedGroup) {
|
|
138
|
+
currentIndex = this.groups.indexOf(this.selectedGroup);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (forward) {
|
|
142
|
+
elementToFocus = await this.findNextFocusableGroupElement(currentIndex);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (backward) {
|
|
146
|
+
elementToFocus = await this.findPreviousFocusableGroupElement(currentIndex);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
elementToFocus?.focus();
|
|
96
150
|
}
|
|
97
151
|
|
|
98
152
|
removeEventListeners() {
|
|
@@ -109,19 +163,19 @@ class F6Navigation {
|
|
|
109
163
|
let element: Element | null | ParentNode = this.deepActive(root);
|
|
110
164
|
|
|
111
165
|
while (element && (element as Element).getAttribute("data-sap-ui-fastnavgroup") !== "true" && element !== htmlElement) {
|
|
112
|
-
|
|
166
|
+
element = element.parentElement ? element.parentNode : (element.parentNode as ShadowRoot).host;
|
|
113
167
|
}
|
|
114
168
|
|
|
115
169
|
this.selectedGroup = element as HTMLElement;
|
|
116
|
-
|
|
170
|
+
}
|
|
117
171
|
|
|
118
|
-
|
|
172
|
+
deepActive(root: DocumentOrShadowRoot): Element | null {
|
|
119
173
|
if (root.activeElement && root.activeElement.shadowRoot) {
|
|
120
|
-
|
|
174
|
+
return this.deepActive(root.activeElement.shadowRoot);
|
|
121
175
|
}
|
|
122
176
|
|
|
123
177
|
return root.activeElement;
|
|
124
|
-
|
|
178
|
+
}
|
|
125
179
|
|
|
126
180
|
destroy() {
|
|
127
181
|
this.removeEventListeners();
|
package/src/getSharedResource.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import getSingletonElementInstance from "./util/getSingletonElementInstance.js";
|
|
2
2
|
|
|
3
|
-
const getSharedResourcesInstance = () =>
|
|
3
|
+
const getSharedResourcesInstance = (): Record<string, unknown> | null => {
|
|
4
|
+
if (typeof document === "undefined") {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
return getSingletonElementInstance("ui5-shared-resources", document.head) as unknown as Record<string, unknown>;
|
|
8
|
+
};
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* Use this method to initialize/get resources that you would like to be shared among UI5 Web Components runtime instances.
|
|
@@ -15,6 +20,10 @@ const getSharedResource = <T>(namespace: string, initialValue: T): T => {
|
|
|
15
20
|
const parts = namespace.split(".");
|
|
16
21
|
let current = getSharedResourcesInstance() as Record<string, any>;
|
|
17
22
|
|
|
23
|
+
if (!current) {
|
|
24
|
+
return initialValue;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
for (let i = 0; i < parts.length; i++) {
|
|
19
28
|
const part = parts[i];
|
|
20
29
|
const lastPart = i === parts.length - 1;
|
|
@@ -4,22 +4,22 @@ import EventProvider from "../EventProvider.js";
|
|
|
4
4
|
|
|
5
5
|
type CustomCSSChangeCallback = (tag: string) => void;
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const getEventProvider = () => getSharedResource("CustomStyle.eventProvider", new EventProvider<string, void>());
|
|
8
8
|
const CUSTOM_CSS_CHANGE = "CustomCSSChange";
|
|
9
9
|
|
|
10
10
|
const attachCustomCSSChange = (listener: CustomCSSChangeCallback) => {
|
|
11
|
-
|
|
11
|
+
getEventProvider().attachEvent(CUSTOM_CSS_CHANGE, listener);
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const detachCustomCSSChange = (listener: CustomCSSChangeCallback) => {
|
|
15
|
-
|
|
15
|
+
getEventProvider().detachEvent(CUSTOM_CSS_CHANGE, listener);
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const fireCustomCSSChange = (tag: string) => {
|
|
19
|
-
return
|
|
19
|
+
return getEventProvider().fireEvent(CUSTOM_CSS_CHANGE, tag);
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const getCustomCSSFor = () => getSharedResource<Record<string, Array<string>>>("CustomStyle.customCSSFor", {});
|
|
23
23
|
|
|
24
24
|
// Listen to the eventProvider, in case other copies of this CustomStyle module fire this
|
|
25
25
|
// event, and this copy would therefore need to reRender the ui5 webcomponents; but
|
|
@@ -32,6 +32,7 @@ attachCustomCSSChange((tag: string) => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
const addCustomCSS = (tag: string, css: string) => {
|
|
35
|
+
const customCSSFor = getCustomCSSFor();
|
|
35
36
|
if (!customCSSFor[tag]) {
|
|
36
37
|
customCSSFor[tag] = [];
|
|
37
38
|
}
|
|
@@ -51,6 +52,7 @@ const addCustomCSS = (tag: string, css: string) => {
|
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
const getCustomCSS = (tag: string) => {
|
|
55
|
+
const customCSSFor = getCustomCSSFor();
|
|
54
56
|
return customCSSFor[tag] ? customCSSFor[tag].join("") : "";
|
|
55
57
|
};
|
|
56
58
|
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
+
import UI5Element, { ChangeInfo } from "../UI5Element.js";
|
|
2
|
+
|
|
3
|
+
type InvalidateCallback = (changeInfo: ChangeInfo) => void;
|
|
4
|
+
type MutationCallback = () => void;
|
|
5
|
+
type AssociatedElement = {
|
|
6
|
+
observer: MutationObserver | null;
|
|
7
|
+
callbacks: Array<MutationCallback>;
|
|
8
|
+
};
|
|
9
|
+
type RegisteredElement = {
|
|
10
|
+
host: UI5Element;
|
|
11
|
+
observedElements: Array<HTMLElement>;
|
|
12
|
+
callback: MutationCallback;
|
|
13
|
+
invalidationCallback: InvalidateCallback;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const associatedElements = new WeakMap<HTMLElement, AssociatedElement>();
|
|
17
|
+
const registeredElements = new WeakMap<UI5Element, RegisteredElement>();
|
|
18
|
+
|
|
1
19
|
type AccessibleElement = HTMLElement & {
|
|
2
|
-
accessibleNameRef: string
|
|
3
|
-
accessibleName: string
|
|
4
|
-
}
|
|
20
|
+
accessibleNameRef: string;
|
|
21
|
+
accessibleName: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const observerOptions = {
|
|
25
|
+
attributes: true,
|
|
26
|
+
childList: true,
|
|
27
|
+
characterData: true,
|
|
28
|
+
subtree: true,
|
|
29
|
+
};
|
|
5
30
|
|
|
6
31
|
const getEffectiveAriaLabelText = (el: HTMLElement) => {
|
|
7
32
|
const accessibleEl = el as AccessibleElement;
|
|
@@ -14,38 +39,68 @@ const getEffectiveAriaLabelText = (el: HTMLElement) => {
|
|
|
14
39
|
return undefined;
|
|
15
40
|
}
|
|
16
41
|
|
|
17
|
-
return
|
|
42
|
+
return getAllAccessibleNameRefTexts(el);
|
|
18
43
|
};
|
|
19
44
|
|
|
20
45
|
/**
|
|
21
46
|
*
|
|
22
47
|
* @param {HTMLElement} el Defines the HTMLElement, for which you need to get all related texts
|
|
23
48
|
*/
|
|
24
|
-
const
|
|
25
|
-
const ids = (el as AccessibleElement).accessibleNameRef
|
|
49
|
+
const getAllAccessibleNameRefTexts = (el: HTMLElement) => {
|
|
50
|
+
const ids = (el as AccessibleElement).accessibleNameRef?.split(" ") ?? [];
|
|
26
51
|
const owner = el.getRootNode() as HTMLElement;
|
|
27
52
|
let result = "";
|
|
28
53
|
|
|
29
54
|
ids.forEach((elementId: string, index: number) => {
|
|
30
55
|
const element = owner.querySelector(`[id='${elementId}']`);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
56
|
+
const text = `${element && element.textContent ? element.textContent : ""}`;
|
|
57
|
+
if (text) {
|
|
58
|
+
result += text;
|
|
59
|
+
if (index < ids.length - 1) {
|
|
60
|
+
result += " ";
|
|
61
|
+
}
|
|
35
62
|
}
|
|
36
63
|
});
|
|
37
64
|
|
|
38
65
|
return result;
|
|
39
66
|
};
|
|
40
67
|
|
|
68
|
+
const _getAllAssociatedElementsFromDOM = (el: UI5Element): Array<HTMLElement> => {
|
|
69
|
+
const set = new Set<HTMLElement>();
|
|
70
|
+
// adding labels with attribute for matching the el.id
|
|
71
|
+
const labelsForAssociated = _getAssociatedLabels(el);
|
|
72
|
+
labelsForAssociated.forEach(itm => {
|
|
73
|
+
set.add(itm);
|
|
74
|
+
});
|
|
75
|
+
// adding other elements that id is the same as accessibleNameRef value
|
|
76
|
+
const value = el["accessibleNameRef" as keyof typeof el] as string;
|
|
77
|
+
const ids = value?.split(" ") ?? [];
|
|
78
|
+
ids.forEach(id => {
|
|
79
|
+
const refEl = _getReferencedElementById(el, id);
|
|
80
|
+
if (refEl) {
|
|
81
|
+
set.add(refEl);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return Array.from(set);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const _getAssociatedLabels = (el: HTMLElement): Array<HTMLElement> => {
|
|
88
|
+
const labels = (el.getRootNode() as HTMLElement).querySelectorAll<HTMLElement>(`[for="${el.id}"]`);
|
|
89
|
+
return Array.from(labels);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const _getReferencedElementById = (el: HTMLElement, elementId: string): HTMLElement | null => {
|
|
93
|
+
return (el.getRootNode() as HTMLElement).querySelector<HTMLElement>(`[id='${elementId}']`);
|
|
94
|
+
};
|
|
95
|
+
|
|
41
96
|
/**
|
|
42
97
|
* @param {HTMLElement} el Defines the HTMLElement, for which you need to get all related "label for" texts
|
|
43
98
|
*/
|
|
44
99
|
const getAssociatedLabelForTexts = (el: HTMLElement) => {
|
|
45
100
|
const results: Array<string> = [];
|
|
46
|
-
const labels = (el
|
|
101
|
+
const labels = _getAssociatedLabels(el);
|
|
47
102
|
|
|
48
|
-
labels.forEach((label:
|
|
103
|
+
labels.forEach((label: HTMLElement) => {
|
|
49
104
|
const labelText = label.textContent;
|
|
50
105
|
labelText && results.push(labelText);
|
|
51
106
|
});
|
|
@@ -57,7 +112,107 @@ const getAssociatedLabelForTexts = (el: HTMLElement) => {
|
|
|
57
112
|
return undefined;
|
|
58
113
|
};
|
|
59
114
|
|
|
115
|
+
const _createInvalidationCallback = (el: UI5Element) => {
|
|
116
|
+
const invalidationCallback = (changeInfo: ChangeInfo) => {
|
|
117
|
+
if (!(changeInfo && changeInfo.type === "property" && changeInfo.name === "accessibleNameRef")) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const registeredElement = registeredElements.get(el);
|
|
121
|
+
if (!registeredElement) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const oldAssociatedElements = registeredElement.observedElements;
|
|
125
|
+
const newAssociatedElements = _getAllAssociatedElementsFromDOM(el);
|
|
126
|
+
oldAssociatedElements.forEach(oldElement => {
|
|
127
|
+
if (!newAssociatedElements.includes(oldElement)) {
|
|
128
|
+
_removeObservedElementFromRegisteredElement(registeredElement, oldElement);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
newAssociatedElements.forEach(newElement => {
|
|
132
|
+
if (!oldAssociatedElements.includes(newElement)) {
|
|
133
|
+
_addObservedElementToRegisteredElement(registeredElement, newElement);
|
|
134
|
+
registeredElement.observedElements.push(newElement);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
registeredElement?.callback();
|
|
138
|
+
};
|
|
139
|
+
return invalidationCallback;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const registerUI5Element = (el: UI5Element, callback: MutationCallback) => {
|
|
143
|
+
if (registeredElements.has(el)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const allAssociatedElements = _getAllAssociatedElementsFromDOM(el);
|
|
147
|
+
const invalidationCallback = _createInvalidationCallback(el);
|
|
148
|
+
const registeredElement = {
|
|
149
|
+
host: el,
|
|
150
|
+
observedElements: allAssociatedElements,
|
|
151
|
+
callback,
|
|
152
|
+
invalidationCallback,
|
|
153
|
+
};
|
|
154
|
+
registeredElements.set(el, registeredElement);
|
|
155
|
+
el.attachInvalidate(invalidationCallback);
|
|
156
|
+
|
|
157
|
+
allAssociatedElements.forEach((element: HTMLElement) => {
|
|
158
|
+
_addObservedElementToRegisteredElement(registeredElement, element);
|
|
159
|
+
});
|
|
160
|
+
callback();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const _addObservedElementToRegisteredElement = (registeredElement: RegisteredElement, element: HTMLElement) => {
|
|
164
|
+
let associatedElement = associatedElements.get(element);
|
|
165
|
+
if (!associatedElement) {
|
|
166
|
+
associatedElement = { observer: null, callbacks: [] };
|
|
167
|
+
const observer = new MutationObserver(() => {
|
|
168
|
+
const callbacks = (associatedElement as AssociatedElement).callbacks;
|
|
169
|
+
callbacks.forEach(callback => {
|
|
170
|
+
callback();
|
|
171
|
+
});
|
|
172
|
+
const domEl = document.getElementById(element.id);
|
|
173
|
+
// if no longer should be observed from this registeredElement, remove it
|
|
174
|
+
if (!(registeredElement.host.id === element.getAttribute("for") || domEl)) {
|
|
175
|
+
_removeObservedElementFromRegisteredElement(registeredElement, element);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
associatedElement.observer = observer;
|
|
179
|
+
observer.observe(element, observerOptions);
|
|
180
|
+
associatedElements.set(element, associatedElement);
|
|
181
|
+
}
|
|
182
|
+
if (!associatedElement.callbacks.includes(registeredElement.callback)) {
|
|
183
|
+
associatedElement.callbacks.push(registeredElement.callback);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const _removeObservedElementFromRegisteredElement = (registeredElement: RegisteredElement, element: HTMLElement) => {
|
|
188
|
+
const associatedElement = associatedElements.get(element);
|
|
189
|
+
if (associatedElement) {
|
|
190
|
+
associatedElement.callbacks = associatedElement.callbacks.filter(itm => itm !== registeredElement.callback);
|
|
191
|
+
if (!associatedElement.callbacks.length) {
|
|
192
|
+
associatedElement.observer?.disconnect();
|
|
193
|
+
associatedElements.delete(element);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
registeredElement.observedElements = registeredElement.observedElements.filter(itm => itm !== element);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const deregisterUI5Element = (el: UI5Element) => {
|
|
200
|
+
const registeredElement = registeredElements.get(el);
|
|
201
|
+
if (!registeredElement) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const oldObservedElements = [...registeredElement.observedElements];
|
|
205
|
+
oldObservedElements.forEach(observedElement => {
|
|
206
|
+
_removeObservedElementFromRegisteredElement(registeredElement, observedElement);
|
|
207
|
+
});
|
|
208
|
+
el.detachInvalidate(registeredElement.invalidationCallback);
|
|
209
|
+
registeredElements.delete(el);
|
|
210
|
+
};
|
|
211
|
+
|
|
60
212
|
export {
|
|
61
213
|
getEffectiveAriaLabelText,
|
|
62
214
|
getAssociatedLabelForTexts,
|
|
215
|
+
registerUI5Element,
|
|
216
|
+
deregisterUI5Element,
|
|
217
|
+
getAllAccessibleNameRefTexts,
|
|
63
218
|
};
|