@ui5/webcomponents-base 0.0.0-306572ffa → 0.0.0-3236d56be

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 (342) hide show
  1. package/.eslintignore +3 -0
  2. package/CHANGELOG.md +308 -0
  3. package/README.md +26 -2
  4. package/bundle.esm.js +13 -11
  5. package/dist/AssetRegistry.js +8 -7
  6. package/dist/Boot.js +59 -0
  7. package/dist/CSP.js +59 -0
  8. package/dist/CustomElementsRegistry.js +60 -4
  9. package/dist/CustomElementsScope.js +20 -98
  10. package/dist/CustomElementsScopeUtils.js +108 -0
  11. package/dist/DOMObserver.js +65 -0
  12. package/dist/Device.js +71 -773
  13. package/dist/EventProvider.js +30 -26
  14. package/dist/FeaturesRegistry.js +4 -1
  15. package/dist/FontFace.js +19 -62
  16. package/dist/InitialConfiguration.js +43 -8
  17. package/dist/Keys.js +107 -0
  18. package/dist/ManagedStyles.js +83 -0
  19. package/dist/MediaRange.js +109 -0
  20. package/{src/util/parseProperties.js → dist/PropertiesFileFormat.js} +6 -0
  21. package/dist/Render.js +175 -0
  22. package/dist/RenderScheduler.js +16 -138
  23. package/dist/Runtimes.js +107 -0
  24. package/dist/StaticArea.js +1 -29
  25. package/dist/StaticAreaItem.js +58 -65
  26. package/dist/SystemCSSVars.js +4 -25
  27. package/dist/Theming.js +2 -1
  28. package/dist/UI5Element.js +319 -364
  29. package/dist/UI5ElementMetadata.js +132 -17
  30. package/dist/asset-registries/Icons.js +117 -13
  31. package/dist/asset-registries/Illustrations.js +30 -0
  32. package/dist/asset-registries/LocaleData.js +105 -66
  33. package/dist/asset-registries/Themes.js +23 -32
  34. package/dist/asset-registries/i18n.js +62 -48
  35. package/dist/assets-meta/IconCollectionsAlias.js +18 -0
  36. package/dist/config/Icons.js +52 -0
  37. package/dist/config/Language.js +33 -6
  38. package/dist/config/Theme.js +25 -0
  39. package/dist/css/BusyIndicator.css +76 -0
  40. package/dist/css/FontFace.css +75 -0
  41. package/dist/css/OverrideFontFace.css +32 -0
  42. package/dist/css/SystemCSSVars.css +17 -0
  43. package/dist/delegate/ItemNavigation.js +239 -170
  44. package/dist/delegate/ResizeHandler.js +78 -37
  45. package/dist/delegate/ScrollEnablement.js +49 -20
  46. package/dist/features/F6Navigation.js +108 -0
  47. package/dist/features/OpenUI5Enablement.js +119 -0
  48. package/dist/features/OpenUI5Support.js +41 -6
  49. package/dist/generated/AssetParameters.js +1 -1
  50. package/dist/generated/VersionInfo.js +10 -0
  51. package/dist/generated/css/BusyIndicator.css.js +5 -0
  52. package/dist/generated/css/FontFace.css.js +5 -0
  53. package/dist/generated/css/OverrideFontFace.css.js +5 -0
  54. package/dist/generated/css/SystemCSSVars.css.js +5 -0
  55. package/dist/i18nBundle.js +38 -4
  56. package/dist/isLegacyBrowser.js +3 -0
  57. package/dist/locale/applyDirection.js +6 -4
  58. package/dist/locale/directionChange.js +32 -0
  59. package/dist/locale/getEffectiveDir.js +28 -0
  60. package/dist/locale/getLocale.js +12 -2
  61. package/dist/locale/languageChange.js +1 -1
  62. package/dist/renderer/LitRenderer.js +39 -18
  63. package/dist/renderer/directives/style-map.js +72 -0
  64. package/dist/renderer/executeTemplate.js +19 -2
  65. package/dist/resources/bundle.esm.js +15 -112
  66. package/dist/resources/bundle.esm.js.map +1 -1
  67. package/dist/sap/base/Log.js +241 -0
  68. package/dist/sap/base/assert.js +8 -0
  69. package/dist/sap/base/security/URLListValidator.js +148 -0
  70. package/dist/sap/base/security/encodeCSS.js +14 -0
  71. package/dist/sap/base/security/encodeXML.js +23 -0
  72. package/dist/sap/base/security/sanitizeHTML.js +16 -0
  73. package/dist/sap/base/strings/toHex.js +8 -0
  74. package/dist/sap/base/util/now.js +7 -0
  75. package/dist/sap/ui/thirdparty/caja-html-sanitizer.js +3585 -0
  76. package/dist/test-resources/assets/Themes.js +8 -8
  77. package/dist/test-resources/elements/Parent.js +6 -2
  78. package/dist/test-resources/elements/WithStaticArea.js +2 -1
  79. package/dist/test-resources/pages/AllTestElements.html +3 -3
  80. package/dist/test-resources/pages/Configuration.html +0 -2
  81. package/dist/test-resources/pages/ConfigurationScript.html +0 -2
  82. package/dist/test-resources/pages/i18n.html +2 -9
  83. package/dist/test-resources/specs/ConfigurationChange.spec.js +10 -8
  84. package/dist/test-resources/specs/ConfigurationScript.spec.js +25 -23
  85. package/dist/test-resources/specs/ConfigurationURL.spec.js +65 -17
  86. package/dist/test-resources/specs/CustomTheme.spec.js +9 -7
  87. package/dist/test-resources/specs/EventProvider.spec.js +63 -0
  88. package/dist/test-resources/specs/StaticArea.spec.js +56 -12
  89. package/dist/test-resources/specs/Theming.spec.js +12 -10
  90. package/dist/test-resources/specs/UI5ElementInvalidation.js +54 -70
  91. package/dist/test-resources/specs/UI5ElementLifecycle.js +17 -15
  92. package/dist/test-resources/specs/UI5ElementListenForChildPropChanges.spec.js +25 -51
  93. package/dist/test-resources/specs/UI5ElementMetadataExt.js +9 -7
  94. package/dist/test-resources/specs/UI5ElementPropertyValidation.js +7 -5
  95. package/dist/test-resources/specs/UI5ElementPropsAndAttrs.spec.js +37 -35
  96. package/dist/test-resources/specs/UI5ElementShadowDOM.js +10 -8
  97. package/dist/test-resources/specs/UI5ElementSlots.js +10 -8
  98. package/dist/theming/CustomStyle.js +25 -5
  99. package/dist/theming/ThemeLoaded.js +22 -0
  100. package/dist/theming/applyTheme.js +13 -15
  101. package/dist/theming/getConstructableStyle.js +7 -6
  102. package/dist/theming/getEffectiveLinksHrefs.js +27 -0
  103. package/dist/theming/getEffectiveStyle.js +24 -8
  104. package/dist/theming/getStylesString.js +4 -2
  105. package/dist/theming/getThemeDesignerTheme.js +25 -5
  106. package/dist/theming/preloadLinks.js +23 -0
  107. package/dist/types/CSSColor.js +9 -0
  108. package/dist/types/CalendarType.js +1 -1
  109. package/dist/types/DataType.js +13 -1
  110. package/dist/types/Float.js +4 -0
  111. package/dist/types/Integer.js +4 -0
  112. package/dist/types/InvisibleMessageMode.js +30 -0
  113. package/dist/types/ItemNavigationBehavior.js +2 -7
  114. package/dist/types/PopupState.js +1 -1
  115. package/dist/types/ValueState.js +1 -1
  116. package/dist/updateShadowRoot.js +30 -0
  117. package/dist/util/AriaLabelHelper.js +41 -0
  118. package/dist/util/Caret.js +45 -0
  119. package/dist/util/ColorConversion.js +370 -0
  120. package/dist/util/FocusableElements.js +22 -9
  121. package/dist/util/HTMLSanitizer.js +7 -0
  122. package/dist/util/InvisibleMessage.js +60 -0
  123. package/dist/util/PopupUtils.js +93 -0
  124. package/dist/util/SlotsHelper.js +43 -0
  125. package/dist/util/TabbableElements.js +5 -1
  126. package/dist/util/arraysAreEqual.js +15 -0
  127. package/dist/util/clamp.js +12 -0
  128. package/dist/util/createLinkInHead.js +19 -0
  129. package/dist/util/debounce.js +17 -0
  130. package/dist/util/escapeRegex.js +10 -0
  131. package/dist/util/generateHighlightedMarkup.js +42 -0
  132. package/dist/util/getActiveElement.js +11 -0
  133. package/dist/util/getClassCopy.js +10 -0
  134. package/dist/util/getEffectiveContentDensity.js +5 -0
  135. package/dist/util/getNormalizedTarget.js +16 -0
  136. package/dist/util/isElementInView.js +15 -0
  137. package/dist/util/isNodeHidden.js +1 -1
  138. package/dist/util/isNodeTabbable.js +4 -4
  139. package/dist/util/isValidPropertyName.js +4 -2
  140. package/index.js +1 -1
  141. package/lib/generate-asset-parameters/index.js +8 -5
  142. package/lib/generate-styles/index.js +26 -0
  143. package/lib/generate-version-info/index.js +32 -0
  144. package/package-scripts.js +30 -19
  145. package/package.json +19 -13
  146. package/src/AssetRegistry.js +8 -7
  147. package/src/Boot.js +59 -0
  148. package/src/CSP.js +59 -0
  149. package/src/CustomElementsRegistry.js +60 -4
  150. package/src/CustomElementsScope.js +20 -98
  151. package/src/CustomElementsScopeUtils.js +108 -0
  152. package/src/DOMObserver.js +65 -0
  153. package/src/Device.js +71 -773
  154. package/src/EventProvider.js +30 -26
  155. package/src/FeaturesRegistry.js +4 -1
  156. package/src/FontFace.js +19 -62
  157. package/src/InitialConfiguration.js +43 -8
  158. package/src/Keys.js +107 -0
  159. package/src/ManagedStyles.js +83 -0
  160. package/src/MediaRange.js +109 -0
  161. package/{dist/util/parseProperties.js → src/PropertiesFileFormat.js} +6 -0
  162. package/src/Render.js +175 -0
  163. package/src/RenderScheduler.js +16 -138
  164. package/src/Runtimes.js +107 -0
  165. package/src/StaticArea.js +1 -29
  166. package/src/StaticAreaItem.js +58 -65
  167. package/src/SystemCSSVars.js +4 -25
  168. package/src/Theming.js +2 -1
  169. package/src/UI5Element.js +319 -364
  170. package/src/UI5ElementMetadata.js +132 -17
  171. package/src/asset-registries/Icons.js +117 -13
  172. package/src/asset-registries/Illustrations.js +30 -0
  173. package/src/asset-registries/LocaleData.js +105 -66
  174. package/src/asset-registries/Themes.js +23 -32
  175. package/src/asset-registries/i18n.js +62 -48
  176. package/src/assets-meta/IconCollectionsAlias.js +18 -0
  177. package/src/config/Icons.js +52 -0
  178. package/src/config/Language.js +33 -6
  179. package/src/config/Theme.js +25 -0
  180. package/src/css/BusyIndicator.css +76 -0
  181. package/src/css/FontFace.css +75 -0
  182. package/src/css/OverrideFontFace.css +32 -0
  183. package/src/css/SystemCSSVars.css +17 -0
  184. package/src/delegate/ItemNavigation.js +239 -170
  185. package/src/delegate/ResizeHandler.js +78 -37
  186. package/src/delegate/ScrollEnablement.js +49 -20
  187. package/src/features/F6Navigation.js +108 -0
  188. package/src/features/OpenUI5Enablement.js +119 -0
  189. package/src/features/OpenUI5Support.js +41 -6
  190. package/src/i18nBundle.js +38 -4
  191. package/src/isLegacyBrowser.js +3 -0
  192. package/src/locale/applyDirection.js +6 -4
  193. package/src/locale/directionChange.js +32 -0
  194. package/src/locale/getEffectiveDir.js +28 -0
  195. package/src/locale/getLocale.js +12 -2
  196. package/src/locale/languageChange.js +1 -1
  197. package/src/renderer/LitRenderer.js +39 -18
  198. package/src/renderer/directives/style-map.js +72 -0
  199. package/src/renderer/executeTemplate.js +19 -2
  200. package/src/theming/CustomStyle.js +25 -5
  201. package/src/theming/ThemeLoaded.js +22 -0
  202. package/src/theming/applyTheme.js +13 -15
  203. package/src/theming/getConstructableStyle.js +7 -6
  204. package/src/theming/getEffectiveLinksHrefs.js +27 -0
  205. package/src/theming/getEffectiveStyle.js +24 -8
  206. package/src/theming/getStylesString.js +4 -2
  207. package/src/theming/getThemeDesignerTheme.js +25 -5
  208. package/src/theming/preloadLinks.js +23 -0
  209. package/src/types/CSSColor.js +9 -0
  210. package/src/types/CalendarType.js +1 -1
  211. package/src/types/DataType.js +13 -1
  212. package/src/types/Float.js +4 -0
  213. package/src/types/Integer.js +4 -0
  214. package/src/types/InvisibleMessageMode.js +30 -0
  215. package/src/types/ItemNavigationBehavior.js +2 -7
  216. package/src/types/PopupState.js +1 -1
  217. package/src/types/ValueState.js +1 -1
  218. package/src/updateShadowRoot.js +30 -0
  219. package/src/util/AriaLabelHelper.js +41 -0
  220. package/src/util/Caret.js +45 -0
  221. package/src/util/ColorConversion.js +370 -0
  222. package/src/util/FocusableElements.js +22 -9
  223. package/src/util/HTMLSanitizer.js +7 -0
  224. package/src/util/InvisibleMessage.js +60 -0
  225. package/src/util/PopupUtils.js +93 -0
  226. package/src/util/SlotsHelper.js +43 -0
  227. package/src/util/TabbableElements.js +5 -1
  228. package/src/util/arraysAreEqual.js +15 -0
  229. package/src/util/clamp.js +12 -0
  230. package/src/util/createLinkInHead.js +19 -0
  231. package/src/util/debounce.js +17 -0
  232. package/src/util/escapeRegex.js +10 -0
  233. package/src/util/generateHighlightedMarkup.js +42 -0
  234. package/src/util/getActiveElement.js +11 -0
  235. package/src/util/getClassCopy.js +10 -0
  236. package/src/util/getEffectiveContentDensity.js +5 -0
  237. package/src/util/getNormalizedTarget.js +16 -0
  238. package/src/util/isElementInView.js +15 -0
  239. package/src/util/isNodeHidden.js +1 -1
  240. package/src/util/isNodeTabbable.js +4 -4
  241. package/src/util/isValidPropertyName.js +4 -2
  242. package/used-modules.txt +10 -0
  243. package/bundle.es5.js +0 -28
  244. package/dist/SVGIconRegistry.js +0 -62
  245. package/dist/boot.js +0 -34
  246. package/dist/compatibility/DOMObserver.js +0 -62
  247. package/dist/compatibility/patchNodeValue.js +0 -24
  248. package/dist/compatibility/whenPolyfillLoaded.js +0 -26
  249. package/dist/delegate/CustomResize.js +0 -78
  250. package/dist/delegate/NativeResize.js +0 -44
  251. package/dist/features/PropertiesFormatSupport.js +0 -8
  252. package/dist/features/browsersupport/Edge.js +0 -6
  253. package/dist/features/browsersupport/IE11.js +0 -41
  254. package/dist/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
  255. package/dist/renderer/ifDefined.js +0 -21
  256. package/dist/renderer/scopeHTML.js +0 -32
  257. package/dist/resources/bundle.es5.js +0 -212
  258. package/dist/resources/bundle.es5.js.map +0 -1
  259. package/dist/theming/CSSVarsPonyfill.js +0 -24
  260. package/dist/theming/adaptCSSForIE.js +0 -95
  261. package/dist/theming/createComponentStyleTag.js +0 -55
  262. package/dist/theming/createThemePropertiesStyleTag.js +0 -20
  263. package/dist/thirdparty/Array.from.js +0 -16
  264. package/dist/thirdparty/Array.prototype.fill.js +0 -44
  265. package/dist/thirdparty/Array.prototype.find.js +0 -46
  266. package/dist/thirdparty/Array.prototype.includes.js +0 -51
  267. package/dist/thirdparty/Element.prototype.closest.js +0 -11
  268. package/dist/thirdparty/Element.prototype.matches.js +0 -6
  269. package/dist/thirdparty/Map.prototype.keys.js +0 -9
  270. package/dist/thirdparty/Number.isInteger.js +0 -5
  271. package/dist/thirdparty/Number.isNaN.js +0 -1
  272. package/dist/thirdparty/Number.parseInt.js +0 -1
  273. package/dist/thirdparty/Object.assign.js +0 -31
  274. package/dist/thirdparty/Object.entries.js +0 -11
  275. package/dist/thirdparty/Symbol.js +0 -2
  276. package/dist/thirdparty/WeakSet.js +0 -27
  277. package/dist/thirdparty/es6-string-methods.js +0 -182
  278. package/dist/thirdparty/events-polyfills.js +0 -89
  279. package/dist/thirdparty/fetch.js +0 -1
  280. package/dist/thirdparty/template.js +0 -600
  281. package/dist/thirdparty/webcomponents-sd-ce-pf.js +0 -318
  282. package/dist/util/getEffectiveAriaLabelText.js +0 -28
  283. package/dist/util/isSlot.js +0 -3
  284. package/dist/webcomponentsjs/LICENSE.md +0 -19
  285. package/dist/webcomponentsjs/README.md +0 -229
  286. package/dist/webcomponentsjs/bundles/webcomponents-ce.js +0 -63
  287. package/dist/webcomponentsjs/bundles/webcomponents-ce.js.map +0 -1
  288. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js +0 -297
  289. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js.map +0 -1
  290. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js +0 -208
  291. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js.map +0 -1
  292. package/dist/webcomponentsjs/bundles/webcomponents-sd.js +0 -166
  293. package/dist/webcomponentsjs/bundles/webcomponents-sd.js.map +0 -1
  294. package/dist/webcomponentsjs/custom-elements-es5-adapter.js +0 -15
  295. package/dist/webcomponentsjs/package.json +0 -46
  296. package/dist/webcomponentsjs/src/entrypoints/custom-elements-es5-adapter-index.js +0 -16
  297. package/dist/webcomponentsjs/src/entrypoints/webcomponents-bundle-index.js +0 -53
  298. package/dist/webcomponentsjs/src/entrypoints/webcomponents-ce-index.js +0 -17
  299. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-index.js +0 -19
  300. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-pf-index.js +0 -28
  301. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-index.js +0 -18
  302. package/dist/webcomponentsjs/webcomponents-bundle.js +0 -298
  303. package/dist/webcomponentsjs/webcomponents-bundle.js.map +0 -1
  304. package/dist/webcomponentsjs/webcomponents-loader.js +0 -185
  305. package/src/SVGIconRegistry.js +0 -62
  306. package/src/boot.js +0 -34
  307. package/src/compatibility/DOMObserver.js +0 -62
  308. package/src/compatibility/patchNodeValue.js +0 -24
  309. package/src/compatibility/whenPolyfillLoaded.js +0 -26
  310. package/src/delegate/CustomResize.js +0 -78
  311. package/src/delegate/NativeResize.js +0 -44
  312. package/src/features/PropertiesFormatSupport.js +0 -8
  313. package/src/features/browsersupport/Edge.js +0 -6
  314. package/src/features/browsersupport/IE11.js +0 -41
  315. package/src/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
  316. package/src/renderer/ifDefined.js +0 -21
  317. package/src/renderer/scopeHTML.js +0 -32
  318. package/src/theming/CSSVarsPonyfill.js +0 -24
  319. package/src/theming/adaptCSSForIE.js +0 -95
  320. package/src/theming/createComponentStyleTag.js +0 -55
  321. package/src/theming/createThemePropertiesStyleTag.js +0 -20
  322. package/src/thirdparty/Array.from.js +0 -16
  323. package/src/thirdparty/Array.prototype.fill.js +0 -44
  324. package/src/thirdparty/Array.prototype.find.js +0 -46
  325. package/src/thirdparty/Array.prototype.includes.js +0 -51
  326. package/src/thirdparty/Element.prototype.closest.js +0 -11
  327. package/src/thirdparty/Element.prototype.matches.js +0 -6
  328. package/src/thirdparty/Map.prototype.keys.js +0 -9
  329. package/src/thirdparty/Number.isInteger.js +0 -5
  330. package/src/thirdparty/Number.isNaN.js +0 -1
  331. package/src/thirdparty/Number.parseInt.js +0 -1
  332. package/src/thirdparty/Object.assign.js +0 -31
  333. package/src/thirdparty/Object.entries.js +0 -11
  334. package/src/thirdparty/Symbol.js +0 -2
  335. package/src/thirdparty/WeakSet.js +0 -27
  336. package/src/thirdparty/es6-string-methods.js +0 -182
  337. package/src/thirdparty/events-polyfills.js +0 -89
  338. package/src/thirdparty/fetch.js +0 -1
  339. package/src/thirdparty/template.js +0 -600
  340. package/src/thirdparty/webcomponents-sd-ce-pf.js +0 -318
  341. package/src/util/getEffectiveAriaLabelText.js +0 -28
  342. package/src/util/isSlot.js +0 -3
@@ -1,36 +1,52 @@
1
1
  import merge from "./thirdparty/merge.js";
2
- import boot from "./boot.js";
2
+ import { boot } from "./Boot.js";
3
3
  import UI5ElementMetadata from "./UI5ElementMetadata.js";
4
- import executeTemplate from "./renderer/executeTemplate.js";
4
+ import EventProvider from "./EventProvider.js";
5
+ import getSingletonElementInstance from "./util/getSingletonElementInstance.js";
5
6
  import StaticAreaItem from "./StaticAreaItem.js";
6
- import RenderScheduler from "./RenderScheduler.js";
7
+ import updateShadowRoot from "./updateShadowRoot.js";
8
+ import { renderDeferred, renderImmediately, cancelRender } from "./Render.js";
7
9
  import { registerTag, isTagRegistered, recordTagRegistrationFailure } from "./CustomElementsRegistry.js";
8
- import DOMObserver from "./compatibility/DOMObserver.js";
10
+ import { observeDOMNode, unobserveDOMNode } from "./DOMObserver.js";
9
11
  import { skipOriginalEvent } from "./config/NoConflict.js";
10
- import { getRTL } from "./config/RTL.js";
11
- import getConstructableStyle from "./theming/getConstructableStyle.js";
12
- import createComponentStyleTag from "./theming/createComponentStyleTag.js";
13
- import getEffectiveStyle from "./theming/getEffectiveStyle.js";
14
- import Integer from "./types/Integer.js";
15
- import Float from "./types/Float.js";
12
+ import getEffectiveDir from "./locale/getEffectiveDir.js";
13
+ import DataType from "./types/DataType.js";
16
14
  import { kebabToCamelCase, camelToKebabCase } from "./util/StringHelper.js";
17
15
  import isValidPropertyName from "./util/isValidPropertyName.js";
18
- import isSlot from "./util/isSlot.js";
16
+ import isDescendantOf from "./util/isDescendantOf.js";
17
+ import { isSlot, getSlotName, getSlottedElementsList } from "./util/SlotsHelper.js";
18
+ import arraysAreEqual from "./util/arraysAreEqual.js";
19
+ import getClassCopy from "./util/getClassCopy.js";
19
20
  import { markAsRtlAware } from "./locale/RTLAwareRegistry.js";
20
-
21
- const metadata = {
22
- events: {
23
- "_property-change": {},
24
- },
25
- };
21
+ import preloadLinks from "./theming/preloadLinks.js";
26
22
 
27
23
  let autoId = 0;
28
24
 
29
25
  const elementTimeouts = new Map();
30
26
  const uniqueDependenciesCache = new Map();
31
27
 
32
- const GLOBAL_CONTENT_DENSITY_CSS_VAR = "--_ui5_content_density";
33
- const GLOBAL_DIR_CSS_VAR = "--_ui5_dir";
28
+ /**
29
+ * Triggers re-rendering of a UI5Element instance due to state change.
30
+ *
31
+ * @param changeInfo An object with information about the change that caused invalidation.
32
+ * @private
33
+ */
34
+ function _invalidate(changeInfo) {
35
+ // Invalidation should be suppressed: 1) before the component is rendered for the first time 2) and during the execution of onBeforeRendering
36
+ // This is necessary not only as an optimization, but also to avoid infinite loops on invalidation between children and parents (when invalidateOnChildChange is used)
37
+ if (this._suppressInvalidation) {
38
+ return;
39
+ }
40
+
41
+ // Call the onInvalidation hook
42
+ this.onInvalidation(changeInfo);
43
+
44
+ this._changedState.push(changeInfo);
45
+ renderDeferred(this);
46
+ this._eventProvider.fireEvent("invalidate", { ...changeInfo, target: this });
47
+ }
48
+
49
+ let metadata = {};
34
50
 
35
51
  /**
36
52
  * Base class for all UI5 Web Components
@@ -45,22 +61,26 @@ const GLOBAL_DIR_CSS_VAR = "--_ui5_dir";
45
61
  class UI5Element extends HTMLElement {
46
62
  constructor() {
47
63
  super();
48
- this._initializeState();
49
- this._upgradeAllProperties();
50
- this._initializeContainers();
51
- this._upToDate = false;
52
- this._inDOM = false;
53
- this._fullyConnected = false;
54
64
 
65
+ this._changedState = []; // Filled on each invalidation, cleared on re-render (used for debugging)
66
+ this._suppressInvalidation = true; // A flag telling whether all invalidations should be ignored. Initialized with "true" because a UI5Element can not be invalidated until it is rendered for the first time
67
+ this._inDOM = false; // A flag telling whether the UI5Element is currently in the DOM tree of the document or not
68
+ this._fullyConnected = false; // A flag telling whether the UI5Element's onEnterDOM hook was called (since it's possible to have the element removed from DOM before that)
69
+ this._childChangeListeners = new Map(); // used to store lazy listeners per slot for the child change event of every child inside that slot
70
+ this._slotChangeListeners = new Map(); // used to store lazy listeners per slot for the slotchange event of all slot children inside that slot
71
+ this._eventProvider = new EventProvider(); // used by parent components for listening to changes to child components
55
72
  let deferredResolve;
56
73
  this._domRefReadyPromise = new Promise(resolve => {
57
74
  deferredResolve = resolve;
58
75
  });
59
76
  this._domRefReadyPromise._deferredResolve = deferredResolve;
60
77
 
61
- this._monitoredChildProps = new Map();
62
- this._firePropertyChange = false;
63
- this._shouldInvalidateParent = false;
78
+ this._initializeState();
79
+ this._upgradeAllProperties();
80
+
81
+ if (this.constructor._needsShadowDOM()) {
82
+ this.attachShadow({ mode: "open" });
83
+ }
64
84
  }
65
85
 
66
86
  /**
@@ -77,32 +97,16 @@ class UI5Element extends HTMLElement {
77
97
  return this.__id;
78
98
  }
79
99
 
80
- /**
81
- * @private
82
- */
83
- _initializeContainers() {
84
- const needsShadowDOM = this.constructor._needsShadowDOM();
85
- const needsStaticArea = this.constructor._needsStaticArea();
86
-
87
- // Init Shadow Root
88
- if (needsShadowDOM) {
89
- this.attachShadow({ mode: "open" });
90
- }
91
-
92
- // Init StaticAreaItem only if needed
93
- if (needsStaticArea) {
94
- this.staticAreaItem = new StaticAreaItem(this);
95
- }
96
- }
97
-
98
100
  /**
99
101
  * Do not call this method from derivatives of UI5Element, use "onEnterDOM" only
100
102
  * @private
101
103
  */
102
104
  async connectedCallback() {
103
105
  this.setAttribute(this.constructor.getMetadata().getPureTag(), "");
106
+ if (this.constructor.getMetadata().supportsF6FastNavigation()) {
107
+ this.setAttribute("data-sap-ui-fastnavgroup", "true");
108
+ }
104
109
 
105
- const needsShadowDOM = this.constructor._needsShadowDOM();
106
110
  const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
107
111
 
108
112
  this._inDOM = true;
@@ -113,23 +117,15 @@ class UI5Element extends HTMLElement {
113
117
  await this._processChildren();
114
118
  }
115
119
 
116
- // Render the Shadow DOM
117
- if (needsShadowDOM) {
118
- if (!this.shadowRoot) { // Workaround for Firefox74 bug
119
- await Promise.resolve();
120
- }
121
-
122
- if (!this._inDOM) { // Component removed from DOM while _processChildren was running
123
- return;
124
- }
120
+ if (!this._inDOM) { // Component removed from DOM while _processChildren was running
121
+ return;
122
+ }
125
123
 
126
- RenderScheduler.register(this);
127
- RenderScheduler.renderImmediately(this);
128
- this._domRefReadyPromise._deferredResolve();
129
- this._fullyConnected = true;
130
- if (typeof this.onEnterDOM === "function") {
131
- this.onEnterDOM();
132
- }
124
+ renderImmediately(this);
125
+ this._domRefReadyPromise._deferredResolve();
126
+ this._fullyConnected = true;
127
+ if (typeof this.onEnterDOM === "function") {
128
+ this.onEnterDOM();
133
129
  }
134
130
  }
135
131
 
@@ -138,8 +134,6 @@ class UI5Element extends HTMLElement {
138
134
  * @private
139
135
  */
140
136
  disconnectedCallback() {
141
- const needsShadowDOM = this.constructor._needsShadowDOM();
142
- const needsStaticArea = this.constructor._needsStaticArea();
143
137
  const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
144
138
 
145
139
  this._inDOM = false;
@@ -148,21 +142,18 @@ class UI5Element extends HTMLElement {
148
142
  this._stopObservingDOMChildren();
149
143
  }
150
144
 
151
- if (needsShadowDOM) {
152
- RenderScheduler.deregister(this);
153
- if (this._fullyConnected) {
154
- if (typeof this.onExitDOM === "function") {
155
- this.onExitDOM();
156
- }
157
- this._fullyConnected = false;
145
+ if (this._fullyConnected) {
146
+ if (typeof this.onExitDOM === "function") {
147
+ this.onExitDOM();
158
148
  }
149
+ this._fullyConnected = false;
159
150
  }
160
151
 
161
- if (needsStaticArea) {
162
- this.staticAreaItem._removeFragmentFromStaticArea();
152
+ if (this.staticAreaItem && this.staticAreaItem.parentElement) {
153
+ this.staticAreaItem.parentElement.removeChild(this.staticAreaItem);
163
154
  }
164
155
 
165
- RenderScheduler.cancelRender(this);
156
+ cancelRender(this);
166
157
  }
167
158
 
168
159
  /**
@@ -178,16 +169,16 @@ class UI5Element extends HTMLElement {
178
169
  const mutationObserverOptions = {
179
170
  childList: true,
180
171
  subtree: canSlotText,
181
- characterData: true,
172
+ characterData: canSlotText,
182
173
  };
183
- DOMObserver.observeDOMNode(this, this._processChildren.bind(this), mutationObserverOptions);
174
+ observeDOMNode(this, this._processChildren.bind(this), mutationObserverOptions);
184
175
  }
185
176
 
186
177
  /**
187
178
  * @private
188
179
  */
189
180
  _stopObservingDOMChildren() {
190
- DOMObserver.unobserveDOMNode(this);
181
+ unobserveDOMNode(this);
191
182
  }
192
183
 
193
184
  /**
@@ -209,8 +200,14 @@ class UI5Element extends HTMLElement {
209
200
  const canSlotText = this.constructor.getMetadata().canSlotText();
210
201
  const domChildren = Array.from(canSlotText ? this.childNodes : this.children);
211
202
 
212
- // Init the _state object based on the supported slots
203
+ const slotsCachedContentMap = new Map(); // Store here the content of each slot before the mutation occurred
204
+ const propertyNameToSlotMap = new Map(); // Used for reverse lookup to determine to which slot the property name corresponds
205
+
206
+ // Init the _state object based on the supported slots and store the previous values
213
207
  for (const [slotName, slotData] of Object.entries(slotsMap)) { // eslint-disable-line
208
+ const propertyName = slotData.propertyName || slotName;
209
+ propertyNameToSlotMap.set(propertyName, slotName);
210
+ slotsCachedContentMap.set(propertyName, [...this._state[propertyName]]);
214
211
  this._clearSlot(slotName, slotData);
215
212
  }
216
213
 
@@ -219,7 +216,7 @@ class UI5Element extends HTMLElement {
219
216
 
220
217
  const allChildrenUpgraded = domChildren.map(async (child, idx) => {
221
218
  // Determine the type of the child (mainly by the slot attribute)
222
- const slotName = this.constructor._getSlotName(child);
219
+ const slotName = getSlotName(child);
223
220
  const slotData = slotsMap[slotName];
224
221
 
225
222
  // Check if the slotName is supported
@@ -257,16 +254,15 @@ class UI5Element extends HTMLElement {
257
254
 
258
255
  child = this.constructor.getMetadata().constructor.validateSlotValue(child, slotData);
259
256
 
260
- if (child.isUI5Element && slotData.listenFor) {
261
- this._attachChildPropertyUpdated(child, slotData.listenFor);
262
- }
263
-
264
- if (child.isUI5Element && slotData.invalidateParent) {
265
- child._shouldInvalidateParent = true;
257
+ // Listen for any invalidation on the child if invalidateOnChildChange is true or an object (ignore when false or not set)
258
+ if (child.isUI5Element && slotData.invalidateOnChildChange) {
259
+ const method = (child.attachInvalidate || child._attachChange).bind(child);
260
+ method(this._getChildChangeListener(slotName));
266
261
  }
267
262
 
263
+ // Listen for the slotchange event if the child is a slot itself
268
264
  if (isSlot(child)) {
269
- this._attachSlotChange(child);
265
+ this._attachSlotChange(child, slotName);
270
266
  }
271
267
 
272
268
  const propertyName = slotData.propertyName || slotName;
@@ -282,10 +278,33 @@ class UI5Element extends HTMLElement {
282
278
 
283
279
  // Distribute the child in the _state object, keeping the Light DOM order,
284
280
  // not the order elements are defined.
285
- slottedChildrenMap.forEach((children, slot) => {
286
- this._state[slot] = children.sort((a, b) => a.idx - b.idx).map(_ => _.child);
281
+ slottedChildrenMap.forEach((children, propertyName) => {
282
+ this._state[propertyName] = children.sort((a, b) => a.idx - b.idx).map(_ => _.child);
287
283
  });
288
- this._invalidate("slots");
284
+
285
+ // Compare the content of each slot with the cached values and invalidate for the ones that changed
286
+ let invalidated = false;
287
+ for (const [slotName, slotData] of Object.entries(slotsMap)) { // eslint-disable-line
288
+ const propertyName = slotData.propertyName || slotName;
289
+ if (!arraysAreEqual(slotsCachedContentMap.get(propertyName), this._state[propertyName])) {
290
+ _invalidate.call(this, {
291
+ type: "slot",
292
+ name: propertyNameToSlotMap.get(propertyName),
293
+ reason: "children",
294
+ });
295
+ invalidated = true;
296
+ }
297
+ }
298
+
299
+ // If none of the slots had an invalidation due to changes to immediate children,
300
+ // the change is considered to be text content of the default slot
301
+ if (!invalidated) {
302
+ _invalidate.call(this, {
303
+ type: "slot",
304
+ name: "default",
305
+ reason: "textcontent",
306
+ });
307
+ }
289
308
  }
290
309
 
291
310
  /**
@@ -294,25 +313,62 @@ class UI5Element extends HTMLElement {
294
313
  */
295
314
  _clearSlot(slotName, slotData) {
296
315
  const propertyName = slotData.propertyName || slotName;
297
-
298
- let children = this._state[propertyName];
299
- if (!Array.isArray(children)) {
300
- children = [children];
301
- }
316
+ const children = this._state[propertyName];
302
317
 
303
318
  children.forEach(child => {
304
319
  if (child && child.isUI5Element) {
305
- this._detachChildPropertyUpdated(child);
306
- child._shouldInvalidateParent = false;
320
+ const method = (child.detachInvalidate || child._detachChange).bind(child);
321
+ method(this._getChildChangeListener(slotName));
307
322
  }
308
323
 
309
324
  if (isSlot(child)) {
310
- this._detachSlotChange(child);
325
+ this._detachSlotChange(child, slotName);
311
326
  }
312
327
  });
313
328
 
314
329
  this._state[propertyName] = [];
315
- this._invalidate(propertyName, []);
330
+ }
331
+
332
+ /**
333
+ * Attach a callback that will be executed whenever the component is invalidated
334
+ *
335
+ * @param callback
336
+ * @public
337
+ */
338
+ attachInvalidate(callback) {
339
+ this._eventProvider.attachEvent("invalidate", callback);
340
+ }
341
+
342
+ /**
343
+ * Detach the callback that is executed whenever the component is invalidated
344
+ *
345
+ * @param callback
346
+ * @public
347
+ */
348
+ detachInvalidate(callback) {
349
+ this._eventProvider.detachEvent("invalidate", callback);
350
+ }
351
+
352
+ /**
353
+ * Callback that is executed whenever a monitored child changes its state
354
+ *
355
+ * @param slotName the slot in which a child was invalidated
356
+ * @param childChangeInfo the changeInfo object for the child in the given slot
357
+ * @private
358
+ */
359
+ _onChildChange(slotName, childChangeInfo) {
360
+ if (!this.constructor.getMetadata().shouldInvalidateOnChildChange(slotName, childChangeInfo.type, childChangeInfo.name)) {
361
+ return;
362
+ }
363
+
364
+ // The component should be invalidated as this type of change on the child is listened for
365
+ // However, no matter what changed on the child (property/slot), the invalidation is registered as "type=slot" for the component itself
366
+ _invalidate.call(this, {
367
+ type: "slot",
368
+ name: slotName,
369
+ reason: "childchange",
370
+ child: childChangeInfo.target,
371
+ });
316
372
  }
317
373
 
318
374
  /**
@@ -327,12 +383,8 @@ class UI5Element extends HTMLElement {
327
383
  const propertyTypeClass = properties[nameInCamelCase].type;
328
384
  if (propertyTypeClass === Boolean) {
329
385
  newValue = newValue !== null;
330
- }
331
- if (propertyTypeClass === Integer) {
332
- newValue = parseInt(newValue);
333
- }
334
- if (propertyTypeClass === Float) {
335
- newValue = parseFloat(newValue);
386
+ } else if (isDescendantOf(propertyTypeClass, DataType)) {
387
+ newValue = propertyTypeClass.attributeToProperty(newValue);
336
388
  }
337
389
  this[nameInCamelCase] = newValue;
338
390
  }
@@ -345,22 +397,24 @@ class UI5Element extends HTMLElement {
345
397
  if (!this.constructor.getMetadata().hasAttribute(name)) {
346
398
  return;
347
399
  }
348
-
349
- if (typeof newValue === "object") {
350
- return;
351
- }
352
-
400
+ const properties = this.constructor.getMetadata().getProperties();
401
+ const propertyTypeClass = properties[name].type;
353
402
  const attrName = camelToKebabCase(name);
354
403
  const attrValue = this.getAttribute(attrName);
355
- if (typeof newValue === "boolean") {
404
+
405
+ if (propertyTypeClass === Boolean) {
356
406
  if (newValue === true && attrValue === null) {
357
407
  this.setAttribute(attrName, "");
358
408
  } else if (newValue === false && attrValue !== null) {
359
409
  this.removeAttribute(attrName);
360
410
  }
361
- } else if (attrValue !== newValue) {
362
- this.setAttribute(attrName, newValue);
363
- }
411
+ } else if (isDescendantOf(propertyTypeClass, DataType)) {
412
+ this.setAttribute(attrName, propertyTypeClass.propertyToAttribute(newValue));
413
+ } else if (typeof newValue !== "object") {
414
+ if (attrValue !== newValue) {
415
+ this.setAttribute(attrName, newValue);
416
+ }
417
+ } // else { return; } // old object handling
364
418
  }
365
419
 
366
420
  /**
@@ -386,124 +440,96 @@ class UI5Element extends HTMLElement {
386
440
  * @private
387
441
  */
388
442
  _initializeState() {
389
- const defaultState = this.constructor._getDefaultState();
390
- this._state = Object.assign({}, defaultState);
443
+ this._state = { ...this.constructor.getMetadata().getInitialState() };
391
444
  }
392
445
 
393
446
  /**
447
+ * Returns a singleton event listener for the "change" event of a child in a given slot
448
+ *
449
+ * @param slotName the name of the slot, where the child is
450
+ * @returns {any}
394
451
  * @private
395
452
  */
396
- _attachChildPropertyUpdated(child, listenFor) {
397
- const childMetadata = child.constructor.getMetadata(),
398
- slotName = this.constructor._getSlotName(child), // all slotted children have the same configuration
399
- childProperties = childMetadata.getProperties();
400
-
401
- let observedProps = [],
402
- notObservedProps = [];
403
-
404
- if (Array.isArray(listenFor)) {
405
- observedProps = listenFor;
406
- } else {
407
- observedProps = Array.isArray(listenFor.props) ? listenFor.props : Object.keys(childProperties);
408
- notObservedProps = Array.isArray(listenFor.exclude) ? listenFor.exclude : [];
453
+ _getChildChangeListener(slotName) {
454
+ if (!this._childChangeListeners.has(slotName)) {
455
+ this._childChangeListeners.set(slotName, this._onChildChange.bind(this, slotName));
409
456
  }
410
-
411
- if (!this._monitoredChildProps.has(slotName)) {
412
- this._monitoredChildProps.set(slotName, { observedProps, notObservedProps });
413
- }
414
-
415
- child.addEventListener("_property-change", this._invalidateParentOnPropertyUpdate);
416
- child._firePropertyChange = true;
417
- }
418
-
419
- /**
420
- * @private
421
- */
422
- _detachChildPropertyUpdated(child) {
423
- child.removeEventListener("_property-change", this._invalidateParentOnPropertyUpdate);
424
- child._firePropertyChange = false;
457
+ return this._childChangeListeners.get(slotName);
425
458
  }
426
459
 
427
460
  /**
461
+ * Returns a singleton slotchange event listener that invalidates the component due to changes in the given slot
462
+ *
463
+ * @param slotName the name of the slot, where the slot element (whose slotchange event we're listening to) is
464
+ * @returns {any}
428
465
  * @private
429
466
  */
430
- _propertyChange(name, value) {
431
- this._updateAttribute(name, value);
432
-
433
- if (this._firePropertyChange) {
434
- this.dispatchEvent(new CustomEvent("_property-change", {
435
- detail: { name, newValue: value },
436
- composed: false,
437
- bubbles: true,
438
- }));
467
+ _getSlotChangeListener(slotName) {
468
+ if (!this._slotChangeListeners.has(slotName)) {
469
+ this._slotChangeListeners.set(slotName, this._onSlotChange.bind(this, slotName));
439
470
  }
471
+ return this._slotChangeListeners.get(slotName);
440
472
  }
441
473
 
442
474
  /**
443
475
  * @private
444
476
  */
445
- _invalidateParentOnPropertyUpdate(prop) {
446
- // The web component to be invalidated
447
- const parentNode = this.parentNode;
448
- if (!parentNode) {
449
- return;
450
- }
451
-
452
- const slotName = parentNode.constructor._getSlotName(this);
453
- const propsMetadata = parentNode._monitoredChildProps.get(slotName);
454
-
455
- if (!propsMetadata) {
456
- return;
457
- }
458
- const { observedProps, notObservedProps } = propsMetadata;
459
-
460
- if (observedProps.includes(prop.detail.name) && !notObservedProps.includes(prop.detail.name)) {
461
- parentNode._invalidate("_parent_", this);
462
- }
477
+ _attachSlotChange(child, slotName) {
478
+ child.addEventListener("slotchange", this._getSlotChangeListener(slotName));
463
479
  }
464
480
 
465
481
  /**
466
482
  * @private
467
483
  */
468
- _attachSlotChange(child) {
469
- if (!this._invalidateOnSlotChange) {
470
- this._invalidateOnSlotChange = () => {
471
- this._invalidate("slotchange");
472
- };
473
- }
474
- child.addEventListener("slotchange", this._invalidateOnSlotChange);
484
+ _detachSlotChange(child, slotName) {
485
+ child.removeEventListener("slotchange", this._getSlotChangeListener(slotName));
475
486
  }
476
487
 
477
488
  /**
489
+ * Whenever a slot element is slotted inside a UI5 Web Component, its slotchange event invalidates the component
490
+ *
491
+ * @param slotName the name of the slot, where the slot element (whose slotchange event we're listening to) is
478
492
  * @private
479
493
  */
480
- _detachSlotChange(child) {
481
- child.removeEventListener("slotchange", this._invalidateOnSlotChange);
494
+ _onSlotChange(slotName) {
495
+ _invalidate.call(this, {
496
+ type: "slot",
497
+ name: slotName,
498
+ reason: "slotchange",
499
+ });
482
500
  }
483
501
 
484
502
  /**
485
- * Asynchronously re-renders an already rendered web component
486
- * @private
503
+ * A callback that is executed each time an already rendered component is invalidated (scheduled for re-rendering)
504
+ *
505
+ * @param changeInfo An object with information about the change that caused invalidation.
506
+ * The object can have the following properties:
507
+ * - type: (property|slot) tells what caused the invalidation
508
+ * 1) property: a property value was changed either directly or as a result of changing the corresponding attribute
509
+ * 2) slot: a slotted node(nodes) changed in one of several ways (see "reason")
510
+ *
511
+ * - name: the name of the property or slot that caused the invalidation
512
+ *
513
+ * - reason: (children|textcontent|childchange|slotchange) relevant only for type="slot" only and tells exactly what changed in the slot
514
+ * 1) children: immediate children (HTML elements or text nodes) were added, removed or reordered in the slot
515
+ * 2) textcontent: text nodes in the slot changed value (or nested text nodes were added or changed value). Can only trigger for slots of "type: Node"
516
+ * 3) slotchange: a slot element, slotted inside that slot had its "slotchange" event listener called. This practically means that transitively slotted children changed.
517
+ * Can only trigger if the child of a slot is a slot element itself.
518
+ * 4) childchange: indicates that a UI5Element child in that slot was invalidated and in turn invalidated the component.
519
+ * Can only trigger for slots with "invalidateOnChildChange" metadata descriptor
520
+ *
521
+ * - newValue: the new value of the property (for type="property" only)
522
+ *
523
+ * - oldValue: the old value of the property (for type="property" only)
524
+ *
525
+ * - child the child that was changed (for type="slot" and reason="childchange" only)
526
+ *
527
+ * @public
487
528
  */
488
- _invalidate() {
489
- if (this._shouldInvalidateParent) {
490
- this.parentNode._invalidate();
491
- }
492
-
493
- if (!this._upToDate) {
494
- // console.log("already invalidated", this, ...arguments);
495
- return;
496
- }
497
-
498
- if (this.getDomRef() && !this._suppressInvalidation) {
499
- this._upToDate = false;
500
- // console.log("INVAL", this, ...arguments);
501
- RenderScheduler.renderDeferred(this);
502
- }
503
- }
529
+ onInvalidation(changeInfo) {}
504
530
 
505
531
  /**
506
- * Do not call this method directly, only intended to be called by RenderScheduler.js
532
+ * Do not call this method directly, only intended to be called by js
507
533
  * @protected
508
534
  */
509
535
  _render() {
@@ -522,15 +548,37 @@ class UI5Element extends HTMLElement {
522
548
  }
523
549
 
524
550
  // resume normal invalidation handling
525
- delete this._suppressInvalidation;
551
+ this._suppressInvalidation = false;
526
552
 
527
553
  // Update the shadow root with the render result
528
- // console.log(this.getDomRef() ? "RE-RENDER" : "FIRST RENDER", this);
529
- this._upToDate = true;
530
- this._updateShadowRoot();
554
+ /*
555
+ if (this._changedState.length) {
556
+ let element = this.localName;
557
+ if (this.id) {
558
+ element = `${element}#${this.id}`;
559
+ }
560
+ console.log("Re-rendering:", element, this._changedState.map(x => { // eslint-disable-line
561
+ let res = `${x.type}`;
562
+ if (x.reason) {
563
+ res = `${res}(${x.reason})`;
564
+ }
565
+ res = `${res}: ${x.name}`;
566
+ if (x.type === "property") {
567
+ res = `${res} ${x.oldValue} => ${x.newValue}`;
568
+ }
531
569
 
532
- if (this._shouldUpdateFragment()) {
533
- this.staticAreaItem._updateFragment(this);
570
+ return res;
571
+ }));
572
+ }
573
+ */
574
+ this._changedState = [];
575
+
576
+ // Update shadow root and static area item
577
+ if (this.constructor._needsShadowDOM()) {
578
+ updateShadowRoot(this);
579
+ }
580
+ if (this.staticAreaItem) {
581
+ this.staticAreaItem.update();
534
582
  }
535
583
 
536
584
  // Safari requires that children get the slot attribute only after the slot tags have been rendered in the shadow DOM
@@ -544,35 +592,6 @@ class UI5Element extends HTMLElement {
544
592
  }
545
593
  }
546
594
 
547
- /**
548
- * @private
549
- */
550
- _updateShadowRoot() {
551
- if (!this.constructor._needsShadowDOM()) {
552
- return;
553
- }
554
-
555
- let styleToPrepend;
556
- const renderResult = executeTemplate(this.constructor.template, this);
557
-
558
- // IE11, Edge
559
- if (window.ShadyDOM) {
560
- createComponentStyleTag(this.constructor);
561
- }
562
-
563
- // Chrome
564
- if (document.adoptedStyleSheets) {
565
- this.shadowRoot.adoptedStyleSheets = getConstructableStyle(this.constructor);
566
- }
567
-
568
- // FF, Safari
569
- if (!document.adoptedStyleSheets && !window.ShadyDOM) {
570
- styleToPrepend = getEffectiveStyle(this.constructor);
571
- }
572
-
573
- this.constructor.render(renderResult, this.shadowRoot, styleToPrepend, { eventContext: this });
574
- }
575
-
576
595
  /**
577
596
  * @private
578
597
  */
@@ -595,16 +614,27 @@ class UI5Element extends HTMLElement {
595
614
 
596
615
  /**
597
616
  * Returns the DOM Element inside the Shadow Root that corresponds to the opening tag in the UI5 Web Component's template
617
+ * *Note:* For logical (abstract) elements (items, options, etc...), returns the part of the parent's DOM that represents this option
598
618
  * Use this method instead of "this.shadowRoot" to read the Shadow DOM, if ever necessary
619
+ *
599
620
  * @public
600
621
  */
601
622
  getDomRef() {
623
+ // If a component set _getRealDomRef to its children, use the return value of this function
624
+ if (typeof this._getRealDomRef === "function") {
625
+ return this._getRealDomRef();
626
+ }
627
+
602
628
  if (!this.shadowRoot || this.shadowRoot.children.length === 0) {
603
629
  return;
604
630
  }
605
631
 
606
- return this.shadowRoot.children.length === 1
607
- ? this.shadowRoot.children[0] : this.shadowRoot.children[1];
632
+ const children = [...this.shadowRoot.children].filter(child => !["link", "style"].includes(child.localName));
633
+ if (children.length !== 1) {
634
+ console.warn(`The shadow DOM for ${this.constructor.getMetadata().getTag()} does not have a top level element, the getDomRef() method might not work as expected`); // eslint-disable-line
635
+ }
636
+
637
+ return children[0];
608
638
  }
609
639
 
610
640
  /**
@@ -621,12 +651,13 @@ class UI5Element extends HTMLElement {
621
651
  }
622
652
 
623
653
  /**
624
- * Use this method in order to get a reference to element in the shadow root of a web component
654
+ * Waits for dom ref and then returns the DOM Element marked with "data-sap-focus-ref" inside the template.
655
+ * This is the element that will receive the focus by default.
625
656
  * @public
626
- * @param {String} refName Defines the name of the stable DOM ref
627
657
  */
628
- getStableDomRef(refName) {
629
- return this.getDomRef().querySelector(`[data-ui5-stable=${refName}]`);
658
+ async getFocusDomRefAsync() {
659
+ await this._waitForDomRef();
660
+ return this.getFocusDomRef();
630
661
  }
631
662
 
632
663
  /**
@@ -664,8 +695,6 @@ class UI5Element extends HTMLElement {
664
695
  }
665
696
 
666
697
  _fireEvent(name, data, cancelable = false, bubbles = true) {
667
- let compatEventResult = true; // Initialized to true, because if the event is not fired at all, it should be considered "not-prevented"
668
-
669
698
  const noConflictEvent = new CustomEvent(`ui5-${name}`, {
670
699
  detail: data,
671
700
  composed: false,
@@ -673,14 +702,14 @@ class UI5Element extends HTMLElement {
673
702
  cancelable,
674
703
  });
675
704
 
676
- // This will be false if the compat event is prevented
677
- compatEventResult = this.dispatchEvent(noConflictEvent);
705
+ // This will be false if the no-conflict event is prevented
706
+ const noConflictEventResult = this.dispatchEvent(noConflictEvent);
678
707
 
679
708
  if (skipOriginalEvent(name)) {
680
- return compatEventResult;
709
+ return noConflictEventResult;
681
710
  }
682
711
 
683
- const customEvent = new CustomEvent(name, {
712
+ const normalEvent = new CustomEvent(name, {
684
713
  detail: data,
685
714
  composed: false,
686
715
  bubbles,
@@ -688,10 +717,10 @@ class UI5Element extends HTMLElement {
688
717
  });
689
718
 
690
719
  // This will be false if the normal event is prevented
691
- const normalEventResult = this.dispatchEvent(customEvent);
720
+ const normalEventResult = this.dispatchEvent(normalEvent);
692
721
 
693
722
  // Return false if any of the two events was prevented (its result was false).
694
- return normalEventResult && compatEventResult;
723
+ return normalEventResult && noConflictEventResult;
695
724
  }
696
725
 
697
726
  /**
@@ -700,18 +729,7 @@ class UI5Element extends HTMLElement {
700
729
  * @public
701
730
  */
702
731
  getSlottedNodes(slotName) {
703
- const reducer = (acc, curr) => {
704
- if (!isSlot(curr)) {
705
- return acc.concat([curr]);
706
- }
707
- return acc.concat(curr.assignedNodes({ flatten: true }).filter(item => item instanceof HTMLElement));
708
- };
709
-
710
- return this[slotName].reduce(reducer, []);
711
- }
712
-
713
- get isCompact() {
714
- return getComputedStyle(this).getPropertyValue(GLOBAL_CONTENT_DENSITY_CSS_VAR) === "compact";
732
+ return getSlottedElementsList(this[slotName]);
715
733
  }
716
734
 
717
735
  /**
@@ -723,33 +741,7 @@ class UI5Element extends HTMLElement {
723
741
  */
724
742
  get effectiveDir() {
725
743
  markAsRtlAware(this.constructor); // if a UI5 Element calls this method, it's considered to be rtl-aware
726
-
727
- const doc = window.document;
728
- const dirValues = ["ltr", "rtl"]; // exclude "auto" and "" from all calculations
729
- const locallyAppliedDir = getComputedStyle(this).getPropertyValue(GLOBAL_DIR_CSS_VAR);
730
-
731
- // In that order, inspect the CSS Var (for modern browsers), the element itself, html and body (for IE fallback)
732
- if (dirValues.includes(locallyAppliedDir)) {
733
- return locallyAppliedDir;
734
- }
735
- if (dirValues.includes(this.dir)) {
736
- return this.dir;
737
- }
738
- if (dirValues.includes(doc.documentElement.dir)) {
739
- return doc.documentElement.dir;
740
- }
741
- if (dirValues.includes(doc.body.dir)) {
742
- return doc.body.dir;
743
- }
744
-
745
- // Finally, check the configuration for explicitly set RTL or language-implied RTL
746
- return getRTL() ? "rtl" : undefined;
747
- }
748
-
749
- updateStaticAreaItemContentDensity() {
750
- if (this.staticAreaItem) {
751
- this.staticAreaItem._updateContentDensity(this.isCompact);
752
- }
744
+ return getEffectiveDir(this);
753
745
  }
754
746
 
755
747
  /**
@@ -769,26 +761,6 @@ class UI5Element extends HTMLElement {
769
761
  return this.getMetadata().getAttributesList();
770
762
  }
771
763
 
772
- /**
773
- * @private
774
- */
775
- static _getSlotName(child) {
776
- // Text nodes can only go to the default slot
777
- if (!(child instanceof HTMLElement)) {
778
- return "default";
779
- }
780
-
781
- // Discover the slot based on the real slot name (f.e. footer => footer, or content-32 => content)
782
- const slot = child.getAttribute("slot");
783
- if (slot) {
784
- const match = slot.match(/^(.+?)-\d+$/);
785
- return match ? match[1] : slot;
786
- }
787
-
788
- // Use default slot as a fallback
789
- return "default";
790
- }
791
-
792
764
  /**
793
765
  * @private
794
766
  */
@@ -796,70 +768,30 @@ class UI5Element extends HTMLElement {
796
768
  return !!this.template;
797
769
  }
798
770
 
799
- _shouldUpdateFragment() {
800
- return this.constructor._needsStaticArea() && this.staticAreaItem.isRendered();
801
- }
802
-
803
771
  /**
804
772
  * @private
805
773
  */
806
774
  static _needsStaticArea() {
807
- return typeof this.staticAreaTemplate === "function";
775
+ return !!this.staticAreaTemplate;
808
776
  }
809
777
 
810
778
  /**
811
779
  * @public
812
780
  */
813
781
  getStaticAreaItemDomRef() {
814
- return this.staticAreaItem.getDomRef();
815
- }
816
-
817
- /**
818
- * @private
819
- */
820
- static _getDefaultState() {
821
- if (this._defaultState) {
822
- return this._defaultState;
782
+ if (!this.constructor._needsStaticArea()) {
783
+ throw new Error("This component does not use the static area");
823
784
  }
824
785
 
825
- const MetadataClass = this.getMetadata();
826
- const defaultState = {};
827
- const slotsAreManaged = MetadataClass.slotsAreManaged();
828
-
829
- // Initialize properties
830
- const props = MetadataClass.getProperties();
831
- for (const propName in props) { // eslint-disable-line
832
- const propType = props[propName].type;
833
- const propDefaultValue = props[propName].defaultValue;
834
-
835
- if (propType === Boolean) {
836
- defaultState[propName] = false;
837
-
838
- if (propDefaultValue !== undefined) {
839
- console.warn("The 'defaultValue' metadata key is ignored for all booleans properties, they would be initialized with 'false' by default"); // eslint-disable-line
840
- }
841
- } else if (props[propName].multiple) {
842
- defaultState[propName] = [];
843
- } else if (propType === Object) {
844
- defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : {};
845
- } else if (propType === String) {
846
- defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : "";
847
- } else {
848
- defaultState[propName] = propDefaultValue;
849
- }
786
+ if (!this.staticAreaItem) {
787
+ this.staticAreaItem = StaticAreaItem.createInstance();
788
+ this.staticAreaItem.setOwnerElement(this);
850
789
  }
851
-
852
- // Initialize slots
853
- if (slotsAreManaged) {
854
- const slots = MetadataClass.getSlots();
855
- for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
856
- const propertyName = slotData.propertyName || slotName;
857
- defaultState[propertyName] = [];
858
- }
790
+ if (!this.staticAreaItem.parentElement) {
791
+ getSingletonElementInstance("ui5-static-area").appendChild(this.staticAreaItem);
859
792
  }
860
793
 
861
- this._defaultState = defaultState;
862
- return defaultState;
794
+ return this.staticAreaItem.getDomRef();
863
795
  }
864
796
 
865
797
  /**
@@ -873,7 +805,7 @@ class UI5Element extends HTMLElement {
873
805
  const properties = this.getMetadata().getProperties();
874
806
  for (const [prop, propData] of Object.entries(properties)) { // eslint-disable-line
875
807
  if (!isValidPropertyName(prop)) {
876
- throw new Error(`"${prop}" is not a valid property name. Use a name that does not collide with DOM APIs`);
808
+ console.warn(`"${prop}" is not a valid property name. Use a name that does not collide with DOM APIs`); /* eslint-disable-line */
877
809
  }
878
810
 
879
811
  if (propData.type === Boolean && propData.defaultValue) {
@@ -911,14 +843,27 @@ class UI5Element extends HTMLElement {
911
843
  }
912
844
  },
913
845
  set(value) {
846
+ let isDifferent;
914
847
  value = this.constructor.getMetadata().constructor.validatePropertyValue(value, propData);
915
848
 
916
849
  const oldState = this._state[prop];
850
+ if (propData.multiple && propData.compareValues) {
851
+ isDifferent = !arraysAreEqual(oldState, value);
852
+ } else if (isDescendantOf(propData.type, DataType)) {
853
+ isDifferent = !propData.type.valuesAreEqual(oldState, value);
854
+ } else {
855
+ isDifferent = oldState !== value;
856
+ }
917
857
 
918
- if (oldState !== value) {
858
+ if (isDifferent) {
919
859
  this._state[prop] = value;
920
- this._invalidate(prop, value);
921
- this._propertyChange(prop, value);
860
+ _invalidate.call(this, {
861
+ type: "property",
862
+ name: prop,
863
+ newValue: value,
864
+ oldValue: oldState,
865
+ });
866
+ this._updateAttribute(prop, value);
922
867
  }
923
868
  },
924
869
  });
@@ -929,7 +874,7 @@ class UI5Element extends HTMLElement {
929
874
  const slots = this.getMetadata().getSlots();
930
875
  for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
931
876
  if (!isValidPropertyName(slotName)) {
932
- throw new Error(`"${slotName}" is not a valid property name. Use a name that does not collide with DOM APIs`);
877
+ console.warn(`"${slotName}" is not a valid property name. Use a name that does not collide with DOM APIs`); /* eslint-disable-line */
933
878
  }
934
879
 
935
880
  const propertyName = slotData.propertyName || slotName;
@@ -941,7 +886,7 @@ class UI5Element extends HTMLElement {
941
886
  return [];
942
887
  },
943
888
  set() {
944
- throw new Error("Cannot set slots directly, use the DOM APIs");
889
+ throw new Error("Cannot set slot content directly, use the DOM APIs (appendChild, removeChild, etc...)");
945
890
  },
946
891
  });
947
892
  }
@@ -956,6 +901,14 @@ class UI5Element extends HTMLElement {
956
901
  return metadata;
957
902
  }
958
903
 
904
+ /**
905
+ * Sets a new metadata object for this UI5 Web Component Class
906
+ * @protected
907
+ */
908
+ static set metadata(newMetadata) {
909
+ metadata = newMetadata;
910
+ }
911
+
959
912
  /**
960
913
  * Returns the CSS for this UI5 Web Component Class
961
914
  * @protected
@@ -1041,11 +994,13 @@ class UI5Element extends HTMLElement {
1041
994
  this._generateAccessors();
1042
995
  registerTag(tag);
1043
996
  window.customElements.define(tag, this);
997
+ preloadLinks(this);
1044
998
 
1045
999
  if (altTag && !customElements.get(altTag)) {
1046
- class oldClassName extends this {}
1047
1000
  registerTag(altTag);
1048
- window.customElements.define(altTag, oldClassName);
1001
+ window.customElements.define(altTag, getClassCopy(this, () => {
1002
+ console.log(`The ${altTag} tag is deprecated and will be removed in the next release, please use ${tag} instead.`); // eslint-disable-line
1003
+ }));
1049
1004
  }
1050
1005
  }
1051
1006
  return this;