@ui5/webcomponents-base 0.0.0-6cb3eb0db → 0.0.0-701b14e36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.eslintignore +3 -2
  2. package/CHANGELOG.md +199 -0
  3. package/README.md +2 -3
  4. package/bundle.esm.js +1 -2
  5. package/dist/Boot.js +34 -18
  6. package/dist/CSP.js +59 -0
  7. package/dist/CustomElementsRegistry.js +60 -4
  8. package/dist/CustomElementsScope.js +20 -98
  9. package/dist/CustomElementsScopeUtils.js +108 -0
  10. package/dist/Device.js +20 -1
  11. package/dist/FeaturesRegistry.js +4 -1
  12. package/dist/FontFace.js +7 -95
  13. package/dist/InitialConfiguration.js +11 -0
  14. package/dist/Keys.js +47 -0
  15. package/dist/ManagedStyles.js +83 -0
  16. package/dist/Render.js +3 -1
  17. package/dist/Runtimes.js +107 -0
  18. package/dist/StaticAreaItem.js +4 -10
  19. package/dist/SystemCSSVars.js +4 -25
  20. package/dist/UI5Element.js +31 -24
  21. package/dist/UI5ElementMetadata.js +17 -1
  22. package/dist/asset-registries/Icons.js +21 -14
  23. package/dist/asset-registries/LocaleData.js +17 -3
  24. package/dist/asset-registries/Themes.js +6 -2
  25. package/dist/assets-meta/IconCollectionsAlias.js +18 -0
  26. package/dist/config/Icons.js +52 -0
  27. package/dist/config/Theme.js +25 -0
  28. package/dist/css/BusyIndicator.css +76 -0
  29. package/dist/css/FontFace.css +75 -0
  30. package/dist/css/OverrideFontFace.css +32 -0
  31. package/dist/css/SystemCSSVars.css +17 -0
  32. package/dist/features/F6Navigation.js +106 -0
  33. package/dist/features/OpenUI5Enablement.js +119 -0
  34. package/dist/generated/AssetParameters.js +1 -1
  35. package/dist/generated/VersionInfo.js +10 -0
  36. package/dist/generated/css/BusyIndicator.css.js +5 -0
  37. package/dist/generated/css/FontFace.css.js +5 -0
  38. package/dist/generated/css/OverrideFontFace.css.js +5 -0
  39. package/dist/generated/css/SystemCSSVars.css.js +5 -0
  40. package/dist/i18nBundle.js +32 -2
  41. package/dist/renderer/LitRenderer.js +32 -12
  42. package/dist/renderer/directives/style-map.js +72 -0
  43. package/dist/renderer/executeTemplate.js +19 -2
  44. package/dist/resources/bundle.esm.js +8 -13
  45. package/dist/resources/bundle.esm.js.map +1 -1
  46. package/dist/sap/base/security/encodeCSS.js +14 -0
  47. package/dist/sap/base/security/encodeXML.js +23 -0
  48. package/dist/sap/base/strings/toHex.js +8 -0
  49. package/dist/sap/ui/thirdparty/caja-html-sanitizer.js +1 -1
  50. package/dist/test-resources/pages/i18n.html +1 -3
  51. package/dist/test-resources/specs/CustomTheme.spec.js +2 -2
  52. package/dist/test-resources/specs/Theming.spec.js +15 -4
  53. package/dist/theming/CustomStyle.js +23 -3
  54. package/dist/theming/applyTheme.js +11 -10
  55. package/dist/theming/getEffectiveLinksHrefs.js +27 -0
  56. package/dist/theming/getEffectiveStyle.js +9 -0
  57. package/dist/theming/getStylesString.js +4 -2
  58. package/dist/theming/getThemeDesignerTheme.js +25 -5
  59. package/dist/theming/preloadLinks.js +23 -0
  60. package/dist/updateShadowRoot.js +8 -4
  61. package/dist/util/FocusableElements.js +8 -6
  62. package/dist/util/InvisibleMessage.js +11 -9
  63. package/dist/util/TabbableElements.js +4 -0
  64. package/dist/util/createLinkInHead.js +19 -0
  65. package/dist/util/escapeRegex.js +10 -0
  66. package/dist/util/generateHighlightedMarkup.js +42 -0
  67. package/dist/util/getNormalizedTarget.js +16 -0
  68. package/dist/util/isNodeHidden.js +1 -1
  69. package/lib/generate-asset-parameters/index.js +8 -5
  70. package/lib/generate-styles/index.js +26 -0
  71. package/lib/generate-version-info/index.js +32 -0
  72. package/package-scripts.js +10 -14
  73. package/package.json +9 -11
  74. package/src/Boot.js +34 -18
  75. package/src/CSP.js +59 -0
  76. package/src/CustomElementsRegistry.js +60 -4
  77. package/src/CustomElementsScope.js +20 -98
  78. package/src/CustomElementsScopeUtils.js +108 -0
  79. package/src/Device.js +20 -1
  80. package/src/FeaturesRegistry.js +4 -1
  81. package/src/FontFace.js +7 -95
  82. package/src/InitialConfiguration.js +11 -0
  83. package/src/Keys.js +47 -0
  84. package/src/ManagedStyles.js +83 -0
  85. package/src/Render.js +3 -1
  86. package/src/Runtimes.js +107 -0
  87. package/src/StaticAreaItem.js +4 -10
  88. package/src/SystemCSSVars.js +4 -25
  89. package/src/UI5Element.js +31 -24
  90. package/src/UI5ElementMetadata.js +17 -1
  91. package/src/asset-registries/Icons.js +21 -14
  92. package/src/asset-registries/LocaleData.js +17 -3
  93. package/src/asset-registries/Themes.js +6 -2
  94. package/src/assets-meta/IconCollectionsAlias.js +18 -0
  95. package/src/config/Icons.js +52 -0
  96. package/src/config/Theme.js +25 -0
  97. package/src/css/BusyIndicator.css +76 -0
  98. package/src/css/FontFace.css +75 -0
  99. package/src/css/OverrideFontFace.css +32 -0
  100. package/src/css/SystemCSSVars.css +17 -0
  101. package/src/features/F6Navigation.js +106 -0
  102. package/src/features/OpenUI5Enablement.js +119 -0
  103. package/src/i18nBundle.js +32 -2
  104. package/src/renderer/LitRenderer.js +32 -12
  105. package/src/renderer/directives/style-map.js +72 -0
  106. package/src/renderer/executeTemplate.js +19 -2
  107. package/src/theming/CustomStyle.js +23 -3
  108. package/src/theming/applyTheme.js +11 -10
  109. package/src/theming/getEffectiveLinksHrefs.js +27 -0
  110. package/src/theming/getEffectiveStyle.js +9 -0
  111. package/src/theming/getStylesString.js +4 -2
  112. package/src/theming/getThemeDesignerTheme.js +25 -5
  113. package/src/theming/preloadLinks.js +23 -0
  114. package/src/updateShadowRoot.js +8 -4
  115. package/src/util/FocusableElements.js +8 -6
  116. package/src/util/InvisibleMessage.js +11 -9
  117. package/src/util/TabbableElements.js +4 -0
  118. package/src/util/createLinkInHead.js +19 -0
  119. package/src/util/escapeRegex.js +10 -0
  120. package/src/util/generateHighlightedMarkup.js +42 -0
  121. package/src/util/getNormalizedTarget.js +16 -0
  122. package/src/util/isNodeHidden.js +1 -1
  123. package/used-modules.txt +4 -1
  124. package/dist/theming/createThemePropertiesStyleTag.js +0 -20
  125. package/dist/util/encodeCSS.js +0 -24
  126. package/hash.txt +0 -1
  127. package/src/theming/createThemePropertiesStyleTag.js +0 -20
  128. package/src/util/encodeCSS.js +0 -24
@@ -0,0 +1,14 @@
1
+ import toHex from '../strings/toHex.js';
2
+ var rCSS = /[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff\u2028\u2029][0-9A-Fa-f]?/g;
3
+ var fnCSS = function (sChar) {
4
+ var iChar = sChar.charCodeAt(0);
5
+ if (sChar.length === 1) {
6
+ return '\\' + toHex(iChar);
7
+ } else {
8
+ return '\\' + toHex(iChar) + ' ' + sChar.substr(1);
9
+ }
10
+ };
11
+ var fnEncodeCSS = function (sString) {
12
+ return sString.replace(rCSS, fnCSS);
13
+ };
14
+ export default fnEncodeCSS;
@@ -0,0 +1,23 @@
1
+ import toHex from '../strings/toHex.js';
2
+ var rHtml = /[\x00-\x2b\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff\u2028\u2029]/g, rHtmlReplace = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/, mHtmlLookup = {
3
+ '<': '&lt;',
4
+ '>': '&gt;',
5
+ '&': '&amp;',
6
+ '"': '&quot;'
7
+ };
8
+ var fnHtml = function (sChar) {
9
+ var sEncoded = mHtmlLookup[sChar];
10
+ if (!sEncoded) {
11
+ if (rHtmlReplace.test(sChar)) {
12
+ sEncoded = '&#xfffd;';
13
+ } else {
14
+ sEncoded = '&#x' + toHex(sChar.charCodeAt(0)) + ';';
15
+ }
16
+ mHtmlLookup[sChar] = sEncoded;
17
+ }
18
+ return sEncoded;
19
+ };
20
+ var fnEncodeXML = function (sString) {
21
+ return sString.replace(rHtml, fnHtml);
22
+ };
23
+ export default fnEncodeXML;
@@ -0,0 +1,8 @@
1
+ var fnToHex = function (iChar, iLength) {
2
+ var sHex = iChar.toString(16);
3
+ if (iLength) {
4
+ sHex = sHex.padStart(iLength, "0");
5
+ }
6
+ return sHex;
7
+ };
8
+ export default fnToHex;
@@ -1989,7 +1989,7 @@ if (typeof window !== 'undefined') {
1989
1989
  }
1990
1990
  /*!
1991
1991
  * OpenUI5
1992
- * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company.
1992
+ * (c) Copyright 2009-2022 SAP SE or an SAP affiliate company.
1993
1993
  * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
1994
1994
  */
1995
1995
  // Based on coding from the HTML4 Sanitizer by Google Inc.
@@ -17,9 +17,7 @@
17
17
  const demo = async () => {
18
18
  ["de", "es", "fr", "en"].forEach(localeId => registerI18nLoader("myApp", localeId, () => `./assets/messagebundle_${localeId}.properties`));
19
19
 
20
- await fetchI18nBundle("myApp");
21
-
22
- const bundle = getI18nBundle("myApp");
20
+ const bundle = await getI18nBundle("myApp");
23
21
 
24
22
  const pleaseWait = bundle.getText("PLEASE_WAIT");
25
23
  console.log("Please wait in the current language is: ", pleaseWait);
@@ -16,8 +16,8 @@ describe("Custom themes can be registered", () => {
16
16
  const config = window['sap-ui-webcomponents-bundle'].configuration;
17
17
  await config.setTheme(newTheme);
18
18
 
19
- const style = document.querySelector(`style[data-ui5-theme-properties="@ui5/webcomponents-base-test"]`);
20
- const varsFound = style && style.textContent.includes(var1);
19
+ const style = document.adoptedStyleSheets.find(sh => sh._ui5StyleId === "data-ui5-theme-properties|@ui5/webcomponents-base-test").cssRules[0].cssText;
20
+ const varsFound = style && style.includes(var1);
21
21
  done(varsFound);
22
22
  }, newTheme);
23
23
 
@@ -8,8 +8,8 @@ describe("Theming works", () => {
8
8
  it("Tests that the parameters for the default theme are embedded on boot", async () => {
9
9
 
10
10
  const res = await browser.executeAsync(done => {
11
- const style = document.querySelector(`style[data-ui5-theme-properties="@ui5/webcomponents-base-test"]`);
12
- done(style && style.textContent.includes("--var1: red")); // see test/assets/Themes.js
11
+ const style = document.adoptedStyleSheets.find(sh => sh._ui5StyleId === "data-ui5-theme-properties|@ui5/webcomponents-base-test").cssRules[0].cssText
12
+ done(style && style.includes("--var1: red")); // see test/assets/Themes.js
13
13
  });
14
14
 
15
15
  assert.strictEqual(res, true, "The fiori3 vars are found");
@@ -22,12 +22,23 @@ describe("Theming works", () => {
22
22
  const config = window['sap-ui-webcomponents-bundle'].configuration;
23
23
  await config.setTheme(newTheme);
24
24
 
25
- const style = document.querySelector(`style[data-ui5-theme-properties="@ui5/webcomponents-base-test"]`);
26
- const varsFound = style && style.textContent.includes("--var1: orange"); // see test/assets/Themes.js
25
+ const style = document.adoptedStyleSheets.find(sh => sh._ui5StyleId === "data-ui5-theme-properties|@ui5/webcomponents-base-test").cssRules[0].cssText
26
+ const varsFound = style && style.includes("--var1: orange"); // see test/assets/Themes.js
27
27
  done(varsFound);
28
28
  }, newTheme);
29
29
 
30
30
  assert.strictEqual(res, true, "Theme parameters changed");
31
31
  });
32
32
 
33
+ it("Tests fallback to default theme when setting unknown theme", async () => {
34
+ const unknownTheme = 'sap_unknown_theme';
35
+ await browser.url(`http://localhost:9191/test-resources/pages/AllTestElements.html?sap-ui-theme=${unknownTheme}`);
36
+
37
+ const res = await browser.executeAsync( async (done) => {
38
+ const cssVarValue = getComputedStyle(document.documentElement).getPropertyValue('--var1');
39
+ done(cssVarValue);
40
+ });
41
+
42
+ assert.strictEqual(res, ' red', "Default theme parameters loaded");
43
+ });
33
44
  });
@@ -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
- fireCustomCSSChange(tag);
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
  };
@@ -1,10 +1,10 @@
1
1
  import { getThemeProperties, getRegisteredPackages, isThemeRegistered } from "../asset-registries/Themes.js";
2
- import createThemePropertiesStyleTag from "./createThemePropertiesStyleTag.js";
2
+ import { removeStyle, createOrUpdateStyle } from "../ManagedStyles.js";
3
3
  import getThemeDesignerTheme from "./getThemeDesignerTheme.js";
4
4
  import { fireThemeLoaded } from "./ThemeLoaded.js";
5
5
  import { getFeature } from "../FeaturesRegistry.js";
6
6
 
7
- const BASE_THEME_PACKAGE = "@ui5/webcomponents-theme-base";
7
+ const BASE_THEME_PACKAGE = "@ui5/webcomponents-theming";
8
8
 
9
9
  const isThemeBaseRegistered = () => {
10
10
  const registeredPackages = getRegisteredPackages();
@@ -16,15 +16,14 @@ const loadThemeBase = async theme => {
16
16
  return;
17
17
  }
18
18
 
19
- const cssText = await getThemeProperties(BASE_THEME_PACKAGE, theme);
20
- createThemePropertiesStyleTag(cssText, BASE_THEME_PACKAGE);
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 = () => {
24
- const styleElement = document.head.querySelector(`style[data-ui5-theme-properties="${BASE_THEME_PACKAGE}"]`);
25
- if (styleElement) {
26
- styleElement.parentElement.removeChild(styleElement);
27
- }
26
+ removeStyle("data-ui5-theme-properties", BASE_THEME_PACKAGE);
28
27
  };
29
28
 
30
29
  const loadComponentPackages = async theme => {
@@ -34,8 +33,10 @@ const loadComponentPackages = async theme => {
34
33
  return;
35
34
  }
36
35
 
37
- const cssText = await getThemeProperties(packageName, theme);
38
- createThemePropertiesStyleTag(cssText, packageName);
36
+ const cssData = await getThemeProperties(packageName, theme);
37
+ if (cssData) {
38
+ createOrUpdateStyle(cssData, "data-ui5-theme-properties", packageName);
39
+ }
39
40
  });
40
41
  };
41
42
 
@@ -0,0 +1,27 @@
1
+ import { getUrl } from "../CSP.js";
2
+ import { getFeature } from "../FeaturesRegistry.js";
3
+
4
+ const flatten = arr => {
5
+ return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val), []);
6
+ };
7
+
8
+ const getEffectiveLinksHrefs = (ElementClass, forStaticArea = false) => {
9
+ let stylesData = ElementClass[forStaticArea ? "staticAreaStyles" : "styles"];
10
+ const OpenUI5Enablement = getFeature("OpenUI5Enablement");
11
+
12
+ if (!stylesData) {
13
+ return;
14
+ }
15
+
16
+ if (!Array.isArray(stylesData)) {
17
+ stylesData = [stylesData];
18
+ }
19
+
20
+ if (OpenUI5Enablement) {
21
+ stylesData.push(OpenUI5Enablement.getBusyIndicatorStyles());
22
+ }
23
+
24
+ return flatten(stylesData).filter(data => !!data).map(data => getUrl(data.packageName, data.fileName));
25
+ };
26
+
27
+ export default getEffectiveLinksHrefs;
@@ -1,5 +1,6 @@
1
1
  import { getCustomCSS, attachCustomCSSChange } from "./CustomStyle.js";
2
2
  import getStylesString from "./getStylesString.js";
3
+ import { getFeature } from "../FeaturesRegistry.js";
3
4
 
4
5
  const effectiveStyleMap = new Map();
5
6
 
@@ -10,9 +11,15 @@ attachCustomCSSChange(tag => {
10
11
  const getEffectiveStyle = (ElementClass, forStaticArea = false) => {
11
12
  const tag = ElementClass.getMetadata().getTag();
12
13
  const key = `${tag}_${forStaticArea ? "static" : "normal"}`;
14
+ const OpenUI5Enablement = getFeature("OpenUI5Enablement");
13
15
 
14
16
  if (!effectiveStyleMap.has(key)) {
15
17
  let effectiveStyle;
18
+ let busyIndicatorStyles = "";
19
+
20
+ if (OpenUI5Enablement) {
21
+ busyIndicatorStyles = getStylesString(OpenUI5Enablement.getBusyIndicatorStyles());
22
+ }
16
23
 
17
24
  if (forStaticArea) {
18
25
  effectiveStyle = getStylesString(ElementClass.staticAreaStyles);
@@ -21,6 +28,8 @@ const getEffectiveStyle = (ElementClass, forStaticArea = false) => {
21
28
  const builtInStyles = getStylesString(ElementClass.styles);
22
29
  effectiveStyle = `${builtInStyles} ${customStyle}`;
23
30
  }
31
+
32
+ effectiveStyle = `${effectiveStyle} ${busyIndicatorStyles}`;
24
33
  effectiveStyleMap.set(key, effectiveStyle);
25
34
  }
26
35
 
@@ -1,9 +1,11 @@
1
1
  const getStylesString = styles => {
2
2
  if (Array.isArray(styles)) {
3
- return flatten(styles.filter(style => !!style)).join(" ");
3
+ return flatten(styles.filter(style => !!style)).map(style => {
4
+ return typeof style === "string" ? style : style.content;
5
+ }).join(" ");
4
6
  }
5
7
 
6
- return styles;
8
+ return typeof styles === "string" ? styles : styles.content;
7
9
  };
8
10
 
9
11
  const flatten = arr => {
@@ -1,15 +1,26 @@
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
- let el = document.querySelector(".sapThemeMetaData-Base-baseLib");
5
+ let el = document.querySelector(".sapThemeMetaData-Base-baseLib") || document.querySelector(".sapThemeMetaData-UI5-sap-ui-core");
4
6
  if (el) {
5
7
  return getComputedStyle(el).backgroundImage;
6
8
  }
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
15
  document.body.appendChild(el);
12
- const metadata = getComputedStyle(el).backgroundImage;
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
+
13
24
  document.body.removeChild(el);
14
25
 
15
26
  return metadata;
@@ -24,14 +35,20 @@ const parseThemeMetadata = metadataString => {
24
35
  try {
25
36
  paramsString = decodeURIComponent(paramsString);
26
37
  } catch (ex) {
27
- console.warn("Malformed theme metadata string, unable to decodeURIComponent"); // eslint-disable-line
38
+ if (!warnings.has("decode")) {
39
+ console.warn("Malformed theme metadata string, unable to decodeURIComponent"); // eslint-disable-line
40
+ warnings.add("decode");
41
+ }
28
42
  return;
29
43
  }
30
44
  }
31
45
  try {
32
46
  return JSON.parse(paramsString);
33
47
  } catch (ex) {
34
- console.warn("Malformed theme metadata string, unable to parse JSON"); // eslint-disable-line
48
+ if (!warnings.has("parse")) {
49
+ console.warn("Malformed theme metadata string, unable to parse JSON"); // eslint-disable-line
50
+ warnings.add("parse");
51
+ }
35
52
  }
36
53
  }
37
54
  };
@@ -44,7 +61,10 @@ const processThemeMetadata = metadata => {
44
61
  themeName = metadata.Path.match(/\.([^.]+)\.css_variables$/)[1];
45
62
  baseThemeName = metadata.Extends[0];
46
63
  } catch (ex) {
47
- console.warn("Malformed theme metadata Object", metadata); // eslint-disable-line
64
+ if (!warnings.has("object")) {
65
+ console.warn("Malformed theme metadata Object", metadata); // eslint-disable-line
66
+ warnings.add("object");
67
+ }
48
68
  return;
49
69
  }
50
70
 
@@ -0,0 +1,23 @@
1
+ import getEffectiveLinksHrefs from "./getEffectiveLinksHrefs.js";
2
+ import createLinkInHead from "../util/createLinkInHead.js";
3
+ import { shouldUseLinks, shouldPreloadLinks } from "../CSP.js";
4
+
5
+ const preloaded = new Set();
6
+
7
+ const preloadLinks = ElementClass => {
8
+ if (!shouldUseLinks() || !shouldPreloadLinks()) {
9
+ return;
10
+ }
11
+
12
+ const linksHrefs = getEffectiveLinksHrefs(ElementClass, false) || [];
13
+ const staticAreaLinksHrefs = getEffectiveLinksHrefs(ElementClass, true) || [];
14
+
15
+ [...linksHrefs, ...staticAreaLinksHrefs].forEach(href => {
16
+ if (!preloaded.has(href)) {
17
+ createLinkInHead(href, { rel: "preload", as: "style" });
18
+ preloaded.add(href);
19
+ }
20
+ });
21
+ };
22
+
23
+ export default preloadLinks;
@@ -1,7 +1,9 @@
1
1
  import executeTemplate from "./renderer/executeTemplate.js";
2
2
  import getConstructableStyle from "./theming/getConstructableStyle.js";
3
3
  import getEffectiveStyle from "./theming/getEffectiveStyle.js";
4
+ import getEffectiveLinksHrefs from "./theming/getEffectiveLinksHrefs.js";
4
5
  import isLegacyBrowser from "./isLegacyBrowser.js";
6
+ import { shouldUseLinks } from "./CSP.js";
5
7
 
6
8
  /**
7
9
  * Updates the shadow root of a UI5Element or its static area item
@@ -9,18 +11,20 @@ import isLegacyBrowser from "./isLegacyBrowser.js";
9
11
  * @param forStaticArea
10
12
  */
11
13
  const updateShadowRoot = (element, forStaticArea = false) => {
12
- let styleToPrepend;
14
+ let styleStrOrHrefsArr;
13
15
  const template = forStaticArea ? "staticAreaTemplate" : "template";
14
16
  const shadowRoot = forStaticArea ? element.staticAreaItem.shadowRoot : element.shadowRoot;
15
17
  const renderResult = executeTemplate(element.constructor[template], element);
16
18
 
17
- if (document.adoptedStyleSheets) { // Chrome
19
+ if (shouldUseLinks()) {
20
+ styleStrOrHrefsArr = getEffectiveLinksHrefs(element.constructor, forStaticArea);
21
+ } else if (document.adoptedStyleSheets) { // Chrome
18
22
  shadowRoot.adoptedStyleSheets = getConstructableStyle(element.constructor, forStaticArea);
19
23
  } else if (!isLegacyBrowser()) { // FF, Safari
20
- styleToPrepend = getEffectiveStyle(element.constructor, forStaticArea);
24
+ styleStrOrHrefsArr = getEffectiveStyle(element.constructor, forStaticArea);
21
25
  }
22
26
 
23
- element.constructor.render(renderResult, shadowRoot, styleToPrepend, { host: element });
27
+ element.constructor.render(renderResult, shadowRoot, styleStrOrHrefsArr, forStaticArea, { host: element });
24
28
  };
25
29
 
26
30
  export default updateShadowRoot;
@@ -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.firstChild : container.lastChild;
39
+ child = forward ? container.firstElementChild : container.lastElementChild;
38
40
  }
39
41
 
40
42
  let focusableDescendant;
@@ -5,18 +5,20 @@ import { attachBoot } from "../Boot.js";
5
5
  let politeSpan;
6
6
  let assertiveSpan;
7
7
 
8
+ const setOutOfViewportStyles = el => {
9
+ el.style.position = "absolute";
10
+ el.style.clip = "rect(1px,1px,1px,1px)";
11
+ el.style.userSelect = "none";
12
+ el.style.left = "-1000px";
13
+ el.style.top = "-1000px";
14
+ el.style.pointerEvents = "none";
15
+ };
16
+
8
17
  attachBoot(() => {
9
18
  if (politeSpan && assertiveSpan) {
10
19
  return;
11
20
  }
12
21
 
13
- const styles = `position: absolute;
14
- clip: rect(1px,1px,1px,1px);
15
- user-select: none;
16
- left: -1000px;
17
- top: -1000px;
18
- pointer-events: none;`;
19
-
20
22
  politeSpan = document.createElement("span");
21
23
  assertiveSpan = document.createElement("span");
22
24
 
@@ -29,8 +31,8 @@ attachBoot(() => {
29
31
  politeSpan.setAttribute("role", "alert");
30
32
  assertiveSpan.setAttribute("role", "alert");
31
33
 
32
- politeSpan.style.cssText = styles;
33
- assertiveSpan.style.cssText = styles;
34
+ setOutOfViewportStyles(politeSpan);
35
+ setOutOfViewportStyles(assertiveSpan);
34
36
 
35
37
  getSingletonElementInstance("ui5-static-area").appendChild(politeSpan);
36
38
  getSingletonElementInstance("ui5-static-area").appendChild(assertiveSpan);
@@ -27,6 +27,10 @@ const getTabbables = (nodes, tabbables) => {
27
27
  currentNode = Array.from(children).find(node => node.tagName !== "STYLE");
28
28
  }
29
29
 
30
+ if (!currentNode) {
31
+ return;
32
+ }
33
+
30
34
  if (isNodeTabbable(currentNode)) {
31
35
  tabbablesNodes.push(currentNode);
32
36
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Creates a <link> tag in the <head> tag
3
+ * @param href - the CSS
4
+ * @param attributes - optional attributes to add to the tag
5
+ * @returns {HTMLElement}
6
+ */
7
+ const createLinkInHead = (href, attributes = {}) => {
8
+ const link = document.createElement("link");
9
+ link.type = "text/css";
10
+ link.rel = "stylesheet";
11
+
12
+ Object.entries(attributes).forEach(pair => link.setAttribute(...pair));
13
+
14
+ link.href = href;
15
+ document.head.appendChild(link);
16
+ return link;
17
+ };
18
+
19
+ export default createLinkInHead;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Escape a regular expression text so that it can be used in a regular expression.
3
+ * @param {string} text The string to be interpreted literally
4
+ * @returns Regular expression string to pass to regex
5
+ */
6
+ function escapeRegex(text) {
7
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8
+ }
9
+
10
+ export default escapeRegex;
@@ -0,0 +1,42 @@
1
+ import escapeRegex from "./escapeRegex.js";
2
+ import encodeXML from "../sap/base/security/encodeXML.js";
3
+
4
+ // utility to replace all occurances of a string
5
+ function replaceAll(text, find, replace, caseInsensitive) {
6
+ return text.replace(new RegExp(escapeRegex(find), `${caseInsensitive ? "i" : ""}g`), replace);
7
+ }
8
+
9
+ /**
10
+ * Generate markup for a raw string where a particular text is wrapped with some tag, by default `<b>` (bold) tag.
11
+ * All inputs to this function are considered literal text, and special characters will always be escaped.
12
+ * @param {string} text The text to add highlighting to
13
+ * @param {string} textToHighlight The text which should be highlighted
14
+ * @return {string} the markup HTML which contains all occurrances of the input text surrounded with a `<b>` tag.
15
+ */
16
+ function generateHighlightedMarkup(text, textToHighlight) {
17
+ if (!text || !textToHighlight) {
18
+ return text;
19
+ }
20
+ // a token is some string that does not appear in either of the input strings
21
+ // repeat the token until it does not appear in the string
22
+ const makeToken = t => {
23
+ const [s, e] = t.split("");
24
+ while (text.indexOf(t) >= 0 || textToHighlight.indexOf(t) >= 0) {
25
+ t = `${s}${t}${e}`;
26
+ }
27
+ return t;
28
+ };
29
+ // It doesn't matter what characters are used as long as all 4 of them are unique
30
+ // And also that encodeXML will not change these characters
31
+ const openToken = makeToken("12");
32
+ const closeToken = makeToken("34");
33
+ // wrap every occurance of the textToHighlight using the open/close tokens (instead of markup at this point)
34
+ let result = encodeXML(replaceAll(text, textToHighlight, match => `${openToken}${match}${closeToken}`, true));
35
+ // now replace the open and close tokens with the markup that we expect
36
+ [[openToken, "<b>"], [closeToken, "</b>"]].forEach(([find, replace]) => {
37
+ result = replaceAll(result, find, replace);
38
+ });
39
+ return result;
40
+ }
41
+
42
+ export default generateHighlightedMarkup;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Returns the normalized event target in cases when it has shadow root.
3
+ * @param {Object} target The original event target
4
+ * @returns {Object} The normalized target
5
+ */
6
+ const getNormalizedTarget = target => {
7
+ let element = target;
8
+
9
+ if (target.shadowRoot && target.shadowRoot.activeElement) {
10
+ element = target.shadowRoot.activeElement;
11
+ }
12
+
13
+ return element;
14
+ };
15
+
16
+ export default getNormalizedTarget;
@@ -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;
@@ -1,6 +1,4 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const mkdirp = require('mkdirp');
1
+ const fs = require('fs').promises;
4
2
  const assets = require('@ui5/webcomponents-tools/assets-meta.js');
5
3
 
6
4
  const fileContent = `const assetParameters = ${JSON.stringify(assets)};
@@ -17,6 +15,11 @@ export {
17
15
  SUPPORTED_LOCALES,
18
16
  };`;
19
17
 
20
- mkdirp.sync("dist/generated/");
21
- fs.writeFileSync("dist/generated/AssetParameters.js", fileContent);
18
+ const generate = async () => {
19
+ await fs.mkdir("dist/generated/", { recursive: true });
20
+ return fs.writeFile("dist/generated/AssetParameters.js", fileContent);
21
+ }
22
22
 
23
+ generate().then(() => {
24
+ console.log("Assets parameters generated.");
25
+ });
@@ -0,0 +1,26 @@
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const CleanCSS = require('clean-css');
4
+
5
+ const generate = async () => {
6
+ await fs.mkdir("dist/generated/css/");
7
+
8
+ const files = (await fs.readdir("src/css/")).filter(file => file.endsWith(".css"));
9
+ const filesPromises = files.map(async file => {
10
+ let content = await fs.readFile(path.join("src/css/", file));
11
+ const res = new CleanCSS().minify(`${content}`);
12
+ content = `export default {
13
+ packageName: "@ui5/webcomponents-base",
14
+ fileName: "${file}",
15
+ content: \`${res.styles}\`
16
+ };`;
17
+
18
+ return fs.writeFile(path.join("dist/generated/css/", `${file}.js`), content);
19
+ });
20
+
21
+ return Promise.all(filesPromises);
22
+ };
23
+
24
+ generate().then(() => {
25
+ console.log("Styles files generated.");
26
+ });