@ui5/webcomponents-base 1.0.0-rc.16 → 1.1.0
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/.eslintignore +1 -0
- package/CHANGELOG.md +47 -0
- package/dist/Boot.js +32 -14
- package/dist/CustomElementsRegistry.js +60 -4
- package/dist/Device.js +6 -0
- package/dist/FeaturesRegistry.js +4 -1
- package/dist/Keys.js +17 -0
- package/dist/Runtimes.js +109 -0
- package/dist/StaticAreaItem.js +3 -0
- package/dist/UI5Element.js +7 -2
- package/dist/UI5ElementMetadata.js +8 -0
- package/dist/features/F6Navigation.js +106 -0
- package/dist/generated/VersionInfo.js +10 -0
- package/dist/resources/bundle.esm.js +8 -8
- package/dist/resources/bundle.esm.js.map +1 -1
- package/dist/sap/base/security/encodeCSS.js +14 -0
- package/dist/sap/base/security/encodeXML.js +23 -0
- package/dist/sap/base/strings/toHex.js +8 -0
- package/dist/theming/CustomStyle.js +23 -3
- package/dist/theming/applyTheme.js +8 -4
- package/dist/theming/getThemeDesignerTheme.js +24 -5
- package/dist/util/FocusableElements.js +8 -6
- package/dist/util/generateHighlightedMarkup.js +1 -1
- package/dist/util/isNodeHidden.js +1 -1
- package/dist/util/metaUrl.js +3 -0
- package/hash.txt +1 -1
- package/lib/generate-version-info/index.js +27 -0
- package/package-scripts.js +3 -1
- package/package.json +4 -4
- package/src/Boot.js +32 -14
- package/src/CustomElementsRegistry.js +60 -4
- package/src/Device.js +6 -0
- package/src/FeaturesRegistry.js +4 -1
- package/src/Keys.js +17 -0
- package/src/Runtimes.js +109 -0
- package/src/StaticAreaItem.js +3 -0
- package/src/UI5Element.js +7 -2
- package/src/UI5ElementMetadata.js +8 -0
- package/src/features/F6Navigation.js +106 -0
- package/src/theming/CustomStyle.js +23 -3
- package/src/theming/applyTheme.js +8 -4
- package/src/theming/getThemeDesignerTheme.js +24 -5
- package/src/util/FocusableElements.js +8 -6
- package/src/util/generateHighlightedMarkup.js +1 -1
- package/src/util/isNodeHidden.js +1 -1
- package/src/util/metaUrl.js +3 -0
- package/used-modules.txt +4 -1
- package/dist/util/encodeCSS.js +0 -24
- package/dist/util/encodeXML.js +0 -35
- package/src/util/encodeCSS.js +0 -24
- package/src/util/encodeXML.js +0 -35
package/src/Runtimes.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import VersionInfo from "./generated/VersionInfo.js";
|
|
2
|
+
import getSharedResource from "./getSharedResource.js";
|
|
3
|
+
import metaUrl from "./util/metaUrl.js"; // eslint-disable-line
|
|
4
|
+
|
|
5
|
+
let currentRuntimeIndex;
|
|
6
|
+
let currentRuntimeAlias = "";
|
|
7
|
+
|
|
8
|
+
const compareCache = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Central registry where all runtimes register themselves by pushing an object.
|
|
12
|
+
* The index in the registry servers as an ID for the runtime.
|
|
13
|
+
* @type {*}
|
|
14
|
+
*/
|
|
15
|
+
const Runtimes = getSharedResource("Runtimes", []);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Registers the current runtime in the shared runtimes resource registry
|
|
19
|
+
*/
|
|
20
|
+
const registerCurrentRuntime = () => {
|
|
21
|
+
if (currentRuntimeIndex === undefined) {
|
|
22
|
+
currentRuntimeIndex = Runtimes.length;
|
|
23
|
+
Runtimes.push({
|
|
24
|
+
...VersionInfo,
|
|
25
|
+
url: metaUrl,
|
|
26
|
+
alias: currentRuntimeAlias,
|
|
27
|
+
description: `Runtime ${currentRuntimeIndex} - ver ${VersionInfo.version}${currentRuntimeAlias ? ` (${currentRuntimeAlias})` : ""}`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the index of the current runtime's object in the shared runtimes resource registry
|
|
34
|
+
* @returns {*}
|
|
35
|
+
*/
|
|
36
|
+
const getCurrentRuntimeIndex = () => {
|
|
37
|
+
return currentRuntimeIndex;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compares two runtimes and returns 1 if the first is of a bigger version, -1 if the second is of a bigger version, and 0 if equal
|
|
42
|
+
* @param index1 The index of the first runtime to compare
|
|
43
|
+
* @param index2 The index of the second runtime to compare
|
|
44
|
+
* @returns {number}
|
|
45
|
+
*/
|
|
46
|
+
const compareRuntimes = (index1, index2) => {
|
|
47
|
+
const cacheIndex = `${index1},${index2}`;
|
|
48
|
+
if (compareCache.has(cacheIndex)) {
|
|
49
|
+
return compareCache.get(cacheIndex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const runtime1 = Runtimes[index1];
|
|
53
|
+
const runtime2 = Runtimes[index2];
|
|
54
|
+
|
|
55
|
+
if (!runtime1 || !runtime2) {
|
|
56
|
+
throw new Error("Invalid runtime index supplied");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If any of the two is a next version, bigger buildTime wins
|
|
60
|
+
if (runtime1.isNext || runtime2.isNext) {
|
|
61
|
+
return runtime1.buildTime - runtime2.buildTime;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// If major versions differ, bigger one wins
|
|
65
|
+
const majorDiff = runtime1.major - runtime2.major;
|
|
66
|
+
if (majorDiff) {
|
|
67
|
+
return majorDiff;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If minor versions differ, bigger one wins
|
|
71
|
+
const minorDiff = runtime1.minor - runtime2.minor;
|
|
72
|
+
if (minorDiff) {
|
|
73
|
+
return minorDiff;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If patch versions differ, bigger one wins
|
|
77
|
+
const patchDiff = runtime1.patch - runtime2.patch;
|
|
78
|
+
if (patchDiff) {
|
|
79
|
+
return patchDiff;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Bigger suffix wins, f.e. rc10 > rc9
|
|
83
|
+
// Important: suffix is alphanumeric, must use natural compare
|
|
84
|
+
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
|
|
85
|
+
const result = collator.compare(runtime1.suffix, runtime2.suffix);
|
|
86
|
+
|
|
87
|
+
compareCache.set(cacheIndex, result);
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set an alias for the the current app/library/microfrontend which will appear in debug messages and console warnings
|
|
93
|
+
* @param alias
|
|
94
|
+
*/
|
|
95
|
+
const setRuntimeAlias = alias => {
|
|
96
|
+
currentRuntimeAlias = alias;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const getAllRuntimes = () => {
|
|
100
|
+
return Runtimes;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
getCurrentRuntimeIndex,
|
|
105
|
+
registerCurrentRuntime,
|
|
106
|
+
compareRuntimes,
|
|
107
|
+
setRuntimeAlias,
|
|
108
|
+
getAllRuntimes,
|
|
109
|
+
};
|
package/src/StaticAreaItem.js
CHANGED
|
@@ -25,6 +25,9 @@ class StaticAreaItem extends HTMLElement {
|
|
|
25
25
|
setOwnerElement(ownerElement) {
|
|
26
26
|
this.ownerElement = ownerElement;
|
|
27
27
|
this.classList.add(this.ownerElement._id); // used for getting the popover in the tests
|
|
28
|
+
if (this.ownerElement.hasAttribute("data-ui5-static-stable")) {
|
|
29
|
+
this.setAttribute("data-ui5-stable", this.ownerElement.getAttribute("data-ui5-static-stable")); // stable selector
|
|
30
|
+
}
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/**
|
package/src/UI5Element.js
CHANGED
|
@@ -101,6 +101,9 @@ class UI5Element extends HTMLElement {
|
|
|
101
101
|
*/
|
|
102
102
|
async connectedCallback() {
|
|
103
103
|
this.setAttribute(this.constructor.getMetadata().getPureTag(), "");
|
|
104
|
+
if (this.constructor.getMetadata().supportsF6FastNavigation()) {
|
|
105
|
+
this.setAttribute("data-sap-ui-fastnavgroup", "true");
|
|
106
|
+
}
|
|
104
107
|
|
|
105
108
|
const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
|
|
106
109
|
|
|
@@ -251,7 +254,8 @@ class UI5Element extends HTMLElement {
|
|
|
251
254
|
|
|
252
255
|
// Listen for any invalidation on the child if invalidateOnChildChange is true or an object (ignore when false or not set)
|
|
253
256
|
if (child.isUI5Element && slotData.invalidateOnChildChange) {
|
|
254
|
-
child.attachInvalidate
|
|
257
|
+
const method = (child.attachInvalidate || child._attachChange).bind(child);
|
|
258
|
+
method(this._getChildChangeListener(slotName));
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
// Listen for the slotchange event if the child is a slot itself
|
|
@@ -311,7 +315,8 @@ class UI5Element extends HTMLElement {
|
|
|
311
315
|
|
|
312
316
|
children.forEach(child => {
|
|
313
317
|
if (child && child.isUI5Element) {
|
|
314
|
-
child.detachInvalidate
|
|
318
|
+
const method = (child.detachInvalidate || child._detachChange).bind(child);
|
|
319
|
+
method(this._getChildChangeListener(slotName));
|
|
315
320
|
}
|
|
316
321
|
|
|
317
322
|
if (isSlot(child)) {
|
|
@@ -188,6 +188,14 @@ class UI5ElementMetadata {
|
|
|
188
188
|
return !!this.metadata.managedSlots;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Determines whether this control supports F6 fast navigation
|
|
193
|
+
* @public
|
|
194
|
+
*/
|
|
195
|
+
supportsF6FastNavigation() {
|
|
196
|
+
return !!this.metadata.fastNavigation;
|
|
197
|
+
}
|
|
198
|
+
|
|
191
199
|
/**
|
|
192
200
|
* Returns an object with key-value pairs of properties and their metadata definitions
|
|
193
201
|
* @public
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { registerFeature } from "../../dist/FeaturesRegistry.js";
|
|
2
|
+
import { isF6Next, isF6Previous } from "../../dist/Keys.js";
|
|
3
|
+
import { getFirstFocusableElement } from "../../dist/util/FocusableElements.js";
|
|
4
|
+
|
|
5
|
+
class F6Navigation {
|
|
6
|
+
init() {
|
|
7
|
+
this.keydownHandler = this._keydownHandler.bind(this);
|
|
8
|
+
this.attachEventListeners();
|
|
9
|
+
this.selectedGroup = null;
|
|
10
|
+
this.groups = [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
attachEventListeners() {
|
|
14
|
+
document.addEventListener("keydown", this.keydownHandler);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async _keydownHandler(event) {
|
|
18
|
+
if (isF6Next(event)) {
|
|
19
|
+
this.updateGroups();
|
|
20
|
+
if (this.groups.length < 1) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const nextIndex = this.groups.indexOf(this.selectedGroup);
|
|
25
|
+
let nextElement = null;
|
|
26
|
+
|
|
27
|
+
if (nextIndex > -1) {
|
|
28
|
+
if (nextIndex + 1 >= this.groups.length) {
|
|
29
|
+
nextElement = this.groups[0];
|
|
30
|
+
} else {
|
|
31
|
+
nextElement = this.groups[nextIndex + 1];
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
nextElement = this.groups[0];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const elementToFocus = await getFirstFocusableElement(nextElement.isUI5Element ? nextElement.getDomRef() : nextElement, true);
|
|
38
|
+
elementToFocus.focus();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isF6Previous(event)) {
|
|
42
|
+
this.updateGroups();
|
|
43
|
+
if (this.groups.length < 1) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const nextIndex = this.groups.indexOf(this.selectedGroup);
|
|
48
|
+
let nextElement = null;
|
|
49
|
+
|
|
50
|
+
if (nextIndex > -1) {
|
|
51
|
+
if (nextIndex - 1 < 0) {
|
|
52
|
+
nextElement = this.groups[this.groups.length - 1];
|
|
53
|
+
} else {
|
|
54
|
+
// Handle the situation where the first focusable element of two neighbor groups is the same
|
|
55
|
+
// For example:
|
|
56
|
+
// <ui5-flexible-column-layout>
|
|
57
|
+
// <ui5-list>
|
|
58
|
+
// <ui5-li>List Item</ui5-li>
|
|
59
|
+
// </ui5-list>
|
|
60
|
+
// </ui5-flexible-column-layout>
|
|
61
|
+
// Here for both FCL & List the firstFoccusableElement is the same (the ui5-li)
|
|
62
|
+
|
|
63
|
+
const firstFocusable = await getFirstFocusableElement(this.groups[nextIndex - 1], true);
|
|
64
|
+
const shouldSkipParent = firstFocusable === await getFirstFocusableElement(this.groups[nextIndex], true);
|
|
65
|
+
|
|
66
|
+
nextElement = this.groups[shouldSkipParent ? nextIndex - 2 : nextIndex - 1];
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
nextElement = this.groups[this.groups.length - 1];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const elementToFocus = await getFirstFocusableElement(nextElement.isUI5Element ? nextElement.getDomRef() : nextElement, true);
|
|
73
|
+
elementToFocus.focus();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
removeEventListeners() {
|
|
78
|
+
document.removeEventListener("keydown", this.keydownHandler);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
updateGroups() {
|
|
82
|
+
this.setSelectedGroup(document.activeElement);
|
|
83
|
+
this.setGroups();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setGroups() {
|
|
87
|
+
this.groups = Array.from(document.querySelectorAll("[data-sap-ui-fastnavgroup='true']")).filter(group => group.clientWidth && window.getComputedStyle(group).visibility !== "hidden");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setSelectedGroup(element) {
|
|
91
|
+
while (element && element.getAttribute("data-sap-ui-fastnavgroup") !== "true" && element !== document.querySelector("html")) {
|
|
92
|
+
element = element.parentElement ? element.parentNode : element.parentNode.host;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.selectedGroup = element;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
destroy() {
|
|
99
|
+
this.removeEventListeners();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const F6HelperInstance = new F6Navigation();
|
|
104
|
+
registerFeature("F6Navigation", F6HelperInstance);
|
|
105
|
+
|
|
106
|
+
export default F6Navigation;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { reRenderAllUI5Elements } from "../Render.js";
|
|
2
|
+
import getSharedResource from "../getSharedResource.js";
|
|
2
3
|
import EventProvider from "../EventProvider.js";
|
|
3
4
|
|
|
4
|
-
const eventProvider = new EventProvider();
|
|
5
|
+
const eventProvider = getSharedResource("CustomStyle.eventProvider", new EventProvider());
|
|
5
6
|
const CUSTOM_CSS_CHANGE = "CustomCSSChange";
|
|
6
7
|
|
|
7
8
|
const attachCustomCSSChange = listener => {
|
|
@@ -16,14 +17,33 @@ const fireCustomCSSChange = tag => {
|
|
|
16
17
|
return eventProvider.fireEvent(CUSTOM_CSS_CHANGE, tag);
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
const customCSSFor = {};
|
|
20
|
+
const customCSSFor = getSharedResource("CustomStyle.customCSSFor", {});
|
|
21
|
+
|
|
22
|
+
// Listen to the eventProvider, in case other copies of this CustomStyle module fire this
|
|
23
|
+
// event, and this copy would therefore need to reRender the ui5 webcomponents; but
|
|
24
|
+
// don't reRender if it was this copy that fired the event to begin with.
|
|
25
|
+
let skipRerender;
|
|
26
|
+
attachCustomCSSChange(tag => {
|
|
27
|
+
if (!skipRerender) {
|
|
28
|
+
reRenderAllUI5Elements({ tag });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
20
31
|
|
|
21
32
|
const addCustomCSS = (tag, css) => {
|
|
22
33
|
if (!customCSSFor[tag]) {
|
|
23
34
|
customCSSFor[tag] = [];
|
|
24
35
|
}
|
|
25
36
|
customCSSFor[tag].push(css);
|
|
26
|
-
|
|
37
|
+
|
|
38
|
+
skipRerender = true;
|
|
39
|
+
try {
|
|
40
|
+
// The event is fired and the attached event listeners are all called synchronously
|
|
41
|
+
// The skipRerender flag will be used to avoid calling reRenderAllUI5Elements twice when it is this copy
|
|
42
|
+
// of CustomStyle.js which is firing the `CustomCSSChange` event.
|
|
43
|
+
fireCustomCSSChange(tag);
|
|
44
|
+
} finally {
|
|
45
|
+
skipRerender = false;
|
|
46
|
+
}
|
|
27
47
|
|
|
28
48
|
return reRenderAllUI5Elements({ tag });
|
|
29
49
|
};
|
|
@@ -16,8 +16,10 @@ const loadThemeBase = async theme => {
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
const cssData = await getThemeProperties(BASE_THEME_PACKAGE, theme);
|
|
20
|
+
if (cssData) {
|
|
21
|
+
createOrUpdateStyle(cssData, "data-ui5-theme-properties", BASE_THEME_PACKAGE);
|
|
22
|
+
}
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const deleteThemeBase = () => {
|
|
@@ -31,8 +33,10 @@ const loadComponentPackages = async theme => {
|
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
+
const cssData = await getThemeProperties(packageName, theme);
|
|
37
|
+
if (cssData) {
|
|
38
|
+
createOrUpdateStyle(cssData, "data-ui5-theme-properties", packageName);
|
|
39
|
+
}
|
|
36
40
|
});
|
|
37
41
|
};
|
|
38
42
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const warnings = new Set();
|
|
2
|
+
|
|
1
3
|
const getThemeMetadata = () => {
|
|
2
4
|
// Check if the class was already applied, most commonly to the link/style tag with the CSS Variables
|
|
3
5
|
let el = document.querySelector(".sapThemeMetaData-Base-baseLib") || document.querySelector(".sapThemeMetaData-UI5-sap-ui-core");
|
|
@@ -7,10 +9,18 @@ const getThemeMetadata = () => {
|
|
|
7
9
|
|
|
8
10
|
el = document.createElement("span");
|
|
9
11
|
el.style.display = "none";
|
|
12
|
+
|
|
13
|
+
// Try with sapThemeMetaData-Base-baseLib first
|
|
10
14
|
el.classList.add("sapThemeMetaData-Base-baseLib");
|
|
11
|
-
el.classList.add("sapThemeMetaData-UI5-sap-ui-core");
|
|
12
15
|
document.body.appendChild(el);
|
|
13
|
-
|
|
16
|
+
let metadata = getComputedStyle(el).backgroundImage;
|
|
17
|
+
|
|
18
|
+
// Try with sapThemeMetaData-UI5-sap-ui-core only if the previous selector was not found
|
|
19
|
+
if (metadata === "none") {
|
|
20
|
+
el.classList.add("sapThemeMetaData-UI5-sap-ui-core");
|
|
21
|
+
metadata = getComputedStyle(el).backgroundImage;
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
document.body.removeChild(el);
|
|
15
25
|
|
|
16
26
|
return metadata;
|
|
@@ -25,14 +35,20 @@ const parseThemeMetadata = metadataString => {
|
|
|
25
35
|
try {
|
|
26
36
|
paramsString = decodeURIComponent(paramsString);
|
|
27
37
|
} catch (ex) {
|
|
28
|
-
|
|
38
|
+
if (!warnings.has("decode")) {
|
|
39
|
+
console.warn("Malformed theme metadata string, unable to decodeURIComponent"); // eslint-disable-line
|
|
40
|
+
warnings.add("decode");
|
|
41
|
+
}
|
|
29
42
|
return;
|
|
30
43
|
}
|
|
31
44
|
}
|
|
32
45
|
try {
|
|
33
46
|
return JSON.parse(paramsString);
|
|
34
47
|
} catch (ex) {
|
|
35
|
-
|
|
48
|
+
if (!warnings.has("parse")) {
|
|
49
|
+
console.warn("Malformed theme metadata string, unable to parse JSON"); // eslint-disable-line
|
|
50
|
+
warnings.add("parse");
|
|
51
|
+
}
|
|
36
52
|
}
|
|
37
53
|
}
|
|
38
54
|
};
|
|
@@ -45,7 +61,10 @@ const processThemeMetadata = metadata => {
|
|
|
45
61
|
themeName = metadata.Path.match(/\.([^.]+)\.css_variables$/)[1];
|
|
46
62
|
baseThemeName = metadata.Extends[0];
|
|
47
63
|
} catch (ex) {
|
|
48
|
-
|
|
64
|
+
if (!warnings.has("object")) {
|
|
65
|
+
console.warn("Malformed theme metadata Object", metadata); // eslint-disable-line
|
|
66
|
+
warnings.add("object");
|
|
67
|
+
}
|
|
49
68
|
return;
|
|
50
69
|
}
|
|
51
70
|
|
|
@@ -5,27 +5,27 @@ const isFocusTrap = el => {
|
|
|
5
5
|
return el.hasAttribute("data-ui5-focus-trap");
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
const getFirstFocusableElement = async container => {
|
|
8
|
+
const getFirstFocusableElement = async (container, startFromContainer) => {
|
|
9
9
|
if (!container || isNodeHidden(container)) {
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
return findFocusableElement(container, true);
|
|
13
|
+
return findFocusableElement(container, true, startFromContainer);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
const getLastFocusableElement = async container => {
|
|
16
|
+
const getLastFocusableElement = async (container, startFromContainer) => {
|
|
17
17
|
if (!container || isNodeHidden(container)) {
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
return findFocusableElement(container, false);
|
|
21
|
+
return findFocusableElement(container, false, startFromContainer);
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const isElemFocusable = el => {
|
|
25
25
|
return el.hasAttribute("data-ui5-focus-redirect") || !isNodeHidden(el);
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const findFocusableElement = async (container, forward) => {
|
|
28
|
+
const findFocusableElement = async (container, forward, startFromContainer) => {
|
|
29
29
|
let child;
|
|
30
30
|
|
|
31
31
|
if (container.shadowRoot) {
|
|
@@ -33,8 +33,10 @@ const findFocusableElement = async (container, forward) => {
|
|
|
33
33
|
} else if (container.assignedNodes && container.assignedNodes()) {
|
|
34
34
|
const assignedElements = container.assignedNodes();
|
|
35
35
|
child = forward ? assignedElements[0] : assignedElements[assignedElements.length - 1];
|
|
36
|
+
} else if (startFromContainer) {
|
|
37
|
+
child = container;
|
|
36
38
|
} else {
|
|
37
|
-
child = forward ? container.
|
|
39
|
+
child = forward ? container.firstElementChild : container.lastElementChild;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
let focusableDescendant;
|
package/src/util/isNodeHidden.js
CHANGED
|
@@ -3,7 +3,7 @@ const isNodeHidden = node => {
|
|
|
3
3
|
return false;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
return (node.offsetWidth <= 0 && node.offsetHeight <= 0) || node.style.visibility === "hidden";
|
|
6
|
+
return (node.offsetWidth <= 0 && node.offsetHeight <= 0) || (node.style && node.style.visibility === "hidden");
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export default isNodeHidden;
|
package/used-modules.txt
CHANGED
package/dist/util/encodeCSS.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const rCSS = /[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff\u2028\u2029][0-9A-Fa-f]?/g; // eslint-disable-line
|
|
2
|
-
|
|
3
|
-
const toHex = (iChar, iLength) => {
|
|
4
|
-
let sHex = iChar.toString(16);
|
|
5
|
-
if (iLength) {
|
|
6
|
-
sHex = sHex.padStart(iLength, "0");
|
|
7
|
-
}
|
|
8
|
-
return sHex;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const fnCSS = sChar => {
|
|
12
|
-
const iChar = sChar.charCodeAt(0);
|
|
13
|
-
if (sChar.length === 1) {
|
|
14
|
-
return `\\${toHex(iChar)}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return `\\${toHex(iChar)} ${sChar.substr(1)}`;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const encodeCSS = string => {
|
|
21
|
-
return string.replace(rCSS, fnCSS);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default encodeCSS;
|
package/dist/util/encodeXML.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const fnToHex = (iChar, iLength) => {
|
|
2
|
-
let sHex = iChar.toString(16);
|
|
3
|
-
if (iLength) {
|
|
4
|
-
sHex = sHex.padStart(iLength, "0");
|
|
5
|
-
}
|
|
6
|
-
return sHex;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const rHtml = /[\x00-\x2b\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff\u2028\u2029]/g, // eslint-disable-line no-control-regex
|
|
10
|
-
rHtmlReplace = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/, // eslint-disable-line no-control-regex
|
|
11
|
-
mHtmlLookup = {
|
|
12
|
-
"<": "<",
|
|
13
|
-
">": ">",
|
|
14
|
-
"&": "&",
|
|
15
|
-
"\"": """,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const fnHtml = sChar => {
|
|
19
|
-
let sEncoded = mHtmlLookup[sChar];
|
|
20
|
-
if (!sEncoded) {
|
|
21
|
-
if (rHtmlReplace.test(sChar)) {
|
|
22
|
-
sEncoded = "�";
|
|
23
|
-
} else {
|
|
24
|
-
sEncoded = `&#x${fnToHex(sChar.charCodeAt(0))};`;
|
|
25
|
-
}
|
|
26
|
-
mHtmlLookup[sChar] = sEncoded;
|
|
27
|
-
}
|
|
28
|
-
return sEncoded;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const fnEncodeXML = sString => {
|
|
32
|
-
return sString.replace(rHtml, fnHtml);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default fnEncodeXML;
|
package/src/util/encodeCSS.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const rCSS = /[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff\u2028\u2029][0-9A-Fa-f]?/g; // eslint-disable-line
|
|
2
|
-
|
|
3
|
-
const toHex = (iChar, iLength) => {
|
|
4
|
-
let sHex = iChar.toString(16);
|
|
5
|
-
if (iLength) {
|
|
6
|
-
sHex = sHex.padStart(iLength, "0");
|
|
7
|
-
}
|
|
8
|
-
return sHex;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const fnCSS = sChar => {
|
|
12
|
-
const iChar = sChar.charCodeAt(0);
|
|
13
|
-
if (sChar.length === 1) {
|
|
14
|
-
return `\\${toHex(iChar)}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return `\\${toHex(iChar)} ${sChar.substr(1)}`;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const encodeCSS = string => {
|
|
21
|
-
return string.replace(rCSS, fnCSS);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default encodeCSS;
|
package/src/util/encodeXML.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const fnToHex = (iChar, iLength) => {
|
|
2
|
-
let sHex = iChar.toString(16);
|
|
3
|
-
if (iLength) {
|
|
4
|
-
sHex = sHex.padStart(iLength, "0");
|
|
5
|
-
}
|
|
6
|
-
return sHex;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const rHtml = /[\x00-\x2b\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff\u2028\u2029]/g, // eslint-disable-line no-control-regex
|
|
10
|
-
rHtmlReplace = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/, // eslint-disable-line no-control-regex
|
|
11
|
-
mHtmlLookup = {
|
|
12
|
-
"<": "<",
|
|
13
|
-
">": ">",
|
|
14
|
-
"&": "&",
|
|
15
|
-
"\"": """,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const fnHtml = sChar => {
|
|
19
|
-
let sEncoded = mHtmlLookup[sChar];
|
|
20
|
-
if (!sEncoded) {
|
|
21
|
-
if (rHtmlReplace.test(sChar)) {
|
|
22
|
-
sEncoded = "�";
|
|
23
|
-
} else {
|
|
24
|
-
sEncoded = `&#x${fnToHex(sChar.charCodeAt(0))};`;
|
|
25
|
-
}
|
|
26
|
-
mHtmlLookup[sChar] = sEncoded;
|
|
27
|
-
}
|
|
28
|
-
return sEncoded;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const fnEncodeXML = sString => {
|
|
32
|
-
return sString.replace(rHtml, fnHtml);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default fnEncodeXML;
|