@ui5/webcomponents-base 0.0.0-e7dd012d7 → 0.0.0-e818f286e

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