@ni/nimble-components 30.1.4 → 30.1.5
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/all-components-bundle.js +374 -349
- package/dist/all-components-bundle.js.map +1 -1
- package/dist/all-components-bundle.min.js +3390 -3386
- package/dist/all-components-bundle.min.js.map +1 -1
- package/dist/esm/menu/index.d.ts +1 -1
- package/dist/esm/menu/index.js +3 -1
- package/dist/esm/menu/index.js.map +1 -1
- package/dist/esm/menu/menu.foundation.d.ts +83 -0
- package/dist/esm/menu/menu.foundation.js +306 -0
- package/dist/esm/menu/menu.foundation.js.map +1 -0
- package/dist/esm/menu/template.d.ts +4 -0
- package/dist/esm/menu/template.js +21 -0
- package/dist/esm/menu/template.js.map +1 -0
- package/package.json +1 -1
package/dist/esm/menu/index.d.ts
CHANGED
package/dist/esm/menu/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { DesignSystem
|
|
1
|
+
import { DesignSystem } from '@microsoft/fast-foundation';
|
|
2
|
+
import { Menu as FoundationMenu } from './menu.foundation';
|
|
3
|
+
import { template } from './template';
|
|
2
4
|
import { styles } from './styles';
|
|
3
5
|
/**
|
|
4
6
|
* A nimble-styled menu
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/menu/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/menu/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAQlC;;GAEG;AACH,MAAM,OAAO,IAAK,SAAQ,cAAc;CAAG;AAE3C;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC;IAC5B,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,cAAc;IACzB,QAAQ;IACR,MAAM;CACT,CAAC,CAAC;AAEH,YAAY,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC","sourcesContent":["import { DesignSystem } from '@microsoft/fast-foundation';\nimport { Menu as FoundationMenu } from './menu.foundation';\nimport { template } from './template';\nimport { styles } from './styles';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nimble-menu': Menu;\n }\n}\n\n/**\n * A nimble-styled menu\n */\nexport class Menu extends FoundationMenu {}\n\n/**\n * A function that returns a nimble-menu registration for configuring the component with a DesignSystem.\n * Implements {@link @microsoft/fast-foundation#menuTemplate}\n *\n * @public\n * @remarks\n * Generates HTML Element: \\<nimble-menu\\>\n *\n */\nconst nimbleMenu = Menu.compose({\n baseName: 'menu',\n baseClass: FoundationMenu,\n template,\n styles\n});\n\nDesignSystem.getOrCreate().withPrefix('nimble').register(nimbleMenu());\nexport const menuTag = 'nimble-menu';\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { FoundationElement } from '@microsoft/fast-foundation';
|
|
2
|
+
/**
|
|
3
|
+
* A Menu Custom HTML Element.
|
|
4
|
+
* Implements the {@link https://www.w3.org/TR/wai-aria-1.1/#menu | ARIA menu }.
|
|
5
|
+
*
|
|
6
|
+
* @slot - The default slot for the menu items
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export declare class Menu extends FoundationElement {
|
|
11
|
+
private static readonly focusableElementRoles;
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
items: HTMLSlotElement;
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
itemIcons?: Element[];
|
|
20
|
+
private menuItems;
|
|
21
|
+
private expandedItem;
|
|
22
|
+
/**
|
|
23
|
+
* The index of the focusable element in the items array
|
|
24
|
+
* defaults to -1
|
|
25
|
+
*/
|
|
26
|
+
private focusIndex;
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
connectedCallback(): void;
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
disconnectedCallback(): void;
|
|
35
|
+
/**
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
readonly isNestedMenu: () => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Focuses the first item in the menu.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
focus(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Collapses any expanded menu items.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
collapseExpandedItem(): void;
|
|
51
|
+
/**
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
handleMenuKeyDown(e: KeyboardEvent): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* if focus is moving out of the menu, reset to a stable initial state
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
handleFocusOut: (e: FocusEvent) => void;
|
|
60
|
+
private readonly handleItemFocus;
|
|
61
|
+
private readonly handleExpandedChanged;
|
|
62
|
+
private readonly removeItemListeners;
|
|
63
|
+
private itemsChanged;
|
|
64
|
+
private itemIconsChanged;
|
|
65
|
+
private readonly setItems;
|
|
66
|
+
/**
|
|
67
|
+
* handle change from child element
|
|
68
|
+
*/
|
|
69
|
+
private readonly changeHandler;
|
|
70
|
+
/**
|
|
71
|
+
* get an array of valid DOM children
|
|
72
|
+
*/
|
|
73
|
+
private domChildren;
|
|
74
|
+
/**
|
|
75
|
+
* check if the item is a menu item
|
|
76
|
+
*/
|
|
77
|
+
private readonly isMenuItemElement;
|
|
78
|
+
/**
|
|
79
|
+
* check if the item is focusable
|
|
80
|
+
*/
|
|
81
|
+
private readonly isFocusableElement;
|
|
82
|
+
private setFocus;
|
|
83
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* Based on implementation in FAST repo: https://github.com/microsoft/fast/blob/9c6dbb66615e6d229fc0ebf8065a67f109139f26/packages/web-components/fast-foundation/src/menu/menu.ts
|
|
4
|
+
*/
|
|
5
|
+
import { DOM, observable } from '@microsoft/fast-element';
|
|
6
|
+
import { isHTMLElement, keyArrowDown, keyArrowUp, keyEnd, keyHome } from '@microsoft/fast-web-utilities';
|
|
7
|
+
import { FoundationElement, MenuItem, MenuItemRole, roleForMenuItem } from '@microsoft/fast-foundation';
|
|
8
|
+
/**
|
|
9
|
+
* A Menu Custom HTML Element.
|
|
10
|
+
* Implements the {@link https://www.w3.org/TR/wai-aria-1.1/#menu | ARIA menu }.
|
|
11
|
+
*
|
|
12
|
+
* @slot - The default slot for the menu items
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export class Menu extends FoundationElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
this.expandedItem = null;
|
|
20
|
+
/**
|
|
21
|
+
* The index of the focusable element in the items array
|
|
22
|
+
* defaults to -1
|
|
23
|
+
*/
|
|
24
|
+
this.focusIndex = -1;
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
this.isNestedMenu = () => {
|
|
29
|
+
return (this.parentElement !== null
|
|
30
|
+
&& isHTMLElement(this.parentElement)
|
|
31
|
+
&& this.parentElement.getAttribute('role') === 'menuitem');
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* if focus is moving out of the menu, reset to a stable initial state
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
this.handleFocusOut = (e) => {
|
|
38
|
+
if (!this.contains(e.relatedTarget)
|
|
39
|
+
&& this.menuItems !== undefined) {
|
|
40
|
+
this.collapseExpandedItem();
|
|
41
|
+
// find our first focusable element
|
|
42
|
+
const focusIndex = this.menuItems.findIndex(this.isFocusableElement);
|
|
43
|
+
// set the current focus index's tabindex to -1
|
|
44
|
+
this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');
|
|
45
|
+
// set the first focusable element tabindex to 0
|
|
46
|
+
this.menuItems[focusIndex]?.setAttribute('tabindex', '0');
|
|
47
|
+
// set the focus index
|
|
48
|
+
this.focusIndex = focusIndex;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
this.handleItemFocus = (e) => {
|
|
52
|
+
const targetItem = e.target;
|
|
53
|
+
if (this.menuItems !== undefined
|
|
54
|
+
&& targetItem !== this.menuItems[this.focusIndex]) {
|
|
55
|
+
this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');
|
|
56
|
+
this.focusIndex = this.menuItems.indexOf(targetItem);
|
|
57
|
+
targetItem.setAttribute('tabindex', '0');
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
this.handleExpandedChanged = (e) => {
|
|
61
|
+
if (e.defaultPrevented
|
|
62
|
+
|| e.target === null
|
|
63
|
+
|| this.menuItems === undefined
|
|
64
|
+
|| !this.menuItems.includes(e.target)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
const changedItem = e.target;
|
|
69
|
+
// closing an expanded item without opening another
|
|
70
|
+
if (this.expandedItem !== null
|
|
71
|
+
&& changedItem === this.expandedItem
|
|
72
|
+
&& changedItem.expanded === false) {
|
|
73
|
+
this.expandedItem = null;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (changedItem.expanded) {
|
|
77
|
+
if (this.expandedItem !== null
|
|
78
|
+
&& this.expandedItem !== changedItem) {
|
|
79
|
+
this.expandedItem.expanded = false;
|
|
80
|
+
}
|
|
81
|
+
this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');
|
|
82
|
+
this.expandedItem = changedItem;
|
|
83
|
+
this.focusIndex = this.menuItems.indexOf(changedItem);
|
|
84
|
+
changedItem.setAttribute('tabindex', '0');
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
this.removeItemListeners = () => {
|
|
88
|
+
if (this.menuItems !== undefined) {
|
|
89
|
+
this.menuItems.forEach((item) => {
|
|
90
|
+
item.removeEventListener('expanded-change', this.handleExpandedChanged);
|
|
91
|
+
item.removeEventListener('focus', this.handleItemFocus);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
this.setItems = () => {
|
|
96
|
+
const newItems = this.domChildren();
|
|
97
|
+
this.removeItemListeners();
|
|
98
|
+
this.menuItems = newItems;
|
|
99
|
+
const menuItems = this.menuItems.filter(this.isMenuItemElement);
|
|
100
|
+
// if our focus index is not -1 we have items
|
|
101
|
+
if (menuItems.length) {
|
|
102
|
+
this.focusIndex = 0;
|
|
103
|
+
}
|
|
104
|
+
function elementIndent(el) {
|
|
105
|
+
const role = el.getAttribute('role');
|
|
106
|
+
const startSlot = el.querySelector('[slot=start]');
|
|
107
|
+
if (role !== MenuItemRole.menuitem && startSlot === null) {
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
if (role === MenuItemRole.menuitem && startSlot !== null) {
|
|
111
|
+
return 1;
|
|
112
|
+
}
|
|
113
|
+
if (role !== MenuItemRole.menuitem && startSlot !== null) {
|
|
114
|
+
return 2;
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
const indent = menuItems.reduce((accum, current) => {
|
|
119
|
+
const elementValue = elementIndent(current);
|
|
120
|
+
return accum > elementValue ? accum : elementValue;
|
|
121
|
+
}, 0);
|
|
122
|
+
menuItems.forEach((item, index) => {
|
|
123
|
+
item.setAttribute('tabindex', index === 0 ? '0' : '-1');
|
|
124
|
+
item.addEventListener('expanded-change', this.handleExpandedChanged);
|
|
125
|
+
item.addEventListener('focus', this.handleItemFocus);
|
|
126
|
+
if (item instanceof MenuItem || 'startColumnCount' in item) {
|
|
127
|
+
item.startColumnCount = indent;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* handle change from child element
|
|
133
|
+
*/
|
|
134
|
+
this.changeHandler = (e) => {
|
|
135
|
+
if (this.menuItems === undefined) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const changedMenuItem = e.target;
|
|
139
|
+
const changeItemIndex = this.menuItems.indexOf(changedMenuItem);
|
|
140
|
+
if (changeItemIndex === -1) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (changedMenuItem.role === 'menuitemradio'
|
|
144
|
+
&& changedMenuItem.checked === true) {
|
|
145
|
+
for (let i = changeItemIndex - 1; i >= 0; --i) {
|
|
146
|
+
const item = this.menuItems[i];
|
|
147
|
+
const role = item.getAttribute('role');
|
|
148
|
+
if (role === MenuItemRole.menuitemradio) {
|
|
149
|
+
item.checked = false;
|
|
150
|
+
}
|
|
151
|
+
if (role === 'separator') {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const maxIndex = this.menuItems.length - 1;
|
|
156
|
+
for (let i = changeItemIndex + 1; i <= maxIndex; ++i) {
|
|
157
|
+
const item = this.menuItems[i];
|
|
158
|
+
const role = item.getAttribute('role');
|
|
159
|
+
if (role === MenuItemRole.menuitemradio) {
|
|
160
|
+
item.checked = false;
|
|
161
|
+
}
|
|
162
|
+
if (role === 'separator') {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* check if the item is a menu item
|
|
170
|
+
*/
|
|
171
|
+
this.isMenuItemElement = (el) => {
|
|
172
|
+
return (isHTMLElement(el)
|
|
173
|
+
&& Object.prototype.hasOwnProperty.call(Menu.focusableElementRoles, el.getAttribute('role')));
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* check if the item is focusable
|
|
177
|
+
*/
|
|
178
|
+
this.isFocusableElement = (el) => {
|
|
179
|
+
return this.isMenuItemElement(el);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* @internal
|
|
184
|
+
*/
|
|
185
|
+
connectedCallback() {
|
|
186
|
+
super.connectedCallback();
|
|
187
|
+
DOM.queueUpdate(() => {
|
|
188
|
+
// wait until children have had a chance to
|
|
189
|
+
// connect before setting/checking their props/attributes
|
|
190
|
+
this.setItems();
|
|
191
|
+
});
|
|
192
|
+
this.addEventListener('change', this.changeHandler);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* @internal
|
|
196
|
+
*/
|
|
197
|
+
disconnectedCallback() {
|
|
198
|
+
super.disconnectedCallback();
|
|
199
|
+
this.removeItemListeners();
|
|
200
|
+
this.menuItems = undefined;
|
|
201
|
+
this.removeEventListener('change', this.changeHandler);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Focuses the first item in the menu.
|
|
205
|
+
*
|
|
206
|
+
* @public
|
|
207
|
+
*/
|
|
208
|
+
focus() {
|
|
209
|
+
this.setFocus(0, 1);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Collapses any expanded menu items.
|
|
213
|
+
*
|
|
214
|
+
* @public
|
|
215
|
+
*/
|
|
216
|
+
collapseExpandedItem() {
|
|
217
|
+
if (this.expandedItem !== null) {
|
|
218
|
+
this.expandedItem.expanded = false;
|
|
219
|
+
this.expandedItem = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
handleMenuKeyDown(e) {
|
|
226
|
+
if (e.defaultPrevented || this.menuItems === undefined) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
switch (e.key) {
|
|
230
|
+
case keyArrowDown:
|
|
231
|
+
// go forward one index
|
|
232
|
+
this.setFocus(this.focusIndex + 1, 1);
|
|
233
|
+
return false;
|
|
234
|
+
case keyArrowUp:
|
|
235
|
+
// go back one index
|
|
236
|
+
this.setFocus(this.focusIndex - 1, -1);
|
|
237
|
+
return false;
|
|
238
|
+
case keyEnd:
|
|
239
|
+
// set focus on last item
|
|
240
|
+
this.setFocus(this.menuItems.length - 1, -1);
|
|
241
|
+
return false;
|
|
242
|
+
case keyHome:
|
|
243
|
+
// set focus on first item
|
|
244
|
+
this.setFocus(0, 1);
|
|
245
|
+
return false;
|
|
246
|
+
default:
|
|
247
|
+
// if we are not handling the event, do not prevent default
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
itemsChanged() {
|
|
252
|
+
// only update children after the component is connected and
|
|
253
|
+
// the setItems has run on connectedCallback
|
|
254
|
+
// (menuItems is undefined until then)
|
|
255
|
+
if (this.$fastController.isConnected && this.menuItems !== undefined) {
|
|
256
|
+
this.setItems();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
itemIconsChanged(oldIcons, newIcons) {
|
|
260
|
+
// must check if set of icon elements is actually different
|
|
261
|
+
if (this.$fastController.isConnected
|
|
262
|
+
&& this.menuItems !== undefined
|
|
263
|
+
&& (oldIcons.length !== newIcons.length
|
|
264
|
+
|| new Set(oldIcons.concat(newIcons)).size !== oldIcons.length)) {
|
|
265
|
+
this.setItems();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* get an array of valid DOM children
|
|
270
|
+
*/
|
|
271
|
+
domChildren() {
|
|
272
|
+
return Array.from(this.children).filter(child => !child.hasAttribute('hidden'));
|
|
273
|
+
}
|
|
274
|
+
setFocus(focusIndex, adjustment) {
|
|
275
|
+
if (this.menuItems === undefined) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
let updatedIndex = focusIndex;
|
|
279
|
+
while (updatedIndex >= 0 && updatedIndex < this.menuItems.length) {
|
|
280
|
+
const child = this.menuItems[updatedIndex];
|
|
281
|
+
if (this.isFocusableElement(child)) {
|
|
282
|
+
// change the previous index to -1
|
|
283
|
+
if (this.focusIndex > -1
|
|
284
|
+
&& this.menuItems.length >= this.focusIndex - 1) {
|
|
285
|
+
this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');
|
|
286
|
+
}
|
|
287
|
+
// update the focus index
|
|
288
|
+
this.focusIndex = updatedIndex;
|
|
289
|
+
// update the tabindex of next focusable element
|
|
290
|
+
child.setAttribute('tabindex', '0');
|
|
291
|
+
// focus the element
|
|
292
|
+
child.focus();
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
updatedIndex += adjustment;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
Menu.focusableElementRoles = roleForMenuItem;
|
|
300
|
+
__decorate([
|
|
301
|
+
observable
|
|
302
|
+
], Menu.prototype, "items", void 0);
|
|
303
|
+
__decorate([
|
|
304
|
+
observable
|
|
305
|
+
], Menu.prototype, "itemIcons", void 0);
|
|
306
|
+
//# sourceMappingURL=menu.foundation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.foundation.js","sourceRoot":"","sources":["../../../src/menu/menu.foundation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Based on implementation in FAST repo: https://github.com/microsoft/fast/blob/9c6dbb66615e6d229fc0ebf8065a67f109139f26/packages/web-components/fast-foundation/src/menu/menu.ts\n */\nimport { DOM, observable } from '@microsoft/fast-element';\nimport {\n isHTMLElement,\n keyArrowDown,\n keyArrowUp,\n keyEnd,\n keyHome\n} from '@microsoft/fast-web-utilities';\nimport {\n FoundationElement,\n MenuItem,\n MenuItemColumnCount,\n MenuItemRole,\n roleForMenuItem\n} from '@microsoft/fast-foundation';\n\n/**\n * A Menu Custom HTML Element.\n * Implements the {@link https://www.w3.org/TR/wai-aria-1.1/#menu | ARIA menu }.\n *\n * @slot - The default slot for the menu items\n *\n * @public\n */\nexport class Menu extends FoundationElement {\n private static readonly focusableElementRoles: { [key: string]: string } = roleForMenuItem;\n\n /**\n * @internal\n */\n @observable\n public items!: HTMLSlotElement;\n\n /**\n * @internal\n */\n @observable\n public itemIcons?: Element[];\n\n private menuItems: Element[] | undefined;\n\n private expandedItem: MenuItem | null = null;\n\n /**\n * The index of the focusable element in the items array\n * defaults to -1\n */\n private focusIndex = -1;\n\n /**\n * @internal\n */\n public override connectedCallback(): void {\n super.connectedCallback();\n DOM.queueUpdate(() => {\n // wait until children have had a chance to\n // connect before setting/checking their props/attributes\n this.setItems();\n });\n\n this.addEventListener('change', this.changeHandler);\n }\n\n /**\n * @internal\n */\n public override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeItemListeners();\n this.menuItems = undefined;\n this.removeEventListener('change', this.changeHandler);\n }\n\n /**\n * @internal\n */\n public readonly isNestedMenu = (): boolean => {\n return (\n this.parentElement !== null\n && isHTMLElement(this.parentElement)\n && this.parentElement.getAttribute('role') === 'menuitem'\n );\n };\n\n /**\n * Focuses the first item in the menu.\n *\n * @public\n */\n public override focus(): void {\n this.setFocus(0, 1);\n }\n\n /**\n * Collapses any expanded menu items.\n *\n * @public\n */\n public collapseExpandedItem(): void {\n if (this.expandedItem !== null) {\n this.expandedItem.expanded = false;\n this.expandedItem = null;\n }\n }\n\n /**\n * @internal\n */\n public handleMenuKeyDown(e: KeyboardEvent): boolean {\n if (e.defaultPrevented || this.menuItems === undefined) {\n return false;\n }\n switch (e.key) {\n case keyArrowDown:\n // go forward one index\n this.setFocus(this.focusIndex + 1, 1);\n return false;\n case keyArrowUp:\n // go back one index\n this.setFocus(this.focusIndex - 1, -1);\n return false;\n case keyEnd:\n // set focus on last item\n this.setFocus(this.menuItems.length - 1, -1);\n return false;\n case keyHome:\n // set focus on first item\n this.setFocus(0, 1);\n return false;\n\n default:\n // if we are not handling the event, do not prevent default\n return true;\n }\n }\n\n /**\n * if focus is moving out of the menu, reset to a stable initial state\n * @internal\n */\n public handleFocusOut = (e: FocusEvent): void => {\n if (\n !this.contains(e.relatedTarget as Element)\n && this.menuItems !== undefined\n ) {\n this.collapseExpandedItem();\n // find our first focusable element\n const focusIndex: number = this.menuItems.findIndex(\n this.isFocusableElement\n );\n // set the current focus index's tabindex to -1\n this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');\n // set the first focusable element tabindex to 0\n this.menuItems[focusIndex]?.setAttribute('tabindex', '0');\n // set the focus index\n this.focusIndex = focusIndex;\n }\n };\n\n private readonly handleItemFocus = (e: Event): void => {\n const targetItem: HTMLElement = e.target as HTMLElement;\n\n if (\n this.menuItems !== undefined\n && targetItem !== this.menuItems[this.focusIndex]\n ) {\n this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');\n this.focusIndex = this.menuItems.indexOf(targetItem);\n targetItem.setAttribute('tabindex', '0');\n }\n };\n\n private readonly handleExpandedChanged = (e: Event): void => {\n if (\n e.defaultPrevented\n || e.target === null\n || this.menuItems === undefined\n || !this.menuItems.includes(e.target as Element)\n ) {\n return;\n }\n\n e.preventDefault();\n const changedItem: MenuItem = e.target as MenuItem;\n\n // closing an expanded item without opening another\n if (\n this.expandedItem !== null\n && changedItem === this.expandedItem\n && changedItem.expanded === false\n ) {\n this.expandedItem = null;\n return;\n }\n\n if (changedItem.expanded) {\n if (\n this.expandedItem !== null\n && this.expandedItem !== changedItem\n ) {\n this.expandedItem.expanded = false;\n }\n this.menuItems[this.focusIndex]?.setAttribute('tabindex', '-1');\n this.expandedItem = changedItem;\n this.focusIndex = this.menuItems.indexOf(changedItem);\n changedItem.setAttribute('tabindex', '0');\n }\n };\n\n private readonly removeItemListeners = (): void => {\n if (this.menuItems !== undefined) {\n this.menuItems.forEach((item: Element) => {\n item.removeEventListener(\n 'expanded-change',\n this.handleExpandedChanged\n );\n item.removeEventListener('focus', this.handleItemFocus);\n });\n }\n };\n\n private itemsChanged(): void {\n // only update children after the component is connected and\n // the setItems has run on connectedCallback\n // (menuItems is undefined until then)\n if (this.$fastController.isConnected && this.menuItems !== undefined) {\n this.setItems();\n }\n }\n\n private itemIconsChanged(oldIcons: Element[], newIcons: Element[]): void {\n // must check if set of icon elements is actually different\n if (\n this.$fastController.isConnected\n && this.menuItems !== undefined\n && (oldIcons.length !== newIcons.length\n || new Set(oldIcons.concat(newIcons)).size !== oldIcons.length)\n ) {\n this.setItems();\n }\n }\n\n private readonly setItems = (): void => {\n const newItems: Element[] = this.domChildren();\n\n this.removeItemListeners();\n this.menuItems = newItems;\n\n const menuItems = this.menuItems.filter(this.isMenuItemElement);\n\n // if our focus index is not -1 we have items\n if (menuItems.length) {\n this.focusIndex = 0;\n }\n\n function elementIndent(el: HTMLElement): MenuItemColumnCount {\n const role = el.getAttribute('role');\n const startSlot = el.querySelector('[slot=start]');\n\n if (role !== MenuItemRole.menuitem && startSlot === null) {\n return 1;\n }\n if (role === MenuItemRole.menuitem && startSlot !== null) {\n return 1;\n }\n if (role !== MenuItemRole.menuitem && startSlot !== null) {\n return 2;\n }\n return 0;\n }\n\n const indent: MenuItemColumnCount = menuItems.reduce<MenuItemColumnCount>((accum, current) => {\n const elementValue = elementIndent(current);\n\n return accum > elementValue ? accum : elementValue;\n }, 0);\n\n menuItems.forEach((item: HTMLElement, index: number) => {\n item.setAttribute('tabindex', index === 0 ? '0' : '-1');\n item.addEventListener(\n 'expanded-change',\n this.handleExpandedChanged\n );\n item.addEventListener('focus', this.handleItemFocus);\n\n if (item instanceof MenuItem || 'startColumnCount' in item) {\n (item as unknown as MenuItem).startColumnCount = indent;\n }\n });\n };\n\n /**\n * handle change from child element\n */\n private readonly changeHandler = (e: Event): void => {\n if (this.menuItems === undefined) {\n return;\n }\n const changedMenuItem: MenuItem = e.target as MenuItem;\n const changeItemIndex: number = this.menuItems.indexOf(changedMenuItem);\n\n if (changeItemIndex === -1) {\n return;\n }\n\n if (\n changedMenuItem.role === 'menuitemradio'\n && changedMenuItem.checked === true\n ) {\n for (let i = changeItemIndex - 1; i >= 0; --i) {\n const item: Element = this.menuItems[i]!;\n const role: string | null = item.getAttribute('role');\n if (role === MenuItemRole.menuitemradio) {\n (item as MenuItem).checked = false;\n }\n if (role === 'separator') {\n break;\n }\n }\n const maxIndex: number = this.menuItems.length - 1;\n for (let i = changeItemIndex + 1; i <= maxIndex; ++i) {\n const item: Element = this.menuItems[i]!;\n const role: string | null = item.getAttribute('role');\n if (role === MenuItemRole.menuitemradio) {\n (item as MenuItem).checked = false;\n }\n if (role === 'separator') {\n break;\n }\n }\n }\n };\n\n /**\n * get an array of valid DOM children\n */\n private domChildren(): Element[] {\n return Array.from(this.children).filter(\n child => !child.hasAttribute('hidden')\n );\n }\n\n /**\n * check if the item is a menu item\n */\n private readonly isMenuItemElement = (el: Element): el is HTMLElement => {\n return (\n isHTMLElement(el)\n && Object.prototype.hasOwnProperty.call(\n Menu.focusableElementRoles,\n el.getAttribute('role')!\n )\n );\n };\n\n /**\n * check if the item is focusable\n */\n private readonly isFocusableElement = (el: Element): el is HTMLElement => {\n return this.isMenuItemElement(el);\n };\n\n private setFocus(focusIndex: number, adjustment: number): void {\n if (this.menuItems === undefined) {\n return;\n }\n\n let updatedIndex = focusIndex;\n while (updatedIndex >= 0 && updatedIndex < this.menuItems.length) {\n const child: Element = this.menuItems[updatedIndex]!;\n\n if (this.isFocusableElement(child)) {\n // change the previous index to -1\n if (\n this.focusIndex > -1\n && this.menuItems.length >= this.focusIndex - 1\n ) {\n this.menuItems[this.focusIndex]?.setAttribute(\n 'tabindex',\n '-1'\n );\n }\n\n // update the focus index\n this.focusIndex = updatedIndex;\n\n // update the tabindex of next focusable element\n child.setAttribute('tabindex', '0');\n\n // focus the element\n child.focus();\n\n break;\n }\n\n updatedIndex += adjustment;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { children, html, slotted } from '@microsoft/fast-element';
|
|
2
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
+
// prettier-ignore
|
|
4
|
+
export const template = () => html `
|
|
5
|
+
<template
|
|
6
|
+
slot="${x => {
|
|
7
|
+
if (x.slot) {
|
|
8
|
+
return x.slot;
|
|
9
|
+
}
|
|
10
|
+
return x.isNestedMenu() ? 'submenu' : undefined;
|
|
11
|
+
}}"
|
|
12
|
+
role="menu"
|
|
13
|
+
@keydown="${(x, c) => x.handleMenuKeyDown(c.event)}"
|
|
14
|
+
@focusout="${(x, c) => x.handleFocusOut(c.event)}"
|
|
15
|
+
${children({ property: 'itemIcons', subtree: true, attributeFilter: ['slot'], selector: ':scope > * > [slot="start"]' })}
|
|
16
|
+
>
|
|
17
|
+
<slot ${slotted('items')}></slot>
|
|
18
|
+
</template>
|
|
19
|
+
`;
|
|
20
|
+
/* eslint-enable @typescript-eslint/indent */
|
|
21
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/menu/template.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,QAAQ,EACR,IAAI,EACJ,OAAO,EAEV,MAAM,yBAAyB,CAAC;AAIjC,8CAA8C;AAC9C,kBAAkB;AAClB,MAAM,CAAC,MAAM,QAAQ,GAEjB,GAAG,EAAE,CAAC,IAAI,CAAA;;gBAEE,CAAC,CAAC,EAAE;IACR,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,CAAC,IAAI,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;;oBAEW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAsB,CAAC;qBACtD,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAmB,CAAC;UAC5D,QAAQ,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,CAAC;;gBAEhH,OAAO,CAAC,OAAO,CAAC;;CAE/B,CAAC;AACF,6CAA6C","sourcesContent":["import {\n children,\n html,\n slotted,\n type ViewTemplate\n} from '@microsoft/fast-element';\nimport type { FoundationElementTemplate } from '@microsoft/fast-foundation';\nimport type { Menu } from '.';\n\n/* eslint-disable @typescript-eslint/indent */\n// prettier-ignore\nexport const template: FoundationElementTemplate<\nViewTemplate<Menu>\n> = () => html`\n <template\n slot=\"${x => {\n if (x.slot) {\n return x.slot;\n }\n return x.isNestedMenu() ? 'submenu' : undefined;\n }}\"\n role=\"menu\"\n @keydown=\"${(x, c) => x.handleMenuKeyDown(c.event as KeyboardEvent)}\"\n @focusout=\"${(x, c) => x.handleFocusOut(c.event as FocusEvent)}\"\n ${children({ property: 'itemIcons', subtree: true, attributeFilter: ['slot'], selector: ':scope > * > [slot=\"start\"]' })}\n >\n <slot ${slotted('items')}></slot>\n </template>\n`;\n/* eslint-enable @typescript-eslint/indent */\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ni/nimble-components",
|
|
3
|
-
"version": "30.1.
|
|
3
|
+
"version": "30.1.5",
|
|
4
4
|
"description": "Styled web components for the NI Nimble Design System",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run generate-icons && npm run generate-workers && npm run build-components && npm run bundle-components && npm run generate-scss",
|