@vollowx/seele 0.13.10 → 0.14.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.
- package/custom-elements.json +604 -359
- package/package.json +1 -1
- package/src/all.d.ts +2 -2
- package/src/all.js +2 -2
- package/src/base/autocomplete.d.ts +9 -33
- package/src/base/autocomplete.js +42 -65
- package/src/base/button.d.ts +0 -1
- package/src/base/button.js +7 -11
- package/src/base/checkbox.d.ts +0 -2
- package/src/base/checkbox.js +6 -13
- package/src/base/controllers/list-controller.d.ts +1 -0
- package/src/base/controllers/list-controller.js +6 -1
- package/src/base/dialog.d.ts +2 -2
- package/src/base/dialog.js +11 -2
- package/src/base/list-item.d.ts +1 -1
- package/src/base/list-item.js +11 -12
- package/src/base/list.d.ts +16 -33
- package/src/base/list.js +91 -173
- package/src/base/menu.d.ts +41 -61
- package/src/base/menu.js +184 -306
- package/src/base/{experimental/popup.d.ts → popup.d.ts} +7 -11
- package/src/base/{experimental/popup.js → popup.js} +45 -90
- package/src/base/radio.js +5 -5
- package/src/base/select.d.ts +7 -53
- package/src/base/select.js +45 -79
- package/src/base/tooltip.js +2 -2
- package/src/core/focus.d.ts +11 -0
- package/src/core/focus.js +53 -0
- package/src/m3/autocomplete/autocomplete-styles.css.js +1 -1
- package/src/m3/autocomplete/autocomplete.d.ts +1 -0
- package/src/m3/autocomplete/autocomplete.js +2 -1
- package/src/m3/button/common-button-styles.css.js +1 -1
- package/src/m3/button/icon-button-styles.css.js +1 -1
- package/src/m3/button/icon-button-toggle.js +1 -1
- package/src/m3/button/shared-button-styles.css.js +1 -1
- package/src/m3/button-group/standard-button-group.js +1 -1
- package/src/m3/checkbox/checkbox.js +1 -1
- package/src/m3/dialog/dialog.d.ts +2 -2
- package/src/m3/dialog/dialog.js +2 -2
- package/src/m3/field/field-styles.css.js +1 -1
- package/src/m3/field/field.js +2 -2
- package/src/m3/field/filled-field-styles.css.js +1 -1
- package/src/m3/field/filled-field.js +1 -1
- package/src/m3/field/outlined-field-styles.css.js +1 -1
- package/src/m3/focus-ring/focus-ring.js +1 -1
- package/src/m3/list/list-item.d.ts +2 -2
- package/src/m3/list/list-item.js +6 -6
- package/src/m3/menu/composed-menu.d.ts +19 -0
- package/src/m3/menu/composed-menu.js +42 -0
- package/src/m3/menu/menu-styles.css.js +1 -1
- package/src/m3/menu/menu.d.ts +1 -7
- package/src/m3/menu/menu.js +1 -13
- package/src/m3/navigation/navigation-rail-item-styles.css.js +1 -1
- package/src/m3/popup/popup-styles.css.js +2 -0
- package/src/m3/{experimental → popup}/popup.d.ts +2 -1
- package/src/m3/popup/popup.js +18 -0
- package/src/m3/radio/radio.js +1 -1
- package/src/m3/ripple/ripple.js +3 -3
- package/src/m3/select/filled-select.d.ts +2 -2
- package/src/m3/select/filled-select.js +5 -5
- package/src/m3/select/outlined-select.d.ts +2 -2
- package/src/m3/select/outlined-select.js +5 -5
- package/src/m3/select/select-styles.css.js +1 -1
- package/src/m3/select/select.d.ts +2 -5
- package/src/m3/select/select.js +3 -6
- package/src/m3/switch/switch-styles.css.js +1 -1
- package/src/m3/switch/switch.js +1 -1
- package/src/m3/tab/tabs-styles.css.js +1 -1
- package/src/m3/tab/tabs.js +1 -1
- package/src/m3/text-field/filled-text-field.js +2 -2
- package/src/m3/text-field/outlined-text-field.js +2 -2
- package/src/m3/tooltip/tooltip-styles.css.js +1 -1
- package/src/core/focus-visible.d.ts +0 -2
- package/src/core/focus-visible.js +0 -13
- package/src/m3/experimental/popup-styles.css.js +0 -2
- package/src/m3/experimental/popup.js +0 -11
- /package/src/base/{experimental/popup-styles.css.js → popup-styles.css.js} +0 -0
package/src/base/menu.js
CHANGED
|
@@ -1,59 +1,46 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { LitElement, html } from 'lit';
|
|
3
|
-
import { property,
|
|
4
|
-
import { setFocusVisible } from '../core/focus
|
|
5
|
-
import {
|
|
6
|
-
import { InternalsAttached } from './mixins/internals-attached.js';
|
|
7
|
-
import { FocusDelegated } from './mixins/focus-delegated.js';
|
|
8
|
-
import { PopoverController } from './controllers/popover-controller.js';
|
|
2
|
+
import { LitElement, html, isServer } from 'lit';
|
|
3
|
+
import { property, queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
import { focusVisible, setFocusVisible } from '../core/focus.js';
|
|
5
|
+
import { internals, InternalsAttached } from './mixins/internals-attached.js';
|
|
9
6
|
import { ListController } from './controllers/list-controller.js';
|
|
10
|
-
const Base = FocusDelegated(InternalsAttached(Attachable(LitElement)));
|
|
11
7
|
/**
|
|
12
|
-
* @csspart menu
|
|
13
8
|
* @csspart items
|
|
14
|
-
*
|
|
15
|
-
* @fires {
|
|
16
|
-
* @fires {
|
|
17
|
-
* @fires {
|
|
18
|
-
* @fires {MenuItemFocusEvent} item-focus - Fires when an item is focused
|
|
19
|
-
*
|
|
20
|
-
* TODO: handle slotchange
|
|
9
|
+
* @slot - menu items
|
|
10
|
+
* @fires {MenuActionEvent} action - Fires when an item is activated.
|
|
11
|
+
* @fires {MenuItemFocusEvent} item-focus - Fires when an item is focused.
|
|
12
|
+
* @fires {Event} request-popup-hide - Fires when menu should be hidden.
|
|
21
13
|
*/
|
|
22
|
-
export class Menu extends
|
|
14
|
+
export class Menu extends InternalsAttached(LitElement) {
|
|
15
|
+
get $items() {
|
|
16
|
+
return this.listController.items || [];
|
|
17
|
+
}
|
|
18
|
+
get currentIndex() {
|
|
19
|
+
return this.listController?.currentIndex;
|
|
20
|
+
}
|
|
21
|
+
focusFirstItem() {
|
|
22
|
+
this.listController.focusFirstItem();
|
|
23
|
+
}
|
|
24
|
+
focusLastItem() {
|
|
25
|
+
this.listController.focusLastItem();
|
|
26
|
+
}
|
|
27
|
+
focusItem(item) {
|
|
28
|
+
this.listController._focusItem(item);
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
return html `<slot part="items"></slot>`;
|
|
32
|
+
}
|
|
23
33
|
constructor() {
|
|
24
|
-
super(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
this.keepOpenSelect = false;
|
|
35
|
-
this.keepOpenClickAway = false;
|
|
36
|
-
this.noFocusControl = false;
|
|
37
|
-
this.tabIndex = 0;
|
|
38
|
-
this.$lastFocused = null;
|
|
39
|
-
this.popoverController = new PopoverController(this, {
|
|
40
|
-
popover: () => this.$menu,
|
|
41
|
-
trigger: () => this.$control,
|
|
42
|
-
positioning: {
|
|
43
|
-
placement: () => this.align,
|
|
44
|
-
strategy: () => this.alignStrategy,
|
|
45
|
-
offset: () => this.offset,
|
|
46
|
-
windowPadding: () => 16,
|
|
47
|
-
},
|
|
48
|
-
durations: {
|
|
49
|
-
open: () => (this.quick ? 0 : this._durations.show),
|
|
50
|
-
close: () => (this.quick ? 0 : this._durations.hide),
|
|
51
|
-
},
|
|
52
|
-
onClickAway: () => {
|
|
53
|
-
if (!this.keepOpenClickAway)
|
|
54
|
-
this.open = false;
|
|
55
|
-
},
|
|
56
|
-
});
|
|
34
|
+
super();
|
|
35
|
+
/**
|
|
36
|
+
* When true, emits `request-popup-hide` on `action`
|
|
37
|
+
*/
|
|
38
|
+
this.keepOpenAction = false;
|
|
39
|
+
/**
|
|
40
|
+
* When true, removes the default focus management and `aria-activedescendant`
|
|
41
|
+
* on the host.
|
|
42
|
+
*/
|
|
43
|
+
this.bare = false;
|
|
57
44
|
this.listController = new ListController(this, {
|
|
58
45
|
isItem: (item) => item.getAttribute('seele-base') === 'option' &&
|
|
59
46
|
!item.hasAttribute('disabled') &&
|
|
@@ -64,313 +51,204 @@ export class Menu extends Base {
|
|
|
64
51
|
},
|
|
65
52
|
focusItem: (item) => {
|
|
66
53
|
item.focused = true;
|
|
67
|
-
if (!this.
|
|
68
|
-
this
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
if (!this.bare)
|
|
55
|
+
this[internals].ariaActiveDescendantElement = item;
|
|
56
|
+
if (focusVisible)
|
|
57
|
+
item.scrollIntoView({ block: 'nearest' });
|
|
71
58
|
this.dispatchEvent(new CustomEvent('item-focus', {
|
|
72
|
-
detail: { item: item },
|
|
59
|
+
detail: { item, index: this.$items.indexOf(item) },
|
|
73
60
|
bubbles: true,
|
|
74
61
|
composed: true,
|
|
75
62
|
}));
|
|
76
63
|
},
|
|
77
64
|
wrapNavigation: () => false,
|
|
78
65
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
render() {
|
|
84
|
-
return html `<div part="menu" role="${this.type}" tabindex="${this.tabIndex}" @keydown="${this.#handleKeyDown.bind(this)}" @focusout="${this.#handleFocusOut.bind(this)}">${this.renderItemSlot()}</div>`;
|
|
85
|
-
}
|
|
86
|
-
renderItemSlot() {
|
|
87
|
-
return html `<slot part="items"></slot>`;
|
|
88
|
-
}
|
|
89
|
-
connectedCallback() {
|
|
90
|
-
super.connectedCallback();
|
|
91
|
-
if (this.$control) {
|
|
92
|
-
// TODO: Manage $control ARIA attributes
|
|
93
|
-
// TODO: Handle $control change
|
|
94
|
-
this.$control.addEventListener('focusout', this.#handleFocusOut.bind(this));
|
|
66
|
+
this[internals].role = 'menu';
|
|
67
|
+
if (!this.hasAttribute('tabindex')) {
|
|
68
|
+
this.setAttribute('tabindex', '0');
|
|
95
69
|
}
|
|
96
|
-
|
|
97
|
-
this
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
disconnectedCallback() {
|
|
104
|
-
super.disconnectedCallback();
|
|
105
|
-
if (this.$control) {
|
|
106
|
-
this.$control.removeEventListener('focusout', this.#handleFocusOut.bind(this));
|
|
70
|
+
if (!isServer) {
|
|
71
|
+
this.addEventListener('keydown', this.handleKeyDown.bind(this));
|
|
72
|
+
this.addEventListener('focusin', this.#handleFocusIn.bind(this));
|
|
73
|
+
this.addEventListener('focusout', this.#handleFocusOut.bind(this));
|
|
74
|
+
this.addEventListener('mouseover', this.#handleMouseOver.bind(this));
|
|
75
|
+
this.addEventListener('click', this.#handleClick.bind(this));
|
|
107
76
|
}
|
|
108
77
|
}
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.popoverController.animateOpen().then(() => {
|
|
118
|
-
if (!this.noFocusControl) {
|
|
119
|
-
this.$menu.focus();
|
|
120
|
-
this.listController.focusFirstItem();
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
if (!this.noFocusControl) {
|
|
126
|
-
this.$menu.ariaActiveDescendantElement = null;
|
|
127
|
-
}
|
|
128
|
-
this.dispatchEvent(new Event('close', { bubbles: true, composed: true }));
|
|
129
|
-
this.listController.clearSearch();
|
|
130
|
-
if (this.$control) {
|
|
131
|
-
this.$control.ariaExpanded = 'false';
|
|
132
|
-
}
|
|
133
|
-
this.popoverController.animateClose().then(() => {
|
|
134
|
-
if (this.$lastFocused) {
|
|
135
|
-
if (!this.noFocusControl)
|
|
136
|
-
this.$lastFocused.focus();
|
|
137
|
-
this.$lastFocused = null;
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
78
|
+
#handleFocusIn() {
|
|
79
|
+
if (this.bare)
|
|
80
|
+
return;
|
|
81
|
+
// TODO: support focusing first selected
|
|
82
|
+
this.listController.focusFirstItem();
|
|
83
|
+
}
|
|
84
|
+
#handleFocusOut() {
|
|
85
|
+
this.listController._blurItem(this.listController._focusedItem);
|
|
142
86
|
}
|
|
143
|
-
|
|
87
|
+
handleKeyDown(event) {
|
|
144
88
|
if (event.defaultPrevented)
|
|
145
89
|
return;
|
|
146
|
-
const action = getActionFromKey(event
|
|
90
|
+
const action = getActionFromKey(event);
|
|
147
91
|
const items = this.$items;
|
|
148
92
|
const currentIndex = this.listController.currentIndex;
|
|
149
93
|
const maxIndex = items.length - 1;
|
|
150
94
|
switch (action) {
|
|
151
|
-
case
|
|
152
|
-
|
|
153
|
-
this.
|
|
154
|
-
|
|
155
|
-
case
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
95
|
+
case MenuAction.First:
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
this.listController.focusFirstItem();
|
|
98
|
+
return;
|
|
99
|
+
case MenuAction.Last:
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
this.listController.focusLastItem();
|
|
102
|
+
return;
|
|
103
|
+
case MenuAction.PageUp:
|
|
104
|
+
case MenuAction.PageDown: {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
const { first, last, pageSize } = getVisibleItems(this, items);
|
|
107
|
+
const isDown = action === MenuAction.PageDown;
|
|
108
|
+
const boundary = isDown ? last : first;
|
|
109
|
+
const atBoundary = isDown
|
|
110
|
+
? currentIndex >= last
|
|
111
|
+
: currentIndex <= first;
|
|
112
|
+
const step = isDown ? pageSize : -pageSize;
|
|
113
|
+
const nextIndex = atBoundary ? currentIndex + step : boundary;
|
|
114
|
+
this.listController._focusItem(items[Math.max(0, Math.min(maxIndex, nextIndex))]);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
case MenuAction.Next:
|
|
159
118
|
event.preventDefault();
|
|
160
|
-
|
|
161
|
-
this.listController._focusItem(items[nextIndex]);
|
|
119
|
+
this.listController.focusNextItem();
|
|
162
120
|
return;
|
|
163
|
-
case
|
|
121
|
+
case MenuAction.Previous:
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
this.listController.focusPreviousItem();
|
|
124
|
+
return;
|
|
125
|
+
case MenuAction.CloseSelect:
|
|
164
126
|
event.preventDefault();
|
|
165
127
|
if (currentIndex >= 0) {
|
|
166
128
|
items[currentIndex].focused = false;
|
|
167
|
-
this
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
bubbles: true,
|
|
173
|
-
composed: true,
|
|
174
|
-
}));
|
|
175
|
-
if (this.keepOpenSelect)
|
|
129
|
+
this.#dispatchAction({
|
|
130
|
+
item: items[currentIndex],
|
|
131
|
+
index: currentIndex,
|
|
132
|
+
});
|
|
133
|
+
if (this.keepOpenAction)
|
|
176
134
|
return;
|
|
177
|
-
this
|
|
135
|
+
this.#dispatchHide();
|
|
178
136
|
}
|
|
179
137
|
return;
|
|
180
|
-
case
|
|
181
|
-
event.preventDefault();
|
|
182
|
-
this.open = false;
|
|
183
|
-
return;
|
|
184
|
-
case MenuActions.Type:
|
|
185
|
-
this.open = true;
|
|
138
|
+
case MenuAction.Type:
|
|
186
139
|
this.listController.handleType(event.key);
|
|
187
140
|
return;
|
|
188
|
-
case MenuActions.Open:
|
|
189
|
-
event.preventDefault();
|
|
190
|
-
this.open = true;
|
|
191
|
-
return;
|
|
192
141
|
}
|
|
193
142
|
}
|
|
194
|
-
#
|
|
195
|
-
if (this.keepOpenBlur)
|
|
196
|
-
return;
|
|
197
|
-
const newFocus = event.relatedTarget;
|
|
198
|
-
const isInside = this.contains(newFocus) ||
|
|
199
|
-
this.shadowRoot?.contains(newFocus) ||
|
|
200
|
-
this.$control?.contains(newFocus);
|
|
201
|
-
if (!isInside) {
|
|
202
|
-
this.open = false;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
#handleItemMouseOver(event) {
|
|
143
|
+
#handleMouseOver(event) {
|
|
206
144
|
setFocusVisible(false);
|
|
207
|
-
const
|
|
208
|
-
this.
|
|
145
|
+
const item = this.#getItemFromEvent(event);
|
|
146
|
+
if (item && this.currentIndex !== item.index)
|
|
147
|
+
this.listController._focusItem(item.item);
|
|
209
148
|
}
|
|
210
|
-
#
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
149
|
+
#handleClick(event) {
|
|
150
|
+
const item = this.#getItemFromEvent(event);
|
|
151
|
+
if (!item)
|
|
152
|
+
return;
|
|
153
|
+
item.item.focused = false;
|
|
154
|
+
this.#dispatchAction({ ...item });
|
|
155
|
+
if (!this.keepOpenAction)
|
|
156
|
+
this.#dispatchHide();
|
|
157
|
+
}
|
|
158
|
+
#getItemFromEvent(event) {
|
|
159
|
+
const item = event.target.closest('[seele-base=option]');
|
|
160
|
+
if (!item || !this.$items.includes(item))
|
|
161
|
+
return null;
|
|
162
|
+
return { item, index: this.$items.indexOf(item) };
|
|
163
|
+
}
|
|
164
|
+
#dispatchAction(detail) {
|
|
165
|
+
this.dispatchEvent(new CustomEvent('action', {
|
|
166
|
+
detail: detail,
|
|
219
167
|
bubbles: true,
|
|
220
168
|
composed: true,
|
|
221
169
|
}));
|
|
222
|
-
if (!this.keepOpenSelect)
|
|
223
|
-
this.open = false;
|
|
224
|
-
}
|
|
225
|
-
get currentIndex() {
|
|
226
|
-
return this.listController?.currentIndex;
|
|
227
|
-
}
|
|
228
|
-
focusFirstItem() {
|
|
229
|
-
this.listController.focusFirstItem();
|
|
230
|
-
}
|
|
231
|
-
focusLastItem() {
|
|
232
|
-
this.listController.focusLastItem();
|
|
233
|
-
}
|
|
234
|
-
focusItem(item) {
|
|
235
|
-
this.listController._focusItem(item);
|
|
236
|
-
}
|
|
237
|
-
show() {
|
|
238
|
-
this.open = true;
|
|
239
170
|
}
|
|
240
|
-
|
|
241
|
-
this.
|
|
171
|
+
#dispatchHide() {
|
|
172
|
+
this.dispatchEvent(new Event('request-popup-hide', { bubbles: true, composed: true }));
|
|
242
173
|
}
|
|
243
174
|
}
|
|
244
175
|
__decorate([
|
|
245
|
-
property()
|
|
246
|
-
], Menu.prototype, "
|
|
176
|
+
property({ type: Boolean, attribute: 'keep-open-action' })
|
|
177
|
+
], Menu.prototype, "keepOpenAction", void 0);
|
|
247
178
|
__decorate([
|
|
248
179
|
property({ type: Boolean, reflect: true })
|
|
249
|
-
], Menu.prototype, "
|
|
250
|
-
__decorate([
|
|
251
|
-
property({ type: Boolean })
|
|
252
|
-
], Menu.prototype, "quick", void 0);
|
|
253
|
-
__decorate([
|
|
254
|
-
property({ type: Number })
|
|
255
|
-
], Menu.prototype, "offset", void 0);
|
|
256
|
-
__decorate([
|
|
257
|
-
property({ reflect: true })
|
|
258
|
-
], Menu.prototype, "align", void 0);
|
|
259
|
-
__decorate([
|
|
260
|
-
property({ type: String, reflect: true, attribute: 'align-strategy' })
|
|
261
|
-
], Menu.prototype, "alignStrategy", void 0);
|
|
262
|
-
__decorate([
|
|
263
|
-
property({ type: Boolean, attribute: 'keep-open-blur' })
|
|
264
|
-
], Menu.prototype, "keepOpenBlur", void 0);
|
|
265
|
-
__decorate([
|
|
266
|
-
property({ type: Boolean, attribute: 'keep-open-select' })
|
|
267
|
-
], Menu.prototype, "keepOpenSelect", void 0);
|
|
268
|
-
__decorate([
|
|
269
|
-
property({ type: Boolean, attribute: 'keep-open-click-away' })
|
|
270
|
-
], Menu.prototype, "keepOpenClickAway", void 0);
|
|
271
|
-
__decorate([
|
|
272
|
-
property({ type: Boolean, attribute: 'no-focus-control' })
|
|
273
|
-
], Menu.prototype, "noFocusControl", void 0);
|
|
274
|
-
__decorate([
|
|
275
|
-
property({ type: Number, attribute: 'data-tabindex' })
|
|
276
|
-
], Menu.prototype, "tabIndex", void 0);
|
|
277
|
-
__decorate([
|
|
278
|
-
query('[part="menu"]')
|
|
279
|
-
], Menu.prototype, "$menu", void 0);
|
|
180
|
+
], Menu.prototype, "bare", void 0);
|
|
280
181
|
__decorate([
|
|
281
182
|
queryAssignedElements({ flatten: true })
|
|
282
183
|
], Menu.prototype, "slotItems", void 0);
|
|
184
|
+
// Might as well be used in listbox, so let's keep it for now
|
|
283
185
|
// Reference: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
|
|
284
|
-
export
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0;
|
|
300
|
-
return matches && exclude.indexOf(option) < 0;
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
export function getActionFromKey(event, menuOpen) {
|
|
186
|
+
export var MenuAction;
|
|
187
|
+
(function (MenuAction) {
|
|
188
|
+
MenuAction[MenuAction["Close"] = -11] = "Close";
|
|
189
|
+
MenuAction[MenuAction["CloseSelect"] = -10] = "CloseSelect";
|
|
190
|
+
MenuAction[MenuAction["First"] = 1] = "First";
|
|
191
|
+
MenuAction[MenuAction["Last"] = 2] = "Last";
|
|
192
|
+
MenuAction[MenuAction["Next"] = 3] = "Next";
|
|
193
|
+
MenuAction[MenuAction["Open"] = 4] = "Open";
|
|
194
|
+
MenuAction[MenuAction["PageDown"] = 5] = "PageDown";
|
|
195
|
+
MenuAction[MenuAction["PageUp"] = 6] = "PageUp";
|
|
196
|
+
MenuAction[MenuAction["Previous"] = 7] = "Previous";
|
|
197
|
+
MenuAction[MenuAction["Select"] = 8] = "Select";
|
|
198
|
+
MenuAction[MenuAction["Type"] = 9] = "Type";
|
|
199
|
+
})(MenuAction || (MenuAction = {}));
|
|
200
|
+
export function getActionFromKey(event) {
|
|
304
201
|
const { key, altKey, ctrlKey, metaKey } = event;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (key === 'Home')
|
|
310
|
-
return
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
202
|
+
if (key === 'Escape')
|
|
203
|
+
return MenuAction.Close;
|
|
204
|
+
if (key === 'Enter' || key === ' ')
|
|
205
|
+
return MenuAction.CloseSelect;
|
|
206
|
+
if (key === 'Home')
|
|
207
|
+
return MenuAction.First;
|
|
208
|
+
if (key === 'End')
|
|
209
|
+
return MenuAction.Last;
|
|
210
|
+
if (key === 'PageUp')
|
|
211
|
+
return MenuAction.PageUp;
|
|
212
|
+
if (key === 'PageDown')
|
|
213
|
+
return MenuAction.PageDown;
|
|
214
|
+
if (key === 'ArrowUp' && altKey)
|
|
215
|
+
return MenuAction.CloseSelect;
|
|
216
|
+
if (key === 'ArrowDown' && !altKey)
|
|
217
|
+
return MenuAction.Next;
|
|
218
|
+
if (key === 'ArrowUp' && !altKey)
|
|
219
|
+
return MenuAction.Previous;
|
|
315
220
|
if (key === 'Backspace' ||
|
|
316
221
|
key === 'Clear' ||
|
|
317
222
|
(key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey)) {
|
|
318
|
-
return
|
|
223
|
+
return MenuAction.Type;
|
|
319
224
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
export function getVisibleItems(container, items) {
|
|
228
|
+
if (!items.length)
|
|
229
|
+
return { first: 0, last: 0, pageSize: 1 };
|
|
230
|
+
const containerRect = container.getBoundingClientRect();
|
|
231
|
+
const style = window.getComputedStyle(container);
|
|
232
|
+
const paddingTop = parseFloat(style.scrollPaddingTop) || 0;
|
|
233
|
+
const paddingBottom = parseFloat(style.scrollPaddingBottom) || 0;
|
|
234
|
+
const top = containerRect.top + container.clientTop + paddingTop;
|
|
235
|
+
const bottom = top + container.clientHeight - paddingTop - paddingBottom;
|
|
236
|
+
let first = -1;
|
|
237
|
+
let last = -1;
|
|
238
|
+
for (let i = 0; i < items.length; i++) {
|
|
239
|
+
const itemRect = items[i].getBoundingClientRect();
|
|
240
|
+
if (itemRect.top < bottom && itemRect.bottom > top) {
|
|
241
|
+
if (first === -1)
|
|
242
|
+
first = i;
|
|
243
|
+
last = i;
|
|
338
244
|
}
|
|
339
|
-
else if (
|
|
340
|
-
|
|
245
|
+
else if (first !== -1) {
|
|
246
|
+
break;
|
|
341
247
|
}
|
|
342
248
|
}
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
export function getUpdatedIndex(currentIndex, maxIndex, action) {
|
|
346
|
-
const pageSize = 10;
|
|
347
|
-
switch (action) {
|
|
348
|
-
case MenuActions.First:
|
|
349
|
-
return 0;
|
|
350
|
-
case MenuActions.Last:
|
|
351
|
-
return maxIndex;
|
|
352
|
-
case MenuActions.Previous:
|
|
353
|
-
return Math.max(0, currentIndex - 1);
|
|
354
|
-
case MenuActions.Next:
|
|
355
|
-
return Math.min(maxIndex, currentIndex + 1);
|
|
356
|
-
case MenuActions.PageUp:
|
|
357
|
-
return Math.max(0, currentIndex - pageSize);
|
|
358
|
-
case MenuActions.PageDown:
|
|
359
|
-
return Math.min(maxIndex, currentIndex + pageSize);
|
|
360
|
-
default:
|
|
361
|
-
return currentIndex;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
export function scrollItemIntoView(menu, item, paddingY = 0) {
|
|
365
|
-
if (!menu)
|
|
366
|
-
return;
|
|
367
|
-
// Basic scroll into view logic
|
|
368
|
-
const menuRect = menu.getBoundingClientRect();
|
|
369
|
-
const itemRect = item.getBoundingClientRect();
|
|
370
|
-
if (itemRect.bottom + paddingY > menuRect.bottom) {
|
|
371
|
-
menu.scrollTop += itemRect.bottom - menuRect.bottom + paddingY;
|
|
372
|
-
}
|
|
373
|
-
else if (itemRect.top - paddingY < menuRect.top) {
|
|
374
|
-
menu.scrollTop -= menuRect.top - itemRect.top + paddingY;
|
|
249
|
+
if (first === -1) {
|
|
250
|
+
return { first: 0, last: 0, pageSize: 1 };
|
|
375
251
|
}
|
|
252
|
+
// pageSize is the span of visible items
|
|
253
|
+
return { first, last, pageSize: Math.max(1, last - first + 1) };
|
|
376
254
|
}
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NOTE: demos at dev/pending.html
|
|
3
|
-
* NOTE: should apply writing style on event listeners to other files
|
|
4
|
-
* TODO: consider removing popoverController, but in case of tooltip, reserve
|
|
5
|
-
* for now
|
|
6
3
|
* TODO: hijack all `hidePopover()`s to allow more complex JS animations
|
|
7
|
-
* TODO: simpler Menu that just inherits List - do we really need a separated
|
|
8
|
-
* Menu? Why not just `<md-list role="presentation">`?
|
|
9
|
-
*
|
|
10
|
-
* Design: TO BE ADDED
|
|
11
4
|
*/
|
|
12
5
|
import { LitElement, PropertyValues } from 'lit';
|
|
13
6
|
import { Placement, Strategy } from '@floating-ui/dom';
|
|
14
|
-
import { autoAttachToParent, handleControlChange } from '
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
import { autoAttachToParent, handleControlChange } from './mixins/attachable.js';
|
|
8
|
+
declare const Popup_base: Constructor<import("./mixins/attachable.js").AttachableInterface> & Constructor<import("./mixins/internals-attached.js").InternalsAttachedInterface> & typeof LitElement;
|
|
9
|
+
/**
|
|
10
|
+
* When using popup with menu, you need to manually bind them using `aria-controls`
|
|
11
|
+
*/
|
|
17
12
|
export declare class Popup extends Popup_base {
|
|
18
13
|
#private;
|
|
19
14
|
open: boolean;
|
|
20
15
|
noFocusControl: boolean;
|
|
21
|
-
|
|
16
|
+
align: Placement;
|
|
22
17
|
strategy: Strategy;
|
|
23
18
|
offset: number;
|
|
24
19
|
windowPadding: number;
|
|
20
|
+
$ariaControl: HTMLElement | null;
|
|
25
21
|
static styles: any[];
|
|
26
22
|
render(): import("lit-html").TemplateResult<1>;
|
|
27
23
|
constructor();
|