@redvars/peacock 3.3.0 → 3.3.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.
- package/dist/assets/components.css +1 -1
- package/dist/assets/components.css.map +1 -1
- package/dist/assets/styles.css +1 -1
- package/dist/assets/styles.css.map +1 -1
- package/dist/button-group-DA7xoziD.js +292 -0
- package/dist/button-group-DA7xoziD.js.map +1 -0
- package/dist/button-group.js +6 -107
- package/dist/button-group.js.map +1 -1
- package/dist/{button-BGFJfbT2.js → button-trIfcqC7.js} +2 -3
- package/dist/{button-BGFJfbT2.js.map → button-trIfcqC7.js.map} +1 -1
- package/dist/button.js +2 -3
- package/dist/button.js.map +1 -1
- package/dist/{class-map-DpeNtqCn.js → class-map-hJdvjl-W.js} +9 -3
- package/dist/class-map-hJdvjl-W.js.map +1 -0
- package/dist/code-editor.js +5 -5
- package/dist/code-editor.js.map +1 -1
- package/dist/code-highlighter.js +5 -23
- package/dist/code-highlighter.js.map +1 -1
- package/dist/custom-elements-jsdocs.json +4706 -2471
- package/dist/custom-elements.json +3444 -1007
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/peacock-loader.js +26 -496
- package/dist/peacock-loader.js.map +1 -1
- package/dist/src/accordion/accordion-item.d.ts +1 -0
- package/dist/src/breadcrumb/breadcrumb/breadcrumb.d.ts +2 -0
- package/dist/src/breadcrumb/breadcrumb-item/breadcrumb-item.d.ts +1 -0
- package/dist/src/button/button-group/button-group.d.ts +4 -0
- package/dist/src/code-editor/code-editor.d.ts +4 -3
- package/dist/src/code-highlighter/code-highlighter.d.ts +4 -7
- package/dist/src/index.d.ts +4 -0
- package/dist/src/menu/index.d.ts +3 -0
- package/dist/src/menu/menu/MenuSurfaceController.d.ts +18 -0
- package/dist/src/menu/menu/menu.d.ts +54 -12
- package/dist/src/menu/menu-item/menu-item.d.ts +12 -5
- package/dist/src/menu/sub-menu/sub-menu.d.ts +36 -0
- package/dist/src/pagination/index.d.ts +1 -0
- package/dist/src/pagination/pagination.d.ts +38 -0
- package/dist/src/popover/PopoverController.d.ts +4 -1
- package/dist/src/table/index.d.ts +1 -0
- package/dist/src/table/table.d.ts +110 -0
- package/dist/src/tabs/tab-group.d.ts +4 -0
- package/dist/src/tabs/tab-panel.d.ts +1 -0
- package/dist/src/tabs/tab.d.ts +1 -0
- package/dist/src/tabs/tabs.d.ts +2 -0
- package/dist/src/tooltip/tooltip.d.ts +1 -3
- package/dist/src/tree-view/index.d.ts +2 -0
- package/dist/src/tree-view/tree-node.d.ts +69 -0
- package/dist/src/tree-view/tree-view.d.ts +40 -0
- package/dist/src/tree-view/wc-tree-view.d.ts +6 -0
- package/dist/test/icon.test.d.ts +1 -1
- package/dist/test/menu.test.d.ts +1 -0
- package/dist/test/sub-menu.test.d.ts +1 -0
- package/dist/test/tree-view.test.d.ts +1 -0
- package/dist/{slider-Dk9CFWTG.js → tree-view-CLolVlU0.js} +3317 -1180
- package/dist/tree-view-CLolVlU0.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/readme.md +40 -40
- package/src/accordion/accordion-item.ts +2 -1
- package/src/breadcrumb/breadcrumb/breadcrumb.ts +3 -0
- package/src/breadcrumb/breadcrumb-item/breadcrumb-item.ts +1 -0
- package/src/button/button-group/button-group.ts +6 -0
- package/src/code-editor/code-editor.ts +4 -3
- package/src/code-highlighter/code-highlighter.ts +4 -22
- package/src/divider/divider.scss +2 -2
- package/src/empty-state/empty-state.scss +1 -1
- package/src/empty-state/empty-state.ts +1 -1
- package/src/index.ts +6 -2
- package/src/menu/index.ts +3 -0
- package/src/menu/menu/MenuSurfaceController.ts +61 -0
- package/src/menu/{menu-list/menu-list.scss → menu/menu.scss} +19 -4
- package/src/menu/menu/menu.ts +389 -81
- package/src/menu/menu-item/menu-item.ts +115 -36
- package/src/menu/sub-menu/sub-menu.scss +7 -0
- package/src/menu/sub-menu/sub-menu.ts +243 -0
- package/src/pagination/index.ts +1 -0
- package/src/pagination/pagination.scss +59 -0
- package/src/pagination/pagination.ts +135 -0
- package/src/peacock-loader.ts +25 -11
- package/src/popover/PopoverController.ts +13 -7
- package/src/table/index.ts +1 -0
- package/src/table/table.scss +174 -0
- package/src/table/table.ts +475 -0
- package/src/tabs/tab-group.ts +12 -6
- package/src/tabs/tab-panel.ts +1 -0
- package/src/tabs/tab.ts +1 -0
- package/src/tabs/tabs.scss +6 -5
- package/src/tabs/tabs.ts +5 -3
- package/src/text/text.css-component.scss +6 -3
- package/src/tooltip/tooltip.scss +16 -13
- package/src/tooltip/tooltip.ts +7 -9
- package/src/tree-view/demo/index.html +57 -0
- package/src/tree-view/index.ts +2 -0
- package/src/tree-view/tree-node.scss +101 -0
- package/src/tree-view/tree-node.ts +268 -0
- package/src/tree-view/tree-view.scss +12 -0
- package/src/tree-view/tree-view.ts +182 -0
- package/src/tree-view/wc-tree-view.ts +9 -0
- package/dist/class-map-DpeNtqCn.js.map +0 -1
- package/dist/slider-Dk9CFWTG.js.map +0 -1
- package/dist/src/menu/menu-list/menu-list.d.ts +0 -22
- package/dist/state-8v48Exzh.js +0 -10
- package/dist/state-8v48Exzh.js.map +0 -1
- package/src/menu/menu-list/menu-list.ts +0 -48
package/src/menu/menu/menu.ts
CHANGED
|
@@ -1,136 +1,444 @@
|
|
|
1
|
-
import { LitElement, html
|
|
2
|
-
import {
|
|
1
|
+
import { LitElement, html } from 'lit';
|
|
2
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
3
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
4
|
+
import type { Placement } from '@floating-ui/dom';
|
|
5
|
+
import styles from './menu.scss';
|
|
6
|
+
import { MenuItem } from '../menu-item/menu-item.js';
|
|
7
|
+
import { MenuSurfaceController } from './MenuSurfaceController.js';
|
|
8
|
+
|
|
9
|
+
type CloseReason =
|
|
10
|
+
| { kind: 'click-selection' }
|
|
11
|
+
| { kind: 'keydown'; key: string }
|
|
12
|
+
| { kind: 'outside-click' }
|
|
13
|
+
| { kind: 'focusout' }
|
|
14
|
+
| { kind: 'programmatic' };
|
|
3
15
|
|
|
4
16
|
/**
|
|
5
17
|
* @label Menu
|
|
6
18
|
* @tag wc-menu
|
|
7
19
|
* @rawTag menu
|
|
8
|
-
* @summary A
|
|
20
|
+
* @summary A list of menu items.
|
|
9
21
|
* @tags navigation
|
|
10
22
|
*
|
|
11
23
|
* @example
|
|
12
24
|
* ```html
|
|
13
25
|
* <wc-menu>
|
|
14
|
-
* <wc-menu-
|
|
15
|
-
*
|
|
16
|
-
* </wc-menu-list>
|
|
26
|
+
* <wc-menu-item>Item 1</wc-menu-item>
|
|
27
|
+
* <wc-menu-item>Item 2</wc-menu-item>
|
|
17
28
|
* </wc-menu>
|
|
18
29
|
* ```
|
|
19
30
|
*/
|
|
20
31
|
export class Menu extends LitElement {
|
|
32
|
+
static styles = [styles];
|
|
33
|
+
|
|
34
|
+
static Item = MenuItem;
|
|
35
|
+
|
|
21
36
|
@property({ type: Boolean, reflect: true }) open = false;
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
@property({ type: String, reflect: true }) variant: 'standard' | 'vibrant' =
|
|
39
|
+
'standard';
|
|
25
40
|
|
|
26
|
-
@
|
|
41
|
+
@property({ type: String }) anchor = '';
|
|
27
42
|
|
|
28
|
-
|
|
43
|
+
@property({ type: Boolean, attribute: 'stay-open-on-outside-click' })
|
|
44
|
+
stayOpenOnOutsideClick = false;
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
46
|
+
@property({ type: Boolean, attribute: 'stay-open-on-focusout' })
|
|
47
|
+
stayOpenOnFocusout = false;
|
|
48
|
+
|
|
49
|
+
@property({ type: Boolean, attribute: 'is-submenu' }) isSubmenu = false;
|
|
50
|
+
|
|
51
|
+
@property({ type: String }) placement: Placement = 'bottom-start';
|
|
52
|
+
|
|
53
|
+
@property({ type: Number }) offset = 6;
|
|
54
|
+
|
|
55
|
+
@state() private activeIndex = -1;
|
|
56
|
+
|
|
57
|
+
@query('.menu') private readonly menuListElement!: HTMLElement;
|
|
58
|
+
|
|
59
|
+
anchorElement: HTMLElement | null = null;
|
|
60
|
+
|
|
61
|
+
private readonly _surfaceController = new MenuSurfaceController(this);
|
|
62
|
+
|
|
63
|
+
private _lastFocusedElement: HTMLElement | null = null;
|
|
64
|
+
|
|
65
|
+
private _closeReason: CloseReason = { kind: 'programmatic' };
|
|
34
66
|
|
|
35
67
|
connectedCallback() {
|
|
36
68
|
// eslint-disable-next-line wc/guard-super-call
|
|
37
69
|
super.connectedCallback();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.addEventListener('
|
|
70
|
+
this.setAttribute('role', 'menu');
|
|
71
|
+
|
|
72
|
+
this.addEventListener('keydown', this._onKeyDown);
|
|
73
|
+
this.addEventListener('focusout', this._onFocusOut);
|
|
74
|
+
this.addEventListener('menu-item-activate', this._onItemActivate);
|
|
75
|
+
this.addEventListener('menu-item-request-close', this._onItemRequestClose);
|
|
76
|
+
window.addEventListener('click', this._onWindowClick, { capture: true });
|
|
77
|
+
this._syncAnchorAria();
|
|
41
78
|
}
|
|
42
79
|
|
|
43
80
|
disconnectedCallback() {
|
|
44
|
-
|
|
81
|
+
this.removeEventListener('keydown', this._onKeyDown);
|
|
82
|
+
this.removeEventListener('focusout', this._onFocusOut);
|
|
83
|
+
this.removeEventListener('menu-item-activate', this._onItemActivate);
|
|
84
|
+
this.removeEventListener(
|
|
85
|
+
'menu-item-request-close',
|
|
86
|
+
this._onItemRequestClose,
|
|
87
|
+
);
|
|
88
|
+
window.removeEventListener('click', this._onWindowClick, { capture: true });
|
|
45
89
|
super.disconnectedCallback();
|
|
46
|
-
window.removeEventListener('click', this._boundClickOutside);
|
|
47
|
-
this.removeEventListener('click', this._handleItemClick);
|
|
48
90
|
}
|
|
49
91
|
|
|
50
|
-
|
|
51
|
-
|
|
92
|
+
get items(): MenuItem[] {
|
|
93
|
+
const slot = this.shadowRoot?.querySelector('slot');
|
|
94
|
+
const elements = slot?.assignedElements({ flatten: true }) ?? [];
|
|
95
|
+
const items: MenuItem[] = [];
|
|
96
|
+
|
|
97
|
+
for (const element of elements) {
|
|
98
|
+
if (element instanceof MenuItem) {
|
|
99
|
+
items.push(element);
|
|
100
|
+
} else {
|
|
101
|
+
const maybeItem = (element as { item?: unknown }).item;
|
|
102
|
+
if (maybeItem instanceof MenuItem) {
|
|
103
|
+
items.push(maybeItem);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return items;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
show() {
|
|
112
|
+
if (this.open) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this._closeReason = { kind: 'programmatic' };
|
|
117
|
+
this.open = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
close(reason: CloseReason = { kind: 'programmatic' }) {
|
|
121
|
+
if (!this.open) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this._closeReason = reason;
|
|
126
|
+
this.open = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override focus() {
|
|
130
|
+
const target = this._getActiveItem() ?? this._getFirstEnabledItem();
|
|
131
|
+
target?.focus();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private _resolveAnchorElement() {
|
|
135
|
+
if (this.anchorElement) {
|
|
136
|
+
return this.anchorElement;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!this.anchor) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const root = this.getRootNode() as Document | ShadowRoot;
|
|
144
|
+
if ('getElementById' in root) {
|
|
145
|
+
return root.getElementById(this.anchor);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return document.getElementById(this.anchor);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private _syncAnchorAria() {
|
|
152
|
+
const anchorEl = this._resolveAnchorElement();
|
|
153
|
+
if (!anchorEl) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!this.id) {
|
|
158
|
+
this.id = `wc-menu-${Math.random().toString(36).slice(2, 9)}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
anchorEl.setAttribute('aria-haspopup', 'menu');
|
|
162
|
+
anchorEl.setAttribute('aria-controls', this.id);
|
|
163
|
+
anchorEl.setAttribute('aria-expanded', String(this.open));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private _enabledItems() {
|
|
167
|
+
return this.items.filter(item => !item.disabled);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private _syncRovingTabIndex() {
|
|
171
|
+
const enabledItems = this._enabledItems();
|
|
172
|
+
if (!enabledItems.length) {
|
|
173
|
+
this.activeIndex = -1;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (this.activeIndex < 0 || this.activeIndex >= enabledItems.length) {
|
|
178
|
+
this.activeIndex = 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (let index = 0; index < enabledItems.length; index += 1) {
|
|
182
|
+
const currentItem = enabledItems[index];
|
|
183
|
+
currentItem.tabIndex = index === this.activeIndex ? 0 : -1;
|
|
184
|
+
currentItem.selected = index === this.activeIndex;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private _setActiveByOffset(offset: 1 | -1) {
|
|
189
|
+
const enabledItems = this._enabledItems();
|
|
190
|
+
if (!enabledItems.length) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
52
193
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
194
|
+
if (this.activeIndex < 0) {
|
|
195
|
+
this.activeIndex = 0;
|
|
196
|
+
} else {
|
|
197
|
+
const count = enabledItems.length;
|
|
198
|
+
this.activeIndex = (this.activeIndex + offset + count) % count;
|
|
56
199
|
}
|
|
200
|
+
|
|
201
|
+
this._syncRovingTabIndex();
|
|
202
|
+
enabledItems[this.activeIndex]?.focus();
|
|
57
203
|
}
|
|
58
204
|
|
|
59
|
-
private
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const value = (target as any).value;
|
|
65
|
-
this.dispatchEvent(
|
|
66
|
-
new CustomEvent('menu-selected', {
|
|
67
|
-
detail: { value },
|
|
68
|
-
bubbles: true,
|
|
69
|
-
composed: true,
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
205
|
+
private _setBoundaryActive(index: number) {
|
|
206
|
+
const enabledItems = this._enabledItems();
|
|
207
|
+
if (!enabledItems.length) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
72
210
|
|
|
73
|
-
|
|
211
|
+
this.activeIndex = index;
|
|
212
|
+
this._syncRovingTabIndex();
|
|
213
|
+
enabledItems[this.activeIndex]?.focus();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private _getActiveItem() {
|
|
217
|
+
const enabledItems = this._enabledItems();
|
|
218
|
+
if (!enabledItems.length || this.activeIndex < 0) {
|
|
219
|
+
return null;
|
|
74
220
|
}
|
|
221
|
+
|
|
222
|
+
return enabledItems[this.activeIndex] ?? null;
|
|
75
223
|
}
|
|
76
224
|
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
this.open = !this.open;
|
|
225
|
+
private _getFirstEnabledItem() {
|
|
226
|
+
return this._enabledItems()[0] ?? null;
|
|
80
227
|
}
|
|
81
228
|
|
|
82
|
-
|
|
83
|
-
:
|
|
84
|
-
|
|
85
|
-
|
|
229
|
+
private _onItemActivate = (event: Event) => {
|
|
230
|
+
const customEvent = event as CustomEvent<{ item: MenuItem }>;
|
|
231
|
+
const enabledItems = this._enabledItems();
|
|
232
|
+
const nextIndex = enabledItems.indexOf(customEvent.detail.item);
|
|
233
|
+
if (nextIndex >= 0) {
|
|
234
|
+
this.activeIndex = nextIndex;
|
|
235
|
+
this._syncRovingTabIndex();
|
|
86
236
|
}
|
|
237
|
+
};
|
|
87
238
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
239
|
+
private _onItemRequestClose = (event: Event) => {
|
|
240
|
+
const customEvent = event as CustomEvent<{
|
|
241
|
+
reason: 'click-selection' | 'keydown';
|
|
242
|
+
key?: string;
|
|
243
|
+
}>;
|
|
244
|
+
|
|
245
|
+
if (customEvent.defaultPrevented) {
|
|
246
|
+
return;
|
|
91
247
|
}
|
|
92
248
|
|
|
93
|
-
.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
z-index: 10;
|
|
97
|
-
opacity: 0;
|
|
98
|
-
transform: scale(0.95);
|
|
99
|
-
transform-origin: top left;
|
|
100
|
-
transition:
|
|
101
|
-
opacity 0.1s ease-out,
|
|
102
|
-
transform 0.1s ease-out;
|
|
103
|
-
pointer-events: none; /* Prevent clicking when hidden */
|
|
104
|
-
margin-top: 4px; /* Slight gap */
|
|
249
|
+
if (customEvent.detail.reason === 'click-selection') {
|
|
250
|
+
this.close({ kind: 'click-selection' });
|
|
251
|
+
return;
|
|
105
252
|
}
|
|
106
253
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
254
|
+
this.close({ kind: 'keydown', key: customEvent.detail.key ?? 'Enter' });
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
private _onKeyDown = (event: KeyboardEvent) => {
|
|
258
|
+
if (!this.open) {
|
|
259
|
+
return;
|
|
111
260
|
}
|
|
112
261
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
262
|
+
switch (event.key) {
|
|
263
|
+
case 'ArrowDown':
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
this._setActiveByOffset(1);
|
|
266
|
+
break;
|
|
267
|
+
case 'ArrowUp':
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
this._setActiveByOffset(-1);
|
|
270
|
+
break;
|
|
271
|
+
case 'Home':
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
this._setBoundaryActive(0);
|
|
274
|
+
break;
|
|
275
|
+
case 'End': {
|
|
276
|
+
event.preventDefault();
|
|
277
|
+
const last = Math.max(this._enabledItems().length - 1, 0);
|
|
278
|
+
this._setBoundaryActive(last);
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case 'Escape':
|
|
282
|
+
event.preventDefault();
|
|
283
|
+
this.close({ kind: 'keydown', key: 'Escape' });
|
|
284
|
+
break;
|
|
285
|
+
default:
|
|
286
|
+
break;
|
|
117
287
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
private _onWindowClick = (event: MouseEvent) => {
|
|
291
|
+
if (!this.open || this.stayOpenOnOutsideClick) {
|
|
292
|
+
return;
|
|
121
293
|
}
|
|
122
|
-
|
|
294
|
+
|
|
295
|
+
const path = event.composedPath();
|
|
296
|
+
const anchorEl = this._resolveAnchorElement();
|
|
297
|
+
const inMenuTree = path.some(
|
|
298
|
+
target => target === this || (target instanceof Node && this.contains(target)),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (inMenuTree || (anchorEl && path.includes(anchorEl))) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.close({ kind: 'outside-click' });
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
private _isWithinMenuTree(node: Node | null) {
|
|
309
|
+
if (!node) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let current: Node | null = node;
|
|
314
|
+
while (current) {
|
|
315
|
+
if (current === this || this.contains(current)) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const root = current.getRootNode();
|
|
320
|
+
if (root instanceof ShadowRoot) {
|
|
321
|
+
current = root.host;
|
|
322
|
+
} else {
|
|
323
|
+
current = null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private _onFocusOut = (event: FocusEvent) => {
|
|
331
|
+
if (!this.open || this.stayOpenOnFocusout) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const next = event.relatedTarget;
|
|
336
|
+
if (!next) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (next instanceof Node && this._isWithinMenuTree(next)) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this.close({ kind: 'focusout' });
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
private _onSlotChange = () => {
|
|
348
|
+
this._syncRovingTabIndex();
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
private _applyPositioning() {
|
|
352
|
+
if (!this.open || !this.menuListElement) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const anchorEl = this._resolveAnchorElement();
|
|
357
|
+
if (!anchorEl) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this._surfaceController.start({
|
|
362
|
+
reference: anchorEl,
|
|
363
|
+
floating: this.menuListElement,
|
|
364
|
+
placement: this.placement,
|
|
365
|
+
offset: this.offset,
|
|
366
|
+
strategy: 'fixed',
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
protected override updated(changedProperties: Map<string, unknown>) {
|
|
371
|
+
if (changedProperties.has('anchor') || changedProperties.has('open')) {
|
|
372
|
+
this._syncAnchorAria();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (changedProperties.has('open')) {
|
|
376
|
+
if (this.open) {
|
|
377
|
+
this._lastFocusedElement = document.activeElement as HTMLElement | null;
|
|
378
|
+
this._syncRovingTabIndex();
|
|
379
|
+
this.dispatchEvent(
|
|
380
|
+
new CustomEvent('opened', {
|
|
381
|
+
bubbles: true,
|
|
382
|
+
composed: true,
|
|
383
|
+
}),
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
this._applyPositioning();
|
|
387
|
+
} else {
|
|
388
|
+
this._surfaceController.stop();
|
|
389
|
+
|
|
390
|
+
const reason = this._closeReason;
|
|
391
|
+
this.dispatchEvent(
|
|
392
|
+
new CustomEvent('close-menu', {
|
|
393
|
+
bubbles: true,
|
|
394
|
+
composed: true,
|
|
395
|
+
detail: {
|
|
396
|
+
reason,
|
|
397
|
+
itemPath: [],
|
|
398
|
+
},
|
|
399
|
+
}),
|
|
400
|
+
);
|
|
401
|
+
this.dispatchEvent(
|
|
402
|
+
new CustomEvent('closed', {
|
|
403
|
+
bubbles: true,
|
|
404
|
+
composed: true,
|
|
405
|
+
detail: { reason },
|
|
406
|
+
}),
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
if (!this.isSubmenu) {
|
|
410
|
+
this._lastFocusedElement?.focus();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
(changedProperties.has('open') ||
|
|
417
|
+
changedProperties.has('anchor') ||
|
|
418
|
+
changedProperties.has('placement') ||
|
|
419
|
+
changedProperties.has('offset')) &&
|
|
420
|
+
this.open
|
|
421
|
+
) {
|
|
422
|
+
this._applyPositioning();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
123
425
|
|
|
124
426
|
render() {
|
|
125
|
-
return html
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
427
|
+
return html`<div
|
|
428
|
+
class=${classMap({
|
|
429
|
+
'menu': true,
|
|
430
|
+
open: this.open,
|
|
431
|
+
closed: !this.open,
|
|
432
|
+
[`variant-${this.variant}`]: true,
|
|
433
|
+
})}
|
|
434
|
+
aria-hidden=${String(!this.open)}
|
|
435
|
+
>
|
|
436
|
+
<div class="background"></div>
|
|
437
|
+
<wc-elevation class="elevation"></wc-elevation>
|
|
129
438
|
|
|
130
|
-
<div class="menu-
|
|
131
|
-
|
|
132
|
-
<slot></slot>
|
|
439
|
+
<div class="menu-content">
|
|
440
|
+
<slot @slotchange=${this._onSlotChange}></slot>
|
|
133
441
|
</div>
|
|
134
|
-
|
|
442
|
+
</div>`;
|
|
135
443
|
}
|
|
136
444
|
}
|