@vollowx/seele 0.13.4 → 0.13.6
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 +1256 -115
- package/package.json +22 -14
- package/src/all.d.ts +34 -0
- package/src/all.js +1 -0
- package/src/base/autocomplete.d.ts +56 -0
- package/src/base/autocomplete.js +82 -87
- package/src/base/button.d.ts +13 -0
- package/src/base/button.js +26 -22
- package/src/base/checkbox.d.ts +18 -0
- package/src/base/checkbox.js +39 -34
- package/src/base/controllers/list-controller.d.ts +37 -0
- package/src/base/controllers/list-controller.js +23 -15
- package/src/base/controllers/popover-controller.d.ts +35 -0
- package/src/base/controllers/popover-controller.js +37 -29
- package/src/base/dialog.d.ts +20 -0
- package/src/base/dialog.js +11 -9
- package/src/base/field.d.ts +3 -0
- package/src/base/input.d.ts +44 -0
- package/src/base/input.js +57 -65
- package/src/base/item.d.ts +7 -0
- package/src/base/item.js +2 -1
- package/src/base/list-item.d.ts +15 -0
- package/src/base/list-item.js +22 -22
- package/src/base/list.d.ts +54 -0
- package/src/base/list.js +69 -55
- package/src/base/menu-item.d.ts +6 -0
- package/src/base/menu-item.js +4 -3
- package/src/base/menu.d.ts +76 -0
- package/src/base/menu.js +135 -143
- package/src/base/mixins/attachable.d.ts +9 -0
- package/src/base/mixins/attachable.js +22 -18
- package/src/base/mixins/focus-delegated.d.ts +5 -0
- package/src/base/mixins/focus-delegated.js +6 -8
- package/src/base/mixins/form-associated.d.ts +15 -0
- package/src/base/mixins/form-associated.js +11 -18
- package/src/base/mixins/internals-attached.d.ts +6 -0
- package/src/base/mixins/internals-attached.js +1 -1
- package/src/base/option.d.ts +9 -0
- package/src/base/option.js +7 -8
- package/src/base/radio.d.ts +13 -0
- package/src/base/radio.js +169 -0
- package/src/base/select.d.ts +116 -0
- package/src/base/select.js +130 -128
- package/src/base/slider.d.ts +75 -0
- package/src/base/slider.js +115 -118
- package/src/base/tab-panel.d.ts +9 -0
- package/src/base/tab-panel.js +11 -11
- package/src/base/tab.d.ts +18 -0
- package/src/base/tab.js +25 -24
- package/src/base/tabs.d.ts +19 -0
- package/src/base/tabs.js +84 -81
- package/src/base/toggle-button.d.ts +47 -0
- package/src/base/toggle-button.js +57 -53
- package/src/base/tooltip.d.ts +37 -0
- package/src/base/tooltip.js +70 -65
- package/src/core/decorators.d.ts +2 -0
- package/src/core/decorators.js +5 -4
- package/src/core/focus-visible.d.ts +2 -0
- package/src/core/focus-visible.js +4 -4
- package/src/core/shared.d.ts +1 -0
- package/src/core/unique-id.d.ts +4 -0
- package/src/core/unique-id.js +2 -1
- package/src/m3/autocomplete/autocomplete.d.ts +8 -0
- package/src/m3/autocomplete/autocomplete.js +10 -10
- package/src/m3/badge/badge.d.ts +12 -0
- package/src/m3/badge/badge.js +24 -37
- package/src/m3/button/common-button-toggle.d.ts +31 -0
- package/src/m3/button/common-button-toggle.js +44 -41
- package/src/m3/button/common-button.d.ts +29 -0
- package/src/m3/button/common-button.js +40 -40
- package/src/m3/button/icon-button-toggle.d.ts +24 -0
- package/src/m3/button/icon-button-toggle.js +35 -32
- package/src/m3/button/icon-button.d.ts +23 -0
- package/src/m3/button/icon-button.js +33 -31
- package/src/m3/button-group/connected-button-group.d.ts +19 -0
- package/src/m3/button-group/connected-button-group.js +30 -28
- package/src/m3/button-group/standard-button-group.d.ts +24 -0
- package/src/m3/button-group/standard-button-group.js +72 -66
- package/src/m3/checkbox/checkbox.d.ts +25 -0
- package/src/m3/checkbox/checkbox.js +24 -22
- package/src/m3/dialog/dialog-spring.d.ts +45 -0
- package/src/m3/dialog/dialog-spring.js +83 -86
- package/src/m3/dialog/dialog.d.ts +46 -0
- package/src/m3/dialog/dialog.js +90 -117
- package/src/m3/divider/divider.d.ts +13 -0
- package/src/m3/divider/divider.js +20 -33
- package/src/m3/fab/fab.d.ts +23 -0
- package/src/m3/fab/fab.js +24 -23
- package/src/m3/field/field.d.ts +28 -0
- package/src/m3/field/field.js +40 -55
- package/src/m3/field/filled-field.d.ts +11 -0
- package/src/m3/field/filled-field.js +7 -11
- package/src/m3/field/outlined-field.d.ts +11 -0
- package/src/m3/field/outlined-field.js +8 -12
- package/src/m3/focus-ring/focus-ring.d.ts +32 -0
- package/src/m3/focus-ring/focus-ring.js +38 -23
- package/src/m3/item/item.d.ts +31 -0
- package/src/m3/item/item.js +32 -17
- package/src/m3/list/list-item.d.ts +22 -0
- package/src/m3/list/list-item.js +18 -16
- package/src/m3/list/list.d.ts +15 -0
- package/src/m3/list/list.js +16 -15
- package/src/m3/loading-indicator/loading-indicator.d.ts +26 -0
- package/src/m3/loading-indicator/loading-indicator.js +113 -161
- package/src/m3/menu/menu-item.d.ts +15 -0
- package/src/m3/menu/menu-item.js +10 -4
- package/src/m3/menu/menu.d.ts +22 -0
- package/src/m3/menu/menu.js +20 -18
- package/src/m3/radio/radio-styles.css.js +2 -0
- package/src/m3/radio/radio.d.ts +17 -0
- package/src/m3/radio/radio.js +33 -0
- package/src/m3/ripple/ripple.d.ts +43 -0
- package/src/m3/ripple/ripple.js +82 -89
- package/src/m3/select/filled-select.d.ts +16 -0
- package/src/m3/select/filled-select.js +12 -10
- package/src/m3/select/option.d.ts +15 -0
- package/src/m3/select/option.js +10 -4
- package/src/m3/select/outlined-select.d.ts +16 -0
- package/src/m3/select/outlined-select.js +12 -10
- package/src/m3/select/select.d.ts +13 -0
- package/src/m3/select/select.js +16 -16
- package/src/m3/slider/slider.d.ts +20 -0
- package/src/m3/slider/slider.js +32 -26
- package/src/m3/switch/switch.d.ts +27 -0
- package/src/m3/switch/switch.js +62 -53
- package/src/m3/system/motion.d.ts +12 -0
- package/src/m3/system/motion.js +16 -9
- package/src/m3/tab/tab-panel.d.ts +9 -0
- package/src/m3/tab/tab-panel.js +6 -9
- package/src/m3/tab/tab.d.ts +19 -0
- package/src/m3/tab/tab.js +14 -12
- package/src/m3/tab/tabs.d.ts +25 -0
- package/src/m3/tab/tabs.js +34 -30
- package/src/m3/text-field/filled-text-field.d.ts +13 -0
- package/src/m3/text-field/filled-text-field.js +15 -20
- package/src/m3/text-field/outlined-text-field.d.ts +13 -0
- package/src/m3/text-field/outlined-text-field.js +15 -20
- package/src/m3/toolbar/toolbar.d.ts +26 -0
- package/src/m3/toolbar/toolbar.js +28 -25
- package/src/m3/tooltip/tooltip.d.ts +19 -0
- package/src/m3/tooltip/tooltip.js +15 -14
- package/src/win98/button/button.d.ts +13 -0
- package/src/win98/button/button.js +13 -11
- package/src/win98/button/radio.d.ts +3 -0
- package/src/win98/button/radio.js +9 -0
- package/src/win98/radio/radio.d.ts +3 -0
- package/src/win98/radio/radio.js +9 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LitElement, PropertyValues } from 'lit';
|
|
2
|
+
declare const Radio_base: Constructor<import("./mixins/form-associated.js").FormAssociatedInterface> & Constructor<import("./mixins/internals-attached.js").InternalsAttachedInterface> & typeof LitElement;
|
|
3
|
+
export declare class Radio extends Radio_base {
|
|
4
|
+
#private;
|
|
5
|
+
value: string;
|
|
6
|
+
checked: boolean;
|
|
7
|
+
constructor();
|
|
8
|
+
connectedCallback(): void;
|
|
9
|
+
disconnectedCallback(): void;
|
|
10
|
+
protected updated(changedProperties: PropertyValues<this>): void;
|
|
11
|
+
formResetCallback(): void;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import { __decorate } from "tslib";
|
|
3
|
+
import { LitElement } from 'lit';
|
|
4
|
+
import { property } from 'lit/decorators.js';
|
|
5
|
+
import { FormAssociated } from './mixins/form-associated.js';
|
|
6
|
+
import { InternalsAttached, internals } from './mixins/internals-attached.js';
|
|
7
|
+
export class Radio extends FormAssociated(InternalsAttached(LitElement)) {
|
|
8
|
+
// Static hidden registry: Scope (Form or RootNode) -> Name -> Set<Radio>
|
|
9
|
+
static #registry = new Map();
|
|
10
|
+
#currentScope;
|
|
11
|
+
#currentName;
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.#currentScope = null;
|
|
15
|
+
this.#currentName = '';
|
|
16
|
+
this.checked = false;
|
|
17
|
+
this[internals].role = 'radio';
|
|
18
|
+
}
|
|
19
|
+
connectedCallback() {
|
|
20
|
+
super.connectedCallback();
|
|
21
|
+
this.#registerToGroup();
|
|
22
|
+
this.#updateAriaState();
|
|
23
|
+
this.addEventListener('click', this.#handleClick);
|
|
24
|
+
this.addEventListener('keydown', this.#handleKeyDown);
|
|
25
|
+
}
|
|
26
|
+
disconnectedCallback() {
|
|
27
|
+
this.removeEventListener('click', this.#handleClick);
|
|
28
|
+
this.removeEventListener('keydown', this.#handleKeyDown);
|
|
29
|
+
this.#unregisterFromGroup();
|
|
30
|
+
super.disconnectedCallback();
|
|
31
|
+
}
|
|
32
|
+
updated(changedProperties) {
|
|
33
|
+
super.updated(changedProperties);
|
|
34
|
+
if (changedProperties.has('name')) {
|
|
35
|
+
this.#unregisterFromGroup();
|
|
36
|
+
this.#registerToGroup();
|
|
37
|
+
}
|
|
38
|
+
if (changedProperties.has('checked')) {
|
|
39
|
+
if (this.checked) {
|
|
40
|
+
this.#uncheckSiblings();
|
|
41
|
+
}
|
|
42
|
+
this[internals].setFormValue(this.checked ? this.value : null);
|
|
43
|
+
this.#updateGroupTabIndices();
|
|
44
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
45
|
+
}
|
|
46
|
+
if (changedProperties.has('disabled') || changedProperties.has('checked'))
|
|
47
|
+
this.#updateAriaState();
|
|
48
|
+
}
|
|
49
|
+
#handleClick() {
|
|
50
|
+
if (this.disabled || this.checked)
|
|
51
|
+
return;
|
|
52
|
+
this.checked = true;
|
|
53
|
+
this.focus();
|
|
54
|
+
}
|
|
55
|
+
#handleKeyDown(event) {
|
|
56
|
+
if (this.disabled)
|
|
57
|
+
return;
|
|
58
|
+
switch (event.key) {
|
|
59
|
+
case ' ':
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
if (!this.checked)
|
|
62
|
+
this.checked = true;
|
|
63
|
+
break;
|
|
64
|
+
case 'ArrowDown':
|
|
65
|
+
case 'ArrowRight':
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
this.#navigateGroup(1);
|
|
68
|
+
break;
|
|
69
|
+
case 'ArrowUp':
|
|
70
|
+
case 'ArrowLeft':
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
this.#navigateGroup(-1);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
#getScope() {
|
|
77
|
+
return this.form ?? this.getRootNode();
|
|
78
|
+
}
|
|
79
|
+
#registerToGroup() {
|
|
80
|
+
if (!this.name)
|
|
81
|
+
return;
|
|
82
|
+
const scope = this.#getScope();
|
|
83
|
+
this.#currentScope = scope;
|
|
84
|
+
this.#currentName = this.name;
|
|
85
|
+
let scopeMap = _a.#registry.get(scope);
|
|
86
|
+
if (!scopeMap) {
|
|
87
|
+
scopeMap = new Map();
|
|
88
|
+
_a.#registry.set(scope, scopeMap);
|
|
89
|
+
}
|
|
90
|
+
let group = scopeMap.get(this.name);
|
|
91
|
+
if (!group) {
|
|
92
|
+
group = new Set();
|
|
93
|
+
scopeMap.set(this.name, group);
|
|
94
|
+
}
|
|
95
|
+
group.add(this);
|
|
96
|
+
this.#updateGroupTabIndices(group);
|
|
97
|
+
}
|
|
98
|
+
#unregisterFromGroup() {
|
|
99
|
+
if (!this.#currentScope || !this.#currentName)
|
|
100
|
+
return;
|
|
101
|
+
const scopeMap = _a.#registry.get(this.#currentScope);
|
|
102
|
+
if (scopeMap) {
|
|
103
|
+
const group = scopeMap.get(this.#currentName);
|
|
104
|
+
if (group) {
|
|
105
|
+
group.delete(this);
|
|
106
|
+
if (group.size === 0) {
|
|
107
|
+
scopeMap.delete(this.#currentName);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.#updateGroupTabIndices(group);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (scopeMap.size === 0)
|
|
114
|
+
_a.#registry.delete(this.#currentScope);
|
|
115
|
+
}
|
|
116
|
+
this.#currentScope = null;
|
|
117
|
+
this.#currentName = '';
|
|
118
|
+
}
|
|
119
|
+
#getGroupRadios() {
|
|
120
|
+
if (!this.name)
|
|
121
|
+
return [this];
|
|
122
|
+
const scope = this.#getScope();
|
|
123
|
+
const group = _a.#registry.get(scope)?.get(this.name);
|
|
124
|
+
return group ? Array.from(group) : [this];
|
|
125
|
+
}
|
|
126
|
+
#uncheckSiblings() {
|
|
127
|
+
const radios = this.#getGroupRadios();
|
|
128
|
+
for (const radio of radios)
|
|
129
|
+
if (radio !== this && radio.checked)
|
|
130
|
+
radio.checked = false;
|
|
131
|
+
}
|
|
132
|
+
#updateGroupTabIndices(groupSet) {
|
|
133
|
+
const radios = groupSet ? Array.from(groupSet) : this.#getGroupRadios();
|
|
134
|
+
if (radios.length === 0)
|
|
135
|
+
return;
|
|
136
|
+
const hasChecked = radios.some((r) => r.checked && !r.disabled);
|
|
137
|
+
let enabledFound = false;
|
|
138
|
+
for (const radio of radios) {
|
|
139
|
+
const active = !radio.disabled && (hasChecked ? radio.checked : !enabledFound);
|
|
140
|
+
if (active)
|
|
141
|
+
enabledFound = true;
|
|
142
|
+
radio.tabIndex = active ? 0 : -1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
#navigateGroup(direction) {
|
|
146
|
+
const radios = this.#getGroupRadios().filter((r) => !r.disabled);
|
|
147
|
+
if (radios.length <= 1)
|
|
148
|
+
return;
|
|
149
|
+
const currentIndex = radios.indexOf(this);
|
|
150
|
+
const nextIndex = (currentIndex + direction + radios.length) % radios.length;
|
|
151
|
+
const targetRadio = radios[nextIndex];
|
|
152
|
+
targetRadio.checked = true;
|
|
153
|
+
targetRadio.focus();
|
|
154
|
+
}
|
|
155
|
+
#updateAriaState() {
|
|
156
|
+
this[internals].ariaChecked = String(this.checked);
|
|
157
|
+
this[internals].ariaDisabled = String(this.disabled);
|
|
158
|
+
}
|
|
159
|
+
formResetCallback() {
|
|
160
|
+
this.checked = this.hasAttribute('checked');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
_a = Radio;
|
|
164
|
+
__decorate([
|
|
165
|
+
property({ type: String })
|
|
166
|
+
], Radio.prototype, "value", void 0);
|
|
167
|
+
__decorate([
|
|
168
|
+
property({ type: Boolean, reflect: true })
|
|
169
|
+
], Radio.prototype, "checked", void 0);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { LitElement, PropertyValues } from 'lit';
|
|
2
|
+
import type { Placement, Strategy } from '@floating-ui/dom';
|
|
3
|
+
import type { Field } from './field.js';
|
|
4
|
+
import type { Menu, MenuItemFocusEvent, MenuSelectEvent } from './menu.js';
|
|
5
|
+
import type { Option } from './option.js';
|
|
6
|
+
declare const VALUE: unique symbol;
|
|
7
|
+
declare const Base: Constructor<import("./mixins/form-associated.js").FormAssociatedInterface> & Constructor<import("./mixins/focus-delegated.js").FocusDelegatedInterface> & Constructor<import("./mixins/internals-attached.js").InternalsAttachedInterface> & typeof LitElement;
|
|
8
|
+
/**
|
|
9
|
+
* @csspart field
|
|
10
|
+
* @csspart menu
|
|
11
|
+
* @csspart items
|
|
12
|
+
*
|
|
13
|
+
* @fires {Event} change - Fires when the value is changed.
|
|
14
|
+
* @fires {Event} input - Fires when the value is changed.
|
|
15
|
+
*
|
|
16
|
+
* TODO: Render multiple values
|
|
17
|
+
* FIXME: `aria-expanded` set twice by both Lit and Menu
|
|
18
|
+
*/
|
|
19
|
+
export declare class Select extends Base {
|
|
20
|
+
#private;
|
|
21
|
+
required: boolean;
|
|
22
|
+
error: boolean;
|
|
23
|
+
quick: boolean;
|
|
24
|
+
offset: number;
|
|
25
|
+
align: Placement;
|
|
26
|
+
alignStrategy: Strategy;
|
|
27
|
+
open: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Text to display in the field. Only set for SSR.
|
|
30
|
+
*/
|
|
31
|
+
displayText: string;
|
|
32
|
+
get value(): string;
|
|
33
|
+
set value(value: string);
|
|
34
|
+
[VALUE]: string;
|
|
35
|
+
get $options(): Option[];
|
|
36
|
+
get selectedIndex(): number;
|
|
37
|
+
set selectedIndex(index: number);
|
|
38
|
+
get selectedOptions(): Option[];
|
|
39
|
+
$field: Field;
|
|
40
|
+
$menu: Menu;
|
|
41
|
+
private lastUserSetValue;
|
|
42
|
+
private lastUserSetSelectedIndex;
|
|
43
|
+
private lastSelectedOption;
|
|
44
|
+
private lastSelectedOptionRecords;
|
|
45
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
46
|
+
/**
|
|
47
|
+
* Example content:
|
|
48
|
+
*
|
|
49
|
+
* ```html
|
|
50
|
+
* <your-field
|
|
51
|
+
* id="field"
|
|
52
|
+
* part="field"
|
|
53
|
+
* @click=${this.toggle}
|
|
54
|
+
* @keydown=${this.handleFieldKeydown}
|
|
55
|
+
* tabindex=${this.disabled ? '-1' : '0'}
|
|
56
|
+
* role="combobox"
|
|
57
|
+
* aria-haspopup="listbox"
|
|
58
|
+
* aria-expanded=${this.open}
|
|
59
|
+
* aria-controls="menu"
|
|
60
|
+
* aria-disabled=${this.disabled}
|
|
61
|
+
* aria-required=${this.required}
|
|
62
|
+
* >
|
|
63
|
+
* ${this.renderFieldContent()}
|
|
64
|
+
* </your-field>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
protected renderField(): import("lit-html").TemplateResult<1>;
|
|
68
|
+
/**
|
|
69
|
+
* Example content:
|
|
70
|
+
*
|
|
71
|
+
* ```html
|
|
72
|
+
* <your-menu
|
|
73
|
+
* id="menu"
|
|
74
|
+
* for="field"
|
|
75
|
+
* type="listbox"
|
|
76
|
+
* data-tabindex="-1"
|
|
77
|
+
* ?quick="${this.quick}"
|
|
78
|
+
* offset="${this.offset}"
|
|
79
|
+
* align="${this.align}"
|
|
80
|
+
* align-strategy="${this.alignStrategy}"
|
|
81
|
+
* keep-open-blur
|
|
82
|
+
* no-focus-control
|
|
83
|
+
* ?open="${this.open}"
|
|
84
|
+
* @item-focus="${this.handleMenuItemFocus}"
|
|
85
|
+
* @select="${this.handleMenuSelect}"
|
|
86
|
+
* >
|
|
87
|
+
* <slot part="items" @slotchange=${this.handleSlotChange}></slot>
|
|
88
|
+
* </your-menu>
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
protected renderMenu(): import("lit-html").TemplateResult<1>;
|
|
92
|
+
protected renderFieldContent(): import("lit-html").TemplateResult<1>;
|
|
93
|
+
constructor();
|
|
94
|
+
protected firstUpdated(changed: PropertyValues<Select>): Promise<void>;
|
|
95
|
+
protected update(changed: PropertyValues<Select>): void;
|
|
96
|
+
protected updated(changed: PropertyValues): void;
|
|
97
|
+
/**
|
|
98
|
+
* TODO: Handle clear action
|
|
99
|
+
*/
|
|
100
|
+
protected _handleFieldKeydown(event: KeyboardEvent): void;
|
|
101
|
+
protected _handleMenuSelect(event: MenuSelectEvent): void;
|
|
102
|
+
protected _handleMenuItemFocus(event: MenuItemFocusEvent): void;
|
|
103
|
+
protected _handleFocusOut(event: FocusEvent): void;
|
|
104
|
+
protected _handleSlotChange(): void;
|
|
105
|
+
private selectItem;
|
|
106
|
+
private initUserSelection;
|
|
107
|
+
private updateValueAndDisplayText;
|
|
108
|
+
private getSelectedOptions;
|
|
109
|
+
formResetCallback(): void;
|
|
110
|
+
formStateRestoreCallback(state: string): void;
|
|
111
|
+
select(value: string): void;
|
|
112
|
+
selectIndex(index: number): void;
|
|
113
|
+
reset(): void;
|
|
114
|
+
toggle(): void;
|
|
115
|
+
}
|
|
116
|
+
export {};
|
package/src/base/select.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
var _a;
|
|
2
|
+
import { __decorate } from "tslib";
|
|
2
3
|
import { LitElement, isServer, html } from 'lit';
|
|
3
4
|
import { property, query } from 'lit/decorators.js';
|
|
4
5
|
import { InternalsAttached } from './mixins/internals-attached.js';
|
|
@@ -16,12 +17,15 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
16
17
|
*
|
|
17
18
|
* TODO: Render multiple values
|
|
18
19
|
* FIXME: `aria-expanded` set twice by both Lit and Menu
|
|
19
|
-
*/
|
|
20
|
+
*/
|
|
21
|
+
export class Select extends Base {
|
|
22
|
+
static { _a = VALUE; }
|
|
20
23
|
get value() {
|
|
21
24
|
return this[VALUE];
|
|
22
25
|
}
|
|
23
26
|
set value(value) {
|
|
24
|
-
if (isServer)
|
|
27
|
+
if (isServer)
|
|
28
|
+
return;
|
|
25
29
|
this.lastUserSetValue = value;
|
|
26
30
|
this.select(value);
|
|
27
31
|
}
|
|
@@ -40,76 +44,88 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
40
44
|
this.selectIndex(index);
|
|
41
45
|
}
|
|
42
46
|
get selectedOptions() {
|
|
43
|
-
return (this.getSelectedOptions() ?? []).map(([option])=>option);
|
|
47
|
+
return (this.getSelectedOptions() ?? []).map(([option]) => option);
|
|
44
48
|
}
|
|
45
49
|
render() {
|
|
46
|
-
return html`${this.renderField()}${this.renderMenu()}`;
|
|
50
|
+
return html `${this.renderField()}${this.renderMenu()}`;
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
* Example content:
|
|
54
|
+
*
|
|
55
|
+
* ```html
|
|
56
|
+
* <your-field
|
|
57
|
+
* id="field"
|
|
58
|
+
* part="field"
|
|
59
|
+
* @click=${this.toggle}
|
|
60
|
+
* @keydown=${this.handleFieldKeydown}
|
|
61
|
+
* tabindex=${this.disabled ? '-1' : '0'}
|
|
62
|
+
* role="combobox"
|
|
63
|
+
* aria-haspopup="listbox"
|
|
64
|
+
* aria-expanded=${this.open}
|
|
65
|
+
* aria-controls="menu"
|
|
66
|
+
* aria-disabled=${this.disabled}
|
|
67
|
+
* aria-required=${this.required}
|
|
68
|
+
* >
|
|
69
|
+
* ${this.renderFieldContent()}
|
|
70
|
+
* </your-field>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
renderField() {
|
|
74
|
+
return html ``;
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
77
|
+
* Example content:
|
|
78
|
+
*
|
|
79
|
+
* ```html
|
|
80
|
+
* <your-menu
|
|
81
|
+
* id="menu"
|
|
82
|
+
* for="field"
|
|
83
|
+
* type="listbox"
|
|
84
|
+
* data-tabindex="-1"
|
|
85
|
+
* ?quick="${this.quick}"
|
|
86
|
+
* offset="${this.offset}"
|
|
87
|
+
* align="${this.align}"
|
|
88
|
+
* align-strategy="${this.alignStrategy}"
|
|
89
|
+
* keep-open-blur
|
|
90
|
+
* no-focus-control
|
|
91
|
+
* ?open="${this.open}"
|
|
92
|
+
* @item-focus="${this.handleMenuItemFocus}"
|
|
93
|
+
* @select="${this.handleMenuSelect}"
|
|
94
|
+
* >
|
|
95
|
+
* <slot part="items" @slotchange=${this.handleSlotChange}></slot>
|
|
96
|
+
* </your-menu>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
renderMenu() {
|
|
100
|
+
return html ``;
|
|
95
101
|
}
|
|
96
102
|
renderFieldContent() {
|
|
97
|
-
return html`<span part="value">${this.displayText || html` `}</span>`;
|
|
103
|
+
return html `<span part="value">${this.displayText || html ` `}</span>`;
|
|
98
104
|
}
|
|
99
|
-
constructor(){
|
|
100
|
-
super()
|
|
101
|
-
|
|
102
|
-
this.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
constructor() {
|
|
106
|
+
super();
|
|
107
|
+
// Form-related
|
|
108
|
+
this.required = false;
|
|
109
|
+
// Passed to field
|
|
110
|
+
this.error = false;
|
|
111
|
+
// Passed to menu
|
|
112
|
+
this.quick = false;
|
|
113
|
+
this.offset = 0;
|
|
114
|
+
this.align = 'bottom-start';
|
|
115
|
+
this.alignStrategy = 'absolute';
|
|
116
|
+
this.open = false;
|
|
117
|
+
/**
|
|
118
|
+
* Text to display in the field. Only set for SSR.
|
|
119
|
+
*/
|
|
120
|
+
this.displayText = '';
|
|
121
|
+
this[_a] = '';
|
|
122
|
+
this.lastUserSetValue = null;
|
|
123
|
+
this.lastUserSetSelectedIndex = null;
|
|
124
|
+
this.lastSelectedOption = null;
|
|
125
|
+
this.lastSelectedOptionRecords = [];
|
|
126
|
+
this.#dispatchChangeEvent = () => {
|
|
127
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
128
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
113
129
|
};
|
|
114
130
|
if (!isServer) {
|
|
115
131
|
this.addEventListener('focusout', this._handleFocusOut);
|
|
@@ -122,8 +138,10 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
122
138
|
}
|
|
123
139
|
// Case for when the DOM is streaming, there are no children, and a child
|
|
124
140
|
// has [selected] set on it, we need to wait for DOM to render something.
|
|
125
|
-
if (!this.lastSelectedOptionRecords.length &&
|
|
126
|
-
|
|
141
|
+
if (!this.lastSelectedOptionRecords.length &&
|
|
142
|
+
!isServer &&
|
|
143
|
+
!this.$options.length) {
|
|
144
|
+
setTimeout(() => {
|
|
127
145
|
this.updateValueAndDisplayText();
|
|
128
146
|
}, 0);
|
|
129
147
|
}
|
|
@@ -140,18 +158,21 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
140
158
|
if (changed.has('open')) {
|
|
141
159
|
if (this.open) {
|
|
142
160
|
this.#focusSelectedItemOrFirst();
|
|
143
|
-
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
144
163
|
this.$field.setAttribute('aria-activedescendant', '');
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
167
|
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
* TODO: Handle clear action
|
|
169
|
+
*/
|
|
170
|
+
_handleFieldKeydown(event) {
|
|
171
|
+
if (this.disabled)
|
|
172
|
+
return;
|
|
152
173
|
const eventClone = new KeyboardEvent(event.type, event);
|
|
153
|
-
eventClone.preventDefault = ()=>event.preventDefault();
|
|
154
|
-
eventClone.stopPropagation = ()=>event.stopPropagation();
|
|
174
|
+
eventClone.preventDefault = () => event.preventDefault();
|
|
175
|
+
eventClone.stopPropagation = () => event.stopPropagation();
|
|
155
176
|
this.$menu.$menu.dispatchEvent(eventClone);
|
|
156
177
|
}
|
|
157
178
|
_handleMenuSelect(event) {
|
|
@@ -177,7 +198,7 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
177
198
|
}
|
|
178
199
|
selectItem(item) {
|
|
179
200
|
const selectedOptions = this.getSelectedOptions() ?? [];
|
|
180
|
-
selectedOptions.forEach(([option])=>{
|
|
201
|
+
selectedOptions.forEach(([option]) => {
|
|
181
202
|
if (item !== option) {
|
|
182
203
|
option.selected = false;
|
|
183
204
|
}
|
|
@@ -188,9 +209,12 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
188
209
|
initUserSelection() {
|
|
189
210
|
if (this.lastUserSetValue && !this.lastSelectedOptionRecords.length) {
|
|
190
211
|
this.select(this.lastUserSetValue);
|
|
191
|
-
}
|
|
212
|
+
}
|
|
213
|
+
else if (this.lastUserSetSelectedIndex !== null &&
|
|
214
|
+
!this.lastSelectedOptionRecords.length) {
|
|
192
215
|
this.selectIndex(this.lastUserSetSelectedIndex);
|
|
193
|
-
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
194
218
|
this.updateValueAndDisplayText();
|
|
195
219
|
}
|
|
196
220
|
}
|
|
@@ -203,7 +227,8 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
203
227
|
this.lastSelectedOption = firstSelectedOption;
|
|
204
228
|
this[VALUE] = firstSelectedOption.value;
|
|
205
229
|
this.displayText = firstSelectedOption.displayText;
|
|
206
|
-
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
207
232
|
changed = this.lastSelectedOption !== null;
|
|
208
233
|
this.lastSelectedOption = null;
|
|
209
234
|
this[VALUE] = '';
|
|
@@ -218,12 +243,9 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
218
243
|
getSelectedOptions() {
|
|
219
244
|
const items = this.$options;
|
|
220
245
|
const records = [];
|
|
221
|
-
items.forEach((item, index)=>{
|
|
246
|
+
items.forEach((item, index) => {
|
|
222
247
|
if (item.selected) {
|
|
223
|
-
records.push([
|
|
224
|
-
item,
|
|
225
|
-
index
|
|
226
|
-
]);
|
|
248
|
+
records.push([item, index]);
|
|
227
249
|
}
|
|
228
250
|
});
|
|
229
251
|
this.lastSelectedOptionRecords = records;
|
|
@@ -235,7 +257,8 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
235
257
|
if (selectedOptions && selectedOptions.length > 0) {
|
|
236
258
|
const [item] = selectedOptions[0];
|
|
237
259
|
this.$menu.focusItem(item);
|
|
238
|
-
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
239
262
|
this.$menu.focusFirstItem();
|
|
240
263
|
}
|
|
241
264
|
}
|
|
@@ -246,7 +269,7 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
246
269
|
this.value = state;
|
|
247
270
|
}
|
|
248
271
|
select(value) {
|
|
249
|
-
const item = this.$options.find((option)=>option.value === value);
|
|
272
|
+
const item = this.$options.find((option) => option.value === value);
|
|
250
273
|
if (item) {
|
|
251
274
|
this.selectItem(item);
|
|
252
275
|
}
|
|
@@ -258,71 +281,50 @@ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
|
|
|
258
281
|
}
|
|
259
282
|
}
|
|
260
283
|
reset() {
|
|
261
|
-
for (const option of this.$options){
|
|
284
|
+
for (const option of this.$options) {
|
|
262
285
|
option.selected = option.hasAttribute('selected');
|
|
263
286
|
}
|
|
264
287
|
this.updateValueAndDisplayText();
|
|
265
288
|
}
|
|
266
289
|
toggle() {
|
|
267
|
-
if (this.disabled)
|
|
290
|
+
if (this.disabled)
|
|
291
|
+
return;
|
|
268
292
|
this.open = !this.open;
|
|
269
293
|
}
|
|
270
294
|
}
|
|
271
|
-
|
|
272
|
-
property({
|
|
273
|
-
type: Boolean,
|
|
274
|
-
reflect: true
|
|
275
|
-
})
|
|
295
|
+
__decorate([
|
|
296
|
+
property({ type: Boolean, reflect: true })
|
|
276
297
|
], Select.prototype, "required", void 0);
|
|
277
|
-
|
|
278
|
-
property({
|
|
279
|
-
type: Boolean
|
|
280
|
-
})
|
|
298
|
+
__decorate([
|
|
299
|
+
property({ type: Boolean })
|
|
281
300
|
], Select.prototype, "error", void 0);
|
|
282
|
-
|
|
283
|
-
property({
|
|
284
|
-
type: Boolean
|
|
285
|
-
})
|
|
301
|
+
__decorate([
|
|
302
|
+
property({ type: Boolean })
|
|
286
303
|
], Select.prototype, "quick", void 0);
|
|
287
|
-
|
|
288
|
-
property({
|
|
289
|
-
type: Number
|
|
290
|
-
})
|
|
304
|
+
__decorate([
|
|
305
|
+
property({ type: Number })
|
|
291
306
|
], Select.prototype, "offset", void 0);
|
|
292
|
-
|
|
293
|
-
property({
|
|
294
|
-
reflect: true
|
|
295
|
-
})
|
|
307
|
+
__decorate([
|
|
308
|
+
property({ reflect: true })
|
|
296
309
|
], Select.prototype, "align", void 0);
|
|
297
|
-
|
|
298
|
-
property({
|
|
299
|
-
type: String,
|
|
300
|
-
reflect: true,
|
|
301
|
-
attribute: 'align-strategy'
|
|
302
|
-
})
|
|
310
|
+
__decorate([
|
|
311
|
+
property({ type: String, reflect: true, attribute: 'align-strategy' })
|
|
303
312
|
], Select.prototype, "alignStrategy", void 0);
|
|
304
|
-
|
|
305
|
-
property({
|
|
306
|
-
type: Boolean
|
|
307
|
-
})
|
|
313
|
+
__decorate([
|
|
314
|
+
property({ type: Boolean })
|
|
308
315
|
], Select.prototype, "open", void 0);
|
|
309
|
-
|
|
310
|
-
property({
|
|
311
|
-
attribute: 'display-text'
|
|
312
|
-
})
|
|
316
|
+
__decorate([
|
|
317
|
+
property({ attribute: 'display-text' })
|
|
313
318
|
], Select.prototype, "displayText", void 0);
|
|
314
|
-
|
|
319
|
+
__decorate([
|
|
315
320
|
property()
|
|
316
321
|
], Select.prototype, "value", null);
|
|
317
|
-
|
|
318
|
-
property({
|
|
319
|
-
type: Number,
|
|
320
|
-
attribute: 'selected-index'
|
|
321
|
-
})
|
|
322
|
+
__decorate([
|
|
323
|
+
property({ type: Number, attribute: 'selected-index' })
|
|
322
324
|
], Select.prototype, "selectedIndex", null);
|
|
323
|
-
|
|
325
|
+
__decorate([
|
|
324
326
|
query('[part="field"]')
|
|
325
327
|
], Select.prototype, "$field", void 0);
|
|
326
|
-
|
|
328
|
+
__decorate([
|
|
327
329
|
query('[part="menu"]')
|
|
328
330
|
], Select.prototype, "$menu", void 0);
|