@vollowx/seele 0.10.4 → 0.11.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/custom-elements.json +5 -14
- package/package.json +4 -4
- package/src/base/controllers/list-controller.js +1 -1
- package/src/base/controllers/popover-controller.js +6 -6
- package/src/base/input.js +3 -8
- package/src/base/menu-item.js +2 -2
- package/src/base/menu.js +185 -38
- package/src/base/mixins/focus-delegated.js +17 -0
- package/src/base/select.js +123 -187
- package/src/base/slider.js +3 -8
- package/src/base/tooltip.js +1 -1
- package/src/m3/button/common-button-styles.css.js +1 -1
- package/src/m3/button/common-button-toggle.js +8 -3
- package/src/m3/button/icon-button-styles.css.js +1 -1
- package/src/m3/button/icon-button-toggle.js +8 -3
- package/src/m3/button/icon-button.js +6 -1
- package/src/m3/button/shared-button-styles.css.js +1 -1
- package/src/m3/button/shared-button-toggle-styles.css.js +1 -1
- package/src/m3/checkbox-styles.css.js +1 -1
- package/src/m3/fab-styles.css.js +1 -1
- package/src/m3/field/field-styles.css.js +1 -1
- package/src/m3/field/filled-field-styles.css.js +1 -1
- package/src/m3/field/outlined-field-styles.css.js +1 -1
- package/src/m3/focus-ring-styles.css.js +1 -1
- package/src/m3/menu-styles.css.js +1 -1
- package/src/m3/menu.js +5 -4
- package/src/m3/ripple-styles.css.js +1 -1
- package/src/m3/select/filled-select.js +4 -5
- package/src/m3/select/outlined-select.js +4 -5
- package/src/m3/select/select-styles.css.js +1 -1
- package/src/m3/select/select.js +27 -7
- package/src/m3/slider-styles.css.js +1 -1
- package/src/m3/switch-styles.css.js +1 -1
- package/src/m3/toolbar-styles.css.js +1 -1
- package/src/m3/tooltip-styles.css.js +1 -1
- package/src/base/menu-utils.js +0 -104
- package/src/m3/menu-part-styles.css.js +0 -2
package/custom-elements.json
CHANGED
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"kind": "field",
|
|
126
126
|
"name": "color",
|
|
127
127
|
"type": {
|
|
128
|
-
"text": "| 'surface'\
|
|
128
|
+
"text": "| 'surface'\n | 'primary-container'\n | 'secondary-container'\n | 'tertiary-container'\n | 'primary'\n | 'secondary'\n | 'tertiary'"
|
|
129
129
|
},
|
|
130
130
|
"default": "'primary'",
|
|
131
131
|
"attribute": "color",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
{
|
|
145
145
|
"name": "color",
|
|
146
146
|
"type": {
|
|
147
|
-
"text": "| 'surface'\
|
|
147
|
+
"text": "| 'surface'\n | 'primary-container'\n | 'secondary-container'\n | 'tertiary-container'\n | 'primary'\n | 'secondary'\n | 'tertiary'"
|
|
148
148
|
},
|
|
149
149
|
"default": "'primary'",
|
|
150
150
|
"fieldName": "color"
|
|
@@ -656,7 +656,7 @@
|
|
|
656
656
|
"text": "array"
|
|
657
657
|
},
|
|
658
658
|
"readonly": true,
|
|
659
|
-
"default": "[ 'md-menu-item', 'md-menu-item-checkbox', 'md-menu-item-radio', ]"
|
|
659
|
+
"default": "[ 'md-menu-item', 'md-menu-item-checkbox', 'md-menu-item-radio', 'md-option', ]"
|
|
660
660
|
},
|
|
661
661
|
{
|
|
662
662
|
"kind": "field",
|
|
@@ -666,15 +666,6 @@
|
|
|
666
666
|
},
|
|
667
667
|
"readonly": true,
|
|
668
668
|
"default": "{ show: 300, hide: 200 }"
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
"kind": "field",
|
|
672
|
-
"name": "_scrollPadding",
|
|
673
|
-
"type": {
|
|
674
|
-
"text": "number"
|
|
675
|
-
},
|
|
676
|
-
"readonly": true,
|
|
677
|
-
"default": "4"
|
|
678
669
|
}
|
|
679
670
|
],
|
|
680
671
|
"superclass": {
|
|
@@ -1184,8 +1175,8 @@
|
|
|
1184
1175
|
}
|
|
1185
1176
|
],
|
|
1186
1177
|
"superclass": {
|
|
1187
|
-
"name": "
|
|
1188
|
-
"module": "/src/base/
|
|
1178
|
+
"name": "ToggleButton",
|
|
1179
|
+
"module": "/src/base/toggle-button.js"
|
|
1189
1180
|
},
|
|
1190
1181
|
"tagName": "md-switch",
|
|
1191
1182
|
"customElement": true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vollowx/seele",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Standard Extensible Elements. A web components library that can be styled and extended freely, pre-providing components in Material Design 3.",
|
|
5
5
|
"author": "vollowx",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,14 +54,14 @@
|
|
|
54
54
|
"@web/dev-server": "^0.4.6",
|
|
55
55
|
"@web/dev-server-esbuild": "^1.0.4",
|
|
56
56
|
"chokidar": "^5.0.0",
|
|
57
|
-
"lightningcss": "^1.
|
|
57
|
+
"lightningcss": "^1.31.1",
|
|
58
58
|
"postcss": "^8.5.6",
|
|
59
59
|
"postcss-cli": "^11.0.1",
|
|
60
60
|
"postcss-sorting": "^9.1.0",
|
|
61
|
-
"prettier": "^3.
|
|
61
|
+
"prettier": "^3.8.1"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@floating-ui/dom": "^1.7.
|
|
64
|
+
"@floating-ui/dom": "^1.7.5",
|
|
65
65
|
"@swc/helpers": "^0.5.18",
|
|
66
66
|
"lit": "^3.3.2"
|
|
67
67
|
}
|
|
@@ -33,13 +33,13 @@ export class PopoverController {
|
|
|
33
33
|
this.#closeTimer = null;
|
|
34
34
|
// TODO: Provide the ability to specify an arrow element.
|
|
35
35
|
this._dummyArrow = isServer ? null : document.createElement('div');
|
|
36
|
-
this.#
|
|
36
|
+
this.#handleClickAway = (event)=>{
|
|
37
37
|
const trigger = this.config.trigger();
|
|
38
38
|
const popover = this.config.popover();
|
|
39
39
|
const path = event.composedPath();
|
|
40
40
|
if (trigger && path.includes(trigger)) return;
|
|
41
41
|
if (popover && path.includes(popover)) return;
|
|
42
|
-
this.config.
|
|
42
|
+
this.config.onClickAway?.();
|
|
43
43
|
};
|
|
44
44
|
(this.host = host).addController(this);
|
|
45
45
|
this.config = config;
|
|
@@ -52,7 +52,7 @@ export class PopoverController {
|
|
|
52
52
|
}
|
|
53
53
|
hostDisconnected() {
|
|
54
54
|
this.cleanupAutoUpdate?.();
|
|
55
|
-
window.removeEventListener('pointerup', this.#
|
|
55
|
+
window.removeEventListener('pointerup', this.#handleClickAway);
|
|
56
56
|
}
|
|
57
57
|
#openTimer;
|
|
58
58
|
#closeTimer;
|
|
@@ -63,7 +63,7 @@ export class PopoverController {
|
|
|
63
63
|
// TODO: Use a global event listener to manage this more effectively
|
|
64
64
|
setTimeout(()=>{
|
|
65
65
|
if (this._open) {
|
|
66
|
-
window.addEventListener('pointerup', this.#
|
|
66
|
+
window.addEventListener('pointerup', this.#handleClickAway);
|
|
67
67
|
}
|
|
68
68
|
}, 0);
|
|
69
69
|
clearTimeout(this.#openTimer);
|
|
@@ -99,7 +99,7 @@ export class PopoverController {
|
|
|
99
99
|
async animateClose() {
|
|
100
100
|
if (!this._open) return;
|
|
101
101
|
this._open = false;
|
|
102
|
-
window.removeEventListener('pointerup', this.#
|
|
102
|
+
window.removeEventListener('pointerup', this.#handleClickAway);
|
|
103
103
|
clearTimeout(this.#openTimer);
|
|
104
104
|
clearTimeout(this.#closeTimer);
|
|
105
105
|
const closeDuration = this.config.durations.close();
|
|
@@ -151,5 +151,5 @@ export class PopoverController {
|
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
|
-
#
|
|
154
|
+
#handleClickAway;
|
|
155
155
|
}
|
package/src/base/input.js
CHANGED
|
@@ -6,16 +6,11 @@ import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
|
|
|
6
6
|
*/ import { LitElement, html, nothing } from 'lit';
|
|
7
7
|
import { property, query } from 'lit/decorators.js';
|
|
8
8
|
import { live } from 'lit/directives/live.js';
|
|
9
|
-
import { FormAssociated } from './mixins/form-associated.js';
|
|
10
9
|
import { InternalsAttached, internals } from './mixins/internals-attached.js';
|
|
11
|
-
|
|
10
|
+
import { FocusDelegated } from './mixins/focus-delegated.js';
|
|
11
|
+
import { FormAssociated } from './mixins/form-associated.js';
|
|
12
|
+
const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
12
13
|
export class Input extends Base {
|
|
13
|
-
static{
|
|
14
|
-
this.shadowRootOptions = {
|
|
15
|
-
...LitElement.shadowRootOptions,
|
|
16
|
-
delegatesFocus: true
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
14
|
render() {
|
|
20
15
|
const isTextarea = this.type === 'textarea';
|
|
21
16
|
const minLength = this.minLength > -1 ? this.minLength : nothing;
|
package/src/base/menu-item.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ListItem } from './list-item.js';
|
|
2
2
|
export const MenuItemMixin = (superClass)=>{
|
|
3
|
-
class
|
|
3
|
+
class MenuItemElement extends superClass {
|
|
4
4
|
constructor(...args){
|
|
5
5
|
super(...args), this._role = 'menuitem';
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
return
|
|
8
|
+
return MenuItemElement;
|
|
9
9
|
};
|
|
10
10
|
export class MenuItem extends MenuItemMixin(ListItem) {
|
|
11
11
|
}
|
package/src/base/menu.js
CHANGED
|
@@ -4,29 +4,29 @@ import { property, query, queryAssignedElements } from 'lit/decorators.js';
|
|
|
4
4
|
import { setFocusVisible } from '../core/focus-visible.js';
|
|
5
5
|
import { Attachable } from './mixins/attachable.js';
|
|
6
6
|
import { InternalsAttached } from './mixins/internals-attached.js';
|
|
7
|
+
import { FocusDelegated } from './mixins/focus-delegated.js';
|
|
7
8
|
import { PopoverController } from './controllers/popover-controller.js';
|
|
8
9
|
import { ListController } from './controllers/list-controller.js';
|
|
9
|
-
|
|
10
|
-
const Base = InternalsAttached(Attachable(LitElement));
|
|
10
|
+
const Base = FocusDelegated(InternalsAttached(Attachable(LitElement)));
|
|
11
11
|
/**
|
|
12
12
|
* @csspart menu
|
|
13
13
|
* @csspart items
|
|
14
14
|
*
|
|
15
|
-
* @fires {Event}
|
|
16
|
-
* @fires {Event}
|
|
17
|
-
* @fires {
|
|
15
|
+
* @fires {Event} open - Fires when the menu is opened.
|
|
16
|
+
* @fires {Event} close - Fires when the menu is closed.
|
|
17
|
+
* @fires {MenuSelectEvent} select - Fires when an item is selected.
|
|
18
|
+
* @fires {MenuItemFocusEvent} item-focus - Fires when an item is focused
|
|
19
|
+
*
|
|
20
|
+
* FIXME: aria-activedescendant may not work in and out shadow DOM
|
|
18
21
|
*/ export class Menu extends Base {
|
|
19
|
-
|
|
20
|
-
this.
|
|
21
|
-
...LitElement.shadowRootOptions,
|
|
22
|
-
delegatesFocus: true
|
|
23
|
-
};
|
|
22
|
+
get $items() {
|
|
23
|
+
return this.listController.items || [];
|
|
24
24
|
}
|
|
25
25
|
render() {
|
|
26
26
|
return html`<div
|
|
27
27
|
part="menu"
|
|
28
|
-
role="
|
|
29
|
-
tabindex="
|
|
28
|
+
role="${this.type}"
|
|
29
|
+
tabindex="${this.tabIndex}"
|
|
30
30
|
@keydown=${this.#handleKeyDown.bind(this)}
|
|
31
31
|
@focusout=${this.#handleFocusOut.bind(this)}
|
|
32
32
|
>
|
|
@@ -44,7 +44,7 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
44
44
|
this.$control.addEventListener('focusout', this.#handleFocusOut.bind(this));
|
|
45
45
|
}
|
|
46
46
|
this.updateComplete.then(()=>{
|
|
47
|
-
this
|
|
47
|
+
this.$items.forEach((item)=>{
|
|
48
48
|
item.addEventListener('mouseover', this.#handleItemMouseOver.bind(this));
|
|
49
49
|
item.addEventListener('click', this.#handleItemClick.bind(this));
|
|
50
50
|
});
|
|
@@ -68,8 +68,10 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
68
68
|
this.$control.ariaExpanded = 'true';
|
|
69
69
|
}
|
|
70
70
|
this.popoverController.animateOpen().then(()=>{
|
|
71
|
-
this
|
|
72
|
-
|
|
71
|
+
if (!this.noFocusControl) {
|
|
72
|
+
this.$menu.focus();
|
|
73
|
+
this.listController.focusFirstItem();
|
|
74
|
+
}
|
|
73
75
|
});
|
|
74
76
|
} else {
|
|
75
77
|
this.dispatchEvent(new Event('close', {
|
|
@@ -82,7 +84,7 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
82
84
|
}
|
|
83
85
|
this.popoverController.animateClose().then(()=>{
|
|
84
86
|
if (this.$lastFocused) {
|
|
85
|
-
this.$lastFocused.focus();
|
|
87
|
+
if (!this.noFocusControl) this.$lastFocused.focus();
|
|
86
88
|
this.$lastFocused = null;
|
|
87
89
|
}
|
|
88
90
|
});
|
|
@@ -92,7 +94,7 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
92
94
|
#handleKeyDown(event) {
|
|
93
95
|
if (event.defaultPrevented) return;
|
|
94
96
|
const action = getActionFromKey(event, this.open);
|
|
95
|
-
const items = this
|
|
97
|
+
const items = this.$items;
|
|
96
98
|
const currentIndex = this.listController.currentIndex;
|
|
97
99
|
const maxIndex = items.length - 1;
|
|
98
100
|
switch(action){
|
|
@@ -111,10 +113,10 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
111
113
|
case MenuActions.CloseSelect:
|
|
112
114
|
event.preventDefault();
|
|
113
115
|
if (currentIndex >= 0) {
|
|
114
|
-
|
|
116
|
+
items[currentIndex].focused = false;
|
|
115
117
|
this.dispatchEvent(new CustomEvent('select', {
|
|
116
118
|
detail: {
|
|
117
|
-
item:
|
|
119
|
+
item: items[currentIndex],
|
|
118
120
|
index: currentIndex
|
|
119
121
|
},
|
|
120
122
|
bubbles: true,
|
|
@@ -163,10 +165,20 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
163
165
|
bubbles: true,
|
|
164
166
|
composed: true
|
|
165
167
|
}));
|
|
166
|
-
if (this.keepOpenClickItem)
|
|
167
|
-
|
|
168
|
+
if (!this.keepOpenClickItem) this.open = false;
|
|
169
|
+
}
|
|
170
|
+
get currentIndex() {
|
|
171
|
+
return this.listController?.currentIndex;
|
|
172
|
+
}
|
|
173
|
+
focusFirstItem() {
|
|
174
|
+
this.listController.focusFirstItem();
|
|
175
|
+
}
|
|
176
|
+
focusLastItem() {
|
|
177
|
+
this.listController.focusLastItem();
|
|
178
|
+
}
|
|
179
|
+
focusItem(item) {
|
|
180
|
+
this.listController._focusItem(item);
|
|
168
181
|
}
|
|
169
|
-
// Exposed functions
|
|
170
182
|
show() {
|
|
171
183
|
this.open = true;
|
|
172
184
|
}
|
|
@@ -177,7 +189,7 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
177
189
|
super(...args), this._possibleItemTags = [], this._durations = {
|
|
178
190
|
show: 0,
|
|
179
191
|
hide: 0
|
|
180
|
-
}, this._scrollPadding = 0, this.open = false, this.quick = false, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.
|
|
192
|
+
}, this._scrollPadding = 0, this.type = 'menu', this.open = false, this.quick = false, this.offset = 0, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.keepOpenBlur = false, this.keepOpenClickItem = false, this.keepOpenClickAway = false, this.noListControl = false, this.noFocusControl = false, this.tabIndex = 0, this.$lastFocused = null, this.popoverController = new PopoverController(this, {
|
|
181
193
|
popover: ()=>this.$menu,
|
|
182
194
|
trigger: ()=>this.$control,
|
|
183
195
|
positioning: {
|
|
@@ -190,8 +202,8 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
190
202
|
open: ()=>this.quick ? 0 : this._durations.show,
|
|
191
203
|
close: ()=>this.quick ? 0 : this._durations.hide
|
|
192
204
|
},
|
|
193
|
-
|
|
194
|
-
if (!this.
|
|
205
|
+
onClickAway: ()=>{
|
|
206
|
+
if (!this.keepOpenClickAway) this.open = false;
|
|
195
207
|
}
|
|
196
208
|
}), this.listController = new ListController(this, {
|
|
197
209
|
isItem: (item)=>this._possibleItemTags.includes(item.tagName.toLowerCase()) && !item.hasAttribute('disabled'),
|
|
@@ -201,13 +213,23 @@ const Base = InternalsAttached(Attachable(LitElement));
|
|
|
201
213
|
},
|
|
202
214
|
focusItem: (item)=>{
|
|
203
215
|
item.focused = true;
|
|
204
|
-
this.$menu.setAttribute('aria-activedescendant', item.id);
|
|
216
|
+
if (!this.noFocusControl) this.$menu.setAttribute('aria-activedescendant', item.id);
|
|
205
217
|
scrollItemIntoView(this.$menu, item, this._scrollPadding);
|
|
218
|
+
this.dispatchEvent(new CustomEvent('item-focus', {
|
|
219
|
+
detail: {
|
|
220
|
+
item: item
|
|
221
|
+
},
|
|
222
|
+
bubbles: true,
|
|
223
|
+
composed: true
|
|
224
|
+
}));
|
|
206
225
|
},
|
|
207
226
|
wrapNavigation: ()=>false
|
|
208
227
|
});
|
|
209
228
|
}
|
|
210
229
|
}
|
|
230
|
+
_ts_decorate([
|
|
231
|
+
property()
|
|
232
|
+
], Menu.prototype, "type", void 0);
|
|
211
233
|
_ts_decorate([
|
|
212
234
|
property({
|
|
213
235
|
type: Boolean,
|
|
@@ -216,10 +238,14 @@ _ts_decorate([
|
|
|
216
238
|
], Menu.prototype, "open", void 0);
|
|
217
239
|
_ts_decorate([
|
|
218
240
|
property({
|
|
219
|
-
type: Boolean
|
|
220
|
-
reflect: true
|
|
241
|
+
type: Boolean
|
|
221
242
|
})
|
|
222
243
|
], Menu.prototype, "quick", void 0);
|
|
244
|
+
_ts_decorate([
|
|
245
|
+
property({
|
|
246
|
+
type: Number
|
|
247
|
+
})
|
|
248
|
+
], Menu.prototype, "offset", void 0);
|
|
223
249
|
_ts_decorate([
|
|
224
250
|
property({
|
|
225
251
|
reflect: true
|
|
@@ -228,30 +254,46 @@ _ts_decorate([
|
|
|
228
254
|
_ts_decorate([
|
|
229
255
|
property({
|
|
230
256
|
type: String,
|
|
231
|
-
reflect: true
|
|
257
|
+
reflect: true,
|
|
258
|
+
attribute: 'align-strategy'
|
|
232
259
|
})
|
|
233
260
|
], Menu.prototype, "alignStrategy", void 0);
|
|
234
261
|
_ts_decorate([
|
|
235
262
|
property({
|
|
236
|
-
type:
|
|
237
|
-
|
|
263
|
+
type: Boolean,
|
|
264
|
+
attribute: 'keep-open-blur'
|
|
238
265
|
})
|
|
239
|
-
], Menu.prototype, "
|
|
266
|
+
], Menu.prototype, "keepOpenBlur", void 0);
|
|
240
267
|
_ts_decorate([
|
|
241
268
|
property({
|
|
242
|
-
type: Boolean
|
|
269
|
+
type: Boolean,
|
|
270
|
+
attribute: 'keep-open-click-item'
|
|
243
271
|
})
|
|
244
|
-
], Menu.prototype, "
|
|
272
|
+
], Menu.prototype, "keepOpenClickItem", void 0);
|
|
245
273
|
_ts_decorate([
|
|
246
274
|
property({
|
|
247
|
-
type: Boolean
|
|
275
|
+
type: Boolean,
|
|
276
|
+
attribute: 'keep-open-click-away'
|
|
248
277
|
})
|
|
249
|
-
], Menu.prototype, "
|
|
278
|
+
], Menu.prototype, "keepOpenClickAway", void 0);
|
|
250
279
|
_ts_decorate([
|
|
251
280
|
property({
|
|
252
|
-
type: Boolean
|
|
281
|
+
type: Boolean,
|
|
282
|
+
attribute: 'no-list-control'
|
|
283
|
+
})
|
|
284
|
+
], Menu.prototype, "noListControl", void 0);
|
|
285
|
+
_ts_decorate([
|
|
286
|
+
property({
|
|
287
|
+
type: Boolean,
|
|
288
|
+
attribute: 'no-focus-control'
|
|
253
289
|
})
|
|
254
|
-
], Menu.prototype, "
|
|
290
|
+
], Menu.prototype, "noFocusControl", void 0);
|
|
291
|
+
_ts_decorate([
|
|
292
|
+
property({
|
|
293
|
+
type: Number,
|
|
294
|
+
attribute: 'data-tabindex'
|
|
295
|
+
})
|
|
296
|
+
], Menu.prototype, "tabIndex", void 0);
|
|
255
297
|
_ts_decorate([
|
|
256
298
|
query('[part="menu"]')
|
|
257
299
|
], Menu.prototype, "$menu", void 0);
|
|
@@ -260,3 +302,108 @@ _ts_decorate([
|
|
|
260
302
|
flatten: true
|
|
261
303
|
})
|
|
262
304
|
], Menu.prototype, "slotItems", void 0);
|
|
305
|
+
// Reference: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
|
|
306
|
+
export const MenuActions = {
|
|
307
|
+
Close: 0,
|
|
308
|
+
CloseSelect: 1,
|
|
309
|
+
First: 2,
|
|
310
|
+
Last: 3,
|
|
311
|
+
Next: 4,
|
|
312
|
+
Open: 5,
|
|
313
|
+
PageDown: 6,
|
|
314
|
+
PageUp: 7,
|
|
315
|
+
Previous: 8,
|
|
316
|
+
Select: 9,
|
|
317
|
+
Type: 10
|
|
318
|
+
};
|
|
319
|
+
export function filterOptions(options = [], filter, exclude = []) {
|
|
320
|
+
return options.filter((option)=>{
|
|
321
|
+
const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0;
|
|
322
|
+
return matches && exclude.indexOf(option) < 0;
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
export function getActionFromKey(event, menuOpen) {
|
|
326
|
+
const { key, altKey, ctrlKey, metaKey } = event;
|
|
327
|
+
const openKeys = [
|
|
328
|
+
'ArrowDown',
|
|
329
|
+
'ArrowUp',
|
|
330
|
+
'Enter',
|
|
331
|
+
' '
|
|
332
|
+
];
|
|
333
|
+
if (!menuOpen && openKeys.includes(key)) {
|
|
334
|
+
return MenuActions.Open;
|
|
335
|
+
}
|
|
336
|
+
if (key === 'Home') {
|
|
337
|
+
return MenuActions.First;
|
|
338
|
+
}
|
|
339
|
+
if (key === 'End') {
|
|
340
|
+
return MenuActions.Last;
|
|
341
|
+
}
|
|
342
|
+
if (key === 'Backspace' || key === 'Clear' || key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey) {
|
|
343
|
+
return MenuActions.Type;
|
|
344
|
+
}
|
|
345
|
+
if (menuOpen) {
|
|
346
|
+
if (key === 'ArrowUp' && altKey) {
|
|
347
|
+
return MenuActions.CloseSelect;
|
|
348
|
+
} else if (key === 'ArrowDown' && !altKey) {
|
|
349
|
+
return MenuActions.Next;
|
|
350
|
+
} else if (key === 'ArrowUp') {
|
|
351
|
+
return MenuActions.Previous;
|
|
352
|
+
} else if (key === 'PageUp') {
|
|
353
|
+
return MenuActions.PageUp;
|
|
354
|
+
} else if (key === 'PageDown') {
|
|
355
|
+
return MenuActions.PageDown;
|
|
356
|
+
} else if (key === 'Escape') {
|
|
357
|
+
return MenuActions.Close;
|
|
358
|
+
} else if (key === 'Enter' || key === ' ') {
|
|
359
|
+
return MenuActions.CloseSelect;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
export function getIndexByLetter(options, filter, startIndex = 0) {
|
|
365
|
+
const orderedOptions = [
|
|
366
|
+
...options.slice(startIndex),
|
|
367
|
+
...options.slice(0, startIndex)
|
|
368
|
+
];
|
|
369
|
+
const firstMatch = filterOptions(orderedOptions, filter)[0];
|
|
370
|
+
const allSameLetter = (array)=>array.every((letter)=>letter === array[0]);
|
|
371
|
+
if (firstMatch) {
|
|
372
|
+
return options.indexOf(firstMatch);
|
|
373
|
+
} else if (allSameLetter(filter.split(''))) {
|
|
374
|
+
const matches = filterOptions(orderedOptions, filter[0]);
|
|
375
|
+
return options.indexOf(matches[0]);
|
|
376
|
+
} else {
|
|
377
|
+
return -1;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
export function getUpdatedIndex(currentIndex, maxIndex, action) {
|
|
381
|
+
const pageSize = 10;
|
|
382
|
+
switch(action){
|
|
383
|
+
case MenuActions.First:
|
|
384
|
+
return 0;
|
|
385
|
+
case MenuActions.Last:
|
|
386
|
+
return maxIndex;
|
|
387
|
+
case MenuActions.Previous:
|
|
388
|
+
return Math.max(0, currentIndex - 1);
|
|
389
|
+
case MenuActions.Next:
|
|
390
|
+
return Math.min(maxIndex, currentIndex + 1);
|
|
391
|
+
case MenuActions.PageUp:
|
|
392
|
+
return Math.max(0, currentIndex - pageSize);
|
|
393
|
+
case MenuActions.PageDown:
|
|
394
|
+
return Math.min(maxIndex, currentIndex + pageSize);
|
|
395
|
+
default:
|
|
396
|
+
return currentIndex;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
export function scrollItemIntoView(menu, item, paddingY = 0) {
|
|
400
|
+
if (!menu) return;
|
|
401
|
+
// Basic scroll into view logic
|
|
402
|
+
const menuRect = menu.getBoundingClientRect();
|
|
403
|
+
const itemRect = item.getBoundingClientRect();
|
|
404
|
+
if (itemRect.bottom + paddingY > menuRect.bottom) {
|
|
405
|
+
menu.scrollTop += itemRect.bottom - menuRect.bottom + paddingY;
|
|
406
|
+
} else if (itemRect.top - paddingY < menuRect.top) {
|
|
407
|
+
menu.scrollTop -= menuRect.top - itemRect.top + paddingY;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { internals } from './internals-attached.js';
|
|
3
|
+
export const FocusDelegated = (superClass)=>{
|
|
4
|
+
class FocusDelegatedElement extends superClass {
|
|
5
|
+
static{
|
|
6
|
+
this.shadowRootOptions = {
|
|
7
|
+
...LitElement.shadowRootOptions,
|
|
8
|
+
delegatesFocus: true
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
constructor(...args){
|
|
12
|
+
super();
|
|
13
|
+
this[internals].role = 'presentation';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return FocusDelegatedElement;
|
|
17
|
+
};
|