@lion/ui 0.17.0 → 0.18.0-menu-system.1

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 (218) hide show
  1. package/collapsible.js +1 -0
  2. package/components/button/src/LionButton.js +4 -0
  3. package/components/button/test-suites/LionButton.suite.js +1 -0
  4. package/components/calendar/src/LionCalendar.js +2 -0
  5. package/components/collapsible/src/DisclosureMixin.js +313 -0
  6. package/components/collapsible/types/DisclosureMixinTypes.ts +48 -0
  7. package/components/combobox/test/lion-combobox.test.js +1 -0
  8. package/components/form-core/src/form-group/FormGroupMixin.js +7 -0
  9. package/components/form-core/src/registration/FormRegistrarMixin.js +11 -0
  10. package/components/form-core/test/FormControlMixin.test.js +1 -0
  11. package/components/form-core/test/validate/Validator.test.js +2 -0
  12. package/components/form-core/test-suites/ValidateMixin.suite.js +2 -0
  13. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +1 -0
  14. package/components/input-datepicker/src/localizeNamespaceLoader.js +1 -0
  15. package/components/input-datepicker/test/lion-input-datepicker.test.js +5 -6
  16. package/components/localize/test/date/formatDate.test.js +1 -0
  17. package/components/menu/CHANGELOG.md +37 -0
  18. package/components/menu/README.md +335 -0
  19. package/components/menu/index.js +2 -0
  20. package/components/menu/lion-menu-overlay.js +3 -0
  21. package/components/menu/lion-menu.js +3 -0
  22. package/components/menu/lion-spinbutton.js +3 -0
  23. package/components/menu/lion-toolbar.js +3 -0
  24. package/components/menu/lion-tree.js +3 -0
  25. package/components/menu/package.json +46 -0
  26. package/components/menu/requirements.temp.md +12 -0
  27. package/components/menu/src/InteractiveListMixin.js +805 -0
  28. package/components/menu/src/LionMenu.js +250 -0
  29. package/components/menu/src/LionMenuHybrid.js +46 -0
  30. package/components/menu/src/LionMenuOverlay.js +155 -0
  31. package/components/menu/src/LionSpinbutton.js +213 -0
  32. package/components/menu/src/LionToolbar.js +94 -0
  33. package/components/menu/src/LionTree.js +52 -0
  34. package/components/menu/src/MoreButtonMenuMixin.js +297 -0
  35. package/components/menu/src/MultiLevelListMixin.js +274 -0
  36. package/components/menu/src/utils/isInView.js +26 -0
  37. package/components/menu/src/utils/listItemInteractions.js +104 -0
  38. package/components/menu/src/utils/uuid.js +3 -0
  39. package/components/menu/test/lion-menu.test.js +3 -0
  40. package/components/menu/test-suites/InteractiveListMixin.suite.js +142 -0
  41. package/components/menu/types/InteractiveListMixinTypes.d.ts +82 -0
  42. package/components/navigation-bar/README.md +1 -0
  43. package/components/navigation-bar/src/LionNavigationBar.js +439 -0
  44. package/components/navigation-bar/src/types.js +41 -0
  45. package/components/overlays/NEXT.md +49 -0
  46. package/components/overlays/src/OverlayController.js +244 -111
  47. package/components/overlays/src/OverlayMixin.js +58 -11
  48. package/components/overlays/src/OverlaysManager.js +2 -2
  49. package/components/overlays/src/utils/adopt-styles.js +3 -2
  50. package/components/overlays/src/utils/get-focusable-elements.js +4 -0
  51. package/components/overlays/src/utils/is-equal-config.js +10 -6
  52. package/components/overlays/test/OverlayController.test.js +5 -3
  53. package/components/overlays/test/utils-tests/is-equal-config.test.js +1 -1
  54. package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
  55. package/components/overlays/types/OverlayConfig.ts +16 -5
  56. package/components/select-rich/test/lion-select-rich.test.js +3 -0
  57. package/custom-elements.json +32144 -19531
  58. package/dist-types/components/button/src/LionButton.d.ts +1 -0
  59. package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
  60. package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
  61. package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
  62. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts +3 -0
  63. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts.map +1 -0
  64. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts +24 -0
  65. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts.map +1 -0
  66. package/dist-types/components/dialog/src/LionDialog.d.ts +3 -1
  67. package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
  68. package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts.map +1 -1
  69. package/dist-types/components/form-core/src/registration/FormRegistrarMixin.d.ts.map +1 -1
  70. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  71. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  72. package/dist-types/components/input-datepicker/src/localizeNamespaceLoader.d.ts.map +1 -1
  73. package/dist-types/components/menu/index.d.ts +3 -0
  74. package/dist-types/components/menu/index.d.ts.map +1 -0
  75. package/dist-types/components/menu/lion-menu-overlay.d.ts +2 -0
  76. package/dist-types/components/menu/lion-menu-overlay.d.ts.map +1 -0
  77. package/dist-types/components/menu/lion-menu.d.ts +2 -0
  78. package/dist-types/components/menu/lion-menu.d.ts.map +1 -0
  79. package/dist-types/components/menu/lion-spinbutton.d.ts +2 -0
  80. package/dist-types/components/menu/lion-spinbutton.d.ts.map +1 -0
  81. package/dist-types/components/menu/lion-toolbar.d.ts +2 -0
  82. package/dist-types/components/menu/lion-toolbar.d.ts.map +1 -0
  83. package/dist-types/components/menu/lion-tree.d.ts +2 -0
  84. package/dist-types/components/menu/lion-tree.d.ts.map +1 -0
  85. package/dist-types/components/menu/src/InteractiveListMixin.d.ts +611 -0
  86. package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -0
  87. package/dist-types/components/menu/src/LionMenu.d.ts +563 -0
  88. package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -0
  89. package/dist-types/components/menu/src/LionMenuHybrid.d.ts +19 -0
  90. package/dist-types/components/menu/src/LionMenuHybrid.d.ts.map +1 -0
  91. package/dist-types/components/menu/src/LionMenuOverlay.d.ts +1007 -0
  92. package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +1 -0
  93. package/dist-types/components/menu/src/LionSpinbutton.d.ts +58 -0
  94. package/dist-types/components/menu/src/LionSpinbutton.d.ts.map +1 -0
  95. package/dist-types/components/menu/src/LionToolbar.d.ts +8 -0
  96. package/dist-types/components/menu/src/LionToolbar.d.ts.map +1 -0
  97. package/dist-types/components/menu/src/LionTree.d.ts +445 -0
  98. package/dist-types/components/menu/src/LionTree.d.ts.map +1 -0
  99. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +31 -0
  100. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +1 -0
  101. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +493 -0
  102. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -0
  103. package/dist-types/components/menu/src/utils/isInView.d.ts +9 -0
  104. package/dist-types/components/menu/src/utils/isInView.d.ts.map +1 -0
  105. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +42 -0
  106. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -0
  107. package/dist-types/components/menu/src/utils/uuid.d.ts +2 -0
  108. package/dist-types/components/menu/src/utils/uuid.d.ts.map +1 -0
  109. package/dist-types/components/menu/test/lion-menu.test.d.ts +2 -0
  110. package/dist-types/components/menu/test/lion-menu.test.d.ts.map +1 -0
  111. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +13 -0
  112. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -0
  113. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts +238 -0
  114. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts.map +1 -0
  115. package/dist-types/components/navigation-bar/src/types.d.ts +85 -0
  116. package/dist-types/components/navigation-bar/src/types.d.ts.map +1 -0
  117. package/dist-types/components/overlays/src/OverlayController.d.ts +9 -13
  118. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  119. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  120. package/dist-types/components/overlays/src/utils/get-focusable-elements.d.ts.map +1 -1
  121. package/dist-types/components/overlays/src/utils/is-equal-config.d.ts.map +1 -1
  122. package/dist-types/components/overlays/types/OverlayConfig.d.ts +10 -2
  123. package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
  124. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +8 -2
  125. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  126. package/dist-types/exports/define/lion-item.d.ts +2 -0
  127. package/dist-types/exports/define/lion-item.d.ts.map +1 -0
  128. package/dist-types/exports/define/lion-menu-overlay.d.ts +2 -0
  129. package/dist-types/exports/define/lion-menu-overlay.d.ts.map +1 -0
  130. package/dist-types/exports/define/lion-menu.d.ts +2 -0
  131. package/dist-types/exports/define/lion-menu.d.ts.map +1 -0
  132. package/dist-types/exports/define/lion-navigation-bar.d.ts +2 -0
  133. package/dist-types/exports/define/lion-navigation-bar.d.ts.map +1 -0
  134. package/dist-types/exports/define/lion-toolbar.d.ts +2 -0
  135. package/dist-types/exports/define/lion-toolbar.d.ts.map +1 -0
  136. package/dist-types/exports/define/lion-tree.d.ts +2 -0
  137. package/dist-types/exports/define/lion-tree.d.ts.map +1 -0
  138. package/dist-types/exports/menu.d.ts +7 -0
  139. package/dist-types/exports/menu.d.ts.map +1 -0
  140. package/dist-types/exports/navigation-bar.d.ts +2 -0
  141. package/dist-types/exports/navigation-bar.d.ts.map +1 -0
  142. package/dist-types/exports/overlays.d.ts +3 -0
  143. package/dist-types/exports/overlays.d.ts.map +1 -1
  144. package/dist-types/tsconfig.tsbuildinfo +1 -1
  145. package/docs/components/checkbox-group/overview.md +2 -2
  146. package/docs/components/collapsible/overview.md +1 -1
  147. package/docs/components/combobox/api-table.md +21 -8
  148. package/docs/components/combobox/overview.md +1 -1
  149. package/docs/components/combobox/use-cases.md +1 -1
  150. package/docs/components/dialog/api-table.md +31 -6
  151. package/docs/components/dialog/overview.md +1 -1
  152. package/docs/components/fieldset/overview.md +5 -5
  153. package/docs/components/form/overview.md +3 -3
  154. package/docs/components/form/use-cases.md +2 -2
  155. package/docs/components/icon/use-cases.md +1 -1
  156. package/docs/components/input/overview.md +1 -1
  157. package/docs/components/input-amount/overview.md +3 -3
  158. package/docs/components/input-date/overview.md +3 -3
  159. package/docs/components/input-datepicker/api-table.md +21 -4
  160. package/docs/components/input-datepicker/overview.md +5 -5
  161. package/docs/components/input-email/overview.md +2 -2
  162. package/docs/components/input-file/overview.md +1 -1
  163. package/docs/components/input-iban/overview.md +2 -2
  164. package/docs/components/input-range/overview.md +2 -2
  165. package/docs/components/input-stepper/overview.md +1 -1
  166. package/docs/components/input-tel/overview.md +1 -1
  167. package/docs/components/input-tel/use-cases.md +6 -6
  168. package/docs/components/input-tel-dropdown/overview.md +1 -1
  169. package/docs/components/listbox/use-cases.md +1 -1
  170. package/docs/components/menu/api-table.md +450 -0
  171. package/docs/components/menu/applyMenuDemoStyles.js +173 -0
  172. package/docs/components/menu/extensions.md +235 -0
  173. package/docs/components/menu/index.md +13 -0
  174. package/docs/components/menu/overview.md +422 -0
  175. package/docs/components/radio-group/overview.md +3 -3
  176. package/docs/components/select/use-cases.md +1 -1
  177. package/docs/components/select-rich/api-table.md +21 -5
  178. package/docs/components/select-rich/use-cases.md +2 -2
  179. package/docs/components/tabs/examples.md +1 -1
  180. package/docs/components/tooltip/api-table.md +36 -11
  181. package/docs/fundamentals/index.md +4 -4
  182. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  183. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  184. package/docs/fundamentals/rationales/accessibility.md +1 -1
  185. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  186. package/docs/fundamentals/systems/core/overview.md +1 -1
  187. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  188. package/docs/fundamentals/systems/form/model-value.md +1 -1
  189. package/docs/fundamentals/systems/form/overview.md +28 -28
  190. package/docs/fundamentals/systems/form/validate.md +2 -2
  191. package/docs/fundamentals/systems/localize/overview.md +4 -4
  192. package/docs/fundamentals/systems/overlays/api-table.md +34 -34
  193. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  194. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  195. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  196. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  197. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  198. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  199. package/docs/guides/how-to/create-a-form.md +4 -4
  200. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  201. package/docs/guides/how-to/index.md +1 -1
  202. package/docs/guides/index.md +8 -8
  203. package/docs/guides/principles/index.md +5 -5
  204. package/docs/guides/principles/styling.md +1 -1
  205. package/exports/collapsible.js +1 -0
  206. package/exports/define/lion-item.js +3 -0
  207. package/exports/define/lion-menu-overlay.js +3 -0
  208. package/exports/define/lion-menu.js +3 -0
  209. package/exports/define/lion-navigation-bar.js +3 -0
  210. package/exports/define/lion-toolbar.js +3 -0
  211. package/exports/define/lion-tree.js +3 -0
  212. package/exports/menu.js +7 -0
  213. package/exports/navigation-bar.js +1 -0
  214. package/exports/overlays.js +6 -0
  215. package/menu.js +7 -0
  216. package/navigation-bar.js +1 -0
  217. package/overlays.js +6 -0
  218. package/package.json +1 -1
@@ -0,0 +1,94 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ import { LionMenu } from './LionMenu.js';
3
+ import { setChecked, toggleChecked } from './utils/listItemInteractions.js';
4
+
5
+ /**
6
+ * @typedef {import('../types/InteractiveListMixinTypes.js').InteractiveListItemRole} InteractiveListItemRole
7
+ */
8
+
9
+ export class LionToolbar extends LionMenu {
10
+ constructor() {
11
+ super();
12
+
13
+ this._listRole = 'toolbar';
14
+ this.bar = true;
15
+ }
16
+
17
+ /**
18
+ * Allows groups ([role=radiogroup|checkboxgroup]) within one level.
19
+ * In case we deal with [role=checkbox], we treat it as multiple choice group
20
+ * @override InteractiveListMixin
21
+ * @param {number} index
22
+ */
23
+ setCheckedIndex(index) {
24
+ const item = this.listItems[index];
25
+ if (item) {
26
+ // @ts-ignore - InteractiveListItemRole type
27
+ const role = /** @type {InteractiveListItemRole} */ (item.getAttribute('role'));
28
+ let listItemsWithinGroup = this.listItems;
29
+ let multiple = this.multipleChoice;
30
+ if (role === 'radio' || role === 'checkbox') {
31
+ /**
32
+ * If index = 1 (radio 'Red'), closest group will be div[role=radiogroup]
33
+ * @example
34
+ * <lion-toolbar>
35
+ * <div role="radiogroup" aria-label="Text Color">
36
+ * <button role="radio" aria-checked="false">Blue</button>
37
+ * <button role="radio" aria-checked="true">Red</button>
38
+ * <button role="radio" aria-checked="false">Green</button>
39
+ * </div>
40
+ * <div role="separator"></div>
41
+ * <div role="checkboxgroup" aria-label="Text Color">
42
+ * <button role="checkbox" aria-checked="false">Blue</button>
43
+ * <button role="checkbox" aria-checked="true">Red</button>
44
+ * <button role="checkbox" aria-checked="false">Green</button>
45
+ * </div>
46
+ * </lion-toolbar>
47
+ */
48
+ let closestGroup;
49
+ if (role === 'radio') {
50
+ closestGroup = item.closest('[role="radiogroup"]');
51
+ } else if (role === 'checkbox') {
52
+ closestGroup = item.closest('[role="checkboxgroup"]');
53
+ }
54
+
55
+ const group = closestGroup && this.contains(closestGroup) ? closestGroup : this;
56
+ listItemsWithinGroup = this.listItems.filter(listItem => group.contains(listItem));
57
+ multiple = role === 'checkbox';
58
+ }
59
+
60
+ if (!multiple) {
61
+ // Uncheck all
62
+ listItemsWithinGroup.forEach(listItem => {
63
+ setChecked(listItem, false);
64
+ });
65
+ setChecked(this.listItems[index]);
66
+ } else {
67
+ toggleChecked(this.listItems[index]);
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param {*} newItems
74
+ */
75
+ _initListItems(newItems) {
76
+ super._initListItems(newItems);
77
+
78
+ // @ts-ignore - forEach parameter
79
+ newItems.forEach(item => {
80
+ if (
81
+ item.getAttribute('role') === 'button' ||
82
+ (item.tagName === 'BUTTON' && !item.hasAttribute('role'))
83
+ ) {
84
+ const initialAriaPressed = item.getAttribute('aria-pressed');
85
+ item.setAttribute('aria-pressed', initialAriaPressed || 'false');
86
+
87
+ item.addEventListener('click', () => {
88
+ const ariaPressed = item.getAttribute('aria-pressed') === 'true';
89
+ item.setAttribute('aria-pressed', `${!ariaPressed}`);
90
+ });
91
+ }
92
+ });
93
+ }
94
+ }
@@ -0,0 +1,52 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ import { LitElement } from 'lit';
3
+ import { MultiLevelListMixin } from './MultiLevelListMixin.js';
4
+
5
+ // @ts-ignore - class extension with mixin
6
+ export class LionTree extends MultiLevelListMixin(LitElement) {
7
+ constructor() {
8
+ super();
9
+
10
+ /** @configure InteractiveListMixin */
11
+ this._listRole = 'tree';
12
+ /** @configure InteractiveListMixin */
13
+ this._activateOnTypedChars = true;
14
+ /** @configure DisclosureMixin */
15
+ this.invokerInteraction = 'click';
16
+ }
17
+
18
+ /**
19
+ * @param {*} changedProperties
20
+ */
21
+ updated(changedProperties) {
22
+ super.updated(changedProperties);
23
+
24
+ if (changedProperties.has('level')) {
25
+ if (this.level > 0) {
26
+ this._listNode.setAttribute('role', 'group');
27
+ }
28
+ this.__setAriaLevelForListItems();
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @param {*} newItems
34
+ */
35
+ _initListItems(newItems) {
36
+ // @ts-ignore - parameter types
37
+ super._initListItems(newItems);
38
+
39
+ this.__setAriaLevelForListItems();
40
+ // @ts-ignore - forEach parameters
41
+ newItems.forEach((item, index) => {
42
+ item.setAttribute('aria-posinset', `${index + 1}`);
43
+ item.setAttribute('aria-setsize', `${this.listItems.length}`);
44
+ });
45
+ }
46
+
47
+ __setAriaLevelForListItems() {
48
+ this.listItems.forEach(item => {
49
+ item.setAttribute?.('aria-level', `${this.level + 1}`);
50
+ });
51
+ }
52
+ }
@@ -0,0 +1,297 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @param {any} superclass
4
+ */
5
+ /** @type {any} */
6
+
7
+ const styles = `
8
+ <style>
9
+ /** More button is hidden by default */
10
+ [data-more-button-wrapper] {
11
+ display: none;
12
+ }
13
+
14
+ [data-more-button-menu]:has([role='listitem'] [active]:focus):not(
15
+ :has([role='listitem'] [active][aria-expanded='true'])
16
+ ) {
17
+ overflow: visible;
18
+ width: auto;
19
+ height: auto;
20
+ position: absolute;
21
+ top: 100%;
22
+ background-color: var(--oj2_bg_default);
23
+ }
24
+ [data-more-button-menu],
25
+ [data-more-button-menu]:has([role='listitem'] [active][aria-expanded='true']) {
26
+ overflow: hidden;
27
+ width: 1px;
28
+ height: 1px;
29
+ }
30
+
31
+ :host([responsive-mode='desktop']) [level='1'] > [slot='list'] > [role='listitem'] > button,
32
+ :host([responsive-mode='desktop']) [level='1'] > [slot='list'] > [role='listitem'] > a,
33
+ :host([responsive-mode='desktop'])
34
+ [level='1']
35
+ > [slot='list']
36
+ > [data-more-button-wrapper]
37
+ > [role='listitem']
38
+ > button,
39
+ :host([responsive-mode='desktop'])
40
+ [level='1']
41
+ > [slot='list']
42
+ > [data-more-button-wrapper]
43
+ > [role='listitem']
44
+ > a {
45
+ white-space: nowrap;
46
+ }
47
+ /**
48
+ * TODO listitem rigth margin/gap is not taken at the account ATM
49
+ */
50
+ /**
51
+ * More button relies on the fact that the menu items do not break the line
52
+ * TODO is it optimal?
53
+ */
54
+ :host([responsive-mode='desktop']) > [role='list'] > [role='listitem'] > button,
55
+ :host([responsive-mode='desktop']) > [role='list'] > [role='listitem'] > a {
56
+ white-space: nowrap;
57
+ }
58
+ </style>
59
+ `;
60
+
61
+ // @ts-ignore - JS mixin typing
62
+ export const MoreButtonMenuMixin = superclass =>
63
+ class MoreButtonMenuMixinClass extends superclass {
64
+ constructor() {
65
+ super();
66
+ this.__resizeTimeout = null;
67
+ this.isMoreButtonShown = null;
68
+ this.handleResize = this.handleResize.bind(this);
69
+ }
70
+
71
+ disconnectedCallback() {
72
+ window.removeEventListener('resize', this.handleResize);
73
+ if (this.__resizeTimeout) {
74
+ clearTimeout(this.__resizeTimeout);
75
+ }
76
+ super.disconnectedCallback?.();
77
+ }
78
+
79
+ _initMoreButtonMenu() {
80
+ this._createMoreButtonWrapper();
81
+ window.addEventListener('resize', this.handleResize);
82
+ this.handleResize();
83
+ }
84
+
85
+ // eslint-disable-next-line class-methods-use-this
86
+ _getDeepActiveElement() {
87
+ let host = document.activeElement || document.body;
88
+ while (host && host.shadowRoot && host.shadowRoot.activeElement) {
89
+ host = host.shadowRoot.activeElement;
90
+ }
91
+ return host;
92
+ }
93
+
94
+ _createMoreButtonWrapper() {
95
+ const moreButtonWrapper = document.createElement('div');
96
+ const style = document.createElement('style');
97
+ style.textContent = styles;
98
+ moreButtonWrapper.appendChild(style);
99
+ moreButtonWrapper.setAttribute('data-more-button-wrapper', '');
100
+ [...this.getMoreButtonSlotProjection().childNodes].forEach(node =>
101
+ moreButtonWrapper.appendChild(node),
102
+ );
103
+ const moreButton = moreButtonWrapper.querySelector('button');
104
+ moreButton?.setAttribute('data-more-button', '');
105
+ moreButton?.setAttribute('tabindex', '-1');
106
+ moreButton?.setAttribute('aria-expanded', 'false');
107
+ const moreButtonMenu = document.createElement('div');
108
+ moreButtonMenu.setAttribute('data-more-button-menu', '');
109
+ moreButtonWrapper.appendChild(moreButtonMenu);
110
+ moreButtonMenu.setAttribute('role', 'none');
111
+ this._listNode.appendChild(moreButtonWrapper);
112
+
113
+ const isElementDirectFocusableItemUnderMoreButtonMenu = element => {
114
+ const targetTagName = element?.tagName;
115
+ return (
116
+ element?.parentElement?.parentElement === moreButtonMenu &&
117
+ (targetTagName === 'BUTTON' || targetTagName === 'A')
118
+ );
119
+ };
120
+
121
+ moreButtonMenu.addEventListener('focusin', event => {
122
+ const target = event?.target;
123
+ if (isElementDirectFocusableItemUnderMoreButtonMenu(target)) {
124
+ if (target?.getAttribute('aria-expanded') === 'true') {
125
+ target?.click();
126
+ }
127
+ moreButton?.setAttribute('aria-expanded', 'true');
128
+ }
129
+ });
130
+
131
+ moreButtonMenu.addEventListener('focusout', event => {
132
+ const target = event?.target;
133
+ if (isElementDirectFocusableItemUnderMoreButtonMenu(target)) {
134
+ moreButton?.setAttribute('aria-expanded', 'false');
135
+ }
136
+ });
137
+
138
+ this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
139
+
140
+ moreButton?.addEventListener('mousedown', () => {
141
+ this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
142
+ const focusableFirstLevelItems = moreButtonMenu.querySelectorAll(
143
+ ':scope > [role="listitem"] > a, :scope > [role="listitem"] > button',
144
+ );
145
+
146
+ [...focusableFirstLevelItems]?.forEach(focusableFirstLevelItem => {
147
+ if (this._getDeepActiveElement() === focusableFirstLevelItem) {
148
+ this.hasMoreButtonMenuAnyFocusedFirstLevelItems = true;
149
+ }
150
+ });
151
+ });
152
+
153
+ moreButton?.addEventListener('click', () => {
154
+ if (!this.hasMoreButtonMenuAnyFocusedFirstLevelItems) {
155
+ moreButtonMenu
156
+ .querySelector(':scope > [role="listitem"] > a, :scope > [role="listitem"] > button')
157
+ ?.focus();
158
+ }
159
+ });
160
+ }
161
+
162
+ getMoreButtonSlotProjection() {
163
+ return this.querySelector('[slot="more-button"]');
164
+ }
165
+
166
+ getMoreButtonMenu() {
167
+ return this.querySelector('[data-more-button-menu]');
168
+ }
169
+
170
+ getMoreButtonMenuWrapper() {
171
+ return this.querySelector('[data-more-button-wrapper]');
172
+ }
173
+
174
+ displayMoreButton() {
175
+ this.getMoreButtonMenuWrapper().style.display = 'block';
176
+ this.isMoreButtonShown = true;
177
+ }
178
+
179
+ hideMoreButton() {
180
+ this.getMoreButtonMenuWrapper().style.display = 'none';
181
+ this.isMoreButtonShown = false;
182
+ }
183
+
184
+ doItemsFit() {
185
+ this.style.display = 'none';
186
+ const parentWidth = this.parentElement.clientWidth;
187
+ this.style.display = '';
188
+ return (
189
+ this._listNode.scrollWidth === this._listNode.clientWidth &&
190
+ this._listNode.scrollWidth <= parentWidth
191
+ );
192
+ }
193
+
194
+ getListItems() {
195
+ const listNode = this._listNode;
196
+ return Array.from(listNode.querySelectorAll(':scope > [role="listitem"]'));
197
+ }
198
+
199
+ /**
200
+ * @param {Node} listItem
201
+ */
202
+ getListItemWithAdjacentNodes = listItem => {
203
+ const markerNodeType = this.nodeType && Node.COMMENT_NODE;
204
+ /** @param {Node | null | undefined} node */
205
+ const isMarkerComment = node => node?.nodeType === markerNodeType && node?.nodeValue === '';
206
+
207
+ const previousNodes = [];
208
+ let { previousSibling } = listItem;
209
+ while (previousSibling) {
210
+ previousNodes.unshift(previousSibling);
211
+ if (isMarkerComment(previousSibling)) {
212
+ break;
213
+ }
214
+ ({ previousSibling } = previousSibling);
215
+ }
216
+
217
+ const nextNodes = [];
218
+ let { nextSibling } = listItem;
219
+ while (nextSibling) {
220
+ nextNodes.push(nextSibling);
221
+
222
+ if (isMarkerComment(nextSibling)) {
223
+ break;
224
+ }
225
+
226
+ ({ nextSibling } = nextSibling);
227
+ }
228
+
229
+ const fragment = document.createDocumentFragment();
230
+ const nodes = [...previousNodes, listItem, ...nextNodes];
231
+ nodes.forEach(node => fragment.appendChild(node));
232
+ return fragment;
233
+ };
234
+
235
+ moveItemToMoreButtonMenuFromMainMenu() {
236
+ const moreButtonMenuElement = this.getMoreButtonMenu();
237
+ const listItems = this._listNode.querySelectorAll(':scope > [role="listitem"]');
238
+ const listItem = listItems[listItems.length - 1];
239
+
240
+ const fragment = this.getListItemWithAdjacentNodes(listItem);
241
+ moreButtonMenuElement.prepend(fragment);
242
+ listItem.style.display = '';
243
+ this.displayMoreButton();
244
+ }
245
+
246
+ moveAllItemsToMainMenuFromMoreButtonMenu() {
247
+ const moreButtonMenu = this.getMoreButtonMenu();
248
+ const moreButtonMenuWrapper = this.getMoreButtonMenuWrapper();
249
+ if (moreButtonMenu.childNodes.length === 0) {
250
+ return;
251
+ }
252
+ [...moreButtonMenu.childNodes].forEach(node => {
253
+ moreButtonMenuWrapper.before(node);
254
+ });
255
+ }
256
+
257
+ renderAllItemsInMainMenu() {
258
+ this.moveAllItemsToMainMenuFromMoreButtonMenu();
259
+ }
260
+
261
+ hideItemsOneByOneInMainMenuUntilTheyFit() {
262
+ let hiddenItemsCount = 0;
263
+ const mainMenuItems = this.getListItems();
264
+ for (let i = mainMenuItems.length - 1; i >= 0 && !this.doItemsFit(); i -= 1) {
265
+ mainMenuItems[i].style.display = 'none';
266
+ hiddenItemsCount += 1;
267
+ }
268
+ return hiddenItemsCount;
269
+ }
270
+
271
+ moveHiddenItemsFromMainMenuToMoreButtonMenu(hiddenItemsCount) {
272
+ for (let i = 0; i < hiddenItemsCount; i += 1) {
273
+ this.moveItemToMoreButtonMenuFromMainMenu();
274
+ }
275
+ }
276
+
277
+ handleResize() {
278
+ if (this.__resizeTimeout) {
279
+ return;
280
+ }
281
+
282
+ this.__resizeTimeout = setTimeout(() => {
283
+ this.__resizeTimeout = null;
284
+
285
+ this.renderAllItemsInMainMenu();
286
+ this.hideMoreButton();
287
+
288
+ if (this.doItemsFit()) {
289
+ return;
290
+ }
291
+
292
+ this.displayMoreButton();
293
+ const hiddenItemsCount = this.hideItemsOneByOneInMainMenuUntilTheyFit();
294
+ this.moveHiddenItemsFromMainMenuToMoreButtonMenu(hiddenItemsCount);
295
+ }, 50);
296
+ }
297
+ };