@ui5/webcomponents-base 0.0.0-9195c41b8 → 0.0.0-9c26e8e4f

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 (354) hide show
  1. package/.eslintignore +3 -0
  2. package/CHANGELOG.md +362 -0
  3. package/README.md +53 -7
  4. package/bundle.esm.js +23 -16
  5. package/dist/AssetRegistry.js +8 -6
  6. package/dist/Boot.js +59 -0
  7. package/dist/CSP.js +59 -0
  8. package/dist/CustomElementsRegistry.js +79 -7
  9. package/dist/CustomElementsScope.js +30 -0
  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 +38 -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 +136 -2
  18. package/dist/ManagedStyles.js +83 -0
  19. package/dist/MediaRange.js +109 -0
  20. package/dist/PropertiesFileFormat.js +95 -0
  21. package/dist/Render.js +175 -0
  22. package/dist/RenderQueue.js +41 -17
  23. package/dist/RenderScheduler.js +24 -126
  24. package/dist/Runtimes.js +107 -0
  25. package/dist/StaticArea.js +1 -39
  26. package/dist/StaticAreaItem.js +58 -62
  27. package/dist/SystemCSSVars.js +10 -0
  28. package/dist/Theming.js +2 -1
  29. package/dist/UI5Element.js +435 -300
  30. package/dist/UI5ElementMetadata.js +173 -19
  31. package/dist/asset-registries/Icons.js +125 -14
  32. package/dist/asset-registries/Illustrations.js +30 -0
  33. package/dist/asset-registries/LocaleData.js +105 -66
  34. package/dist/asset-registries/Themes.js +30 -35
  35. package/dist/asset-registries/i18n.js +73 -29
  36. package/dist/assets/bundle.esm.eb35c35a.js +43 -0
  37. package/dist/assets-meta/IconCollectionsAlias.js +18 -0
  38. package/dist/config/AnimationMode.js +11 -1
  39. package/dist/config/Icons.js +52 -0
  40. package/dist/config/Language.js +58 -2
  41. package/dist/config/Theme.js +25 -0
  42. package/dist/css/BusyIndicator.css +80 -0
  43. package/dist/css/FontFace.css +75 -0
  44. package/dist/css/OverrideFontFace.css +32 -0
  45. package/dist/css/SystemCSSVars.css +17 -0
  46. package/dist/delegate/ItemNavigation.js +249 -171
  47. package/dist/delegate/ResizeHandler.js +78 -38
  48. package/dist/delegate/ScrollEnablement.js +49 -20
  49. package/dist/features/F6Navigation.js +110 -0
  50. package/dist/features/OpenUI5Enablement.js +119 -0
  51. package/dist/features/OpenUI5Support.js +42 -7
  52. package/dist/generated/AssetParameters.js +1 -1
  53. package/dist/generated/VersionInfo.js +10 -0
  54. package/dist/generated/css/BusyIndicator.css.js +5 -0
  55. package/dist/generated/css/FontFace.css.js +5 -0
  56. package/dist/generated/css/OverrideFontFace.css.js +5 -0
  57. package/dist/generated/css/SystemCSSVars.css.js +5 -0
  58. package/dist/getSharedResource.js +30 -0
  59. package/dist/i18nBundle.js +58 -5
  60. package/dist/locale/RTLAwareRegistry.js +14 -0
  61. package/dist/locale/applyDirection.js +17 -0
  62. package/dist/locale/directionChange.js +32 -0
  63. package/dist/locale/getEffectiveDir.js +28 -0
  64. package/dist/locale/getLocale.js +12 -2
  65. package/dist/locale/languageChange.js +22 -0
  66. package/dist/renderer/LitRenderer.js +44 -7
  67. package/dist/renderer/directives/style-map.js +72 -0
  68. package/dist/renderer/executeTemplate.js +34 -0
  69. package/dist/sap/base/Log.js +241 -0
  70. package/dist/sap/base/assert.js +8 -0
  71. package/dist/sap/base/security/URLListValidator.js +148 -0
  72. package/dist/sap/base/security/encodeCSS.js +14 -0
  73. package/dist/sap/base/security/encodeXML.js +23 -0
  74. package/dist/sap/base/security/sanitizeHTML.js +16 -0
  75. package/dist/sap/base/strings/toHex.js +8 -0
  76. package/dist/sap/base/util/now.js +7 -0
  77. package/dist/sap/ui/thirdparty/caja-html-sanitizer.js +3585 -0
  78. package/dist/theming/CustomStyle.js +47 -6
  79. package/dist/theming/ThemeLoaded.js +22 -0
  80. package/dist/theming/applyTheme.js +13 -15
  81. package/dist/theming/getConstructableStyle.js +15 -10
  82. package/dist/theming/getEffectiveLinksHrefs.js +27 -0
  83. package/dist/theming/getEffectiveStyle.js +32 -8
  84. package/dist/theming/getStylesString.js +15 -0
  85. package/dist/theming/getThemeDesignerTheme.js +25 -5
  86. package/dist/theming/preloadLinks.js +23 -0
  87. package/dist/types/CSSColor.js +9 -0
  88. package/dist/types/CalendarType.js +1 -1
  89. package/dist/types/DataType.js +13 -1
  90. package/dist/types/Float.js +14 -0
  91. package/dist/types/Integer.js +4 -0
  92. package/dist/types/InvisibleMessageMode.js +30 -0
  93. package/dist/types/ItemNavigationBehavior.js +2 -7
  94. package/dist/types/NavigationMode.js +1 -0
  95. package/dist/types/PopupState.js +1 -1
  96. package/dist/types/ValueState.js +1 -1
  97. package/dist/updateShadowRoot.js +29 -0
  98. package/dist/util/AriaLabelHelper.js +41 -0
  99. package/dist/util/Caret.js +32 -0
  100. package/dist/util/ColorConversion.js +398 -0
  101. package/dist/util/FocusableElements.js +30 -11
  102. package/dist/util/HTMLSanitizer.js +7 -0
  103. package/dist/util/InvisibleMessage.js +69 -0
  104. package/dist/util/PopupUtils.js +93 -0
  105. package/dist/util/SlotsHelper.js +43 -0
  106. package/dist/util/TabbableElements.js +5 -1
  107. package/dist/util/arraysAreEqual.js +15 -0
  108. package/dist/util/clamp.js +12 -0
  109. package/dist/util/createLinkInHead.js +19 -0
  110. package/dist/util/debounce.js +17 -0
  111. package/dist/util/escapeRegex.js +10 -0
  112. package/dist/util/generateHighlightedMarkup.js +42 -0
  113. package/dist/util/getActiveElement.js +11 -0
  114. package/dist/util/getClassCopy.js +10 -0
  115. package/dist/util/getEffectiveContentDensity.js +5 -0
  116. package/dist/util/getNormalizedTarget.js +16 -0
  117. package/dist/util/getSingletonElementInstance.js +13 -0
  118. package/dist/util/isDefaultSlotProvided.js +11 -0
  119. package/dist/util/isElementInView.js +15 -0
  120. package/dist/util/isNodeHidden.js +1 -5
  121. package/dist/util/isNodeTabbable.js +4 -4
  122. package/dist/util/isValidPropertyName.js +9 -3
  123. package/dist/util/setToArray.js +10 -0
  124. package/hash.txt +1 -0
  125. package/index.js +1 -1
  126. package/lib/generate-asset-parameters/index.js +8 -5
  127. package/lib/generate-styles/index.js +26 -0
  128. package/lib/generate-version-info/index.js +32 -0
  129. package/package-scripts.js +37 -28
  130. package/package.json +20 -12
  131. package/src/AssetRegistry.js +8 -6
  132. package/src/Boot.js +59 -0
  133. package/src/CSP.js +59 -0
  134. package/src/CustomElementsRegistry.js +79 -7
  135. package/src/CustomElementsScope.js +30 -0
  136. package/src/CustomElementsScopeUtils.js +108 -0
  137. package/src/DOMObserver.js +65 -0
  138. package/src/Device.js +71 -773
  139. package/src/EventProvider.js +38 -26
  140. package/src/FeaturesRegistry.js +4 -1
  141. package/src/FontFace.js +19 -62
  142. package/src/InitialConfiguration.js +43 -8
  143. package/src/Keys.js +136 -2
  144. package/src/ManagedStyles.js +83 -0
  145. package/src/MediaRange.js +109 -0
  146. package/src/PropertiesFileFormat.js +95 -0
  147. package/src/Render.js +175 -0
  148. package/src/RenderQueue.js +41 -17
  149. package/src/RenderScheduler.js +24 -126
  150. package/src/Runtimes.js +107 -0
  151. package/src/StaticArea.js +1 -39
  152. package/src/StaticAreaItem.js +58 -62
  153. package/src/SystemCSSVars.js +10 -0
  154. package/src/Theming.js +2 -1
  155. package/src/UI5Element.js +435 -300
  156. package/src/UI5ElementMetadata.js +173 -19
  157. package/src/asset-registries/Icons.js +125 -14
  158. package/src/asset-registries/Illustrations.js +30 -0
  159. package/src/asset-registries/LocaleData.js +105 -66
  160. package/src/asset-registries/Themes.js +30 -35
  161. package/src/asset-registries/i18n.js +73 -29
  162. package/src/assets-meta/IconCollectionsAlias.js +18 -0
  163. package/src/config/AnimationMode.js +11 -1
  164. package/src/config/Icons.js +52 -0
  165. package/src/config/Language.js +58 -2
  166. package/src/config/Theme.js +25 -0
  167. package/src/css/BusyIndicator.css +80 -0
  168. package/src/css/FontFace.css +75 -0
  169. package/src/css/OverrideFontFace.css +32 -0
  170. package/src/css/SystemCSSVars.css +17 -0
  171. package/src/delegate/ItemNavigation.js +249 -171
  172. package/src/delegate/ResizeHandler.js +78 -38
  173. package/src/delegate/ScrollEnablement.js +49 -20
  174. package/src/features/F6Navigation.js +110 -0
  175. package/src/features/OpenUI5Enablement.js +119 -0
  176. package/src/features/OpenUI5Support.js +42 -7
  177. package/src/getSharedResource.js +30 -0
  178. package/src/i18nBundle.js +58 -5
  179. package/src/locale/RTLAwareRegistry.js +14 -0
  180. package/src/locale/applyDirection.js +17 -0
  181. package/src/locale/directionChange.js +32 -0
  182. package/src/locale/getEffectiveDir.js +28 -0
  183. package/src/locale/getLocale.js +12 -2
  184. package/src/locale/languageChange.js +22 -0
  185. package/src/renderer/LitRenderer.js +44 -7
  186. package/src/renderer/directives/style-map.js +72 -0
  187. package/src/renderer/executeTemplate.js +34 -0
  188. package/src/theming/CustomStyle.js +47 -6
  189. package/src/theming/ThemeLoaded.js +22 -0
  190. package/src/theming/applyTheme.js +13 -15
  191. package/src/theming/getConstructableStyle.js +15 -10
  192. package/src/theming/getEffectiveLinksHrefs.js +27 -0
  193. package/src/theming/getEffectiveStyle.js +32 -8
  194. package/src/theming/getStylesString.js +15 -0
  195. package/src/theming/getThemeDesignerTheme.js +25 -5
  196. package/src/theming/preloadLinks.js +23 -0
  197. package/src/types/CSSColor.js +9 -0
  198. package/src/types/CalendarType.js +1 -1
  199. package/src/types/DataType.js +13 -1
  200. package/src/types/Float.js +14 -0
  201. package/src/types/Integer.js +4 -0
  202. package/src/types/InvisibleMessageMode.js +30 -0
  203. package/src/types/ItemNavigationBehavior.js +2 -7
  204. package/src/types/NavigationMode.js +1 -0
  205. package/src/types/PopupState.js +1 -1
  206. package/src/types/ValueState.js +1 -1
  207. package/src/updateShadowRoot.js +29 -0
  208. package/src/util/AriaLabelHelper.js +41 -0
  209. package/src/util/Caret.js +32 -0
  210. package/src/util/ColorConversion.js +398 -0
  211. package/src/util/FocusableElements.js +30 -11
  212. package/src/util/HTMLSanitizer.js +7 -0
  213. package/src/util/InvisibleMessage.js +69 -0
  214. package/src/util/PopupUtils.js +93 -0
  215. package/src/util/SlotsHelper.js +43 -0
  216. package/src/util/TabbableElements.js +5 -1
  217. package/src/util/arraysAreEqual.js +15 -0
  218. package/src/util/clamp.js +12 -0
  219. package/src/util/createLinkInHead.js +19 -0
  220. package/src/util/debounce.js +17 -0
  221. package/src/util/escapeRegex.js +10 -0
  222. package/src/util/generateHighlightedMarkup.js +42 -0
  223. package/src/util/getActiveElement.js +11 -0
  224. package/src/util/getClassCopy.js +10 -0
  225. package/src/util/getEffectiveContentDensity.js +5 -0
  226. package/src/util/getNormalizedTarget.js +16 -0
  227. package/src/util/getSingletonElementInstance.js +13 -0
  228. package/src/util/isDefaultSlotProvided.js +11 -0
  229. package/src/util/isElementInView.js +15 -0
  230. package/src/util/isNodeHidden.js +1 -5
  231. package/src/util/isNodeTabbable.js +4 -4
  232. package/src/util/isValidPropertyName.js +9 -3
  233. package/src/util/setToArray.js +10 -0
  234. package/used-modules.txt +10 -0
  235. package/bundle.es5.js +0 -28
  236. package/config/.eslintrc.js +0 -3
  237. package/config/rollup.config.js +0 -1
  238. package/dist/SVGIconRegistry.js +0 -60
  239. package/dist/boot.js +0 -32
  240. package/dist/compatibility/DOMObserver.js +0 -62
  241. package/dist/compatibility/patchNodeValue.js +0 -24
  242. package/dist/compatibility/whenPolyfillLoaded.js +0 -26
  243. package/dist/delegate/CustomResize.js +0 -78
  244. package/dist/delegate/NativeResize.js +0 -44
  245. package/dist/features/browsersupport/Edge.js +0 -6
  246. package/dist/features/browsersupport/IE11.js +0 -41
  247. package/dist/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
  248. package/dist/renderer/ifDefined.js +0 -21
  249. package/dist/resources/bundle.es5.js +0 -212
  250. package/dist/resources/bundle.es5.js.map +0 -1
  251. package/dist/resources/bundle.esm.js +0 -129
  252. package/dist/resources/bundle.esm.js.map +0 -1
  253. package/dist/test-resources/assets/Themes.js +0 -13
  254. package/dist/test-resources/elements/Child.js +0 -35
  255. package/dist/test-resources/elements/Generic.js +0 -84
  256. package/dist/test-resources/elements/GenericExt.js +0 -26
  257. package/dist/test-resources/elements/NoShadowDOM.js +0 -13
  258. package/dist/test-resources/elements/Parent.js +0 -37
  259. package/dist/test-resources/elements/WithStaticArea.js +0 -76
  260. package/dist/test-resources/pages/AllTestElements.html +0 -42
  261. package/dist/test-resources/pages/Configuration.html +0 -20
  262. package/dist/test-resources/pages/ConfigurationScript.html +0 -36
  263. package/dist/test-resources/specs/ConfigurationChange.spec.js +0 -25
  264. package/dist/test-resources/specs/ConfigurationScript.spec.js +0 -61
  265. package/dist/test-resources/specs/ConfigurationURL.spec.js +0 -45
  266. package/dist/test-resources/specs/CustomTheme.spec.js +0 -25
  267. package/dist/test-resources/specs/StaticArea.spec.js +0 -34
  268. package/dist/test-resources/specs/Theming.spec.js +0 -31
  269. package/dist/test-resources/specs/UI5ElementInvalidation.js +0 -258
  270. package/dist/test-resources/specs/UI5ElementLifecycle.js +0 -93
  271. package/dist/test-resources/specs/UI5ElementListenForChildPropChanges.spec.js +0 -101
  272. package/dist/test-resources/specs/UI5ElementMetadataExt.js +0 -40
  273. package/dist/test-resources/specs/UI5ElementPropertyValidation.js +0 -12
  274. package/dist/test-resources/specs/UI5ElementPropsAndAttrs.spec.js +0 -65
  275. package/dist/test-resources/specs/UI5ElementShadowDOM.js +0 -22
  276. package/dist/test-resources/specs/UI5ElementSlots.js +0 -34
  277. package/dist/theming/CSSVarsPonyfill.js +0 -24
  278. package/dist/theming/adaptCSSForIE.js +0 -90
  279. package/dist/theming/createComponentStyleTag.js +0 -49
  280. package/dist/theming/createThemePropertiesStyleTag.js +0 -20
  281. package/dist/thirdparty/Array.from.js +0 -16
  282. package/dist/thirdparty/Array.prototype.fill.js +0 -44
  283. package/dist/thirdparty/Array.prototype.find.js +0 -46
  284. package/dist/thirdparty/Array.prototype.includes.js +0 -51
  285. package/dist/thirdparty/Element.prototype.closest.js +0 -11
  286. package/dist/thirdparty/Element.prototype.matches.js +0 -6
  287. package/dist/thirdparty/Map.prototype.keys.js +0 -9
  288. package/dist/thirdparty/Number.isInteger.js +0 -5
  289. package/dist/thirdparty/Number.isNaN.js +0 -1
  290. package/dist/thirdparty/Number.parseInt.js +0 -1
  291. package/dist/thirdparty/Object.assign.js +0 -31
  292. package/dist/thirdparty/Object.entries.js +0 -11
  293. package/dist/thirdparty/Symbol.js +0 -2
  294. package/dist/thirdparty/WeakSet.js +0 -27
  295. package/dist/thirdparty/es6-string-methods.js +0 -182
  296. package/dist/thirdparty/events-polyfills.js +0 -89
  297. package/dist/thirdparty/fetch.js +0 -1
  298. package/dist/thirdparty/template.js +0 -600
  299. package/dist/thirdparty/webcomponents-sd-ce-pf.js +0 -318
  300. package/dist/webcomponentsjs/LICENSE.md +0 -19
  301. package/dist/webcomponentsjs/README.md +0 -229
  302. package/dist/webcomponentsjs/bundles/webcomponents-ce.js +0 -63
  303. package/dist/webcomponentsjs/bundles/webcomponents-ce.js.map +0 -1
  304. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js +0 -297
  305. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce-pf.js.map +0 -1
  306. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js +0 -208
  307. package/dist/webcomponentsjs/bundles/webcomponents-sd-ce.js.map +0 -1
  308. package/dist/webcomponentsjs/bundles/webcomponents-sd.js +0 -166
  309. package/dist/webcomponentsjs/bundles/webcomponents-sd.js.map +0 -1
  310. package/dist/webcomponentsjs/custom-elements-es5-adapter.js +0 -15
  311. package/dist/webcomponentsjs/package.json +0 -46
  312. package/dist/webcomponentsjs/src/entrypoints/custom-elements-es5-adapter-index.js +0 -16
  313. package/dist/webcomponentsjs/src/entrypoints/webcomponents-bundle-index.js +0 -53
  314. package/dist/webcomponentsjs/src/entrypoints/webcomponents-ce-index.js +0 -17
  315. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-index.js +0 -19
  316. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-ce-pf-index.js +0 -28
  317. package/dist/webcomponentsjs/src/entrypoints/webcomponents-sd-index.js +0 -18
  318. package/dist/webcomponentsjs/webcomponents-bundle.js +0 -298
  319. package/dist/webcomponentsjs/webcomponents-bundle.js.map +0 -1
  320. package/dist/webcomponentsjs/webcomponents-loader.js +0 -185
  321. package/src/SVGIconRegistry.js +0 -60
  322. package/src/boot.js +0 -32
  323. package/src/compatibility/DOMObserver.js +0 -62
  324. package/src/compatibility/patchNodeValue.js +0 -24
  325. package/src/compatibility/whenPolyfillLoaded.js +0 -26
  326. package/src/delegate/CustomResize.js +0 -78
  327. package/src/delegate/NativeResize.js +0 -44
  328. package/src/features/browsersupport/Edge.js +0 -6
  329. package/src/features/browsersupport/IE11.js +0 -41
  330. package/src/features/browsersupport/IE11WithWebComponentsPolyfill.js +0 -5
  331. package/src/renderer/ifDefined.js +0 -21
  332. package/src/theming/CSSVarsPonyfill.js +0 -24
  333. package/src/theming/adaptCSSForIE.js +0 -90
  334. package/src/theming/createComponentStyleTag.js +0 -49
  335. package/src/theming/createThemePropertiesStyleTag.js +0 -20
  336. package/src/thirdparty/Array.from.js +0 -16
  337. package/src/thirdparty/Array.prototype.fill.js +0 -44
  338. package/src/thirdparty/Array.prototype.find.js +0 -46
  339. package/src/thirdparty/Array.prototype.includes.js +0 -51
  340. package/src/thirdparty/Element.prototype.closest.js +0 -11
  341. package/src/thirdparty/Element.prototype.matches.js +0 -6
  342. package/src/thirdparty/Map.prototype.keys.js +0 -9
  343. package/src/thirdparty/Number.isInteger.js +0 -5
  344. package/src/thirdparty/Number.isNaN.js +0 -1
  345. package/src/thirdparty/Number.parseInt.js +0 -1
  346. package/src/thirdparty/Object.assign.js +0 -31
  347. package/src/thirdparty/Object.entries.js +0 -11
  348. package/src/thirdparty/Symbol.js +0 -2
  349. package/src/thirdparty/WeakSet.js +0 -27
  350. package/src/thirdparty/es6-string-methods.js +0 -182
  351. package/src/thirdparty/events-polyfills.js +0 -89
  352. package/src/thirdparty/fetch.js +0 -1
  353. package/src/thirdparty/template.js +0 -600
  354. package/src/thirdparty/webcomponents-sd-ce-pf.js +0 -318
@@ -1,29 +1,53 @@
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 EventProvider from "./EventProvider.js";
5
+ import getSingletonElementInstance from "./util/getSingletonElementInstance.js";
4
6
  import StaticAreaItem from "./StaticAreaItem.js";
5
- import RenderScheduler from "./RenderScheduler.js";
6
- import { registerTag, isTagRegistered } from "./CustomElementsRegistry.js";
7
- import DOMObserver from "./compatibility/DOMObserver.js";
7
+ import updateShadowRoot from "./updateShadowRoot.js";
8
+ import { renderDeferred, renderImmediately, cancelRender } from "./Render.js";
9
+ import { registerTag, isTagRegistered, recordTagRegistrationFailure } from "./CustomElementsRegistry.js";
10
+ import { observeDOMNode, unobserveDOMNode } from "./DOMObserver.js";
8
11
  import { skipOriginalEvent } from "./config/NoConflict.js";
9
- import getConstructableStyle from "./theming/getConstructableStyle.js";
10
- import createComponentStyleTag from "./theming/createComponentStyleTag.js";
11
- import getEffectiveStyle from "./theming/getEffectiveStyle.js";
12
- import Integer from "./types/Integer.js";
12
+ import getEffectiveDir from "./locale/getEffectiveDir.js";
13
+ import DataType from "./types/DataType.js";
13
14
  import { kebabToCamelCase, camelToKebabCase } from "./util/StringHelper.js";
14
15
  import isValidPropertyName from "./util/isValidPropertyName.js";
15
-
16
- const metadata = {
17
- events: {
18
- _propertyChange: {},
19
- },
20
- };
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";
20
+ import { markAsRtlAware } from "./locale/RTLAwareRegistry.js";
21
+ import preloadLinks from "./theming/preloadLinks.js";
21
22
 
22
23
  let autoId = 0;
23
24
 
24
25
  const elementTimeouts = new Map();
26
+ const uniqueDependenciesCache = new Map();
27
+
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 = {};
25
50
 
26
- const GLOBAL_CONTENT_DENSITY_CSS_VAR = "--_ui5_content_density";
27
51
  /**
28
52
  * Base class for all UI5 Web Components
29
53
  *
@@ -37,56 +61,40 @@ const GLOBAL_CONTENT_DENSITY_CSS_VAR = "--_ui5_content_density";
37
61
  class UI5Element extends HTMLElement {
38
62
  constructor() {
39
63
  super();
40
- this._generateId();
41
- this._initializeState();
42
- this._upgradeAllProperties();
43
- this._initializeContainers();
44
- this._upToDate = false;
45
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
46
72
  let deferredResolve;
47
73
  this._domRefReadyPromise = new Promise(resolve => {
48
74
  deferredResolve = resolve;
49
75
  });
50
76
  this._domRefReadyPromise._deferredResolve = deferredResolve;
51
77
 
52
- this._monitoredChildProps = new Map();
53
- this._firePropertyChange = false;
54
- }
78
+ this._initializeState();
79
+ this._upgradeAllProperties();
55
80
 
56
- /**
57
- * @private
58
- */
59
- _generateId() {
60
- this._id = `ui5wc_${++autoId}`;
81
+ if (this.constructor._needsShadowDOM()) {
82
+ this.attachShadow({ mode: "open" });
83
+ }
61
84
  }
62
85
 
63
86
  /**
64
- * @private
87
+ * Returns a unique ID for this UI5 Element
88
+ *
89
+ * @deprecated - This property is not guaranteed in future releases
90
+ * @protected
65
91
  */
66
- _initializeContainers() {
67
- const needsShadowDOM = this.constructor._needsShadowDOM();
68
- const needsStaticArea = this.constructor._needsStaticArea();
69
-
70
- // Init Shadow Root
71
- if (needsShadowDOM) {
72
- this.attachShadow({ mode: "open" });
73
-
74
- // IE11, Edge
75
- if (window.ShadyDOM) {
76
- createComponentStyleTag(this.constructor);
77
- }
78
-
79
- // Chrome
80
- if (document.adoptedStyleSheets) {
81
- const style = getConstructableStyle(this.constructor);
82
- this.shadowRoot.adoptedStyleSheets = [style];
83
- }
92
+ get _id() {
93
+ if (!this.__id) {
94
+ this.__id = `ui5wc_${++autoId}`;
84
95
  }
85
96
 
86
- // Init StaticAreaItem only if needed
87
- if (needsStaticArea) {
88
- this.staticAreaItem = new StaticAreaItem(this);
89
- }
97
+ return this.__id;
90
98
  }
91
99
 
92
100
  /**
@@ -94,26 +102,30 @@ class UI5Element extends HTMLElement {
94
102
  * @private
95
103
  */
96
104
  async connectedCallback() {
97
- const needsShadowDOM = this.constructor._needsShadowDOM();
105
+ this.setAttribute(this.constructor.getMetadata().getPureTag(), "");
106
+ if (this.constructor.getMetadata().supportsF6FastNavigation()) {
107
+ this.setAttribute("data-sap-ui-fastnavgroup", "true");
108
+ }
109
+
98
110
  const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
99
111
 
100
- // Render the Shadow DOM
101
- if (needsShadowDOM) {
102
- if (slotsAreManaged) {
103
- // always register the observer before yielding control to the main thread (await)
104
- this._startObservingDOMChildren();
105
- await this._processChildren();
106
- }
112
+ this._inDOM = true;
107
113
 
108
- if (!this.shadowRoot) { // Workaround for Firefox74 bug
109
- await Promise.resolve();
110
- }
114
+ if (slotsAreManaged) {
115
+ // always register the observer before yielding control to the main thread (await)
116
+ this._startObservingDOMChildren();
117
+ await this._processChildren();
118
+ }
111
119
 
112
- await RenderScheduler.renderImmediately(this);
113
- this._domRefReadyPromise._deferredResolve();
114
- if (typeof this.onEnterDOM === "function") {
115
- this.onEnterDOM();
116
- }
120
+ if (!this._inDOM) { // Component removed from DOM while _processChildren was running
121
+ return;
122
+ }
123
+
124
+ renderImmediately(this);
125
+ this._domRefReadyPromise._deferredResolve();
126
+ this._fullyConnected = true;
127
+ if (typeof this.onEnterDOM === "function") {
128
+ this.onEnterDOM();
117
129
  }
118
130
  }
119
131
 
@@ -122,23 +134,26 @@ class UI5Element extends HTMLElement {
122
134
  * @private
123
135
  */
124
136
  disconnectedCallback() {
125
- const needsShadowDOM = this.constructor._needsShadowDOM();
126
- const needsStaticArea = this.constructor._needsStaticArea();
127
137
  const slotsAreManaged = this.constructor.getMetadata().slotsAreManaged();
128
138
 
129
- if (needsShadowDOM) {
130
- if (slotsAreManaged) {
131
- this._stopObservingDOMChildren();
132
- }
139
+ this._inDOM = false;
133
140
 
141
+ if (slotsAreManaged) {
142
+ this._stopObservingDOMChildren();
143
+ }
144
+
145
+ if (this._fullyConnected) {
134
146
  if (typeof this.onExitDOM === "function") {
135
147
  this.onExitDOM();
136
148
  }
149
+ this._fullyConnected = false;
137
150
  }
138
151
 
139
- if (needsStaticArea) {
140
- this.staticAreaItem._removeFragmentFromStaticArea();
152
+ if (this.staticAreaItem && this.staticAreaItem.parentElement) {
153
+ this.staticAreaItem.parentElement.removeChild(this.staticAreaItem);
141
154
  }
155
+
156
+ cancelRender(this);
142
157
  }
143
158
 
144
159
  /**
@@ -154,16 +169,16 @@ class UI5Element extends HTMLElement {
154
169
  const mutationObserverOptions = {
155
170
  childList: true,
156
171
  subtree: canSlotText,
157
- characterData: true,
172
+ characterData: canSlotText,
158
173
  };
159
- DOMObserver.observeDOMNode(this, this._processChildren.bind(this), mutationObserverOptions);
174
+ observeDOMNode(this, this._processChildren.bind(this), mutationObserverOptions);
160
175
  }
161
176
 
162
177
  /**
163
178
  * @private
164
179
  */
165
180
  _stopObservingDOMChildren() {
166
- DOMObserver.unobserveDOMNode(this);
181
+ unobserveDOMNode(this);
167
182
  }
168
183
 
169
184
  /**
@@ -185,8 +200,14 @@ class UI5Element extends HTMLElement {
185
200
  const canSlotText = this.constructor.getMetadata().canSlotText();
186
201
  const domChildren = Array.from(canSlotText ? this.childNodes : this.children);
187
202
 
188
- // 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
189
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]]);
190
211
  this._clearSlot(slotName, slotData);
191
212
  }
192
213
 
@@ -195,7 +216,7 @@ class UI5Element extends HTMLElement {
195
216
 
196
217
  const allChildrenUpgraded = domChildren.map(async (child, idx) => {
197
218
  // Determine the type of the child (mainly by the slot attribute)
198
- const slotName = this.constructor._getSlotName(child);
219
+ const slotName = getSlotName(child);
199
220
  const slotData = slotsMap[slotName];
200
221
 
201
222
  // Check if the slotName is supported
@@ -233,8 +254,15 @@ class UI5Element extends HTMLElement {
233
254
 
234
255
  child = this.constructor.getMetadata().constructor.validateSlotValue(child, slotData);
235
256
 
236
- if (child.isUI5Element && slotData.listenFor) {
237
- this._attachChildPropertyUpdated(child, slotData.listenFor);
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));
261
+ }
262
+
263
+ // Listen for the slotchange event if the child is a slot itself
264
+ if (isSlot(child)) {
265
+ this._attachSlotChange(child, slotName);
238
266
  }
239
267
 
240
268
  const propertyName = slotData.propertyName || slotName;
@@ -250,10 +278,33 @@ class UI5Element extends HTMLElement {
250
278
 
251
279
  // Distribute the child in the _state object, keeping the Light DOM order,
252
280
  // not the order elements are defined.
253
- slottedChildrenMap.forEach((children, slot) => {
254
- 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);
255
283
  });
256
- 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
+ }
257
308
  }
258
309
 
259
310
  /**
@@ -262,20 +313,62 @@ class UI5Element extends HTMLElement {
262
313
  */
263
314
  _clearSlot(slotName, slotData) {
264
315
  const propertyName = slotData.propertyName || slotName;
265
-
266
- let children = this._state[propertyName];
267
- if (!Array.isArray(children)) {
268
- children = [children];
269
- }
316
+ const children = this._state[propertyName];
270
317
 
271
318
  children.forEach(child => {
272
319
  if (child && child.isUI5Element) {
273
- this._detachChildPropertyUpdated(child);
320
+ const method = (child.detachInvalidate || child._detachChange).bind(child);
321
+ method(this._getChildChangeListener(slotName));
322
+ }
323
+
324
+ if (isSlot(child)) {
325
+ this._detachSlotChange(child, slotName);
274
326
  }
275
327
  });
276
328
 
277
329
  this._state[propertyName] = [];
278
- 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
+ });
279
372
  }
280
373
 
281
374
  /**
@@ -290,9 +383,8 @@ class UI5Element extends HTMLElement {
290
383
  const propertyTypeClass = properties[nameInCamelCase].type;
291
384
  if (propertyTypeClass === Boolean) {
292
385
  newValue = newValue !== null;
293
- }
294
- if (propertyTypeClass === Integer) {
295
- newValue = parseInt(newValue);
386
+ } else if (isDescendantOf(propertyTypeClass, DataType)) {
387
+ newValue = propertyTypeClass.attributeToProperty(newValue);
296
388
  }
297
389
  this[nameInCamelCase] = newValue;
298
390
  }
@@ -305,22 +397,24 @@ class UI5Element extends HTMLElement {
305
397
  if (!this.constructor.getMetadata().hasAttribute(name)) {
306
398
  return;
307
399
  }
308
-
309
- if (typeof newValue === "object") {
310
- return;
311
- }
312
-
400
+ const properties = this.constructor.getMetadata().getProperties();
401
+ const propertyTypeClass = properties[name].type;
313
402
  const attrName = camelToKebabCase(name);
314
403
  const attrValue = this.getAttribute(attrName);
315
- if (typeof newValue === "boolean") {
404
+
405
+ if (propertyTypeClass === Boolean) {
316
406
  if (newValue === true && attrValue === null) {
317
407
  this.setAttribute(attrName, "");
318
408
  } else if (newValue === false && attrValue !== null) {
319
409
  this.removeAttribute(attrName);
320
410
  }
321
- } else if (attrValue !== newValue) {
322
- this.setAttribute(attrName, newValue);
323
- }
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
324
418
  }
325
419
 
326
420
  /**
@@ -346,101 +440,96 @@ class UI5Element extends HTMLElement {
346
440
  * @private
347
441
  */
348
442
  _initializeState() {
349
- const defaultState = this.constructor._getDefaultState();
350
- this._state = Object.assign({}, defaultState);
443
+ this._state = { ...this.constructor.getMetadata().getInitialState() };
351
444
  }
352
445
 
353
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}
354
451
  * @private
355
452
  */
356
- _attachChildPropertyUpdated(child, listenFor) {
357
- const childMetadata = child.constructor.getMetadata(),
358
- slotName = this.constructor._getSlotName(child), // all slotted children have the same configuration
359
- childProperties = childMetadata.getProperties();
360
-
361
- let observedProps = [],
362
- notObservedProps = [];
363
-
364
- if (Array.isArray(listenFor)) {
365
- observedProps = listenFor;
366
- } else {
367
- observedProps = Array.isArray(listenFor.props) ? listenFor.props : Object.keys(childProperties);
368
- notObservedProps = Array.isArray(listenFor.exclude) ? listenFor.exclude : [];
369
- }
370
-
371
- if (!this._monitoredChildProps.has(slotName)) {
372
- this._monitoredChildProps.set(slotName, { observedProps, notObservedProps });
453
+ _getChildChangeListener(slotName) {
454
+ if (!this._childChangeListeners.has(slotName)) {
455
+ this._childChangeListeners.set(slotName, this._onChildChange.bind(this, slotName));
373
456
  }
374
-
375
- child.addEventListener("_propertyChange", this._invalidateParentOnPropertyUpdate);
376
- child._firePropertyChange = true;
457
+ return this._childChangeListeners.get(slotName);
377
458
  }
378
459
 
379
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}
380
465
  * @private
381
466
  */
382
- _detachChildPropertyUpdated(child) {
383
- child.removeEventListener("_propertyChange", this._invalidateParentOnPropertyUpdate);
384
- child._firePropertyChange = false;
467
+ _getSlotChangeListener(slotName) {
468
+ if (!this._slotChangeListeners.has(slotName)) {
469
+ this._slotChangeListeners.set(slotName, this._onSlotChange.bind(this, slotName));
470
+ }
471
+ return this._slotChangeListeners.get(slotName);
385
472
  }
386
473
 
387
474
  /**
388
475
  * @private
389
476
  */
390
- _propertyChange(name, value) {
391
- this._updateAttribute(name, value);
392
-
393
- if (this._firePropertyChange) {
394
- this.dispatchEvent(new CustomEvent("_propertyChange", {
395
- detail: { name, newValue: value },
396
- composed: false,
397
- bubbles: true,
398
- }));
399
- }
477
+ _attachSlotChange(child, slotName) {
478
+ child.addEventListener("slotchange", this._getSlotChangeListener(slotName));
400
479
  }
401
480
 
402
481
  /**
403
482
  * @private
404
483
  */
405
- _invalidateParentOnPropertyUpdate(prop) {
406
- // The web component to be invalidated
407
- const parentNode = this.parentNode;
408
- if (!parentNode) {
409
- return;
410
- }
411
-
412
- const slotName = parentNode.constructor._getSlotName(this);
413
- const propsMetadata = parentNode._monitoredChildProps.get(slotName);
414
-
415
- if (!propsMetadata) {
416
- return;
417
- }
418
- const { observedProps, notObservedProps } = propsMetadata;
419
-
420
- if (observedProps.includes(prop.detail.name) && !notObservedProps.includes(prop.detail.name)) {
421
- parentNode._invalidate("_parent_", this);
422
- }
484
+ _detachSlotChange(child, slotName) {
485
+ child.removeEventListener("slotchange", this._getSlotChangeListener(slotName));
423
486
  }
424
487
 
425
488
  /**
426
- * Asynchronously re-renders an already rendered web component
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
427
492
  * @private
428
493
  */
429
- _invalidate() {
430
- if (!this._upToDate) {
431
- // console.log("already invalidated", this, ...arguments);
432
- return;
433
- }
434
-
435
- if (this.getDomRef() && !this._suppressInvalidation) {
436
- this._upToDate = false;
437
- // console.log("INVAL", this, ...arguments);
438
- RenderScheduler.renderDeferred(this);
439
- }
494
+ _onSlotChange(slotName) {
495
+ _invalidate.call(this, {
496
+ type: "slot",
497
+ name: slotName,
498
+ reason: "slotchange",
499
+ });
440
500
  }
441
501
 
442
502
  /**
443
- * Do not call this method directly, only intended to be called by RenderScheduler.js
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
528
+ */
529
+ onInvalidation(changeInfo) {}
530
+
531
+ /**
532
+ * Do not call this method directly, only intended to be called by js
444
533
  * @protected
445
534
  */
446
535
  _render() {
@@ -459,15 +548,37 @@ class UI5Element extends HTMLElement {
459
548
  }
460
549
 
461
550
  // resume normal invalidation handling
462
- delete this._suppressInvalidation;
551
+ this._suppressInvalidation = false;
463
552
 
464
553
  // Update the shadow root with the render result
465
- // console.log(this.getDomRef() ? "RE-RENDER" : "FIRST RENDER", this);
466
- this._upToDate = true;
467
- 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
+ }
569
+
570
+ return res;
571
+ }));
572
+ }
573
+ */
574
+ this._changedState = [];
468
575
 
469
- if (this._shouldUpdateFragment()) {
470
- this.staticAreaItem._updateFragment(this);
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();
471
582
  }
472
583
 
473
584
  // Safari requires that children get the slot attribute only after the slot tags have been rendered in the shadow DOM
@@ -481,23 +592,6 @@ class UI5Element extends HTMLElement {
481
592
  }
482
593
  }
483
594
 
484
- /**
485
- * @private
486
- */
487
- _updateShadowRoot() {
488
- if (!this.constructor._needsShadowDOM()) {
489
- return;
490
- }
491
-
492
- let styleToPrepend;
493
- const renderResult = this.constructor.template(this);
494
-
495
- if (!document.adoptedStyleSheets && !window.ShadyDOM) {
496
- styleToPrepend = getEffectiveStyle(this.constructor);
497
- }
498
- this.constructor.render(renderResult, this.shadowRoot, styleToPrepend, { eventContext: this });
499
- }
500
-
501
595
  /**
502
596
  * @private
503
597
  */
@@ -520,16 +614,27 @@ class UI5Element extends HTMLElement {
520
614
 
521
615
  /**
522
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
523
618
  * Use this method instead of "this.shadowRoot" to read the Shadow DOM, if ever necessary
619
+ *
524
620
  * @public
525
621
  */
526
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
+
527
628
  if (!this.shadowRoot || this.shadowRoot.children.length === 0) {
528
629
  return;
529
630
  }
530
631
 
531
- return this.shadowRoot.children.length === 1
532
- ? 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];
533
638
  }
534
639
 
535
640
  /**
@@ -545,6 +650,16 @@ class UI5Element extends HTMLElement {
545
650
  }
546
651
  }
547
652
 
653
+ /**
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.
656
+ * @public
657
+ */
658
+ async getFocusDomRefAsync() {
659
+ await this._waitForDomRef();
660
+ return this.getFocusDomRef();
661
+ }
662
+
548
663
  /**
549
664
  * Set the focus to the element, returned by "getFocusDomRef()" (marked by "data-sap-focus-ref")
550
665
  * @public
@@ -565,37 +680,47 @@ class UI5Element extends HTMLElement {
565
680
  * @param name - name of the event
566
681
  * @param data - additional data for the event
567
682
  * @param cancelable - true, if the user can call preventDefault on the event object
683
+ * @param bubbles - true, if the event bubbles
568
684
  * @returns {boolean} false, if the event was cancelled (preventDefault called), true otherwise
569
685
  */
570
- fireEvent(name, data, cancelable) {
571
- let compatEventResult = true; // Initialized to true, because if the event is not fired at all, it should be considered "not-prevented"
686
+ fireEvent(name, data, cancelable = false, bubbles = true) {
687
+ const eventResult = this._fireEvent(name, data, cancelable, bubbles);
688
+ const camelCaseEventName = kebabToCamelCase(name);
689
+
690
+ if (camelCaseEventName !== name) {
691
+ return eventResult && this._fireEvent(camelCaseEventName, data, cancelable);
692
+ }
572
693
 
694
+ return eventResult;
695
+ }
696
+
697
+ _fireEvent(name, data, cancelable = false, bubbles = true) {
573
698
  const noConflictEvent = new CustomEvent(`ui5-${name}`, {
574
699
  detail: data,
575
700
  composed: false,
576
- bubbles: true,
701
+ bubbles,
577
702
  cancelable,
578
703
  });
579
704
 
580
- // This will be false if the compat event is prevented
581
- compatEventResult = this.dispatchEvent(noConflictEvent);
705
+ // This will be false if the no-conflict event is prevented
706
+ const noConflictEventResult = this.dispatchEvent(noConflictEvent);
582
707
 
583
708
  if (skipOriginalEvent(name)) {
584
- return compatEventResult;
709
+ return noConflictEventResult;
585
710
  }
586
711
 
587
- const customEvent = new CustomEvent(name, {
712
+ const normalEvent = new CustomEvent(name, {
588
713
  detail: data,
589
714
  composed: false,
590
- bubbles: true,
715
+ bubbles,
591
716
  cancelable,
592
717
  });
593
718
 
594
719
  // This will be false if the normal event is prevented
595
- const normalEventResult = this.dispatchEvent(customEvent);
720
+ const normalEventResult = this.dispatchEvent(normalEvent);
596
721
 
597
722
  // Return false if any of the two events was prevented (its result was false).
598
- return normalEventResult && compatEventResult;
723
+ return normalEventResult && noConflictEventResult;
599
724
  }
600
725
 
601
726
  /**
@@ -604,24 +729,19 @@ class UI5Element extends HTMLElement {
604
729
  * @public
605
730
  */
606
731
  getSlottedNodes(slotName) {
607
- const reducer = (acc, curr) => {
608
- if (curr.localName !== "slot") {
609
- return acc.concat([curr]);
610
- }
611
- return acc.concat(curr.assignedNodes({ flatten: true }).filter(item => item instanceof HTMLElement));
612
- };
613
-
614
- return this[slotName].reduce(reducer, []);
615
- }
616
-
617
- get isCompact() {
618
- return getComputedStyle(this).getPropertyValue(GLOBAL_CONTENT_DENSITY_CSS_VAR) === "compact";
732
+ return getSlottedElementsList(this[slotName]);
619
733
  }
620
734
 
621
- updateStaticAreaItemContentDensity() {
622
- if (this.staticAreaItem) {
623
- this.staticAreaItem._updateContentDensity(this.isCompact);
624
- }
735
+ /**
736
+ * Determines whether the component should be rendered in RTL mode or not.
737
+ * Returns: "rtl", "ltr" or undefined
738
+ *
739
+ * @public
740
+ * @returns {String|undefined}
741
+ */
742
+ get effectiveDir() {
743
+ markAsRtlAware(this.constructor); // if a UI5 Element calls this method, it's considered to be rtl-aware
744
+ return getEffectiveDir(this);
625
745
  }
626
746
 
627
747
  /**
@@ -641,26 +761,6 @@ class UI5Element extends HTMLElement {
641
761
  return this.getMetadata().getAttributesList();
642
762
  }
643
763
 
644
- /**
645
- * @private
646
- */
647
- static _getSlotName(child) {
648
- // Text nodes can only go to the default slot
649
- if (!(child instanceof HTMLElement)) {
650
- return "default";
651
- }
652
-
653
- // Discover the slot based on the real slot name (f.e. footer => footer, or content-32 => content)
654
- const slot = child.getAttribute("slot");
655
- if (slot) {
656
- const match = slot.match(/^(.+?)-\d+$/);
657
- return match ? match[1] : slot;
658
- }
659
-
660
- // Use default slot as a fallback
661
- return "default";
662
- }
663
-
664
764
  /**
665
765
  * @private
666
766
  */
@@ -668,70 +768,30 @@ class UI5Element extends HTMLElement {
668
768
  return !!this.template;
669
769
  }
670
770
 
671
- _shouldUpdateFragment() {
672
- return this.constructor._needsStaticArea() && this.staticAreaItem.isRendered();
673
- }
674
-
675
771
  /**
676
772
  * @private
677
773
  */
678
774
  static _needsStaticArea() {
679
- return typeof this.staticAreaTemplate === "function";
775
+ return !!this.staticAreaTemplate;
680
776
  }
681
777
 
682
778
  /**
683
779
  * @public
684
780
  */
685
781
  getStaticAreaItemDomRef() {
686
- return this.staticAreaItem.getDomRef();
687
- }
688
-
689
- /**
690
- * @private
691
- */
692
- static _getDefaultState() {
693
- if (this._defaultState) {
694
- return this._defaultState;
782
+ if (!this.constructor._needsStaticArea()) {
783
+ throw new Error("This component does not use the static area");
695
784
  }
696
785
 
697
- const MetadataClass = this.getMetadata();
698
- const defaultState = {};
699
- const slotsAreManaged = MetadataClass.slotsAreManaged();
700
-
701
- // Initialize properties
702
- const props = MetadataClass.getProperties();
703
- for (const propName in props) { // eslint-disable-line
704
- const propType = props[propName].type;
705
- const propDefaultValue = props[propName].defaultValue;
706
-
707
- if (propType === Boolean) {
708
- defaultState[propName] = false;
709
-
710
- if (propDefaultValue !== undefined) {
711
- console.warn("The 'defaultValue' metadata key is ignored for all booleans properties, they would be initialized with 'false' by default"); // eslint-disable-line
712
- }
713
- } else if (props[propName].multiple) {
714
- defaultState[propName] = [];
715
- } else if (propType === Object) {
716
- defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : {};
717
- } else if (propType === String) {
718
- defaultState[propName] = "defaultValue" in props[propName] ? props[propName].defaultValue : "";
719
- } else {
720
- defaultState[propName] = propDefaultValue;
721
- }
786
+ if (!this.staticAreaItem) {
787
+ this.staticAreaItem = StaticAreaItem.createInstance();
788
+ this.staticAreaItem.setOwnerElement(this);
722
789
  }
723
-
724
- // Initialize slots
725
- if (slotsAreManaged) {
726
- const slots = MetadataClass.getSlots();
727
- for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
728
- const propertyName = slotData.propertyName || slotName;
729
- defaultState[propertyName] = [];
730
- }
790
+ if (!this.staticAreaItem.parentElement) {
791
+ getSingletonElementInstance("ui5-static-area").appendChild(this.staticAreaItem);
731
792
  }
732
793
 
733
- this._defaultState = defaultState;
734
- return defaultState;
794
+ return this.staticAreaItem.getDomRef();
735
795
  }
736
796
 
737
797
  /**
@@ -745,7 +805,7 @@ class UI5Element extends HTMLElement {
745
805
  const properties = this.getMetadata().getProperties();
746
806
  for (const [prop, propData] of Object.entries(properties)) { // eslint-disable-line
747
807
  if (!isValidPropertyName(prop)) {
748
- 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 */
749
809
  }
750
810
 
751
811
  if (propData.type === Boolean && propData.defaultValue) {
@@ -783,14 +843,27 @@ class UI5Element extends HTMLElement {
783
843
  }
784
844
  },
785
845
  set(value) {
846
+ let isDifferent;
786
847
  value = this.constructor.getMetadata().constructor.validatePropertyValue(value, propData);
787
848
 
788
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
+ }
789
857
 
790
- if (oldState !== value) {
858
+ if (isDifferent) {
791
859
  this._state[prop] = value;
792
- this._invalidate(prop, value);
793
- 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);
794
867
  }
795
868
  },
796
869
  });
@@ -801,7 +874,7 @@ class UI5Element extends HTMLElement {
801
874
  const slots = this.getMetadata().getSlots();
802
875
  for (const [slotName, slotData] of Object.entries(slots)) { // eslint-disable-line
803
876
  if (!isValidPropertyName(slotName)) {
804
- 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 */
805
878
  }
806
879
 
807
880
  const propertyName = slotData.propertyName || slotName;
@@ -813,7 +886,7 @@ class UI5Element extends HTMLElement {
813
886
  return [];
814
887
  },
815
888
  set() {
816
- 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...)");
817
890
  },
818
891
  });
819
892
  }
@@ -828,6 +901,14 @@ class UI5Element extends HTMLElement {
828
901
  return metadata;
829
902
  }
830
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
+
831
912
  /**
832
913
  * Returns the CSS for this UI5 Web Component Class
833
914
  * @protected
@@ -844,6 +925,50 @@ class UI5Element extends HTMLElement {
844
925
  return "";
845
926
  }
846
927
 
928
+ /**
929
+ * Returns an array with the dependencies for this UI5 Web Component, which could be:
930
+ * - composed components (used in its shadow root or static area item)
931
+ * - slotted components that the component may need to communicate with
932
+ *
933
+ * @protected
934
+ */
935
+ static get dependencies() {
936
+ return [];
937
+ }
938
+
939
+ /**
940
+ * Returns a list of the unique dependencies for this UI5 Web Component
941
+ *
942
+ * @public
943
+ */
944
+ static getUniqueDependencies() {
945
+ if (!uniqueDependenciesCache.has(this)) {
946
+ const filtered = this.dependencies.filter((dep, index, deps) => deps.indexOf(dep) === index);
947
+ uniqueDependenciesCache.set(this, filtered);
948
+ }
949
+
950
+ return uniqueDependenciesCache.get(this);
951
+ }
952
+
953
+ /**
954
+ * Returns a promise that resolves whenever all dependencies for this UI5 Web Component have resolved
955
+ *
956
+ * @returns {Promise<any[]>}
957
+ */
958
+ static whenDependenciesDefined() {
959
+ return Promise.all(this.getUniqueDependencies().map(dep => dep.define()));
960
+ }
961
+
962
+ /**
963
+ * Hook that will be called upon custom element definition
964
+ *
965
+ * @protected
966
+ * @returns {Promise<void>}
967
+ */
968
+ static async onDefine() {
969
+ return Promise.resolve();
970
+ }
971
+
847
972
  /**
848
973
  * Registers a UI5 Web Component in the browser window object
849
974
  * @public
@@ -852,21 +977,31 @@ class UI5Element extends HTMLElement {
852
977
  static async define() {
853
978
  await boot();
854
979
 
855
- if (this.onDefine) {
856
- await this.onDefine();
857
- }
980
+ await Promise.all([
981
+ this.whenDependenciesDefined(),
982
+ this.onDefine(),
983
+ ]);
858
984
 
859
985
  const tag = this.getMetadata().getTag();
986
+ const altTag = this.getMetadata().getAltTag();
860
987
 
861
988
  const definedLocally = isTagRegistered(tag);
862
989
  const definedGlobally = customElements.get(tag);
863
990
 
864
991
  if (definedGlobally && !definedLocally) {
865
- console.warn(`Skipping definition of tag ${tag}, because it was already defined by another instance of ui5-webcomponents.`); // eslint-disable-line
992
+ recordTagRegistrationFailure(tag);
866
993
  } else if (!definedGlobally) {
867
994
  this._generateAccessors();
868
995
  registerTag(tag);
869
996
  window.customElements.define(tag, this);
997
+ preloadLinks(this);
998
+
999
+ if (altTag && !customElements.get(altTag)) {
1000
+ registerTag(altTag);
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
+ }));
1004
+ }
870
1005
  }
871
1006
  return this;
872
1007
  }