@sapudex/web-components 2.48.0 → 2.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/bundle.esm.js +2 -0
  2. package/cypress/specs/AccordionSpotlight.cy.tsx +402 -0
  3. package/cypress/specs/BusyIndicator.cy.tsx +15 -0
  4. package/cypress/specs/Carousel.cy.tsx +142 -0
  5. package/cypress/specs/FilterMenu.cy.tsx +968 -10
  6. package/cypress/specs/List.cy.tsx +106 -0
  7. package/cypress/specs/ListItem.cy.tsx +291 -1
  8. package/cypress/specs/MediaPlayer.cy.tsx +32 -0
  9. package/cypress/specs/SelectBox.cy.tsx +134 -3
  10. package/cypress/support/component-imports.instrumented.ts +6 -0
  11. package/cypress/support/component-imports.src.ts +6 -0
  12. package/dist/AccordionSpotlight.d.ts +149 -0
  13. package/dist/AccordionSpotlight.js +284 -0
  14. package/dist/AccordionSpotlight.js.map +1 -0
  15. package/dist/AccordionSpotlightItem.d.ts +76 -0
  16. package/dist/AccordionSpotlightItem.js +115 -0
  17. package/dist/AccordionSpotlightItem.js.map +1 -0
  18. package/dist/AccordionSpotlightItemTemplate.d.ts +2 -0
  19. package/dist/AccordionSpotlightItemTemplate.js +5 -0
  20. package/dist/AccordionSpotlightItemTemplate.js.map +1 -0
  21. package/dist/AccordionSpotlightTemplate.d.ts +2 -0
  22. package/dist/AccordionSpotlightTemplate.js +10 -0
  23. package/dist/AccordionSpotlightTemplate.js.map +1 -0
  24. package/dist/BusyIndicator.d.ts +9 -0
  25. package/dist/BusyIndicator.js +7 -1
  26. package/dist/BusyIndicator.js.map +1 -1
  27. package/dist/Carousel.d.ts +26 -15
  28. package/dist/Carousel.js +71 -55
  29. package/dist/Carousel.js.map +1 -1
  30. package/dist/FilterMenu.d.ts +12 -2
  31. package/dist/FilterMenu.js +24 -4
  32. package/dist/FilterMenu.js.map +1 -1
  33. package/dist/FilterMenuTemplate.js +1 -1
  34. package/dist/FilterMenuTemplate.js.map +1 -1
  35. package/dist/List.d.ts +1 -0
  36. package/dist/List.js +24 -11
  37. package/dist/List.js.map +1 -1
  38. package/dist/ListGroupHeader.js +5 -1
  39. package/dist/ListGroupHeader.js.map +1 -1
  40. package/dist/ListGroupHeaderBase.d.ts +22 -0
  41. package/dist/ListGroupHeaderBase.js +44 -0
  42. package/dist/ListGroupHeaderBase.js.map +1 -1
  43. package/dist/ListItem.d.ts +4 -0
  44. package/dist/ListItem.js +32 -0
  45. package/dist/ListItem.js.map +1 -1
  46. package/dist/MediaPlayer.d.ts +2 -0
  47. package/dist/MediaPlayer.js +12 -0
  48. package/dist/MediaPlayer.js.map +1 -1
  49. package/dist/PromoTile.js +1 -1
  50. package/dist/PromoTile.js.map +1 -1
  51. package/dist/SearchableListBase.d.ts +10 -1
  52. package/dist/SearchableListBase.js +23 -7
  53. package/dist/SearchableListBase.js.map +1 -1
  54. package/dist/SearchableListBaseTemplate.js +3 -2
  55. package/dist/SearchableListBaseTemplate.js.map +1 -1
  56. package/dist/SelectBase.d.ts +14 -0
  57. package/dist/SelectBase.js +56 -3
  58. package/dist/SelectBase.js.map +1 -1
  59. package/dist/SelectBoxTemplate.js +1 -1
  60. package/dist/SelectBoxTemplate.js.map +1 -1
  61. package/dist/TabNavigation.d.ts +6 -0
  62. package/dist/TabNavigation.js +5 -0
  63. package/dist/TabNavigation.js.map +1 -1
  64. package/dist/assets/{InputSuggestions-BcK6O4Bk.js → InputSuggestions-DaB1A6jW.js} +1 -1
  65. package/dist/assets/{ListItemStandardExpandableTextTemplate-u600dmHC.js → ListItemStandardExpandableTextTemplate-Co4KQuVw.js} +1 -1
  66. package/dist/assets/{bundle-JYo3bV8v.css → bundle-CkxyEA8c.css} +1 -1
  67. package/dist/assets/{bundle.esm-C6Ie2WeI.js → bundle.esm-al3NYhoB.js} +241 -238
  68. package/dist/assets/messagebundle_ar-BUZ_gTvQ.js +1 -0
  69. package/dist/assets/messagebundle_no-BZQfeWxT.js +1 -0
  70. package/dist/css/themes/Accordion.css +1 -1
  71. package/dist/css/themes/AccordionItem.css +1 -1
  72. package/dist/css/themes/AccordionSpotlight.css +1 -0
  73. package/dist/css/themes/AccordionSpotlightItem.css +1 -0
  74. package/dist/css/themes/Badge.css +1 -1
  75. package/dist/css/themes/BusyIndicator.css +1 -0
  76. package/dist/css/themes/Button.css +1 -1
  77. package/dist/css/themes/Divider.css +1 -1
  78. package/dist/css/themes/Dropdown.css +1 -1
  79. package/dist/css/themes/FilterMenu.css +1 -1
  80. package/dist/css/themes/Footer.css +1 -1
  81. package/dist/css/themes/ImageGridTableItem.css +1 -1
  82. package/dist/css/themes/MastheadShell.css +1 -1
  83. package/dist/css/themes/MediaPlayer.css +1 -1
  84. package/dist/css/themes/PhoneNumberField.css +1 -1
  85. package/dist/css/themes/ProgressBar.css +1 -1
  86. package/dist/css/themes/PromoTile.css +1 -1
  87. package/dist/css/themes/Review.css +1 -1
  88. package/dist/css/themes/ReviewItem.css +1 -1
  89. package/dist/css/themes/Search.css +1 -1
  90. package/dist/css/themes/SelectBase.css +1 -1
  91. package/dist/css/themes/SelectBox.css +1 -1
  92. package/dist/css/themes/SideNavigation.css +1 -1
  93. package/dist/css/themes/SideNavigationGroup.css +1 -1
  94. package/dist/css/themes/SocialMediaButton.css +1 -1
  95. package/dist/css/themes/StepInput.css +1 -1
  96. package/dist/css/themes/Switch.css +1 -1
  97. package/dist/css/themes/TabNavigation.css +1 -1
  98. package/dist/css/themes/Tag.css +1 -1
  99. package/dist/css/themes/TagGroup.css +1 -1
  100. package/dist/css/themes/TextArea.css +1 -1
  101. package/dist/css/themes/TextField.css +1 -1
  102. package/dist/css/themes/Toast.css +1 -1
  103. package/dist/custom-elements-internal.json +426 -35
  104. package/dist/custom-elements.json +384 -49
  105. package/dist/generated/assets/i18n/messagebundle_ar.json +1 -1
  106. package/dist/generated/assets/i18n/messagebundle_no.json +1 -1
  107. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js +24 -21
  108. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js.map +1 -1
  109. package/dist/generated/templates/ListItemTemplate.lit.js +2 -2
  110. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  111. package/dist/generated/themes/Accordion.css.d.ts +1 -1
  112. package/dist/generated/themes/Accordion.css.js +1 -1
  113. package/dist/generated/themes/Accordion.css.js.map +1 -1
  114. package/dist/generated/themes/AccordionItem.css.d.ts +1 -1
  115. package/dist/generated/themes/AccordionItem.css.js +1 -1
  116. package/dist/generated/themes/AccordionItem.css.js.map +1 -1
  117. package/dist/generated/themes/AccordionSpotlight.css.d.ts +2 -0
  118. package/dist/generated/themes/AccordionSpotlight.css.js +8 -0
  119. package/dist/generated/themes/AccordionSpotlight.css.js.map +1 -0
  120. package/dist/generated/themes/AccordionSpotlightItem.css.d.ts +2 -0
  121. package/dist/generated/themes/AccordionSpotlightItem.css.js +8 -0
  122. package/dist/generated/themes/AccordionSpotlightItem.css.js.map +1 -0
  123. package/dist/generated/themes/Badge.css.d.ts +1 -1
  124. package/dist/generated/themes/Badge.css.js +1 -1
  125. package/dist/generated/themes/Badge.css.js.map +1 -1
  126. package/dist/generated/themes/BusyIndicator.css.d.ts +2 -0
  127. package/dist/generated/themes/BusyIndicator.css.js +8 -0
  128. package/dist/generated/themes/BusyIndicator.css.js.map +1 -0
  129. package/dist/generated/themes/Button.css.d.ts +1 -1
  130. package/dist/generated/themes/Button.css.js +1 -1
  131. package/dist/generated/themes/Button.css.js.map +1 -1
  132. package/dist/generated/themes/Divider.css.d.ts +1 -1
  133. package/dist/generated/themes/Divider.css.js +1 -1
  134. package/dist/generated/themes/Divider.css.js.map +1 -1
  135. package/dist/generated/themes/Dropdown.css.d.ts +1 -1
  136. package/dist/generated/themes/Dropdown.css.js +1 -1
  137. package/dist/generated/themes/Dropdown.css.js.map +1 -1
  138. package/dist/generated/themes/FilterMenu.css.d.ts +1 -1
  139. package/dist/generated/themes/FilterMenu.css.js +1 -1
  140. package/dist/generated/themes/FilterMenu.css.js.map +1 -1
  141. package/dist/generated/themes/Footer.css.d.ts +1 -1
  142. package/dist/generated/themes/Footer.css.js +1 -1
  143. package/dist/generated/themes/Footer.css.js.map +1 -1
  144. package/dist/generated/themes/ImageGridTableItem.css.d.ts +1 -1
  145. package/dist/generated/themes/ImageGridTableItem.css.js +1 -1
  146. package/dist/generated/themes/ImageGridTableItem.css.js.map +1 -1
  147. package/dist/generated/themes/MastheadShell.css.d.ts +1 -1
  148. package/dist/generated/themes/MastheadShell.css.js +1 -1
  149. package/dist/generated/themes/MastheadShell.css.js.map +1 -1
  150. package/dist/generated/themes/MediaPlayer.css.d.ts +1 -1
  151. package/dist/generated/themes/MediaPlayer.css.js +1 -1
  152. package/dist/generated/themes/MediaPlayer.css.js.map +1 -1
  153. package/dist/generated/themes/PhoneNumberField.css.d.ts +1 -1
  154. package/dist/generated/themes/PhoneNumberField.css.js +1 -1
  155. package/dist/generated/themes/PhoneNumberField.css.js.map +1 -1
  156. package/dist/generated/themes/ProgressBar.css.d.ts +1 -1
  157. package/dist/generated/themes/ProgressBar.css.js +1 -1
  158. package/dist/generated/themes/ProgressBar.css.js.map +1 -1
  159. package/dist/generated/themes/PromoTile.css.d.ts +1 -1
  160. package/dist/generated/themes/PromoTile.css.js +1 -1
  161. package/dist/generated/themes/PromoTile.css.js.map +1 -1
  162. package/dist/generated/themes/Review.css.d.ts +1 -1
  163. package/dist/generated/themes/Review.css.js +1 -1
  164. package/dist/generated/themes/Review.css.js.map +1 -1
  165. package/dist/generated/themes/ReviewItem.css.d.ts +1 -1
  166. package/dist/generated/themes/ReviewItem.css.js +1 -1
  167. package/dist/generated/themes/ReviewItem.css.js.map +1 -1
  168. package/dist/generated/themes/Search.css.d.ts +1 -1
  169. package/dist/generated/themes/Search.css.js +1 -1
  170. package/dist/generated/themes/Search.css.js.map +1 -1
  171. package/dist/generated/themes/SelectBase.css.d.ts +1 -1
  172. package/dist/generated/themes/SelectBase.css.js +1 -1
  173. package/dist/generated/themes/SelectBase.css.js.map +1 -1
  174. package/dist/generated/themes/SelectBox.css.d.ts +1 -1
  175. package/dist/generated/themes/SelectBox.css.js +1 -1
  176. package/dist/generated/themes/SelectBox.css.js.map +1 -1
  177. package/dist/generated/themes/SideNavigation.css.d.ts +1 -1
  178. package/dist/generated/themes/SideNavigation.css.js +1 -1
  179. package/dist/generated/themes/SideNavigation.css.js.map +1 -1
  180. package/dist/generated/themes/SideNavigationGroup.css.d.ts +1 -1
  181. package/dist/generated/themes/SideNavigationGroup.css.js +1 -1
  182. package/dist/generated/themes/SideNavigationGroup.css.js.map +1 -1
  183. package/dist/generated/themes/SocialMediaButton.css.d.ts +1 -1
  184. package/dist/generated/themes/SocialMediaButton.css.js +1 -1
  185. package/dist/generated/themes/SocialMediaButton.css.js.map +1 -1
  186. package/dist/generated/themes/StepInput.css.d.ts +1 -1
  187. package/dist/generated/themes/StepInput.css.js +1 -1
  188. package/dist/generated/themes/StepInput.css.js.map +1 -1
  189. package/dist/generated/themes/Switch.css.d.ts +1 -1
  190. package/dist/generated/themes/Switch.css.js +1 -1
  191. package/dist/generated/themes/Switch.css.js.map +1 -1
  192. package/dist/generated/themes/TabNavigation.css.d.ts +1 -1
  193. package/dist/generated/themes/TabNavigation.css.js +1 -1
  194. package/dist/generated/themes/TabNavigation.css.js.map +1 -1
  195. package/dist/generated/themes/Tag.css.d.ts +1 -1
  196. package/dist/generated/themes/Tag.css.js +1 -1
  197. package/dist/generated/themes/Tag.css.js.map +1 -1
  198. package/dist/generated/themes/TagGroup.css.d.ts +1 -1
  199. package/dist/generated/themes/TagGroup.css.js +1 -1
  200. package/dist/generated/themes/TagGroup.css.js.map +1 -1
  201. package/dist/generated/themes/TextArea.css.d.ts +1 -1
  202. package/dist/generated/themes/TextArea.css.js +1 -1
  203. package/dist/generated/themes/TextArea.css.js.map +1 -1
  204. package/dist/generated/themes/TextField.css.d.ts +1 -1
  205. package/dist/generated/themes/TextField.css.js +1 -1
  206. package/dist/generated/themes/TextField.css.js.map +1 -1
  207. package/dist/generated/themes/Toast.css.d.ts +1 -1
  208. package/dist/generated/themes/Toast.css.js +1 -1
  209. package/dist/generated/themes/Toast.css.js.map +1 -1
  210. package/dist/messagebundle_ar.properties +99 -0
  211. package/dist/messagebundle_no.properties +126 -1
  212. package/dist/vscode.html-custom-data.json +76 -11
  213. package/dist/web-types.json +194 -26
  214. package/package.json +1 -1
  215. package/src/AccordionSpotlight.ts +321 -0
  216. package/src/AccordionSpotlightItem.ts +117 -0
  217. package/src/AccordionSpotlightItemTemplate.tsx +7 -0
  218. package/src/AccordionSpotlightTemplate.tsx +107 -0
  219. package/src/BusyIndicator.ts +17 -2
  220. package/src/Carousel.ts +118 -80
  221. package/src/FilterMenu.ts +25 -4
  222. package/src/FilterMenuTemplate.tsx +1 -1
  223. package/src/List.ts +23 -11
  224. package/src/ListGroupHeader.hbs +11 -10
  225. package/src/ListGroupHeader.ts +5 -1
  226. package/src/ListGroupHeaderBase.ts +48 -0
  227. package/src/ListItem.hbs +5 -1
  228. package/src/ListItem.ts +36 -0
  229. package/src/MediaPlayer.ts +11 -0
  230. package/src/PromoTile.ts +1 -1
  231. package/src/SearchableListBase.ts +24 -7
  232. package/src/SearchableListBaseTemplate.tsx +5 -2
  233. package/src/SelectBase.ts +58 -3
  234. package/src/SelectBoxTemplate.tsx +1 -0
  235. package/src/TabNavigation.ts +6 -0
  236. package/src/generated/templates/ListGroupHeaderTemplate.lit.ts +24 -21
  237. package/src/generated/templates/ListItemTemplate.lit.ts +2 -2
  238. package/src/generated/themes/Accordion.css.ts +1 -1
  239. package/src/generated/themes/AccordionItem.css.ts +1 -1
  240. package/src/generated/themes/AccordionSpotlight.css.ts +9 -0
  241. package/src/generated/themes/AccordionSpotlightItem.css.ts +9 -0
  242. package/src/generated/themes/Badge.css.ts +1 -1
  243. package/src/generated/themes/BusyIndicator.css.ts +9 -0
  244. package/src/generated/themes/Button.css.ts +1 -1
  245. package/src/generated/themes/Divider.css.ts +1 -1
  246. package/src/generated/themes/Dropdown.css.ts +1 -1
  247. package/src/generated/themes/FilterMenu.css.ts +1 -1
  248. package/src/generated/themes/Footer.css.ts +1 -1
  249. package/src/generated/themes/ImageGridTableItem.css.ts +1 -1
  250. package/src/generated/themes/MastheadShell.css.ts +1 -1
  251. package/src/generated/themes/MediaPlayer.css.ts +1 -1
  252. package/src/generated/themes/PhoneNumberField.css.ts +1 -1
  253. package/src/generated/themes/ProgressBar.css.ts +1 -1
  254. package/src/generated/themes/PromoTile.css.ts +1 -1
  255. package/src/generated/themes/Review.css.ts +1 -1
  256. package/src/generated/themes/ReviewItem.css.ts +1 -1
  257. package/src/generated/themes/Search.css.ts +1 -1
  258. package/src/generated/themes/SelectBase.css.ts +1 -1
  259. package/src/generated/themes/SelectBox.css.ts +1 -1
  260. package/src/generated/themes/SideNavigation.css.ts +1 -1
  261. package/src/generated/themes/SideNavigationGroup.css.ts +1 -1
  262. package/src/generated/themes/SocialMediaButton.css.ts +1 -1
  263. package/src/generated/themes/StepInput.css.ts +1 -1
  264. package/src/generated/themes/Switch.css.ts +1 -1
  265. package/src/generated/themes/TabNavigation.css.ts +1 -1
  266. package/src/generated/themes/Tag.css.ts +1 -1
  267. package/src/generated/themes/TagGroup.css.ts +1 -1
  268. package/src/generated/themes/TextArea.css.ts +1 -1
  269. package/src/generated/themes/TextField.css.ts +1 -1
  270. package/src/generated/themes/Toast.css.ts +1 -1
  271. package/src/i18n/messagebundle_ar.properties +99 -0
  272. package/src/i18n/messagebundle_no.properties +126 -1
  273. package/src/themes/Accordion.css +1 -1
  274. package/src/themes/AccordionItem.css +4 -4
  275. package/src/themes/AccordionSpotlight.css +264 -0
  276. package/src/themes/AccordionSpotlightItem.css +3 -0
  277. package/src/themes/Badge.css +7 -7
  278. package/src/themes/BusyIndicator.css +24 -0
  279. package/src/themes/Button.css +57 -57
  280. package/src/themes/Divider.css +1 -1
  281. package/src/themes/Dropdown.css +1 -1
  282. package/src/themes/FilterMenu.css +2 -2
  283. package/src/themes/Footer.css +1 -1
  284. package/src/themes/ImageGridTableItem.css +1 -1
  285. package/src/themes/MastheadShell.css +2 -2
  286. package/src/themes/MediaPlayer.css +5 -34
  287. package/src/themes/PhoneNumberField.css +6 -6
  288. package/src/themes/ProgressBar.css +4 -0
  289. package/src/themes/PromoTile.css +4 -4
  290. package/src/themes/Review.css +2 -2
  291. package/src/themes/ReviewItem.css +2 -2
  292. package/src/themes/Search.css +5 -5
  293. package/src/themes/SelectBase.css +14 -14
  294. package/src/themes/SelectBox.css +1 -1
  295. package/src/themes/SideNavigation.css +1 -1
  296. package/src/themes/SideNavigationGroup.css +1 -1
  297. package/src/themes/SocialMediaButton.css +2 -2
  298. package/src/themes/StepInput.css +8 -8
  299. package/src/themes/Switch.css +9 -9
  300. package/src/themes/TabNavigation.css +1 -1
  301. package/src/themes/Tag.css +4 -4
  302. package/src/themes/TagGroup.css +1 -0
  303. package/src/themes/TextArea.css +4 -4
  304. package/src/themes/TextField.css +13 -13
  305. package/src/themes/Toast.css +1 -1
  306. package/dist/assets/messagebundle_ar-BNhVFt1-.js +0 -1
  307. package/dist/assets/messagebundle_no-BIUfmTWJ.js +0 -1
package/src/Carousel.ts CHANGED
@@ -7,6 +7,8 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
7
7
  import jsxRenderer from "@ui5/webcomponents-base/renderer/JsxRenderer.js";
8
8
  import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
9
9
  import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
10
+ import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
11
+ import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
10
12
  import {
11
13
  isUp, isDown, isRight, isLeft,
12
14
  } from "@ui5/webcomponents-base/dist/Keys.js";
@@ -194,6 +196,15 @@ class UDExCarousel extends UI5Element {
194
196
  @property({ type: String })
195
197
  accessibleName: string = "";
196
198
 
199
+ /**
200
+ * Tracks whether the inner carousel has multiple pages (updated after render).
201
+ *
202
+ * @default false
203
+ * @private
204
+ */
205
+ @property({ type: Boolean, noAttribute: true })
206
+ _hasManyPages = false;
207
+
197
208
  @i18n("@sapudex/web-components")
198
209
  static i18nBundle: I18nBundle;
199
210
 
@@ -201,88 +212,66 @@ class UDExCarousel extends UI5Element {
201
212
  _autoplayLastToggle = Date.now();
202
213
  _autoplayMinTimeBetweenToggle = 250;
203
214
  _autoplayManualStop = false;
204
- _autoplayLoop = () => {
205
- const items = this._carousel?.items;
206
- if (!items) { return; }
207
-
208
- const currentSlide = this._carousel._currentPageIndex;
209
- const itemsPerPage = this._carousel.effectiveItemsPerPage;
210
- const isLastSlide = currentSlide === (items.length - itemsPerPage);
211
-
212
- if (isLastSlide && !this.cyclic) {
213
- this._stopAutoplay();
214
- this._autoplayManualStop = true;
215
-
216
- return;
217
- }
218
-
219
- const nextSlide = isLastSlide ? 0 : (currentSlide + itemsPerPage);
220
-
221
- this._carousel?.navigateTo(nextSlide);
222
- this._calculateDisabledButtonsState();
223
- };
215
+ _resizeHandler: ResizeObserverCallback;
224
216
 
225
- get nextPageText(): string {
226
- return translate(UDExCarousel, CAROUSEL_NEXT_ARROW_TEXT);
217
+ constructor() {
218
+ super();
219
+ this._resizeHandler = this._onResize.bind(this);
227
220
  }
228
221
 
229
- get previousPageText(): string {
230
- return translate(UDExCarousel, CAROUSEL_PREVIOUS_ARROW_TEXT);
231
- }
222
+ onAfterRendering() {
223
+ redirectAttributes(this, "carousel");
232
224
 
233
- get carouselClass(): string {
234
- const classes = "udex-carousel";
225
+ this._calculateDisabledButtonsState();
235
226
 
236
- return `${classes} udex-carousel__navigation--${this.navigationPlacement}`;
227
+ if (this._autoplayInterval === null) {
228
+ this._startAutoplay();
229
+ }
237
230
  }
238
231
 
239
- get navigationWrapperClass(): string {
240
- let _class = "udex-carousel__navigation-wrapper";
232
+ onExitDOM(): void {
233
+ ResizeHandler.deregister(this, this._resizeHandler);
241
234
 
242
- if (this.autoplay) {
243
- _class += " udex-carousel__navigation-wrapper--autoplay";
235
+ if (this._autoplayInterval) {
236
+ clearInterval(this._autoplayInterval);
244
237
  }
238
+ }
245
239
 
246
- return _class;
240
+ onEnterDOM(): void {
241
+ ResizeHandler.register(this, this._resizeHandler);
247
242
  }
248
243
 
249
- get navigationButtonWrapperClass(): string {
250
- let _class = "udex-carousel__navigation-button-wrapper";
244
+ _onResize(): void {
245
+ this._hasManyPages = this._carousel?.hasManyPages ?? false;
246
+ }
251
247
 
252
- if (this._carousel?.isPageTypeDots) {
253
- _class += " udex-carousel__navigation-button-wrapper--dot-indicators";
248
+ _autoplayLoop = () => {
249
+ const items = this._carousel?.items;
250
+ if (!items) {
251
+ return;
254
252
  }
255
253
 
256
- return _class;
257
- }
258
-
259
- get buttonDesign(): UDExCarouselButtonDesign {
260
- return this.navigationPlacement === UDExCarouselNavigationPosition.top
261
- ? UDExCarouselButtonDesign.Secondary
262
- : UDExCarouselButtonDesign.Tertiary;
263
- }
254
+ const currentSlide = this._carousel._currentPageIndex;
255
+ const itemsPerPage = this._carousel.effectiveItemsPerPage;
256
+ const isLastSlide = currentSlide === (items.length - itemsPerPage);
264
257
 
265
- get showNavigationButtons(): { top: boolean; bottom: boolean } {
266
- return {
267
- top: this.navigationPlacement === UDExCarouselNavigationPosition.top,
268
- bottom: this.navigationPlacement === UDExCarouselNavigationPosition.bottom,
269
- };
270
- }
258
+ if (isLastSlide && !this.cyclic) {
259
+ this._stopAutoplay();
260
+ this._autoplayManualStop = true;
271
261
 
272
- get _hidePageIndicator(): boolean {
273
- return this.navigationPlacement === UDExCarouselNavigationPosition.top;
274
- }
262
+ return;
263
+ }
275
264
 
276
- get _carousel(): UI5Carousel | null {
277
- return this.shadowRoot!.querySelector<UI5Carousel>("[ui5-carousel]");
278
- }
265
+ const nextSlide = isLastSlide ? 0 : (currentSlide + itemsPerPage);
279
266
 
280
- get _ui5CarouselRoot(): HTMLElement | null | undefined {
281
- return this._carousel?.shadowRoot?.querySelector(".ui5-carousel-root");
282
- }
267
+ this._carousel?.navigateTo(nextSlide);
268
+ this._calculateDisabledButtonsState();
269
+ };
283
270
 
284
271
  _startAutoplay(): void {
285
- if (!this.autoplay) { return; }
272
+ if (!this.autoplay) {
273
+ return;
274
+ }
286
275
 
287
276
  if (!this._carousel?.items) {
288
277
  console.warn("[UDExCarousel] Cannot start autoplay on an empty carousel");
@@ -305,7 +294,9 @@ class UDExCarousel extends UI5Element {
305
294
  }
306
295
 
307
296
  _stopAutoplay(): void {
308
- if (!this._autoplayInterval) { return; }
297
+ if (!this._autoplayInterval) {
298
+ return;
299
+ }
309
300
 
310
301
  clearInterval(this._autoplayInterval);
311
302
  this._autoplayIsPaused = true;
@@ -326,22 +317,14 @@ class UDExCarousel extends UI5Element {
326
317
  });
327
318
  }
328
319
 
329
- get _isBottomNavigation(): boolean {
330
- return this.navigationPlacement === UDExCarouselNavigationPosition.bottom;
331
- }
332
-
333
- get autoplayToggleButtonTooltipText(): string {
334
- return this._autoplayIsPaused
335
- ? translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_START)
336
- : translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_STOP);
337
- }
338
-
339
320
  _onFocusIn(): void {
340
321
  this._stopAutoplay();
341
322
  }
342
323
 
343
324
  _onFocusOut(): void {
344
- if (!this._autoplayManualStop) { this._startAutoplay(); }
325
+ if (!this._autoplayManualStop) {
326
+ this._startAutoplay();
327
+ }
345
328
  }
346
329
 
347
330
  _onMouseEnter(): void {
@@ -349,7 +332,9 @@ class UDExCarousel extends UI5Element {
349
332
  }
350
333
 
351
334
  _onMouseLeave(): void {
352
- if (!this._autoplayManualStop) { this._startAutoplay(); }
335
+ if (!this._autoplayManualStop) {
336
+ this._startAutoplay();
337
+ }
353
338
  }
354
339
 
355
340
  _calculateDisabledButtonsState() {
@@ -387,16 +372,69 @@ class UDExCarousel extends UI5Element {
387
372
  this.fireDecoratorEvent("navigate", e.detail);
388
373
  }
389
374
 
390
- onAfterRendering() {
391
- redirectAttributes(this, "carousel");
375
+ get nextPageText(): string {
376
+ return translate(UDExCarousel, CAROUSEL_NEXT_ARROW_TEXT);
377
+ }
392
378
 
393
- this._calculateDisabledButtonsState();
379
+ get previousPageText(): string {
380
+ return translate(UDExCarousel, CAROUSEL_PREVIOUS_ARROW_TEXT);
381
+ }
382
+
383
+ get carouselClass(): string {
384
+ const classes = "udex-carousel";
394
385
 
395
- if (this._autoplayInterval === null) { this._startAutoplay(); }
386
+ return `${classes} udex-carousel__navigation--${this.navigationPlacement}`;
396
387
  }
397
388
 
398
- onExitDOM(): void {
399
- if (this._autoplayInterval) { clearInterval(this._autoplayInterval); }
389
+ get navigationWrapperClass(): string {
390
+ let _class = "udex-carousel__navigation-wrapper";
391
+
392
+ if (this.autoplay) {
393
+ _class += " udex-carousel__navigation-wrapper--autoplay";
394
+ }
395
+
396
+ return _class;
397
+ }
398
+
399
+ get navigationButtonWrapperClass(): string {
400
+ let _class = "udex-carousel__navigation-button-wrapper";
401
+
402
+ if (this._carousel?.isPageTypeDots) {
403
+ _class += " udex-carousel__navigation-button-wrapper--dot-indicators";
404
+ }
405
+
406
+ return _class;
407
+ }
408
+
409
+ get buttonDesign(): UDExCarouselButtonDesign {
410
+ return this.navigationPlacement === UDExCarouselNavigationPosition.top
411
+ ? UDExCarouselButtonDesign.Secondary
412
+ : UDExCarouselButtonDesign.Tertiary;
413
+ }
414
+
415
+ get showNavigationButtons(): { top: boolean; bottom: boolean } {
416
+ return {
417
+ top: this.navigationPlacement === UDExCarouselNavigationPosition.top && this._hasManyPages,
418
+ bottom: this.navigationPlacement === UDExCarouselNavigationPosition.bottom && this._hasManyPages,
419
+ };
420
+ }
421
+
422
+ get _hidePageIndicator(): boolean {
423
+ return this.navigationPlacement === UDExCarouselNavigationPosition.top;
424
+ }
425
+
426
+ get _carousel(): UI5Carousel | null {
427
+ return this.shadowRoot!.querySelector<UI5Carousel>("[ui5-carousel]");
428
+ }
429
+
430
+ get _isBottomNavigation(): boolean {
431
+ return this.navigationPlacement === UDExCarouselNavigationPosition.bottom;
432
+ }
433
+
434
+ get autoplayToggleButtonTooltipText(): string {
435
+ return this._autoplayIsPaused
436
+ ? translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_START)
437
+ : translate(UDExCarousel, CAROUSEL_AUTOPLAY_TOGGLE_STOP);
400
438
  }
401
439
  }
402
440
 
package/src/FilterMenu.ts CHANGED
@@ -296,19 +296,19 @@ class UDExFilterMenu extends UDExSearchableListBase {
296
296
  accessibleNameRef!: string;
297
297
  /**
298
298
  * Allows setting a custom role.
299
- * @default "None"
299
+ * @default "Dialog"
300
300
  * @public
301
301
  */
302
302
  @property()
303
- accessibleRole: `${PopupAccessibleRole}` = PopupAccessibleRole.None;
303
+ accessibleRole: `${PopupAccessibleRole}` = PopupAccessibleRole.Dialog;
304
304
 
305
305
  /**
306
306
  * Allows setting a custom role for dropdown trigger.
307
- * @default "combobox"
307
+ * @default "button"
308
308
  * @public
309
309
  */
310
310
  @property({ type: String })
311
- triggerAccessibleRole: string = "combobox";
311
+ triggerAccessibleRole: string = "button";
312
312
 
313
313
  /**
314
314
  * Determines whether the filter should be applied.
@@ -591,6 +591,27 @@ class UDExFilterMenu extends UDExSearchableListBase {
591
591
  return translate(UDExFilterMenu, SELECT_OPTIONS);
592
592
  }
593
593
 
594
+ /**
595
+ * FilterMenu uses the ARIA tree pattern for genuinely grouped items when:
596
+ * - MultiSelect mode (headers/items expose aria-checked), or
597
+ * - SingleSelect mode with at least one collapsible group
598
+ * (headers expose aria-expanded, items expose aria-selected).
599
+ *
600
+ * A "Select All" header is NOT a hierarchical group — it is a bulk-select
601
+ * affordance for a flat list — so it must not trigger the tree pattern.
602
+ */
603
+ override get isTreeStructure(): boolean {
604
+ const groups = this._filteredItems.filter(e => e.isGroup && !(e.item as UDExListGroupHeaderBase).isSelectAllHeader);
605
+ if (!groups.length) {
606
+ return false;
607
+ }
608
+ if (this.modeMultiSelect) {
609
+ return true;
610
+ }
611
+ return this.modeSingleSelect
612
+ && groups.some(e => (e.item as UDExListGroupHeaderBase).collapsible);
613
+ }
614
+
594
615
  get triggerButtonAccessibleName(): string {
595
616
  return this.accessibleName || translate(UDExFilterMenu, SELECT_OPTIONS);
596
617
  }
@@ -19,7 +19,7 @@ export default function FilterMenuTemplate(this: UDExFilterMenu) {
19
19
  aria-label={this.triggerButtonAccessibleName}
20
20
  aria-labelledby={this.accessibleNameRef}
21
21
  aria-expanded={this._open}
22
- aria-haspopup="true"
22
+ aria-haspopup="dialog"
23
23
  role={this.triggerAccessibleRole as AriaRole}
24
24
  aria-controls={`${this._id}_udex-filter-menu-dropdown`}
25
25
  disabled={this.disabled}
package/src/List.ts CHANGED
@@ -212,7 +212,7 @@ class UDExList extends UI5Element {
212
212
 
213
213
  handleArrowRight(e: KeyboardEvent): void {
214
214
  const target = e.target as UDExListItemBase;
215
- if ((target.tagName === "UDEX-LIST-GROUP-HEADER" || target.tagName === "UDEX-LIST-ITEM") && target.hasAttribute("collapsible")) {
215
+ if (target.isGroup && target.hasAttribute("collapsible") && target.collapsed) {
216
216
  target.collapsed = false;
217
217
  this.setCollapsedItems(false, target);
218
218
  }
@@ -220,7 +220,7 @@ class UDExList extends UI5Element {
220
220
 
221
221
  handleArrowLeft(e: KeyboardEvent): void {
222
222
  const target = e.target as UDExListItemBase;
223
- if ((target.tagName === "UDEX-LIST-GROUP-HEADER" || target.tagName === "UDEX-LIST-ITEM") && target.hasAttribute("collapsible")) {
223
+ if (target.isGroup && target.hasAttribute("collapsible") && !target.collapsed) {
224
224
  target.collapsed = true;
225
225
  this.setCollapsedItems(true, target);
226
226
  }
@@ -241,7 +241,7 @@ class UDExList extends UI5Element {
241
241
  if (this._scrollIntoView) {
242
242
  this.scrollToItem(items[lastItemIndex]);
243
243
  }
244
- items[lastItemIndex].focus();
244
+ this.focusItem(items[lastItemIndex]);
245
245
  return;
246
246
  }
247
247
 
@@ -251,7 +251,7 @@ class UDExList extends UI5Element {
251
251
  if (this._scrollIntoView) {
252
252
  this.scrollToItem(items[previousSelectIndex - 1]);
253
253
  }
254
- items[previousSelectIndex - 1].focus();
254
+ this.focusItem(items[previousSelectIndex - 1]);
255
255
  return;
256
256
  }
257
257
 
@@ -276,7 +276,7 @@ class UDExList extends UI5Element {
276
276
  if (this._scrollIntoView) {
277
277
  this.scrollToItem(items[previousSelectIndex]);
278
278
  }
279
- items[previousSelectIndex].focus();
279
+ this.focusItem(items[previousSelectIndex]);
280
280
  }, 0);
281
281
  }
282
282
 
@@ -294,7 +294,7 @@ class UDExList extends UI5Element {
294
294
  if (this._scrollIntoView) {
295
295
  this.scrollToItem(items[nextSelectIndex + 1]);
296
296
  }
297
- items[nextSelectIndex + 1].focus();
297
+ this.focusItem(items[nextSelectIndex + 1]);
298
298
  return;
299
299
  }
300
300
 
@@ -319,7 +319,7 @@ class UDExList extends UI5Element {
319
319
  if (this._scrollIntoView) {
320
320
  this.scrollToItem(items[nextSelectIndex]);
321
321
  }
322
- items[nextSelectIndex].focus();
322
+ this.focusItem(items[nextSelectIndex]);
323
323
  }, 0);
324
324
  }
325
325
 
@@ -334,7 +334,7 @@ class UDExList extends UI5Element {
334
334
  if (nonInteractiveItem) {
335
335
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
336
336
  this.setTabIndexByIndex(items, 1);
337
- items[1].focus();
337
+ this.focusItem(items[1]);
338
338
  return;
339
339
  }
340
340
 
@@ -352,7 +352,7 @@ class UDExList extends UI5Element {
352
352
 
353
353
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
354
354
  this.setTabIndexByIndex(items, 0);
355
- items[0].focus();
355
+ this.focusItem(items[0]);
356
356
  }
357
357
 
358
358
  handleEndKey(e: KeyboardEvent): void {
@@ -367,7 +367,7 @@ class UDExList extends UI5Element {
367
367
 
368
368
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
369
369
  this.setTabIndexByIndex(items, items.length - 1);
370
- items[items.length - 1].focus();
370
+ this.focusItem(items[items.length - 1]);
371
371
  }
372
372
 
373
373
  handleLetterKey(e: KeyboardEvent): void {
@@ -417,7 +417,19 @@ class UDExList extends UI5Element {
417
417
 
418
418
  focusHeader(item: UDExListItemBase): void {
419
419
  const header = item.shadowRoot?.querySelector(".udex-list__group-header-wrap") as HTMLElement;
420
- header.focus();
420
+ if (!header) { return; }
421
+ // If the wrap element is a custom element (e.g. udex-list-item used for MultiSelect
422
+ // collapsible headers), its host has no tabindex so .focus() is a no-op. Delegate
423
+ // to the inner [part=item] div which carries the actual tabindex.
424
+ const focusTarget = header.shadowRoot?.querySelector<HTMLElement>("[part=item]") ?? header;
425
+ focusTarget.focus();
426
+ }
427
+
428
+ focusItem(item: UDExListItemBase): void {
429
+ // UI5Element.focus() is async. Synchronously drill to the focusable shadow element
430
+ // so focus is set immediately (required for JAWS and reliable Cypress assertions).
431
+ const focusTarget = item.shadowRoot?.querySelector<HTMLElement>("[part=item]") ?? item;
432
+ focusTarget.focus();
421
433
  }
422
434
 
423
435
  scrollToItem(item: UDExListItemBase): void {
@@ -1,27 +1,27 @@
1
1
  <div
2
2
  part="item"
3
3
  class="udex-list__group"
4
- role="group"
5
- aria-labelledby="group-heading">
4
+ role="{{groupWrapperRole}}"
5
+ {{#unless isTreeAccessibleRole}}aria-labelledby="group-heading"{{/unless}}>
6
6
  {{#if collapsible}}
7
7
  <div
8
8
  class="udex-list__group-list"
9
9
  name="group"
10
10
  @ui5-item-press="{{getHandleGroupItemsClick}}">
11
- {{#if modeMultiSelectOrRadioGroup}}
11
+ {{#if useListItemHeader}}
12
12
  {{> multi-or-radio-group-header-collapsible}}
13
13
  {{else}}
14
14
  {{> single-group-header-collapsible}}
15
15
  {{/if}}
16
- <div id="{{ariaControlsId}}" role="region" class="udex-list__group-list__content">
17
- <div role="listbox" aria-label="group">
16
+ <div id="{{ariaControlsId}}" role="{{contentRegionRole}}" class="udex-list__group-list__content">
17
+ {{#unless isTreeAccessibleRole}}<div role="listbox" aria-label="group">{{/unless}}
18
18
  <slot></slot>
19
- </div>
19
+ {{#unless isTreeAccessibleRole}}</div>{{/unless}}
20
20
  </div>
21
21
  </div>
22
22
  {{else}}
23
23
  <div
24
- role="listbox"
24
+ role="{{nonCollapsibleContainerRole}}"
25
25
  class="udex-list__group-list"
26
26
  name="group"
27
27
  aria-label="group"
@@ -65,7 +65,8 @@
65
65
  type="{{type}}"
66
66
  label="{{label}}"
67
67
  _icon="{{_icon}}"
68
- size="{{size}}">
68
+ size="{{size}}"
69
+ accessible-role="{{nonCollapsibleHeaderAccessibleRole}}">
69
70
  </udex-list-item>
70
71
  {{/if}}
71
72
  {{/inline}}
@@ -98,7 +99,7 @@
98
99
  id="group-heading"
99
100
  aria-expanded="{{expanded}}"
100
101
  aria-controls="{{ariaControlsId}}"
101
- accessible-role="button"
102
+ accessible-role="{{collapsibleHeaderAccessibleRole}}"
102
103
  _additional-class-name="{{_additionalClassName}}"
103
104
  ?selected="{{selected}}"
104
105
  _tab-index="{{_effectiveTabIndex}}"
@@ -148,7 +149,7 @@
148
149
  {{#*inline "single-group-header-collapsible"}}
149
150
  <div class="udex-list__group-content udex-list__group-header-wrap {{sizeClass}}" part="group-item"
150
151
  id="{{this._id}}_group-content__focusable"
151
- role="button"
152
+ role="{{singleCollapsibleHeaderRole}}"
152
153
  tabindex="0"
153
154
  @keydown="{{handleKeyDown}}"
154
155
  _tab-index="{{_effectiveTabIndex}}"
@@ -78,7 +78,11 @@ class UDExListGroupHeader extends UDExListGroupHeaderBase {
78
78
  }
79
79
 
80
80
  handleKeyDown(e: KeyboardEvent) {
81
- if (isRight(e) || isLeft(e)) {
81
+ if (isLeft(e) && !this.collapsed) {
82
+ this.handleCollapseIconClick(e);
83
+ return;
84
+ }
85
+ if (isRight(e) && this.collapsed) {
82
86
  this.handleCollapseIconClick(e);
83
87
  }
84
88
  }
@@ -36,6 +36,16 @@ class UDExListGroupHeaderBase extends UDExListItemBase {
36
36
  @property({ type: String })
37
37
  _additionalClassName!: string;
38
38
 
39
+ /**
40
+ * Marks the group header as part of a tree structure. When true, the header
41
+ * renders as a treeitem and its children container as a group.
42
+ *
43
+ * @default false
44
+ * @private
45
+ */
46
+ @property({ type: Boolean })
47
+ _treeMode: boolean = false;
48
+
39
49
  /**
40
50
  * Slot for group header list items.
41
51
  *
@@ -186,6 +196,44 @@ class UDExListGroupHeaderBase extends UDExListItemBase {
186
196
  return this.nonInteractive || this.modeRadioGroup;
187
197
  }
188
198
 
199
+ /**
200
+ * Decides which collapsible-header partial to render.
201
+ * Uses the udex-list-item-based partial for MultiSelect/RadioGroup only.
202
+ * SingleSelect collapsible headers use the plain div partial — the list-item
203
+ * partial fires its own Enter/Space/click selection handlers that must not run.
204
+ */
205
+ get useListItemHeader(): boolean {
206
+ return this.modeMultiSelectOrRadioGroup;
207
+ }
208
+
209
+ get isTreeAccessibleRole(): boolean {
210
+ return this._treeMode;
211
+ }
212
+
213
+ get singleCollapsibleHeaderRole(): string {
214
+ return this._treeMode ? "treeitem" : "button";
215
+ }
216
+
217
+ get collapsibleHeaderAccessibleRole(): string {
218
+ return this._treeMode ? "treeitem" : "button";
219
+ }
220
+
221
+ get groupWrapperRole(): string {
222
+ return this._treeMode ? "none" : "group";
223
+ }
224
+
225
+ get contentRegionRole(): string {
226
+ return this._treeMode ? "group" : "region";
227
+ }
228
+
229
+ get nonCollapsibleContainerRole(): string {
230
+ return "group";
231
+ }
232
+
233
+ get nonCollapsibleHeaderAccessibleRole(): string | undefined {
234
+ return this._treeMode ? "treeitem" : undefined;
235
+ }
236
+
189
237
  get className(): string {
190
238
  const controlledClassName = this.controlled ? `${listGroupHeaderClass}_controlled` : "";
191
239
  return `${listGroupHeaderClass} ${this._additionalClassName} ${controlledClassName} ${listGroupHeaderClass}-wrap`;
package/src/ListItem.hbs CHANGED
@@ -5,6 +5,9 @@
5
5
  aria-disabled="{{_ariaDisabled}}"
6
6
  class="{{className}}"
7
7
  role="{{roleItem}}"
8
+ aria-checked="{{ariaCheckedValue}}"
9
+ aria-selected="{{ariaSelectedValue}}"
10
+ aria-expanded="{{ariaExpandedValue}}"
8
11
  @click="{{handleItemClick}}"
9
12
  @mousedown="{{handleMouseDown}}"
10
13
  @mouseup="{{handleMouseUp}}"
@@ -24,7 +27,8 @@
24
27
  ?indeterminate="{{indeterminate}}"
25
28
  accessible-name="checkbox {{stateAccessibleName}}"
26
29
  ?checked="{{selected}}"
27
- ?disabled="{{disabled}}">
30
+ ?disabled="{{disabled}}"
31
+ ?aria-hidden="{{checkboxAriaHidden}}">
28
32
  </udex-checkbox>
29
33
  {{/if}}
30
34
  {{#if modeRadioGroup}}
package/src/ListItem.ts CHANGED
@@ -369,6 +369,42 @@ class UDExListItem extends UDExListItemBase {
369
369
  return this.accessibleRole.length > 0 ? this.accessibleRole : listItemRole;
370
370
  }
371
371
 
372
+ get ariaCheckedValue(): "true" | "false" | "mixed" | undefined {
373
+ if (this.roleItem !== "treeitem") {
374
+ return undefined;
375
+ }
376
+ // Only MultiSelect treeitems expose aria-checked; SingleSelect uses aria-selected.
377
+ if (!this.modeMultiSelect) {
378
+ return undefined;
379
+ }
380
+ if (this.indeterminate) {
381
+ return "mixed";
382
+ }
383
+ return this.selected ? "true" : "false";
384
+ }
385
+
386
+ get ariaSelectedValue(): "true" | "false" | undefined {
387
+ if (this.roleItem !== "treeitem") {
388
+ return undefined;
389
+ }
390
+ // Only SingleSelect treeitems expose aria-selected; MultiSelect uses aria-checked.
391
+ if (this.modeMultiSelect) {
392
+ return undefined;
393
+ }
394
+ return this.selected ? "true" : "false";
395
+ }
396
+
397
+ get ariaExpandedValue(): "true" | "false" | undefined {
398
+ if (!this.collapsible) {
399
+ return undefined;
400
+ }
401
+ return this.expanded ? "true" : "false";
402
+ }
403
+
404
+ get checkboxAriaHidden(): boolean {
405
+ return this.roleItem === "treeitem";
406
+ }
407
+
372
408
  get _ariaDisabled(): boolean | undefined {
373
409
  return this.disabled ? true : undefined;
374
410
  }
@@ -10,6 +10,7 @@ import type { VimeoPlayer } from "./MediaPlayerVimeo.js";
10
10
  import mapLangFile from "./mediaPlayerLanguageMapping.js";
11
11
  import type UDExMediaPlayerYoutube from "./MediaPlayerYoutube.js";
12
12
  import type UDExMediaPlayerVimeo from "./MediaPlayerVimeo.js";
13
+ import "./BusyIndicator.js";
13
14
 
14
15
  // Template
15
16
  import MediaPlayerTemplate from "./MediaPlayerTemplate.js";
@@ -281,6 +282,15 @@ class UDExMediaPlayer extends UI5Element {
281
282
  this._pendingPlugins.push({ name, plugin });
282
283
  }
283
284
 
285
+ _injectBusyIndicator(): void {
286
+ const spinner = this.shadowRoot?.querySelector(".vjs-loading-spinner");
287
+ if (!spinner || spinner.querySelector("[udex-busy-indicator]")) { return; }
288
+ const busyIndicator = document.createElement("udex-busy-indicator");
289
+ busyIndicator.setAttribute("active", "");
290
+ busyIndicator.setAttribute("surface-container", "");
291
+ spinner.appendChild(busyIndicator);
292
+ }
293
+
284
294
  getPlayedTime = (target: EventTarget): number => {
285
295
  const progressControl = (target as any)?.player.controlBar?.progressControl?.childNameIndex_.seekBar;
286
296
  if (!progressControl) {
@@ -566,6 +576,7 @@ class UDExMediaPlayer extends UI5Element {
566
576
 
567
577
  this.player = videojs(this.shadowRoot?.querySelector(".video-js") || "", options);
568
578
  this.attachListeners(this.player);
579
+ this._injectBusyIndicator();
569
580
 
570
581
  if (this.audioOnly) { return; }
571
582