@sapudex/web-components 2.30.0 → 2.32.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 (215) hide show
  1. package/cypress/specs/Breadcrumbs.cy.tsx +26 -0
  2. package/cypress/specs/ListItem.cy.tsx +22 -26
  3. package/cypress/specs/MediaPlayer.cy.tsx +209 -0
  4. package/cypress/specs/PromoTile.cy.tsx +10 -0
  5. package/cypress/specs/SecondaryNavigationDesktop.cy.tsx +74 -21
  6. package/cypress/specs/SecondaryNavigationMobile.cy.tsx +58 -1
  7. package/cypress/specs/SelectBox.cy.tsx +76 -0
  8. package/cypress/specs/TabNavigation.cy.tsx +603 -39
  9. package/cypress/support/component-imports.instrumented.ts +2 -0
  10. package/cypress/support/component-imports.src.ts +2 -0
  11. package/dist/Breadcrumbs.d.ts +5 -4
  12. package/dist/Breadcrumbs.js +15 -17
  13. package/dist/Breadcrumbs.js.map +1 -1
  14. package/dist/BreadcrumbsTemplate.js +1 -1
  15. package/dist/BreadcrumbsTemplate.js.map +1 -1
  16. package/dist/List.js +3 -3
  17. package/dist/List.js.map +1 -1
  18. package/dist/ListItemBase.d.ts +0 -2
  19. package/dist/ListItemBase.js +0 -6
  20. package/dist/ListItemBase.js.map +1 -1
  21. package/dist/MastheadCountrySelector.d.ts +3 -15
  22. package/dist/MastheadCountrySelector.js +28 -72
  23. package/dist/MastheadCountrySelector.js.map +1 -1
  24. package/dist/MastheadItem.d.ts +1 -0
  25. package/dist/MastheadItem.js +12 -4
  26. package/dist/MastheadItem.js.map +1 -1
  27. package/dist/MastheadMenuItem.d.ts +7 -0
  28. package/dist/MastheadMenuItem.js +3 -0
  29. package/dist/MastheadMenuItem.js.map +1 -1
  30. package/dist/MastheadMobileMenu.d.ts +2 -12
  31. package/dist/MastheadMobileMenu.js +31 -116
  32. package/dist/MastheadMobileMenu.js.map +1 -1
  33. package/dist/MastheadProfileMenu.js +1 -1
  34. package/dist/MastheadProfileMenu.js.map +1 -1
  35. package/dist/MastheadShell.d.ts +1 -6
  36. package/dist/MastheadShell.js +7 -44
  37. package/dist/MastheadShell.js.map +1 -1
  38. package/dist/MediaPlayer.d.ts +15 -0
  39. package/dist/MediaPlayer.js +28 -0
  40. package/dist/MediaPlayer.js.map +1 -1
  41. package/dist/Pagination.d.ts +14 -1
  42. package/dist/Pagination.js +39 -6
  43. package/dist/Pagination.js.map +1 -1
  44. package/dist/PromoTile.d.ts +8 -0
  45. package/dist/PromoTile.js +4 -0
  46. package/dist/PromoTile.js.map +1 -1
  47. package/dist/PromoTileTemplate.js +3 -1
  48. package/dist/PromoTileTemplate.js.map +1 -1
  49. package/dist/SecondaryNavigation.d.ts +12 -2
  50. package/dist/SecondaryNavigation.js +59 -8
  51. package/dist/SecondaryNavigation.js.map +1 -1
  52. package/dist/SecondaryNavigationItem.d.ts +0 -1
  53. package/dist/SecondaryNavigationItem.js +7 -5
  54. package/dist/SecondaryNavigationItem.js.map +1 -1
  55. package/dist/SecondaryNavigationSubItem.js +1 -1
  56. package/dist/SecondaryNavigationSubItem.js.map +1 -1
  57. package/dist/SelectBase.js +28 -7
  58. package/dist/SelectBase.js.map +1 -1
  59. package/dist/Tab.d.ts +1 -1
  60. package/dist/Tab.js +3 -3
  61. package/dist/Tab.js.map +1 -1
  62. package/dist/TabNavigation.d.ts +64 -0
  63. package/dist/TabNavigation.js +124 -1
  64. package/dist/TabNavigation.js.map +1 -1
  65. package/dist/assets/{InputSuggestions-CaAXs3ok.js → InputSuggestions-CHLLTki3.js} +1 -1
  66. package/dist/assets/{ListItemStandardExpandableTextTemplate-CPqUBd--.js → ListItemStandardExpandableTextTemplate-vM1eIFe4.js} +1 -1
  67. package/dist/assets/{bundle.esm-L-7H6J4f.js → bundle.esm-fUZ1I3qm.js} +218 -218
  68. package/dist/assets/messagebundle_en-iNqRB1VZ.js +1 -0
  69. package/dist/css/themes/BreadcrumbsPopover.css +1 -1
  70. package/dist/css/themes/Dropdown.css +1 -1
  71. package/dist/css/themes/Link.css +1 -1
  72. package/dist/css/themes/ListGroupHeader.css +1 -1
  73. package/dist/css/themes/MastheadCountrySelector.css +1 -1
  74. package/dist/css/themes/MastheadItem.css +1 -1
  75. package/dist/css/themes/MastheadShell.css +1 -1
  76. package/dist/css/themes/RatingIndicator.css +1 -1
  77. package/dist/css/themes/SecondaryNavigation.css +1 -1
  78. package/dist/css/themes/SecondaryNavigationItem.css +1 -1
  79. package/dist/css/themes/SecondaryNavigationSubItem.css +1 -1
  80. package/dist/css/themes/Slider.css +1 -1
  81. package/dist/css/themes/TabNavigation.css +1 -1
  82. package/dist/custom-elements-internal.json +407 -167
  83. package/dist/custom-elements.json +320 -152
  84. package/dist/generated/assets/i18n/messagebundle_en.json +1 -1
  85. package/dist/generated/i18n/i18n-defaults.d.ts +2 -1
  86. package/dist/generated/i18n/i18n-defaults.js +3 -2
  87. package/dist/generated/i18n/i18n-defaults.js.map +1 -1
  88. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js +12 -16
  89. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js.map +1 -1
  90. package/dist/generated/templates/ListItemTemplate.lit.js +32 -36
  91. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  92. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js +3 -3
  93. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js.map +1 -1
  94. package/dist/generated/templates/MastheadMenuItemTemplate.lit.js +1 -1
  95. package/dist/generated/templates/MastheadMenuItemTemplate.lit.js.map +1 -1
  96. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js +1 -1
  97. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js.map +1 -1
  98. package/dist/generated/templates/MastheadShellTemplate.lit.js +9 -10
  99. package/dist/generated/templates/MastheadShellTemplate.lit.js.map +1 -1
  100. package/dist/generated/templates/SecondaryNavigationTemplate.lit.js +1 -1
  101. package/dist/generated/templates/SecondaryNavigationTemplate.lit.js.map +1 -1
  102. package/dist/generated/templates/TabNavigationTemplate.lit.js +10 -8
  103. package/dist/generated/templates/TabNavigationTemplate.lit.js.map +1 -1
  104. package/dist/generated/themes/BreadcrumbsPopover.css.d.ts +1 -1
  105. package/dist/generated/themes/BreadcrumbsPopover.css.js +1 -1
  106. package/dist/generated/themes/BreadcrumbsPopover.css.js.map +1 -1
  107. package/dist/generated/themes/Dropdown.css.d.ts +1 -1
  108. package/dist/generated/themes/Dropdown.css.js +1 -1
  109. package/dist/generated/themes/Dropdown.css.js.map +1 -1
  110. package/dist/generated/themes/Link.css.d.ts +1 -1
  111. package/dist/generated/themes/Link.css.js +1 -1
  112. package/dist/generated/themes/Link.css.js.map +1 -1
  113. package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
  114. package/dist/generated/themes/ListGroupHeader.css.js +1 -1
  115. package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
  116. package/dist/generated/themes/MastheadCountrySelector.css.d.ts +1 -1
  117. package/dist/generated/themes/MastheadCountrySelector.css.js +1 -1
  118. package/dist/generated/themes/MastheadCountrySelector.css.js.map +1 -1
  119. package/dist/generated/themes/MastheadItem.css.d.ts +1 -1
  120. package/dist/generated/themes/MastheadItem.css.js +1 -1
  121. package/dist/generated/themes/MastheadItem.css.js.map +1 -1
  122. package/dist/generated/themes/MastheadShell.css.d.ts +1 -1
  123. package/dist/generated/themes/MastheadShell.css.js +1 -1
  124. package/dist/generated/themes/MastheadShell.css.js.map +1 -1
  125. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  126. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  127. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  128. package/dist/generated/themes/SecondaryNavigation.css.d.ts +1 -1
  129. package/dist/generated/themes/SecondaryNavigation.css.js +1 -1
  130. package/dist/generated/themes/SecondaryNavigation.css.js.map +1 -1
  131. package/dist/generated/themes/SecondaryNavigationItem.css.d.ts +1 -1
  132. package/dist/generated/themes/SecondaryNavigationItem.css.js +1 -1
  133. package/dist/generated/themes/SecondaryNavigationItem.css.js.map +1 -1
  134. package/dist/generated/themes/SecondaryNavigationSubItem.css.d.ts +1 -1
  135. package/dist/generated/themes/SecondaryNavigationSubItem.css.js +1 -1
  136. package/dist/generated/themes/SecondaryNavigationSubItem.css.js.map +1 -1
  137. package/dist/generated/themes/Slider.css.d.ts +1 -1
  138. package/dist/generated/themes/Slider.css.js +1 -1
  139. package/dist/generated/themes/Slider.css.js.map +1 -1
  140. package/dist/generated/themes/TabNavigation.css.d.ts +1 -1
  141. package/dist/generated/themes/TabNavigation.css.js +1 -1
  142. package/dist/generated/themes/TabNavigation.css.js.map +1 -1
  143. package/dist/messagebundle.properties +4 -1
  144. package/dist/messagebundle_en.properties +2 -1
  145. package/dist/types/List.d.ts +0 -2
  146. package/dist/types/List.js +0 -2
  147. package/dist/types/List.js.map +1 -1
  148. package/dist/vscode.html-custom-data.json +45 -76
  149. package/dist/web-types.json +96 -76
  150. package/package.json +2 -1
  151. package/src/Breadcrumbs.ts +17 -21
  152. package/src/BreadcrumbsTemplate.tsx +1 -1
  153. package/src/List.ts +3 -3
  154. package/src/ListGroupHeader.hbs +7 -11
  155. package/src/ListItem.hbs +0 -2
  156. package/src/ListItemBase.ts +0 -8
  157. package/src/MastheadCountrySelector.hbs +5 -2
  158. package/src/MastheadCountrySelector.ts +31 -85
  159. package/src/MastheadItem.ts +13 -4
  160. package/src/MastheadMenuItem.hbs +1 -0
  161. package/src/MastheadMenuItem.ts +9 -0
  162. package/src/MastheadMobileMenu.hbs +0 -1
  163. package/src/MastheadMobileMenu.ts +34 -130
  164. package/src/MastheadProfileMenu.ts +1 -1
  165. package/src/MastheadShell.hbs +9 -11
  166. package/src/MastheadShell.ts +8 -50
  167. package/src/MediaPlayer.ts +35 -0
  168. package/src/Pagination.ts +51 -7
  169. package/src/PromoTile.ts +10 -0
  170. package/src/PromoTileTemplate.tsx +4 -1
  171. package/src/SecondaryNavigation.hbs +1 -0
  172. package/src/SecondaryNavigation.ts +77 -9
  173. package/src/SecondaryNavigationItem.ts +1 -8
  174. package/src/SecondaryNavigationSubItem.ts +1 -1
  175. package/src/SelectBase.ts +33 -7
  176. package/src/Tab.ts +4 -4
  177. package/src/TabNavigation.hbs +15 -0
  178. package/src/TabNavigation.ts +134 -1
  179. package/src/generated/i18n/i18n-defaults.ts +2 -2
  180. package/src/generated/templates/ListGroupHeaderTemplate.lit.ts +12 -16
  181. package/src/generated/templates/ListItemTemplate.lit.ts +32 -36
  182. package/src/generated/templates/MastheadCountrySelectorTemplate.lit.ts +3 -3
  183. package/src/generated/templates/MastheadMenuItemTemplate.lit.ts +1 -1
  184. package/src/generated/templates/MastheadMobileMenuTemplate.lit.ts +1 -1
  185. package/src/generated/templates/MastheadShellTemplate.lit.ts +9 -10
  186. package/src/generated/templates/SecondaryNavigationTemplate.lit.ts +1 -1
  187. package/src/generated/templates/TabNavigationTemplate.lit.ts +10 -8
  188. package/src/generated/themes/BreadcrumbsPopover.css.ts +1 -1
  189. package/src/generated/themes/Dropdown.css.ts +1 -1
  190. package/src/generated/themes/Link.css.ts +1 -1
  191. package/src/generated/themes/ListGroupHeader.css.ts +1 -1
  192. package/src/generated/themes/MastheadCountrySelector.css.ts +1 -1
  193. package/src/generated/themes/MastheadItem.css.ts +1 -1
  194. package/src/generated/themes/MastheadShell.css.ts +1 -1
  195. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  196. package/src/generated/themes/SecondaryNavigation.css.ts +1 -1
  197. package/src/generated/themes/SecondaryNavigationItem.css.ts +1 -1
  198. package/src/generated/themes/SecondaryNavigationSubItem.css.ts +1 -1
  199. package/src/generated/themes/Slider.css.ts +1 -1
  200. package/src/generated/themes/TabNavigation.css.ts +1 -1
  201. package/src/i18n/messagebundle.properties +4 -1
  202. package/src/i18n/messagebundle_en.properties +2 -1
  203. package/src/themes/BreadcrumbsPopover.css +4 -2
  204. package/src/themes/Dropdown.css +5 -1
  205. package/src/themes/Link.css +2 -3
  206. package/src/themes/ListGroupHeader.css +3 -11
  207. package/src/themes/MastheadCountrySelector.css +2 -2
  208. package/src/themes/MastheadItem.css +57 -0
  209. package/src/themes/MastheadShell.css +0 -2
  210. package/src/themes/SecondaryNavigation.css +6 -3
  211. package/src/themes/SecondaryNavigationItem.css +11 -10
  212. package/src/themes/SecondaryNavigationSubItem.css +3 -2
  213. package/src/themes/TabNavigation.css +27 -16
  214. package/src/types/List.ts +0 -2
  215. package/dist/assets/messagebundle_en-J-MHTtdL.js +0 -1
@@ -35,6 +35,10 @@ type UDExMediaPlayerVideoProgressEventDetail = {
35
35
  progress: number,
36
36
  }
37
37
 
38
+ type UDExMediaPlayerVideojsReadyEventDetail = {
39
+ videojs: any,
40
+ }
41
+
38
42
  /**
39
43
  * @class
40
44
  *
@@ -117,6 +121,15 @@ type UDExMediaPlayerVideoProgressEventDetail = {
117
121
  * @public
118
122
  */
119
123
  @event("video75", { bubbles: true })
124
+ /**
125
+ * Fires when the VideoJS library is loaded but before the player is created.
126
+ * Use this event to register custom VideoJS plugins via
127
+ * <code>event.detail.videojs.registerPlugin()</code>.
128
+ *
129
+ * @public
130
+ * @param {object} videojs the VideoJS constructor object
131
+ */
132
+ @event("videojsReady", { bubbles: true })
120
133
  class UDExMediaPlayer extends UI5Element {
121
134
  eventDetails!: {
122
135
  "videoStart": void,
@@ -127,6 +140,7 @@ class UDExMediaPlayer extends UI5Element {
127
140
  "video25": void,
128
141
  "video50": void,
129
142
  "video75": void,
143
+ "videojsReady": UDExMediaPlayerVideojsReadyEventDetail,
130
144
  };
131
145
  /**
132
146
  * Defines the options object of video.js element
@@ -251,6 +265,20 @@ class UDExMediaPlayer extends UI5Element {
251
265
  videoStart!: boolean;
252
266
  videoReady!: boolean;
253
267
  videoProgress!: number;
268
+ private _pendingPlugins: Array<{ name: string; plugin: (...args: any[]) => any }> = [];
269
+
270
+ /**
271
+ * Registers a custom VideoJS plugin to be applied before player initialization.
272
+ * Must be called before the component renders.
273
+ * For plugins registered after rendering, use the videojsReady event instead.
274
+ *
275
+ * @param name - The plugin name
276
+ * @param plugin - The plugin function or class
277
+ * @public
278
+ */
279
+ registerPlugin(name: string, plugin: (...args: any[]) => any): void {
280
+ this._pendingPlugins.push({ name, plugin });
281
+ }
254
282
 
255
283
  getPlayedTime = (target: EventTarget): number => {
256
284
  const progressControl = (target as any)?.player.controlBar?.progressControl?.childNameIndex_.seekBar;
@@ -518,6 +546,13 @@ class UDExMediaPlayer extends UI5Element {
518
546
  window.videojs = videojs;
519
547
  }
520
548
 
549
+ this._pendingPlugins.forEach(({ name, plugin }) => {
550
+ videojs.registerPlugin(name, plugin);
551
+ });
552
+ this._pendingPlugins = [];
553
+
554
+ this.fireDecoratorEvent("videojsReady", { videojs });
555
+
521
556
  const options = this.getOptionsValue();
522
557
  const language: string = options?.language;
523
558
  if (language) {
package/src/Pagination.ts CHANGED
@@ -40,7 +40,8 @@ export enum UDExPaginationType {
40
40
 
41
41
  export enum UDExDisplayType {
42
42
  desktop = "desktop",
43
- mobile = "mobile"
43
+ mobile = "mobile",
44
+ auto = "auto"
44
45
  }
45
46
 
46
47
  type UDExPaginationChangeEventDetail = {
@@ -121,6 +122,14 @@ class UDExPagination extends UI5Element {
121
122
  @property({ type: String })
122
123
  _direction?: string;
123
124
 
125
+ /**
126
+ * Tracks the resolved display mode for auto display.
127
+ *
128
+ * @private
129
+ */
130
+ @property({ noAttribute: true })
131
+ _effectiveDisplay?: `${UDExDisplayType}`;
132
+
124
133
  /**
125
134
  * Defines if the previous button is in pseudo-disabled.
126
135
  *
@@ -140,6 +149,8 @@ class UDExPagination extends UI5Element {
140
149
  @i18n("@sapudex/web-components")
141
150
  static i18nBundle: I18nBundle;
142
151
 
152
+ static readonly AUTO_DISPLAY_BREAKPOINT = 980;
153
+
143
154
  _pages: Page[];
144
155
  _visiblePages: Page[];
145
156
 
@@ -150,6 +161,7 @@ class UDExPagination extends UI5Element {
150
161
  _firstPageIndex!: number;
151
162
  _lastPageIndex!: number;
152
163
  _pageFocusedManually!: boolean;
164
+ _resizeHandler!: () => void;
153
165
 
154
166
  constructor() {
155
167
  super();
@@ -160,12 +172,44 @@ class UDExPagination extends UI5Element {
160
172
  this._mobileDisplayNumber = 5;
161
173
  this._desktopDisplayNumber = 10;
162
174
  this._pageFocusedManually = false;
175
+ this._resizeHandler = this._handleResize.bind(this);
163
176
  }
164
177
 
165
178
  onEnterDOM() {
166
179
  if (isDesktop()) {
167
180
  this.setAttribute("desktop", "");
168
181
  }
182
+ if (this.display === UDExDisplayType.auto) {
183
+ this._effectiveDisplay = this._displayType;
184
+ window.addEventListener("resize", this._resizeHandler);
185
+ }
186
+ }
187
+
188
+ onExitDOM() {
189
+ window.removeEventListener("resize", this._resizeHandler);
190
+ }
191
+
192
+ _handleResize(): void {
193
+ if (this.display === UDExDisplayType.auto) {
194
+ const newDisplay = this._displayType;
195
+ if (this._effectiveDisplay !== newDisplay) {
196
+ this._effectiveDisplay = newDisplay;
197
+ this._pages = [];
198
+ }
199
+ }
200
+ }
201
+
202
+ get _displayType(): `${UDExDisplayType}` {
203
+ return window.innerWidth > UDExPagination.AUTO_DISPLAY_BREAKPOINT
204
+ ? UDExDisplayType.desktop
205
+ : UDExDisplayType.mobile;
206
+ }
207
+
208
+ get effectiveDisplay(): `${UDExDisplayType}` {
209
+ if (this.display === UDExDisplayType.auto) {
210
+ return this._effectiveDisplay ?? (this._displayType);
211
+ }
212
+ return this.display;
169
213
  }
170
214
 
171
215
  onBeforeRendering(): void {
@@ -182,13 +226,13 @@ class UDExPagination extends UI5Element {
182
226
 
183
227
  get typeFull(): boolean {
184
228
  return (
185
- this.type === UDExPaginationType.full || this.display === UDExDisplayType.mobile
229
+ this.type === UDExPaginationType.full || this.effectiveDisplay === UDExDisplayType.mobile
186
230
  );
187
231
  }
188
232
 
189
233
  get typeShort(): boolean {
190
234
  return (
191
- this.type === UDExPaginationType.short && this.display !== UDExDisplayType.mobile
235
+ this.type === UDExPaginationType.short && this.effectiveDisplay !== UDExDisplayType.mobile
192
236
  );
193
237
  }
194
238
 
@@ -265,19 +309,19 @@ class UDExPagination extends UI5Element {
265
309
  if (this._direction === "rtl") {
266
310
  classes = `${classes} udex-pagination--${this._direction}`;
267
311
  }
268
- if (!this.display) {
312
+ if (!this.effectiveDisplay) {
269
313
  return classes;
270
314
  }
271
- return `${classes} udex-pagination--${this.display} udex-pagination--${this.type}`;
315
+ return `${classes} udex-pagination--${this.effectiveDisplay} udex-pagination--${this.type}`;
272
316
  }
273
317
 
274
318
  get displayNumber(): number {
275
- const lastIndex = this.display === UDExDisplayType.mobile ? this._mobileDisplayNumber : this._desktopDisplayNumber;
319
+ const lastIndex = this.effectiveDisplay === UDExDisplayType.mobile ? this._mobileDisplayNumber : this._desktopDisplayNumber;
276
320
  return lastIndex;
277
321
  }
278
322
 
279
323
  get pageOffset(): number {
280
- if (this.display === UDExDisplayType.mobile) {
324
+ if (this.effectiveDisplay === UDExDisplayType.mobile) {
281
325
  return Math.floor(this._mobileDisplayNumber / 2);
282
326
  }
283
327
  return this._desktopDisplayNumber / 2 - 1;
package/src/PromoTile.ts CHANGED
@@ -81,6 +81,15 @@ class UDExPromoTile extends UI5Element {
81
81
  @slot({ type: HTMLElement })
82
82
  pictogram?: Array<HTMLElement>;
83
83
 
84
+ /**
85
+ * Allows control over setting the CSS gradient.
86
+ *
87
+ * @default undefined
88
+ * @public
89
+ */
90
+ @property({ type: String })
91
+ backgroundGradient?: string;
92
+
84
93
  /**
85
94
  * Defines the orientation of the Promo Tile component.
86
95
  *
@@ -179,6 +188,7 @@ class UDExPromoTile extends UI5Element {
179
188
  _colorMode: UDExPromoTileMode = UDExPromoTileMode.light;
180
189
  _lightModeColors: Array<number> = [1, 2];
181
190
  _darkModeColors: Array<number> = [7, 8, 9, 10];
191
+ _defaultBackgroundColor = "#FFF";
182
192
 
183
193
  constructor() {
184
194
  super();
@@ -12,7 +12,10 @@ export default function PromoTileTemplate(this: UDExPromoTile) {
12
12
  <div
13
13
  role="region"
14
14
  class={`${rootClass}__container ${rootClass}--${this.orientation?.toLowerCase()} ${rootClass}--${this._colorMode}`}
15
- aria-label={this.promoTileContainerText}>
15
+ aria-label={this.promoTileContainerText}
16
+ style={this.backgroundGradient
17
+ ? { backgroundImage: this.backgroundGradient, backgroundColor: this._defaultBackgroundColor }
18
+ : undefined}>
16
19
  {this._showPictogram &&
17
20
  <div
18
21
  part="image"
@@ -70,6 +70,7 @@
70
70
  aria-label="{{mobileMenuOpenerAriaLabel}}"
71
71
  label="{{heading}}"
72
72
  mobile-link-title="{{title}}"
73
+ @ui5-nav-item-click="{{handleMobileMenuOpenerClick}}"
73
74
  @ui5-sub-item-click="{{handleSubItemClick}}"
74
75
  _is-mobile-menu-opener
75
76
  .subItems="{{this.parsedItems}}">
@@ -34,8 +34,10 @@ type UDExSecondaryNavigationClickEventDetail = {
34
34
  originalEvent: Event,
35
35
  clickedItem: NavItem;
36
36
  clickedIndex: number;
37
+ isMore?: boolean,
37
38
  isMobile?: boolean,
38
39
  analyticsName: AnalyticsName,
40
+ analyticsPath: Array<string>,
39
41
  sourceNode: HTMLElement,
40
42
  };
41
43
 
@@ -45,6 +47,12 @@ type UDExSecondaryNavigationHeadingClickEventDetail = {
45
47
  sourceNode: HTMLElement,
46
48
  };
47
49
 
50
+ type UDExSecondaryNavigationMobileMenuOpenerClickEventDetail = {
51
+ originalEvent: Event,
52
+ analyticsName: AnalyticsName,
53
+ sourceNode: HTMLElement,
54
+ };
55
+
48
56
  /**
49
57
  * @class
50
58
  *
@@ -97,10 +105,23 @@ type UDExSecondaryNavigationHeadingClickEventDetail = {
97
105
  */
98
106
  @event("heading-click", { cancelable: true, bubbles: true })
99
107
 
108
+ /**
109
+ * The event is triggered on user interaction with the mobile menu opener
110
+ *
111
+ * @allowPreventDefault
112
+ * @public
113
+ * @param {Event} originalEvent The original event object
114
+ * @param {AnalyticsName} analyticsName The analytics name for the mobile menu opener
115
+ * @param {HTMLElement} sourceNode The source node of the mobile menu opener
116
+ *
117
+ */
118
+ @event("mobile-menu-opener-click", { cancelable: true, bubbles: true })
119
+
100
120
  class UDExSecondaryNavigation extends UI5Element {
101
121
  eventDetails!: {
102
122
  "item-click": UDExSecondaryNavigationClickEventDetail,
103
123
  "heading-click": UDExSecondaryNavigationHeadingClickEventDetail,
124
+ "mobile-menu-opener-click": UDExSecondaryNavigationMobileMenuOpenerClickEventDetail,
104
125
  };
105
126
  /**
106
127
  * Defines an aria label for the mobile menu opener
@@ -247,7 +268,7 @@ class UDExSecondaryNavigation extends UI5Element {
247
268
  });
248
269
 
249
270
  if ((steps.at(-1) || 0) <= availableWidth) {
250
- this._visibleItems.splice(0, Infinity, ...this.parsedItems);
271
+ this._visibleItems = [...this.parsedItems];
251
272
  this._showMoreButton = false;
252
273
  this.refreshRendering();
253
274
  return;
@@ -256,8 +277,8 @@ class UDExSecondaryNavigation extends UI5Element {
256
277
  if (this.parsedItems) {
257
278
  // @ts-ignore
258
279
  const lastVisibleIndex: number = steps.findLastIndex((size: number) => size < (availableWidth - moreButtonWidth - itemsGap));
259
- this._visibleItems.splice(0, Infinity, ...this.parsedItems.slice(0, lastVisibleIndex + 1));
260
- this._overflowItems.splice(0, Infinity, ...this.parsedItems.slice(lastVisibleIndex + 1));
280
+ this._visibleItems = this.parsedItems.slice(0, lastVisibleIndex + 1);
281
+ this._overflowItems = this.parsedItems.slice(lastVisibleIndex + 1);
261
282
  this._showMoreButton = true;
262
283
  this.refreshRendering();
263
284
  }
@@ -345,7 +366,7 @@ class UDExSecondaryNavigation extends UI5Element {
345
366
  active,
346
367
  mobileLinkTitle,
347
368
  items: subItems,
348
- });
369
+ }, this.getAnalyticsPath(title, path), title === this.moreButtonText);
349
370
 
350
371
  if (prevented) {
351
372
  e.preventDefault();
@@ -366,7 +387,9 @@ class UDExSecondaryNavigation extends UI5Element {
366
387
  const { originalEvent: actualOriginalEvent, index } = e.detail;
367
388
  const item = e.detail.item as UDExSecondaryNavigationSubItem;
368
389
  const { items } = item as NavItem;
369
- const prevented = !this.fireEventItemClick(actualOriginalEvent, index, item);
390
+ const analyticsPath = this.getAnalyticsPath(item.title, item.path);
391
+ const isMore = this._clickedNavItem?.title === this.moreButtonText;
392
+ const prevented = !this.fireEventItemClick(actualOriginalEvent, index, item, analyticsPath, isMore);
370
393
 
371
394
  if (prevented) {
372
395
  e.preventDefault();
@@ -397,10 +420,14 @@ class UDExSecondaryNavigation extends UI5Element {
397
420
  {
398
421
  items, target, path, title, active, mobileLinkTitle,
399
422
  }: NavItem,
423
+ analyticsPath: Array<string> = [],
424
+ isMore: boolean = false,
400
425
  ): boolean {
401
426
  return this.fireDecoratorEvent("item-click", {
402
427
  ...(this.isMobileScreen && { isMobile: true }),
428
+ ...(isMore && { isMore: true }),
403
429
  analyticsName: AnalyticsName.HeaderSub,
430
+ analyticsPath,
404
431
  sourceNode: e?.currentTarget as HTMLElement,
405
432
  originalEvent: e,
406
433
  clickedItem: {
@@ -415,25 +442,62 @@ class UDExSecondaryNavigation extends UI5Element {
415
442
  });
416
443
  }
417
444
 
445
+ getAnalyticsPath(clickedTitle: string, clickedPath?: string): Array<string> {
446
+ const getLabel = (item: NavItem) => (this.isMobileScreen ? (item.mobileLinkTitle || item.title) : item.title);
447
+ const findPath = (items: Array<NavItem>, parentPath: Array<string>): Array<string> | null => items.reduce<Array<string> | null>((found, item) => {
448
+ if (found !== null) {
449
+ return found;
450
+ }
451
+ if (item.title === clickedTitle && (!clickedPath || item.path === clickedPath)) {
452
+ return [...parentPath, getLabel(item)];
453
+ }
454
+ if (item.items?.length) {
455
+ return findPath(item.items, [...parentPath, getLabel(item)]);
456
+ }
457
+ return null;
458
+ }, null);
459
+ return findPath(this.parsedItems, []) || [];
460
+ }
461
+
418
462
  setActiveItem(navItems: Array<NavItem>, clickedItem: UDExSecondaryNavigationItemBase): void {
419
- const updateNav = (items: Array<NavItem>, { title, path }: UDExSecondaryNavigationItemBase) => {
463
+ const updateNav = (items: Array<NavItem>, { title, path }: UDExSecondaryNavigationItemBase): boolean => {
464
+ let hasActiveDescendant = false;
465
+
420
466
  items.forEach(item => {
421
467
  if (item.title === title && (item.path === path || !item.path)) {
422
468
  item.active = true;
469
+ hasActiveDescendant = true;
423
470
  return;
424
471
  }
425
- delete item.active;
426
472
 
427
473
  if (item.items) {
428
- updateNav(item.items, clickedItem);
474
+ const childIsActive = updateNav(item.items, clickedItem);
475
+ if (childIsActive) {
476
+ item.active = true;
477
+ hasActiveDescendant = true;
478
+ } else {
479
+ delete item.active;
480
+ }
481
+ } else {
482
+ delete item.active;
429
483
  }
430
484
  });
485
+
486
+ return hasActiveDescendant;
431
487
  };
432
488
 
433
489
  updateNav(navItems, clickedItem);
434
490
  this.navItems = JSON.stringify(navItems);
435
491
  }
436
492
 
493
+ handleMobileMenuOpenerClick(e: CustomEvent): void {
494
+ this.fireDecoratorEvent("mobile-menu-opener-click", {
495
+ analyticsName: AnalyticsName.HeaderSub,
496
+ sourceNode: e.target as HTMLElement,
497
+ originalEvent: e,
498
+ });
499
+ }
500
+
437
501
  handleOutsideClick(): void {
438
502
  document.addEventListener("mousedown", this.outsideClickListener);
439
503
  }
@@ -545,4 +609,8 @@ class UDExSecondaryNavigation extends UI5Element {
545
609
  UDExSecondaryNavigation.define();
546
610
 
547
611
  export default UDExSecondaryNavigation;
548
- export type { UDExSecondaryNavigationClickEventDetail, UDExSecondaryNavigationHeadingClickEventDetail };
612
+ export type {
613
+ UDExSecondaryNavigationClickEventDetail,
614
+ UDExSecondaryNavigationHeadingClickEventDetail,
615
+ UDExSecondaryNavigationMobileMenuOpenerClickEventDetail,
616
+ };
@@ -127,7 +127,7 @@ class UDExSecondaryNavigationItem extends UDExSecondaryNavigationItemBase {
127
127
  * @default false
128
128
  */
129
129
  @property({ type: Boolean })
130
- isOpen!: boolean;
130
+ isOpen: boolean = false;
131
131
 
132
132
  /**
133
133
  * Defines the length of items in the secondary navigation.
@@ -166,7 +166,6 @@ class UDExSecondaryNavigationItem extends UDExSecondaryNavigationItemBase {
166
166
  _dropdownHistory: Array<Array<NavItem>> = [];
167
167
  _shouldFocusFirstSubItem!: boolean;
168
168
  _subTitle!: string | undefined;
169
- _shouldResetTabIndex!: boolean;
170
169
  _shouldSetTabIndex!: boolean;
171
170
 
172
171
  onAfterRendering(): void {
@@ -187,11 +186,6 @@ class UDExSecondaryNavigationItem extends UDExSecondaryNavigationItemBase {
187
186
  this._tabIndex = "0";
188
187
  this._shouldSetTabIndex = false;
189
188
  }
190
-
191
- if (this._shouldResetTabIndex) {
192
- this._tabIndex = "-1";
193
- this._shouldResetTabIndex = false;
194
- }
195
189
  }
196
190
 
197
191
  onBeforeRendering() : void {
@@ -220,7 +214,6 @@ class UDExSecondaryNavigationItem extends UDExSecondaryNavigationItemBase {
220
214
  if (this.hasNestedItems) {
221
215
  this.setDropdownItems();
222
216
  this.isOpen = !this.isOpen;
223
- this._shouldResetTabIndex = true;
224
217
  this.onShouldFocusFirstSubItem();
225
218
  }
226
219
 
@@ -121,7 +121,7 @@ class UDExSecondaryNavigationSubItem extends UDExSecondaryNavigationItemBase {
121
121
 
122
122
  get backButtonAriaLabel(): string {
123
123
  const label = translate(UDExSecondaryNavigationSubItem, BACK_BUTTON_LABEL);
124
- return `${label} ${this.itemLabel}`;
124
+ return `${this.itemLabel}, ${label}`;
125
125
  }
126
126
  }
127
127
 
package/src/SelectBase.ts CHANGED
@@ -286,16 +286,28 @@ class UDExSelectBase extends UDExSearchableListBase {
286
286
  }
287
287
 
288
288
  handleHomeKeyPress(items: UDExListItemBase[]) {
289
- this._selectedOption = items[0].label;
289
+ const firstIndex = (items[0]?.nonInteractive && !items[0]?.collapsible) ? 1 : 0;
290
+ this._selectedOption = items[firstIndex].label;
290
291
 
291
292
  if (this.showImageFlags) {
292
- this.handleImagePosition(items, 0);
293
+ this.handleImagePosition(items, firstIndex);
293
294
  }
294
295
  }
295
296
 
296
297
  handleDownKeyPress(items: UDExListItemBase[], currentSelectedIndex: number, lastItemIndex: number) {
297
298
  const nextIndex = currentSelectedIndex + 1;
298
299
  const nextSelectIndex = nextIndex > lastItemIndex ? 0 : nextIndex;
300
+ const isNonInteractive = items[nextSelectIndex].nonInteractive && !items[nextSelectIndex].collapsible;
301
+
302
+ if (isNonInteractive) {
303
+ const skipIndex = nextSelectIndex + 1 > lastItemIndex ? 0 : nextSelectIndex + 1;
304
+ this._selectedOption = items[skipIndex].label;
305
+ if (this.showImageFlags) {
306
+ this.handleImagePosition(items, skipIndex);
307
+ }
308
+ return;
309
+ }
310
+
299
311
  this._selectedOption = items[nextSelectIndex].label;
300
312
 
301
313
  if (this.showImageFlags) {
@@ -306,6 +318,17 @@ class UDExSelectBase extends UDExSearchableListBase {
306
318
  handleUpKeyPress(items: UDExListItemBase[], currentSelectedIndex: number, lastItemIndex: number) {
307
319
  const prevIndex = currentSelectedIndex - 1;
308
320
  const previousSelectIndex = prevIndex < 0 ? lastItemIndex : prevIndex;
321
+ const isNonInteractive = items[previousSelectIndex].nonInteractive && !items[previousSelectIndex].collapsible;
322
+
323
+ if (isNonInteractive) {
324
+ const skipIndex = previousSelectIndex === 0 ? lastItemIndex : previousSelectIndex - 1;
325
+ this._selectedOption = items[skipIndex].label;
326
+ if (this.showImageFlags) {
327
+ this.handleImagePosition(items, skipIndex);
328
+ }
329
+ return;
330
+ }
331
+
309
332
  this._selectedOption = items[previousSelectIndex].label;
310
333
 
311
334
  if (this.showImageFlags) {
@@ -381,15 +404,18 @@ class UDExSelectBase extends UDExSearchableListBase {
381
404
  }
382
405
 
383
406
  focusFirstItem(items: UDExListItemBase[]): void {
384
- this._list.setTabIndexByIndex(items, 0);
407
+ const firstIndex = (items[0]?.nonInteractive && !items[0]?.collapsible) ? 1 : 0;
408
+ const firstItem = items[firstIndex];
409
+ this._list.setTabIndexByIndex(items, firstIndex);
410
+
385
411
  if (!this.tabNavigationEnabled) {
386
- this._selectedOption = items[0].label;
412
+ this._selectedOption = firstItem.label;
387
413
  }
388
414
 
389
- if (items[0].isGroup) {
390
- this._list.focusHeader(items[0]);
415
+ if (firstItem.isGroup) {
416
+ this._list.focusHeader(firstItem);
391
417
  } else {
392
- items[0].focus();
418
+ firstItem.focus();
393
419
  }
394
420
  }
395
421
 
package/src/Tab.ts CHANGED
@@ -299,6 +299,10 @@ class UDExTab extends UI5Element implements ITab {
299
299
  return this.effectiveSelected && this.hasBodyText && this._isVertical;
300
300
  }
301
301
 
302
+ get hasBodyTextOrLink(): boolean {
303
+ return this._isVertical && (this.hasBodyText || this.hasLinkSlot);
304
+ }
305
+
302
306
  get hasLinkSlot(): boolean {
303
307
  return this.link && this.link.length > 0;
304
308
  }
@@ -314,10 +318,6 @@ class UDExTab extends UI5Element implements ITab {
314
318
  return this.link.map(l => l.outerHTML).join("");
315
319
  }
316
320
 
317
- get hasBodyTextOrLink(): boolean {
318
- return this._isVertical && (this.hasBodyText || this.hasLinkSlot);
319
- }
320
-
321
321
  get effectiveSelected(): boolean {
322
322
  return this.selected || this._selected;
323
323
  }
@@ -4,6 +4,16 @@
4
4
  class="udex-tab-navigation__header"
5
5
  part="tab-navigation-header"
6
6
  @keydown="{{handleKeyDown}}">
7
+ {{#if showPlayButton}}
8
+ <udex-button
9
+ class="udex-tab-navigation__play-button"
10
+ design="Tertiary"
11
+ size="Large"
12
+ icon="{{playBtnIcon}}"
13
+ tooltip="{{playBtnTooltipText}}"
14
+ @click="{{onPlayButtonClick}}">
15
+ </udex-button>
16
+ {{/if}}
7
17
  <div
8
18
  id="{{_id}}-header-tabs"
9
19
  class="udex-tab-navigation__header-tabs"
@@ -49,6 +59,11 @@
49
59
  {{#if this._overflowItems.length}}
50
60
  {{tabNavigationPopover}}
51
61
  {{/if}}
62
+ {{#if showFooterSlot}}
63
+ <div class="udex-tab-navigation__footer" part="tab-navigation-footer">
64
+ <slot name="footer"></slot>
65
+ </div>
66
+ {{/if}}
52
67
  </div>
53
68
  <div
54
69
  class="udex-tab-navigation__content"