@sapudex/web-components 2.25.1 → 2.26.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 (108) hide show
  1. package/cypress/fixtures/profile-menu.ts +2 -1
  2. package/cypress/specs/MastheadProfileMenu.cy.tsx +21 -6
  3. package/dist/List.d.ts +9 -0
  4. package/dist/List.js +33 -2
  5. package/dist/List.js.map +1 -1
  6. package/dist/MastheadCountrySelector.d.ts +11 -0
  7. package/dist/MastheadCountrySelector.js +71 -14
  8. package/dist/MastheadCountrySelector.js.map +1 -1
  9. package/dist/MastheadItem.d.ts +1 -0
  10. package/dist/MastheadItem.js +9 -1
  11. package/dist/MastheadItem.js.map +1 -1
  12. package/dist/MastheadMenu.d.ts +11 -0
  13. package/dist/MastheadMenu.js +43 -13
  14. package/dist/MastheadMenu.js.map +1 -1
  15. package/dist/MastheadProfileMenu.d.ts +33 -3
  16. package/dist/MastheadProfileMenu.js +93 -5
  17. package/dist/MastheadProfileMenu.js.map +1 -1
  18. package/dist/Pagination.d.ts +2 -0
  19. package/dist/Pagination.js +31 -6
  20. package/dist/Pagination.js.map +1 -1
  21. package/dist/PaginationTemplate.js +6 -5
  22. package/dist/PaginationTemplate.js.map +1 -1
  23. package/dist/Tooltip.js +3 -5
  24. package/dist/Tooltip.js.map +1 -1
  25. package/dist/TooltipTemplate.d.ts +2 -0
  26. package/dist/TooltipTemplate.js +7 -0
  27. package/dist/TooltipTemplate.js.map +1 -0
  28. package/dist/assets/{InputSuggestions-CTFZ6hH-.js → InputSuggestions-CG5owMFQ.js} +1 -1
  29. package/dist/assets/{ListItemStandardExpandableTextTemplate-wXdCijez.js → ListItemStandardExpandableTextTemplate-DpVr7qdn.js} +1 -1
  30. package/dist/assets/{bundle.esm-BXCZ9BP3.js → bundle.esm-CTkLMr6f.js} +185 -185
  31. package/dist/css/themes/MastheadCountrySelector.css +1 -1
  32. package/dist/css/themes/MastheadItem.css +1 -1
  33. package/dist/css/themes/MastheadMenu.css +1 -1
  34. package/dist/css/themes/MastheadMenuItem.css +1 -1
  35. package/dist/css/themes/MastheadProfileMenu.css +1 -1
  36. package/dist/css/themes/Quote.css +1 -1
  37. package/dist/css/themes/RatingIndicator.css +1 -1
  38. package/dist/css/themes/Slider.css +1 -1
  39. package/dist/custom-elements-internal.json +163 -155
  40. package/dist/custom-elements.json +144 -140
  41. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js +1 -1
  42. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js.map +1 -1
  43. package/dist/generated/templates/MastheadProfileMenuTemplate.lit.js +7 -5
  44. package/dist/generated/templates/MastheadProfileMenuTemplate.lit.js.map +1 -1
  45. package/dist/generated/themes/MastheadCountrySelector.css.d.ts +1 -1
  46. package/dist/generated/themes/MastheadCountrySelector.css.js +1 -1
  47. package/dist/generated/themes/MastheadCountrySelector.css.js.map +1 -1
  48. package/dist/generated/themes/MastheadItem.css.d.ts +1 -1
  49. package/dist/generated/themes/MastheadItem.css.js +1 -1
  50. package/dist/generated/themes/MastheadItem.css.js.map +1 -1
  51. package/dist/generated/themes/MastheadMenu.css.d.ts +1 -1
  52. package/dist/generated/themes/MastheadMenu.css.js +1 -1
  53. package/dist/generated/themes/MastheadMenu.css.js.map +1 -1
  54. package/dist/generated/themes/MastheadMenuItem.css.d.ts +1 -1
  55. package/dist/generated/themes/MastheadMenuItem.css.js +1 -1
  56. package/dist/generated/themes/MastheadMenuItem.css.js.map +1 -1
  57. package/dist/generated/themes/MastheadProfileMenu.css.d.ts +1 -1
  58. package/dist/generated/themes/MastheadProfileMenu.css.js +1 -1
  59. package/dist/generated/themes/MastheadProfileMenu.css.js.map +1 -1
  60. package/dist/generated/themes/Quote.css.d.ts +1 -1
  61. package/dist/generated/themes/Quote.css.js +1 -1
  62. package/dist/generated/themes/Quote.css.js.map +1 -1
  63. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  64. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  65. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  66. package/dist/generated/themes/Slider.css.d.ts +1 -1
  67. package/dist/generated/themes/Slider.css.js +1 -1
  68. package/dist/generated/themes/Slider.css.js.map +1 -1
  69. package/dist/types/List.d.ts +6 -0
  70. package/dist/types/List.js +7 -0
  71. package/dist/types/List.js.map +1 -1
  72. package/dist/vscode.html-custom-data.json +1 -1
  73. package/dist/web-types.json +4 -6
  74. package/package.json +1 -1
  75. package/src/List.ts +41 -2
  76. package/src/MastheadCountrySelector.hbs +20 -16
  77. package/src/MastheadCountrySelector.ts +83 -16
  78. package/src/MastheadItem.ts +10 -1
  79. package/src/MastheadMenu.ts +56 -13
  80. package/src/MastheadProfileMenu.hbs +43 -26
  81. package/src/MastheadProfileMenu.ts +124 -7
  82. package/src/Pagination.ts +32 -6
  83. package/src/PaginationTemplate.tsx +19 -16
  84. package/src/Tooltip.ts +7 -6
  85. package/src/TooltipTemplate.tsx +29 -0
  86. package/src/generated/templates/MastheadCountrySelectorTemplate.lit.ts +1 -1
  87. package/src/generated/templates/MastheadProfileMenuTemplate.lit.ts +7 -5
  88. package/src/generated/themes/MastheadCountrySelector.css.ts +1 -1
  89. package/src/generated/themes/MastheadItem.css.ts +1 -1
  90. package/src/generated/themes/MastheadMenu.css.ts +1 -1
  91. package/src/generated/themes/MastheadMenuItem.css.ts +1 -1
  92. package/src/generated/themes/MastheadProfileMenu.css.ts +1 -1
  93. package/src/generated/themes/Quote.css.ts +1 -1
  94. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  95. package/src/generated/themes/Slider.css.ts +1 -1
  96. package/src/themes/MastheadCountrySelector.css +70 -10
  97. package/src/themes/MastheadItem.css +0 -1
  98. package/src/themes/MastheadMenu.css +1 -2
  99. package/src/themes/MastheadMenuItem.css +1 -0
  100. package/src/themes/MastheadProfileMenu.css +113 -17
  101. package/src/themes/Quote.css +1 -1
  102. package/src/types/List.ts +7 -0
  103. package/cypress/downloads/downloads.html +0 -0
  104. package/dist/generated/templates/TooltipTemplate.lit.d.ts +0 -3
  105. package/dist/generated/templates/TooltipTemplate.lit.js +0 -8
  106. package/dist/generated/templates/TooltipTemplate.lit.js.map +0 -1
  107. package/src/Tooltip.hbs +0 -22
  108. package/src/generated/templates/TooltipTemplate.lit.ts +0 -12
package/src/List.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  isUp, isDown, isHome, isEnd,
9
9
  } from "@ui5/webcomponents-base/dist/Keys.js";
10
10
  import type UDExListItem from "./ListItem.js";
11
+ import type { UDExScrollIntoViewOptions } from "./types/List.js";
11
12
  import { UDExListStyleType, UDExListMode } from "./types/List.js";
12
13
  import type UDExListGroupHeader from "./ListGroupHeader.js";
13
14
  import type UDExListGroupHeaderBase from "./ListGroupHeaderBase.js";
@@ -162,6 +163,15 @@ class UDExList extends UI5Element {
162
163
  @slot({ type: HTMLElement, "default": true })
163
164
  items!: Array<UDExListItemBase>;
164
165
 
166
+ /**
167
+ * Defines the scroll behavior for the items of the component.
168
+ *
169
+ * @default undefined
170
+ * @private
171
+ */
172
+ @property()
173
+ _scrollIntoView?: `${UDExScrollIntoViewOptions}`;
174
+
165
175
  _focusHandler: UDExListWithFirstLetterFocus = new UDExFirstLetterFocusHandler();
166
176
 
167
177
  @i18n("@sapudex/web-components")
@@ -211,6 +221,9 @@ class UDExList extends UI5Element {
211
221
  if (nonInteractiveHeader && previousSelectIndex === 0) {
212
222
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
213
223
  this.setTabIndexByIndex(items, lastItemIndex);
224
+ if (this._scrollIntoView) {
225
+ this.scrollToItem(items[lastItemIndex]);
226
+ }
214
227
  items[lastItemIndex].focus();
215
228
  return;
216
229
  }
@@ -218,6 +231,9 @@ class UDExList extends UI5Element {
218
231
  if (nonInteractiveHeader) {
219
232
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
220
233
  this.setTabIndexByIndex(items, previousSelectIndex - 1);
234
+ if (this._scrollIntoView) {
235
+ this.scrollToItem(items[previousSelectIndex - 1]);
236
+ }
221
237
  items[previousSelectIndex - 1].focus();
222
238
  return;
223
239
  }
@@ -225,6 +241,9 @@ class UDExList extends UI5Element {
225
241
  if (isHeaderItem) {
226
242
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
227
243
  this.setTabIndexByIndex(items, previousSelectIndex);
244
+ if (this._scrollIntoView) {
245
+ this.scrollToItem(items[previousSelectIndex]);
246
+ }
228
247
  this.focusHeader(items[previousSelectIndex]);
229
248
  return;
230
249
  }
@@ -236,7 +255,12 @@ class UDExList extends UI5Element {
236
255
 
237
256
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
238
257
  this.setTabIndexByIndex(items, previousSelectIndex);
239
- setTimeout(() => { items[previousSelectIndex].focus(); }, 0);
258
+ setTimeout(() => {
259
+ if (this._scrollIntoView) {
260
+ this.scrollToItem(items[previousSelectIndex]);
261
+ }
262
+ items[previousSelectIndex].focus();
263
+ }, 0);
240
264
  }
241
265
 
242
266
  handleDownArrow(e: KeyboardEvent): void {
@@ -250,6 +274,9 @@ class UDExList extends UI5Element {
250
274
  if (nonInteractiveHeader) {
251
275
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
252
276
  this.setTabIndexByIndex(items, nextSelectIndex + 1);
277
+ if (this._scrollIntoView) {
278
+ this.scrollToItem(items[nextSelectIndex + 1]);
279
+ }
253
280
  items[nextSelectIndex + 1].focus();
254
281
  return;
255
282
  }
@@ -257,6 +284,9 @@ class UDExList extends UI5Element {
257
284
  if (isHeaderItem) {
258
285
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
259
286
  this.setTabIndexByIndex(items, nextSelectIndex);
287
+ if (this._scrollIntoView) {
288
+ this.scrollToItem(items[nextSelectIndex]);
289
+ }
260
290
  this.focusHeader(items[nextSelectIndex]);
261
291
  return;
262
292
  }
@@ -268,7 +298,12 @@ class UDExList extends UI5Element {
268
298
 
269
299
  this.unsetTabIndexByIndex(items, currentSelectedIndex);
270
300
  this.setTabIndexByIndex(items, nextSelectIndex);
271
- setTimeout(() => { items[nextSelectIndex].focus(); }, 0);
301
+ setTimeout(() => {
302
+ if (this._scrollIntoView) {
303
+ this.scrollToItem(items[nextSelectIndex]);
304
+ }
305
+ items[nextSelectIndex].focus();
306
+ }, 0);
272
307
  }
273
308
 
274
309
  handleHomeKey(e: KeyboardEvent): void {
@@ -370,6 +405,10 @@ class UDExList extends UI5Element {
370
405
  header.focus();
371
406
  }
372
407
 
408
+ scrollToItem(item: UDExListItemBase): void {
409
+ item.scrollIntoView({ block: this._scrollIntoView?.toLowerCase() as ScrollLogicalPosition });
410
+ }
411
+
373
412
  unsetTabIndexByIndex(items: UDExListItemBase[], index: number): void {
374
413
  items[index]._tabIndex = "-1";
375
414
  }
@@ -25,22 +25,26 @@
25
25
  show-submit-button>
26
26
  </udex-search>
27
27
  <span aria-live="polite" aria-atomic="true" class="hidden">{{_accSearchResultInfo}}</span>
28
- <ul class="udex-masthead-country-selector__list udex-masthead-country-selector__list_countries" role="menu">
29
- {{#each _activeCountries}}
30
- <li role="none">
31
- <udex-masthead-menu-item
32
- class="udex-masthead-country-selector__item udex-masthead-country-selector__item_countries"
33
- @click="{{../handleCountryClick}}"
34
- _country-code="{{this.countryCode}}"
35
- url="{{this.url}}"
36
- label="{{this.displayTitle}}"
37
- index="{{@index}}"
38
- part="menu-item"
39
- is-nav-item>
40
- </udex-masthead-menu-item>
41
- </li>
42
- {{/each}}
43
- </ul>
28
+ <div class="udex-masthead-country-selector__list-container">
29
+ <div class="udex-masthead-country-selector__list-wrap" @scroll={{this.handleCountryListShadowVisible}}>
30
+ <ul class="udex-masthead-country-selector__list udex-masthead-country-selector__list_countries" role="menu">
31
+ {{#each _activeCountries}}
32
+ <li role="none">
33
+ <udex-masthead-menu-item
34
+ class="udex-masthead-country-selector__item udex-masthead-country-selector__item_countries"
35
+ @click="{{../handleCountryClick}}"
36
+ _country-code="{{this.countryCode}}"
37
+ url="{{this.url}}"
38
+ label="{{this.displayTitle}}"
39
+ index="{{@index}}"
40
+ part="menu-item"
41
+ is-nav-item>
42
+ </udex-masthead-menu-item>
43
+ </li>
44
+ {{/each}}
45
+ </ul>
46
+ </div>
47
+ </div>
44
48
  {{else}}
45
49
  <span class="udex-masthead-country-selector__title">{{regionTitle}}</span>
46
50
  <ul class="udex-masthead-country-selector__list" role="menu">
@@ -215,6 +215,8 @@ class UDExMastheadCountrySelector extends UI5Element {
215
215
  _accSearchResultInfo?: string;
216
216
  _activeRegionIndex!: number;
217
217
  _mastheadShell!: UDExMastheadShell;
218
+ _countriesListHolderClassName = "udex-masthead-country-selector__list-wrap";
219
+ _itemClassName = "udex-masthead-country-selector__item";
218
220
 
219
221
  @i18n("@sapudex/web-components")
220
222
  static i18nBundle: I18nBundle;
@@ -234,6 +236,10 @@ class UDExMastheadCountrySelector extends UI5Element {
234
236
  if (this.isSingleMode) {
235
237
  this.handleSingleRegionMode();
236
238
  }
239
+
240
+ if (this._activeCountries) {
241
+ this.checkScrollableCountriesList();
242
+ }
237
243
  }
238
244
 
239
245
  onEnterDOM(): void {
@@ -397,10 +403,10 @@ class UDExMastheadCountrySelector extends UI5Element {
397
403
  const lastItemIndex = this.items.length - 1;
398
404
  const prevIndex = currentIndex - 1 < 0 ? lastItemIndex : currentIndex - 1;
399
405
  const prevItem = this.items[prevIndex];
400
-
401
406
  this.items[currentIndex]._tabIndex = "-1";
402
407
  prevItem._tabIndex = "0";
403
- prevItem.focus();
408
+ prevItem.scrollIntoView({ block: "center" });
409
+ prevItem.focus({ preventScroll: true });
404
410
  }
405
411
 
406
412
  focusNextItem(e: KeyboardEvent): void {
@@ -408,34 +414,53 @@ class UDExMastheadCountrySelector extends UI5Element {
408
414
  const lastItemIndex = this.items.length - 1;
409
415
  const nextIndex = currentIndex + 1 > lastItemIndex ? 0 : currentIndex + 1;
410
416
  const nextItem = this.items[nextIndex];
411
-
412
417
  this.items[currentIndex]._tabIndex = "-1";
413
418
  nextItem._tabIndex = "0";
414
- nextItem.focus();
419
+ nextItem.scrollIntoView({ block: "center" });
420
+ nextItem.focus({ preventScroll: true });
421
+ }
422
+
423
+ focusLastItem(e: KeyboardEvent): void {
424
+ const currentIndex = (e.target as UDExMastheadMenuItem).index;
425
+ const lastItemIndex = this.items.length - 1;
426
+ const lastItem = this.items[lastItemIndex];
427
+
428
+ this.items[currentIndex]._tabIndex = "-1";
429
+ lastItem._tabIndex = "0";
430
+ lastItem.scrollIntoView({ block: "end" });
431
+ lastItem.focus({ preventScroll: true });
432
+ }
433
+
434
+ focusFirstItem(e: KeyboardEvent): void {
435
+ const currentIndex = (e.target as UDExMastheadMenuItem).index;
436
+ const firstItem = this.items[0];
437
+
438
+ this.items[currentIndex]._tabIndex = "-1";
439
+ firstItem._tabIndex = "0";
440
+ firstItem.focus();
415
441
  }
416
442
 
417
443
  handleEndKey(e: KeyboardEvent): void {
418
444
  if (isEnd(e)) {
419
445
  e.preventDefault();
420
- const currentIndex = (e.target as UDExMastheadMenuItem).index;
421
- const lastItemIndex = this.items.length - 1;
422
- const lastItem = this.items[lastItemIndex];
423
446
 
424
- this.items[currentIndex]._tabIndex = "-1";
425
- lastItem._tabIndex = "0";
426
- lastItem.focus();
447
+ if (this.isScrollableCountriesList) {
448
+ this.removeScrollableClassInCountryList();
449
+ }
450
+
451
+ this.focusLastItem(e);
427
452
  }
428
453
  }
429
454
 
430
455
  handleHomeKey(e: KeyboardEvent): void {
431
456
  if (isHome(e)) {
432
457
  e.preventDefault();
433
- const currentIndex = (e.target as UDExMastheadMenuItem).index;
434
- const firstItem = this.items[0];
435
458
 
436
- this.items[currentIndex]._tabIndex = "-1";
437
- firstItem._tabIndex = "0";
438
- firstItem.focus();
459
+ if (this.isScrollableCountriesList) {
460
+ this.addScrollableClassToCountryList();
461
+ }
462
+
463
+ this.focusFirstItem(e);
439
464
  }
440
465
  }
441
466
 
@@ -509,6 +534,48 @@ class UDExMastheadCountrySelector extends UI5Element {
509
534
  this._mastheadShell.mobileBtn?.focus();
510
535
  }
511
536
 
537
+ checkScrollableCountriesList(): void {
538
+ if (this.countryListHolder) {
539
+ requestAnimationFrame(() => {
540
+ if (this.isScrollableCountriesList) {
541
+ this.addScrollableClassToCountryList();
542
+ } else {
543
+ this.removeScrollableClassInCountryList();
544
+ }
545
+ });
546
+ }
547
+ }
548
+
549
+ handleCountryListShadowVisible(): void {
550
+ if (this.scrollBottomCountriesList === 0) {
551
+ this.removeScrollableClassInCountryList();
552
+ } else {
553
+ this.addScrollableClassToCountryList();
554
+ }
555
+ }
556
+
557
+ addScrollableClassToCountryList(): void {
558
+ this.countryListHolder.classList.add(`${this._countriesListHolderClassName}_scrollable`);
559
+ }
560
+
561
+ removeScrollableClassInCountryList(): void {
562
+ this.countryListHolder.classList.remove(`${this._countriesListHolderClassName}_scrollable`);
563
+ }
564
+
565
+ get scrollBottomCountriesList(): number {
566
+ const countriesList = this.countryListHolder;
567
+ return countriesList.scrollHeight - countriesList.scrollTop - countriesList.clientHeight;
568
+ }
569
+
570
+ get isScrollableCountriesList(): boolean {
571
+ const countriesList = this.countryListHolder;
572
+ return countriesList ? countriesList.scrollHeight > countriesList.clientHeight : false;
573
+ }
574
+
575
+ get countryListHolder(): HTMLElement {
576
+ return this.shadowRoot?.querySelector<HTMLElement>(`.${this._countriesListHolderClassName}`) as HTMLElement;
577
+ }
578
+
512
579
  get noMatchesFoundText(): string {
513
580
  return translate(UDExMastheadCountrySelector, NO_MATCHES_FOUND);
514
581
  }
@@ -518,7 +585,7 @@ class UDExMastheadCountrySelector extends UI5Element {
518
585
  }
519
586
 
520
587
  get items(): Array<UDExMastheadMenuItem> {
521
- const items = this.shadowRoot?.querySelectorAll<UDExMastheadMenuItem>(".udex-masthead-country-selector__item");
588
+ const items = this.shadowRoot?.querySelectorAll<UDExMastheadMenuItem>(`.${this._itemClassName}`);
522
589
  return items ? Array.from(items) : [];
523
590
  }
524
591
 
@@ -412,7 +412,7 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
412
412
 
413
413
  handleItemClick(e: PointerEvent): void {
414
414
  if (this.isProfileBtn) {
415
- this.profileSlot?.closeIdentitiesPanel();
415
+ this.handleProfileButtonClick();
416
416
  }
417
417
  this.handleSelectedChange();
418
418
  this.fireItemClickEvent(e);
@@ -423,6 +423,15 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
423
423
  this.selected = this.isMobile ? true : !this.selected;
424
424
  }
425
425
 
426
+ handleProfileButtonClick(): void {
427
+ const profile = this.profileSlot;
428
+ if (profile) {
429
+ profile.closeIdentitiesPanel();
430
+ profile.scrollToTopProfileMenu();
431
+ profile._requestRender = true;
432
+ }
433
+ }
434
+
426
435
  fireDropdownEvents(): void {
427
436
  if (this.hasDropdownSlot) {
428
437
  const { Open, Close } = UDExMastheadDropdownItemAction;
@@ -8,7 +8,9 @@ import Icon from "@ui5/webcomponents/dist/Icon.js";
8
8
  import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
9
9
  import "@ui5/webcomponents-icons/dist/slim-arrow-right.js";
10
10
  import "@ui5/webcomponents-icons/dist/action.js";
11
- import { isUp, isDown } from "@ui5/webcomponents-base/dist/Keys.js";
11
+ import {
12
+ isUp, isDown, isTabNext, isTabPrevious, isHome, isEnd,
13
+ } from "@ui5/webcomponents-base/dist/Keys.js";
12
14
  import UDExBadge from "./Badge.js";
13
15
  import UDExMastheadMenuItem, { UDExItemType } from "./MastheadMenuItem.js";
14
16
  import type UDExMastheadItem from "./MastheadItem.js";
@@ -17,6 +19,7 @@ import MastheadMenuTemplate from "./generated/templates/MastheadMenuTemplate.lit
17
19
  import parseArrayFromString from "./utils/parseArrayFromString.js";
18
20
  import AnalyticsName from "./types/AnalyticsName.js";
19
21
  import { OVERVIEW } from "./generated/i18n/i18n-defaults.js";
22
+ import type { UDExScrollIntoViewOptions } from "./types/List.js";
20
23
  import translate from "./utils/translate.js";
21
24
 
22
25
  // Styles
@@ -120,28 +123,58 @@ class UDExMastheadMenu extends UI5Element {
120
123
  @property({ type: Boolean })
121
124
  _isMobileMode!: boolean;
122
125
 
126
+ /**
127
+ * Defines the scroll behavior for the items of the component.
128
+ *
129
+ * @default undefined
130
+ * @private
131
+ */
132
+ @property()
133
+ _scrollIntoView?: `${UDExScrollIntoViewOptions}`;
134
+
123
135
  @i18n("@sapudex/web-components")
124
136
  static i18nBundle: I18nBundle;
125
137
 
126
138
  handleKeyDown(e: KeyboardEvent): void {
127
- this.handleUpArrow(e);
128
- this.handleDownArrow(e);
139
+ const isFirstItem = e.target === this.links[0];
140
+ const isLastItem = e.target === this.links[this.links.length - 1];
141
+
142
+ if (isUp(e) || (isTabPrevious(e) && !isFirstItem)) {
143
+ this.handleUpArrow(e);
144
+ }
145
+ if (isDown(e) || (isTabNext(e) && !isLastItem)) {
146
+ this.handleDownArrow(e);
147
+ }
148
+ if (isHome(e)) {
149
+ this.handleHomeKey(e);
150
+ }
151
+ if (isEnd(e)) {
152
+ this.handleEndKey(e);
153
+ }
154
+ }
155
+
156
+ handleHomeKey(e: KeyboardEvent): void {
157
+ e.preventDefault();
158
+ const firstItem = this.links[0];
159
+ firstItem.focus();
160
+ }
161
+
162
+ handleEndKey(e: KeyboardEvent): void {
163
+ e.preventDefault();
164
+ const lastItem = this.links[this.links.length - 1];
165
+ lastItem.focus();
129
166
  }
130
167
 
131
168
  handleUpArrow(e: KeyboardEvent): void {
132
- if (isUp(e)) {
133
- e.preventDefault();
134
- const currentItem = e.target as UDExMastheadMenuItem;
135
- this.focusPreviousItem(currentItem);
136
- }
169
+ e.preventDefault();
170
+ const currentItem = e.target as UDExMastheadMenuItem;
171
+ this.focusPreviousItem(currentItem);
137
172
  }
138
173
 
139
174
  handleDownArrow(e: KeyboardEvent): void {
140
- if (isDown(e)) {
141
- e.preventDefault();
142
- const currentItem = e.target as UDExMastheadMenuItem;
143
- this.focusNextItem(currentItem);
144
- }
175
+ e.preventDefault();
176
+ const currentItem = e.target as UDExMastheadMenuItem;
177
+ this.focusNextItem(currentItem);
145
178
  }
146
179
 
147
180
  handleMenuItemClick(e: PointerEvent): void {
@@ -171,12 +204,18 @@ class UDExMastheadMenu extends UI5Element {
171
204
  const currentSelectedIndex = this.getCurrentIndex(item);
172
205
  const lastItemIndex = this.links.length - 1;
173
206
  const previousSelectIndex = currentSelectedIndex - 1 < 0 ? lastItemIndex : currentSelectedIndex - 1;
207
+ if (this._scrollIntoView) {
208
+ this.scrollToItem(this.links[previousSelectIndex]);
209
+ }
174
210
  this.links[previousSelectIndex].focus();
175
211
  }
176
212
 
177
213
  focusNextItem(item: UDExMastheadMenuItem): void {
178
214
  const currentSelectedIndex = this.getCurrentIndex(item);
179
215
  const nextSelectIndex = currentSelectedIndex + 1 > this.links.length - 1 ? 0 : currentSelectedIndex + 1;
216
+ if (this._scrollIntoView) {
217
+ this.scrollToItem(this.links[nextSelectIndex]);
218
+ }
180
219
  this.links[nextSelectIndex].focus();
181
220
  }
182
221
 
@@ -184,6 +223,10 @@ class UDExMastheadMenu extends UI5Element {
184
223
  return this.links.indexOf(item);
185
224
  }
186
225
 
226
+ scrollToItem(item: UDExMastheadMenuItem): void {
227
+ item.scrollIntoView({ block: this._scrollIntoView?.toLowerCase() as ScrollLogicalPosition });
228
+ }
229
+
187
230
  get links(): Array<UDExMastheadMenuItem> {
188
231
  const menuItems: Array<UDExMastheadMenuItem> = Array.from(this.shadowRoot!.querySelectorAll("[udex-masthead-menu-item]"));
189
232
  return menuItems.filter(({ url }) => url && url.length > 0);
@@ -14,7 +14,8 @@
14
14
  <udex-avatar
15
15
  aria-hidden="true"
16
16
  size="L"
17
- initials="{{userData.avatarInitials}}">
17
+ initials="{{userData.avatarInitials}}"
18
+ fallback-icon="{{userData.fallbackIcon}}">
18
19
  {{#if userData.avatarUrl}}
19
20
  <img src="{{userData.avatarUrl}}" alt="avatar"/>
20
21
  {{/if}}
@@ -40,25 +41,39 @@
40
41
  <slot name="message"></slot>
41
42
  </div>
42
43
  {{/if}}
43
- <div class="udex-masthead-profile-menu__links">
44
- <udex-masthead-menu
45
- exportparts="badge, menu-item"
46
- part="menu"
47
- sub-items='{{linksList}}'
48
- @ui5-menu-item-click="{{handleProfileMenuItemClick}}">
49
- </udex-masthead-menu>
50
- {{#if logoutLink}}
51
- <div class="udex-masthead-profile-menu__actions">
52
- <udex-masthead-menu-item
53
- @click="{{handleUserLogoutItemClick}}"
54
- label="{{logoutText}}"
55
- url="{{logoutUrl}}">
56
- </udex-masthead-menu-item>
44
+ <div class="udex-masthead-profile-menu__content">
45
+ <div
46
+ class="udex-masthead-profile-menu__links"
47
+ @scroll="{{handleProfileMenuShadowVisible}}">
48
+ <udex-masthead-menu
49
+ exportparts="badge, menu-item"
50
+ part="menu"
51
+ sub-items='{{linksList}}'
52
+ @ui5-menu-item-click="{{handleProfileMenuItemClick}}"
53
+ _scroll-into-view="Center">
54
+ </udex-masthead-menu>
57
55
  </div>
58
- {{/if}}
59
56
  </div>
57
+ {{#if hasActionButton}}
58
+ <div class="udex-masthead-profile-menu__actions">
59
+ {{#if hasLogoutButton}}
60
+ <div class="udex-masthead-profile-menu__btn">
61
+ <slot name="logoutButton"></slot>
62
+ </div>
63
+ {{else}}
64
+ <udex-link
65
+ @click="{{handleUserLogoutClick}}"
66
+ end-icon="action"
67
+ design="Default"
68
+ accessible-role="Link"
69
+ href="{{logoutUrl}}">
70
+ {{logoutText}}
71
+ </udex-link>
72
+ {{/if}}
73
+ </div>
74
+ {{/if}}
60
75
  {{/inline}}
61
-
76
+
62
77
  {{#*inline "account-indetities"}}
63
78
  <div class="udex-masthead-profile-menu__header-wrap back-btn">
64
79
  <udex-masthead-menu-item
@@ -70,13 +85,15 @@
70
85
  </udex-masthead-menu-item>
71
86
  <span class="udex-masthead-profile-menu__switcher-title">{{switcherAccountText}}</span>
72
87
  </div>
88
+ <span class="udex-masthead-profile-menu__identities-list-title">{{listTitleText}}</span>
73
89
  <div class="udex-masthead-profile-menu__identities-list-wrap">
74
- <span class="udex-masthead-profile-menu__identities-list-title">{{listTitleText}}</span>
75
90
  {{#if identitiesList.length}}
76
91
  <udex-list
77
92
  class="udex-masthead-profile-menu__identities-list"
78
93
  mode="SingleSelect"
79
94
  accessible-name="{{listTitleText}}"
95
+ @scroll="{{handleIdentitiesListShadowVisible}}"
96
+ _scroll-into-view="Center"
80
97
  controlled>
81
98
  {{#each identitiesList}}
82
99
  <udex-list-item
@@ -92,13 +109,13 @@
92
109
  {{/each}}
93
110
  </udex-list>
94
111
  {{/if}}
95
- <udex-button
96
- @click="{{fireEventAddOrRemoveAccountClick}}"
97
- class="udex-masthead-profile-menu__add-remove-btn"
98
- design="Primary"
99
- end-icon="action"
100
- size="Large">
101
- {{addOrRemoveAccountText}}
102
- </udex-button>
103
112
  </div>
113
+ <udex-button
114
+ @click="{{fireEventAddOrRemoveAccountClick}}"
115
+ class="udex-masthead-profile-menu__add-remove-btn"
116
+ design="Primary"
117
+ end-icon="action"
118
+ size="Large">
119
+ {{addOrRemoveAccountText}}
120
+ </udex-button>
104
121
  {{/inline}}