material-inspired-component-library 1.2.2 → 2.0.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 (59) hide show
  1. package/README.md +18 -0
  2. package/components/badge/README.md +65 -0
  3. package/components/badge/index.scss +68 -0
  4. package/components/button/README.md +1 -1
  5. package/components/button/index.scss +60 -89
  6. package/components/button/index.ts +5 -0
  7. package/components/card/index.scss +29 -30
  8. package/components/checkbox/index.scss +1 -6
  9. package/components/dialog/index.scss +11 -7
  10. package/components/iconbutton/README.md +1 -1
  11. package/components/iconbutton/index.scss +46 -80
  12. package/components/iconbutton/index.ts +5 -0
  13. package/components/list/index.scss +39 -30
  14. package/components/list/index.ts +10 -9
  15. package/components/menu/README.md +67 -4
  16. package/components/menu/index.scss +29 -29
  17. package/components/menu/index.ts +47 -16
  18. package/components/navigationrail/README.md +150 -0
  19. package/components/navigationrail/index.scss +468 -0
  20. package/components/{checkbox → navigationrail}/index.ts +12 -7
  21. package/components/radio/index.scss +22 -12
  22. package/components/select/index.scss +5 -11
  23. package/components/textfield/index.scss +8 -2
  24. package/dist/badge.css +1 -0
  25. package/dist/badge.js +1 -0
  26. package/dist/bottomsheet.css +1 -1
  27. package/dist/button.css +1 -1
  28. package/dist/card.css +1 -1
  29. package/dist/checkbox.css +1 -1
  30. package/dist/components/menu/index.d.ts +0 -11
  31. package/dist/components/navigationrail/index.d.ts +5 -0
  32. package/dist/dialog.css +1 -1
  33. package/dist/iconbutton.css +1 -1
  34. package/dist/list.css +1 -1
  35. package/dist/menu.css +1 -1
  36. package/dist/micl.css +1 -1
  37. package/dist/micl.js +1 -1
  38. package/dist/navigationrail.css +1 -0
  39. package/dist/navigationrail.js +1 -0
  40. package/dist/radio.css +1 -1
  41. package/dist/select.css +1 -1
  42. package/dist/slider.css +1 -1
  43. package/dist/switch.css +1 -1
  44. package/dist/textfield.css +1 -1
  45. package/docs/button.html +5 -5
  46. package/docs/docs.css +2 -1
  47. package/docs/docs.js +2 -2
  48. package/docs/index.html +35 -2
  49. package/docs/menu.html +183 -3
  50. package/docs/micl.css +1 -1
  51. package/docs/micl.js +1 -1
  52. package/docs/navigationrail.html +81 -0
  53. package/micl.ts +20 -25
  54. package/package.json +10 -7
  55. package/styles/statelayer.scss +14 -0
  56. package/styles.scss +18 -1
  57. package/webpack.config.js +37 -0
  58. package/dist/components/checkbox/index.d.ts +0 -5
  59. package/styles/ripple.scss +0 -50
@@ -20,12 +20,12 @@
20
20
  // SOFTWARE.
21
21
 
22
22
  @use '../../styles/motion';
23
- @use '../../styles/ripple';
24
23
  @use '../../styles/shapes';
25
24
  @use '../../styles/statelayer';
26
25
  @use '../../styles/typography';
27
26
 
28
27
  :root {
28
+ --md-sys-list-padding: 8px;
29
29
  --md-sys-list-item-one-height: 56px;
30
30
  --md-sys-list-item-two-height: 72px;
31
31
  --md-sys-list-item-three-height: 88px;
@@ -43,7 +43,7 @@
43
43
  --md-sys-accordion-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
44
44
 
45
45
  margin: 0;
46
- padding: 8px 0;
46
+ padding: var(--md-sys-list-padding) 0;
47
47
  interpolate-size: allow-keywords;
48
48
  list-style-type: none;
49
49
 
@@ -71,9 +71,6 @@
71
71
  pointer-events: none;
72
72
  }
73
73
  &:not(.micl-list-item--disabled) {
74
- @include ripple.effect;
75
- --miclripple: 1;
76
-
77
74
  cursor: pointer;
78
75
  }
79
76
  }
@@ -98,6 +95,7 @@
98
95
  .micl-list-item-one,
99
96
  .micl-list-item-two,
100
97
  .micl-list-item-three {
98
+ --statelayer-color: var(--md-sys-color-on-surface);
101
99
  --md-sys-list-item-thumbnail-aspect-ratio: 1.778;
102
100
 
103
101
  box-sizing: border-box;
@@ -105,24 +103,39 @@
105
103
  align-items: center;
106
104
  column-gap: var(--md-sys-list-item-space);
107
105
  padding-inline: var(--md-sys-list-item-space);
106
+ border: none;
108
107
  border-radius: var(--md-sys-shape-corner-none);
109
108
  background-color: var(--md-sys-list-item-container-color);
109
+ background-image:
110
+ radial-gradient(circle at var(--micl-x, center) var(--micl-y, center), transparent 0%, rgb(from var(--statelayer-color) r g b / var(--statelayer-opacity)) 10%, transparent 10%),
111
+ linear-gradient(rgb(from var(--statelayer-color) r g b / var(--statelayer-opacity)));
112
+ background-repeat: no-repeat;
113
+ background-size: 10000%, 100%;
110
114
  list-style: none;
111
- transition: background-color var(--md-sys-list-motion-duration) linear;
115
+ transition:
116
+ background-size 3000ms,
117
+ --statelayer-opacity var(--md-sys-list-motion-duration) linear;
112
118
 
119
+ &:not(:has(> button)) {
120
+ --micl-ripple: 1;
121
+ }
113
122
  &> a,
123
+ &> button,
114
124
  &> label {
125
+ box-sizing: border-box;
115
126
  display: inherit;
116
127
  align-items: inherit;
117
128
  column-gap: inherit;
118
- border-radius: inherit;
119
- background-color: inherit;
129
+ min-height: inherit;
120
130
  inline-size: 100%;
121
131
  padding-inline: var(--md-sys-list-item-space);
132
+ border: none;
133
+ border-radius: inherit;
134
+ background-color: transparent;
122
135
  text-decoration: none;
123
136
  cursor: pointer;
124
137
  }
125
- &:has(> a,> label) {
138
+ &:has(> a,> button,> label) {
126
139
  padding-inline: 0;
127
140
  }
128
141
  &:disabled,
@@ -139,14 +152,14 @@
139
152
  .micl-list-item__thumbnail {
140
153
  opacity: 38%;
141
154
  }
142
- a, label {
155
+ a, button, label {
143
156
  pointer-events: none;
144
157
  cursor: auto;
145
158
  }
146
159
  }
147
160
  &:not(:disabled):not(.micl-list-item--disabled) {
148
161
  &:hover {
149
- background-color: color-mix(in srgb, var(--md-sys-list-item-container-color), var(--md-sys-color-on-surface) var(--md-sys-state-hover-state-layer-opacity));
162
+ --statelayer-opacity: var(--md-sys-state-hover-state-layer-opacity);
150
163
 
151
164
  &:has(input[type=checkbox]) {
152
165
  cursor: pointer;
@@ -156,26 +169,25 @@
156
169
  }
157
170
  }
158
171
  &:focus-visible {
172
+ --statelayer-opacity: var(--md-sys-state-focus-state-layer-opacity);
173
+
159
174
  outline: var(--md-sys-state-focus-indicator-thickness) solid var(--md-sys-color-secondary);
160
- outline-offset: calc(-1 * var(--md-sys-state-focus-indicator-thickness));
161
- background-color: color-mix(in srgb, var(--md-sys-list-item-container-color), var(--md-sys-color-on-surface) var(--md-sys-state-focus-state-layer-opacity));
175
+ outline-offset: var(--md-sys-state-focus-indicator-inner-offset);
162
176
 
163
177
  .micl-list-item__icon {
164
178
  font-variation-settings: 'FILL' 1;
165
179
  }
166
180
  }
167
181
  &:active {
168
- background-color: color-mix(in srgb, var(--md-sys-list-item-container-color), var(--md-sys-color-on-surface) var(--md-sys-state-pressed-state-layer-opacity));
182
+ --statelayer-opacity: var(--md-sys-state-pressed-state-layer-opacity);
183
+
184
+ background-size: 0%, 100%;
185
+ transition: background-size 0ms;
169
186
 
170
187
  .micl-list-item__icon {
171
188
  font-variation-settings: 'FILL' 1;
172
189
  }
173
190
  }
174
- &:has(input[type=checkbox]) {
175
- @include ripple.effect;
176
- --miclripple: 1;
177
- --md-sys-ripple-background-color: var(--md-sys-color-primary);
178
- }
179
191
  &:has(input[type=checkbox]:checked) {
180
192
  background-color: var(--md-sys-color-secondary-container);
181
193
 
@@ -187,18 +199,11 @@
187
199
  .micl-list-item__trailing-text {
188
200
  color: var(--md-sys-color-on-surface);
189
201
  }
190
- &:focus-visible {
191
- background-color: color-mix(in srgb, var(--md-sys-color-secondary-container), var(--md-sys-color-on-surface) var(--md-sys-state-focus-state-layer-opacity));
192
- }
193
- &:active {
194
- background-color: color-mix(in srgb, var(--md-sys-color-secondary-container), var(--md-sys-color-on-surface) var(--md-sys-state-pressed-state-layer-opacity));
195
- }
196
202
  }
197
203
  input[type=checkbox]:not(:disabled):hover,
198
204
  input[type=checkbox]:not(:disabled):active {
199
205
  --checkbox-state-layer-color: initial;
200
206
  --checkbox-outline-color: initial;
201
- --md-sys-ripple-background-color: transparent;
202
207
  }
203
208
  }
204
209
  }
@@ -206,7 +211,7 @@
206
211
  .micl-list-item-one {
207
212
  min-height: var(--md-sys-list-item-one-height);
208
213
 
209
- &:not(:has(> a,> label)) {
214
+ &:not(:has(> a,> button,> label)) {
210
215
  padding-block-start: var(--md-sys-list-item-one-padding);
211
216
  padding-block-end: var(--md-sys-list-item-one-padding);
212
217
 
@@ -217,6 +222,7 @@
217
222
  }
218
223
  }
219
224
  &> a,
225
+ &> button,
220
226
  &> label {
221
227
  padding-block-start: var(--md-sys-list-item-one-padding);
222
228
  padding-block-end: var(--md-sys-list-item-one-padding);
@@ -231,7 +237,7 @@
231
237
  .micl-list-item-two {
232
238
  min-height: var(--md-sys-list-item-two-height);
233
239
 
234
- &:not(:has(> a, > label)) {
240
+ &:not(:has(> a,> button,> label)) {
235
241
  padding-block-start: var(--md-sys-list-item-two-padding);
236
242
  padding-block-end: var(--md-sys-list-item-two-padding);
237
243
 
@@ -242,6 +248,7 @@
242
248
  }
243
249
  }
244
250
  &> a,
251
+ &> button,
245
252
  &> label {
246
253
  padding-block-start: var(--md-sys-list-item-two-padding);
247
254
  padding-block-end: var(--md-sys-list-item-two-padding);
@@ -261,7 +268,7 @@
261
268
  .micl-list-item-three {
262
269
  min-height: var(--md-sys-list-item-three-height);
263
270
 
264
- &:not(:has(> a, > label)) {
271
+ &:not(:has(> a,> button,> label)) {
265
272
  padding-block-start: var(--md-sys-list-item-three-padding);
266
273
  padding-block-end: var(--md-sys-list-item-three-padding);
267
274
 
@@ -272,6 +279,7 @@
272
279
  }
273
280
  }
274
281
  &> a,
282
+ &> button,
275
283
  &> label {
276
284
  padding-block-start: var(--md-sys-list-item-three-padding);
277
285
  padding-block-end: var(--md-sys-list-item-three-padding);
@@ -300,9 +308,10 @@
300
308
  }
301
309
 
302
310
  .micl-list-item__icon {
303
- color: var(--md-sys-color-on-surface-variant);
311
+ min-width: 24px;
304
312
  font-size: 24px;
305
313
  font-variation-settings: 'FILL' 0;
314
+ color: var(--md-sys-color-on-surface-variant);
306
315
  transition: font-variation-settings var(--md-sys-list-motion-duration) linear;
307
316
  }
308
317
  .micl-list-item__avatar {
@@ -58,14 +58,9 @@ export default (() =>
58
58
  return;
59
59
  }
60
60
 
61
- let currentIndex = items.findIndex(item => isSelected(item));
62
- if (currentIndex === -1) {
63
- currentIndex = items.findIndex(item => item.tabIndex === 0);
64
- }
65
- if (currentIndex === -1) {
66
- currentIndex = 0;
67
- }
68
- let nextIndex = currentIndex;
61
+ let selectedIndex = items.findIndex(item => isSelected(item)),
62
+ currentIndex = items.findIndex(item => item.tabIndex === 0),
63
+ nextIndex = currentIndex;
69
64
 
70
65
  switch (event.key) {
71
66
  case 'ArrowDown':
@@ -77,7 +72,6 @@ export default (() =>
77
72
  event.preventDefault();
78
73
  break;
79
74
  case 'Tab':
80
- let selectedIndex = items.findIndex(item => isSelected(item));
81
75
  if (selectedIndex === -1) {
82
76
  if (currentIndex !== 0) {
83
77
  items[currentIndex].tabIndex = -1;
@@ -102,6 +96,13 @@ export default (() =>
102
96
  items[currentIndex].tabIndex = -1;
103
97
  items[nextIndex].tabIndex = 0;
104
98
  items[nextIndex].focus();
99
+
100
+ const btn = items[nextIndex].querySelector(':scope > button');
101
+ btn?.dispatchEvent(new MouseEvent('mouseenter', {
102
+ bubbles : true,
103
+ cancelable: true,
104
+ view : window
105
+ }));
105
106
  }
106
107
  }
107
108
  };
@@ -4,7 +4,7 @@ This component implements the [Material Design 3 Expressive Menu](https://m3.mat
4
4
  ## Basic Usage
5
5
 
6
6
  ### HTML
7
- The Menu component is an extension of the [List component](../list/README.md). It consists of a `<nav>` element with the `micl-menu` class, which acts as the container for a `<ul>` with the `micl-list` class. The menu can be opened and closed using a control element with the `popovertarget` attribute.
7
+ The Menu component is an extension of the [List component](../list/README.md). It consists of a `<nav>` element with the `micl-menu` class, which acts as the container for a `<ul>` with the `micl-list` class. The menu can be opened and closed using a control element with the `popovertarget` attribute, where the value of the attribute matches the `id` of the menu's `<nav>` element.
8
8
 
9
9
  ```HTML
10
10
  <nav id="mymenu" class="micl-menu" popover>
@@ -17,7 +17,7 @@ The Menu component is an extension of the [List component](../list/README.md). I
17
17
  </ul>
18
18
  </nav>
19
19
 
20
- <button type="button" popovertarget="mymenu">Open Basic Menu</button>
20
+ <button type="button" popovertarget="mymenu">Open Menu</button>
21
21
  ```
22
22
 
23
23
  ### CSS
@@ -68,17 +68,18 @@ Since the Menu component is based on the **List component**, all of its list ite
68
68
  ```
69
69
 
70
70
  **Example: A menu with a link**
71
+
71
72
  To make a menu item function as a link, wrap its content in an `<a>` tag and set the `tabindex` to `-1`. This ensures the link is clickable but does not interfere with menu navigation.
72
73
 
73
74
  ```HTML
74
75
  <nav id="mymenu" class="micl-menu" popover>
75
76
  <ul class="micl-list">
76
- <li class="micl-list-item-two micl-list-item__divider">
77
+ <li class="micl-list-item-two" tabindex="0">
77
78
  <a href="https://www.nytimes.com" tabindex="-1">
78
79
  <span class="micl-list-item__icon material-symbols-outlined" aria-hidden="true">newspaper</span>
79
80
  <span class="micl-list-item__text">
80
81
  <span class="micl-list-item__headline">The New York Times</span>
81
- <span class="micl-list-item__supporting-text">Clicking this item opens the front page of The New York Times</span>
82
+ <span class="micl-list-item__supporting-text">Open the front page of The New York Times</span>
82
83
  </span>
83
84
  </a>
84
85
  </li>
@@ -86,9 +87,71 @@ To make a menu item function as a link, wrap its content in an `<a>` tag and set
86
87
  </nav>
87
88
  ```
88
89
 
90
+ **Example: A menu with a submenu**
91
+
92
+ A menu item may trigger opening a submenu when invoked by a button. Wrap the menu item content in a `<button>` tag, set its `tabindex` to `-1` and add the `popovertarget` attribute that points to the submenu. The button acts then as the control element for the submenu.
93
+
94
+ ```HTML
95
+ <nav id="mymenu" class="micl-menu" popover>
96
+ <ul class="micl-list">
97
+ <li class="micl-list-item-one" tabindex="0">
98
+ <button popovertarget="mysubmenu" tabindex="-1">
99
+ <span class="micl-list-item__text">
100
+ <span class="micl-list-item__headline">Item 1</span>
101
+ </span>
102
+ <span class="micl-list-item__icon material-symbols-outlined">arrow_right</span>
103
+ </button>
104
+ <nav id="mysubmenu" class="micl-menu" popover>
105
+ <ul class="micl-list">
106
+ <li class="micl-list-item-one" tabindex="0">
107
+ <span class="micl-list-item__text">
108
+ <span class="micl-list-item__headline">Item 1-1</span>
109
+ </span>
110
+ </li>
111
+ <li class="micl-list-item-one" tabindex="0">
112
+ <span class="micl-list-item__text">
113
+ <span class="micl-list-item__headline">Item 1-2</span>
114
+ </span>
115
+ </li>
116
+ </ul>
117
+ </nav>
118
+ </li>
119
+ <li class="micl-list-item-one" tabindex="0">
120
+ <span class="micl-list-item__text">
121
+ <span class="micl-list-item__headline">Item 2</span>
122
+ </span>
123
+ </li>
124
+ </ul>
125
+ </nav>
126
+ ```
127
+
89
128
  Adding the `micl-list-item--disabled` class to a menu item causes the item to be displayed in a disabled state.
90
129
 
91
130
  Add the `micl-list-item__divider` class to a menu item to create a divider between the item and the previous menu item.
92
131
 
132
+ ## Customizations
133
+ You can customize the appearance of the Menu component by overriding its global CSS variables. These variables are declared on the `:root` pseudo-class and can be changed on any appropriate parent element to affect its child menus.
134
+
135
+ | Variable name | Default Value | Description |
136
+ | ------------- | ----- | ----------- |
137
+ | --md-sys-menu-width-max | 280px | The maximum width allowed for a menu |
138
+ | --md-sys-menu-width-min | 112px | The minimum allowed width for a menu |
139
+
140
+ **Example: Changing the maximum width**
141
+
142
+ ```HTML
143
+ <div style="--md-sys-menu-width-max:320px">
144
+ <nav id="mymenu" class="micl-menu" popover>
145
+ <ul class="micl-list">
146
+ <li class="micl-list-item-one" tabindex="0">
147
+ <span class="micl-list-item__text">
148
+ <span class="micl-list-item__headline">Menu item</span>
149
+ </span>
150
+ </li>
151
+ </ul>
152
+ </nav>
153
+ </div>
154
+ ```
155
+
93
156
  ## Compatibility
94
157
  This component uses **popover anchor positioning** to place the menu next to its invoker. This is a modern CSS feature that may not be fully supported in all browsers. To ensure the menu works in browsers that do not support anchor positioning, wrap the menu and its invoker in a `<div>` element with `position:relative`. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor#browser_compatibility) for details.
@@ -21,23 +21,25 @@
21
21
 
22
22
  @use '../../styles/elevation';
23
23
  @use '../../styles/motion';
24
- @use '../../styles/ripple';
25
24
  @use '../../styles/shapes';
26
- @use '../../styles/statelayer';
27
25
 
28
- .micl-menu[popover] {
26
+ :root {
29
27
  --md-sys-menu-motion-spatial: #{motion.$md-sys-motion-expressive-default-spatial};
30
28
  --md-sys-menu-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
31
29
  --md-sys-menu-motion-duration-reverse: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
30
+ --md-sys-menu-width-max: 280px;
31
+ --md-sys-menu-width-min: 112px;
32
+ }
32
33
 
34
+ .micl-menu[popover] {
33
35
  position: absolute;
34
36
  inset: unset;
35
37
  inset-block-start: anchor(end);
36
38
  inset-inline-start: anchor(start);
37
- position-try-fallbacks: flip-block, flip-inline;
38
- min-width: 112px;
39
- max-width: 280px;
40
- padding-inline: 0;
39
+ position-try: most-block-size flip-block, flip-inline;
40
+ min-width: var(--md-sys-menu-width-min);
41
+ max-width: var(--md-sys-menu-width-max);
42
+ padding: 0;
41
43
  border: none;
42
44
  border-radius: var(--md-sys-shape-corner-extra-small);
43
45
  background-color: var(--md-sys-color-surface-container);
@@ -45,7 +47,7 @@
45
47
  opacity: 0;
46
48
  overflow: hidden;
47
49
  transform: scaleY(0);
48
- transform-origin: top left;
50
+ transform-origin: left top;
49
51
  transition:
50
52
  opacity var(--md-sys-menu-motion-duration-reverse) linear,
51
53
  transform var(--md-sys-menu-motion-duration-reverse) var(--md-sys-menu-motion-spatial),
@@ -61,17 +63,9 @@
61
63
  overlay var(--md-sys-menu-motion-duration) linear allow-discrete,
62
64
  display var(--md-sys-menu-motion-duration) linear allow-discrete;
63
65
 
64
- @position-try-on {
65
- transform-origin: bottom left;
66
- }
67
-
68
66
  @starting-style {
69
67
  opacity: 0;
70
68
  transform: scaleY(0);
71
-
72
- @position-try-on {
73
- transform-origin: bottom left;
74
- }
75
69
  }
76
70
  &::backdrop {
77
71
  background-color: rgba(0, 0, 0, 0.2);
@@ -89,7 +83,8 @@
89
83
  display var(--md-sys-menu-motion-duration) linear allow-discrete;
90
84
  }
91
85
 
92
- .micl-list {
86
+ &> .micl-list {
87
+ --md-sys-list-padding: 4px;
93
88
  --md-sys-list-item-one-height: 48px;
94
89
  --md-sys-list-item-one-padding: 0;
95
90
  --md-sys-list-item-two-padding: 0;
@@ -99,18 +94,6 @@
99
94
  .micl-list-item-one,
100
95
  .micl-list-item-two,
101
96
  .micl-list-item-three {
102
- &:not(.micl-list-item--disabled) {
103
- --miclripple: 1;
104
-
105
- @include ripple.effect;
106
-
107
- cursor: pointer;
108
-
109
- &:focus-visible {
110
- outline-offset: calc(-1 * var(--md-sys-state-focus-indicator-thickness));
111
- z-index: 1;
112
- }
113
- }
114
97
  &.micl-list-item__divider {
115
98
  position: relative;
116
99
  margin-block-start: 16px;
@@ -126,6 +109,23 @@
126
109
  border-top: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
127
110
  }
128
111
  }
112
+ &> .micl-menu[popover] {
113
+ transform: scaleX(0);
114
+
115
+ &:popover-open {
116
+ transform: scaleX(1);
117
+
118
+ @starting-style {
119
+ transform: scaleX(0);
120
+ }
121
+ }
122
+ &:popover-open {
123
+ &::backdrop {
124
+ background-color: rgba(0, 0, 0, 0);
125
+ }
126
+ }
127
+ }
129
128
  }
130
129
  }
131
130
  }
131
+
@@ -21,27 +21,15 @@
21
21
 
22
22
  export const menuSelector = '.micl-menu[popover]';
23
23
 
24
- /**
25
- * Set the origin for menu transformations just before transitions start.
26
- * By default, the origin is "top left" (the menu opens just below the invoker, left aligned),
27
- * but could also be "top right", "bottom left" or "bottom right".
28
- * When the browser needs to apply a position-try-fallbacks, because there is not enough space
29
- * for the menu in the default location, then the reverse transformation will be applied from
30
- * the wrong origin.
31
- * Therefore, when the menu is open, calculate the transformation origin just before the
32
- * transitions start. When the menu is closed, do the same just after the 'display:none' has
33
- * been removed by the browser (the 'toggle' event has then been triggered).
34
- */
35
24
  export default (() =>
36
25
  {
37
26
  const getOrigin = (invoker: Element, popover: Element): string =>
38
27
  {
39
- const invokerY = invoker.getBoundingClientRect().y,
40
- popoverY = popover.getBoundingClientRect().y,
41
- oldOrigin = window.getComputedStyle(popover).getPropertyValue('transform-origin');
28
+ const invokerRect = invoker.getBoundingClientRect(),
29
+ popoverRect = popover.getBoundingClientRect();
42
30
 
43
- return ((invokerY > popoverY) ? 'bottom ' : 'top ') +
44
- ((parseInt(oldOrigin) > 0) ? 'right' : 'left');
31
+ return ((invokerRect.x > popoverRect.x) ? 'right ' : 'left ') +
32
+ ((invokerRect.y > popoverRect.y) ? 'bottom' : 'top');
45
33
  };
46
34
 
47
35
  return {
@@ -72,6 +60,49 @@ export default (() =>
72
60
  }
73
61
  });
74
62
 
63
+ element.querySelectorAll<HTMLButtonElement>(
64
+ ':scope > ul.micl-list > li > button[popovertarget]'
65
+ ).forEach(submenuinvoker =>
66
+ {
67
+ if (submenuinvoker.popoverTargetElement?.matches('.micl-menu[popover]')) {
68
+ let hoverTimeout: any,
69
+ id = `--${submenuinvoker.popoverTargetElement.id}`;
70
+
71
+ if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
72
+ submenuinvoker.style.setProperty('anchor-name', id);
73
+ submenuinvoker.popoverTargetElement.style.insetBlockStart = `anchor(${id} start)`;
74
+ submenuinvoker.popoverTargetElement.style.insetInlineStart = `anchor(${id} end)`;
75
+ }
76
+
77
+ submenuinvoker.addEventListener('mouseenter', () =>
78
+ {
79
+ if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
80
+ submenuinvoker.popoverTargetElement.showPopover();
81
+ }
82
+ });
83
+ submenuinvoker.addEventListener('mouseleave', () =>
84
+ {
85
+ hoverTimeout = setTimeout(() => {
86
+ if (
87
+ submenuinvoker.popoverTargetElement instanceof HTMLElement
88
+ && !submenuinvoker.popoverTargetElement.matches(':hover')
89
+ ) {
90
+ submenuinvoker.popoverTargetElement.hidePopover();
91
+ }
92
+ }, 100);
93
+ });
94
+ submenuinvoker.popoverTargetElement.addEventListener('mouseenter', () =>
95
+ {
96
+ clearTimeout(hoverTimeout);
97
+ });
98
+ submenuinvoker.popoverTargetElement.addEventListener('mouseleave', () =>
99
+ {
100
+ if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
101
+ submenuinvoker.popoverTargetElement.hidePopover();
102
+ }
103
+ });
104
+ }
105
+ });
75
106
  }
76
107
  };
77
108
  })();