aeico-components 0.1.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/README.md +0 -0
- package/dist/index.cjs +4226 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +4226 -0
- package/dist/index.js.map +1 -0
- package/dist/types/aeico-component.d.ts +8 -0
- package/dist/types/aeico-field.d.ts +132 -0
- package/dist/types/alert/alert.d.ts +49 -0
- package/dist/types/alert/defines.d.ts +3 -0
- package/dist/types/alert/index.d.ts +3 -0
- package/dist/types/badge/badge.d.ts +34 -0
- package/dist/types/badge/defines.d.ts +3 -0
- package/dist/types/badge/index.d.ts +3 -0
- package/dist/types/breadcrumb/breadcrumb-item.d.ts +31 -0
- package/dist/types/breadcrumb/breadcrumb.d.ts +60 -0
- package/dist/types/breadcrumb/defines.d.ts +1 -0
- package/dist/types/breadcrumb/index.d.ts +5 -0
- package/dist/types/button/button.d.ts +60 -0
- package/dist/types/button/defines.d.ts +3 -0
- package/dist/types/button/index.d.ts +3 -0
- package/dist/types/button-group/button-group.d.ts +56 -0
- package/dist/types/button-group/index.d.ts +2 -0
- package/dist/types/card/card.d.ts +19 -0
- package/dist/types/card/defines.d.ts +2 -0
- package/dist/types/card/index.d.ts +3 -0
- package/dist/types/checkbox/checkbox.d.ts +37 -0
- package/dist/types/checkbox/defines.d.ts +1 -0
- package/dist/types/checkbox/index.d.ts +3 -0
- package/dist/types/detail/defines.d.ts +2 -0
- package/dist/types/detail/detail.d.ts +40 -0
- package/dist/types/detail/index.d.ts +3 -0
- package/dist/types/dialog/dialog.d.ts +29 -0
- package/dist/types/dialog/index.d.ts +2 -0
- package/dist/types/divider/divider.d.ts +34 -0
- package/dist/types/divider/index.d.ts +2 -0
- package/dist/types/dropdown/defines.d.ts +1 -0
- package/dist/types/dropdown/dropdown-button.d.ts +60 -0
- package/dist/types/dropdown/dropdown-item.d.ts +56 -0
- package/dist/types/dropdown/dropdown.d.ts +84 -0
- package/dist/types/dropdown/index.d.ts +7 -0
- package/dist/types/icon/defines.d.ts +10 -0
- package/dist/types/icon/icon.d.ts +21 -0
- package/dist/types/icon/index.d.ts +4 -0
- package/dist/types/icon/registry.d.ts +8 -0
- package/dist/types/icon-button/icon-button.d.ts +32 -0
- package/dist/types/icon-button/index.d.ts +2 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/navbar/defines.d.ts +2 -0
- package/dist/types/navbar/index.d.ts +3 -0
- package/dist/types/navbar/navbar.d.ts +73 -0
- package/dist/types/radio-group/defines.d.ts +6 -0
- package/dist/types/radio-group/index.d.ts +5 -0
- package/dist/types/radio-group/radio-group.d.ts +41 -0
- package/dist/types/radio-group/radio.d.ts +47 -0
- package/dist/types/select/defines.d.ts +8 -0
- package/dist/types/select/index.d.ts +5 -0
- package/dist/types/select/select-option.d.ts +20 -0
- package/dist/types/select/select.d.ts +60 -0
- package/dist/types/slider/defines.d.ts +31 -0
- package/dist/types/slider/index.d.ts +3 -0
- package/dist/types/slider/slider.d.ts +45 -0
- package/dist/types/switch/index.d.ts +2 -0
- package/dist/types/switch/switch.d.ts +35 -0
- package/dist/types/tabs/defines.d.ts +1 -0
- package/dist/types/tabs/index.d.ts +3 -0
- package/dist/types/tabs/tab-panel.d.ts +11 -0
- package/dist/types/tabs/tab.d.ts +18 -0
- package/dist/types/tabs/tabs.d.ts +24 -0
- package/dist/types/tag/defines.d.ts +3 -0
- package/dist/types/tag/index.d.ts +3 -0
- package/dist/types/tag/tag.d.ts +36 -0
- package/dist/types/text-input/index.d.ts +2 -0
- package/dist/types/text-input/text-input.d.ts +26 -0
- package/dist/types/utils.d.ts +2 -0
- package/package.json +63 -0
- package/src/aeico-component.ts +17 -0
- package/src/aeico-field.ts +228 -0
- package/src/alert/alert.ts +107 -0
- package/src/alert/defines.ts +11 -0
- package/src/alert/index.ts +3 -0
- package/src/badge/badge.ts +62 -0
- package/src/badge/defines.ts +12 -0
- package/src/badge/index.ts +3 -0
- package/src/breadcrumb/breadcrumb-item.ts +61 -0
- package/src/breadcrumb/breadcrumb.ts +138 -0
- package/src/breadcrumb/defines.ts +10 -0
- package/src/breadcrumb/index.ts +5 -0
- package/src/button/button.ts +147 -0
- package/src/button/defines.ts +12 -0
- package/src/button/index.ts +3 -0
- package/src/button-group/button-group.ts +140 -0
- package/src/button-group/index.ts +2 -0
- package/src/card/card.ts +57 -0
- package/src/card/defines.ts +11 -0
- package/src/card/index.ts +3 -0
- package/src/checkbox/checkbox.ts +90 -0
- package/src/checkbox/defines.ts +1 -0
- package/src/checkbox/index.ts +3 -0
- package/src/detail/defines.ts +11 -0
- package/src/detail/detail.ts +122 -0
- package/src/detail/index.ts +3 -0
- package/src/dialog/dialog.ts +149 -0
- package/src/dialog/index.ts +2 -0
- package/src/divider/divider.ts +56 -0
- package/src/divider/index.ts +2 -0
- package/src/dropdown/defines.ts +13 -0
- package/src/dropdown/dropdown-button.ts +130 -0
- package/src/dropdown/dropdown-item.ts +136 -0
- package/src/dropdown/dropdown.ts +211 -0
- package/src/dropdown/index.ts +7 -0
- package/src/icon/defines.ts +21 -0
- package/src/icon/icon.ts +84 -0
- package/src/icon/index.ts +4 -0
- package/src/icon/registry.ts +25 -0
- package/src/icon-button/icon-button.ts +64 -0
- package/src/icon-button/index.ts +2 -0
- package/src/index.ts +85 -0
- package/src/navbar/defines.ts +11 -0
- package/src/navbar/index.ts +3 -0
- package/src/navbar/navbar.ts +162 -0
- package/src/radio-group/defines.ts +5 -0
- package/src/radio-group/index.ts +5 -0
- package/src/radio-group/radio-group.ts +227 -0
- package/src/radio-group/radio.ts +58 -0
- package/src/select/defines.ts +12 -0
- package/src/select/index.ts +5 -0
- package/src/select/select-option.ts +59 -0
- package/src/select/select.ts +387 -0
- package/src/slider/defines.ts +33 -0
- package/src/slider/index.ts +3 -0
- package/src/slider/slider.ts +364 -0
- package/src/styles/color.css +117 -0
- package/src/styles/components/alert.css +104 -0
- package/src/styles/components/badge.css +67 -0
- package/src/styles/components/breadcrumb-item.css +59 -0
- package/src/styles/components/breadcrumb.css +19 -0
- package/src/styles/components/button-group.css +25 -0
- package/src/styles/components/button.css +213 -0
- package/src/styles/components/card.css +64 -0
- package/src/styles/components/checkbox.css +78 -0
- package/src/styles/components/detail.css +127 -0
- package/src/styles/components/dialog.css +103 -0
- package/src/styles/components/divider.css +18 -0
- package/src/styles/components/dropdown-item.css +91 -0
- package/src/styles/components/dropdown.css +179 -0
- package/src/styles/components/icon-button.css +116 -0
- package/src/styles/components/icon.css +29 -0
- package/src/styles/components/navbar.css +250 -0
- package/src/styles/components/radio-group.css +360 -0
- package/src/styles/components/select-option.css +43 -0
- package/src/styles/components/select.css +222 -0
- package/src/styles/components/slider.css +326 -0
- package/src/styles/components/switch.css +117 -0
- package/src/styles/components/tab-panel.css +8 -0
- package/src/styles/components/tab.css +44 -0
- package/src/styles/components/tabs.css +16 -0
- package/src/styles/components/tag.css +107 -0
- package/src/styles/components/text-input.css +110 -0
- package/src/styles/layout.css +43 -0
- package/src/styles/size.css +7 -0
- package/src/styles/variables.css +368 -0
- package/src/switch/index.ts +2 -0
- package/src/switch/switch.ts +88 -0
- package/src/tabs/defines.ts +1 -0
- package/src/tabs/index.ts +3 -0
- package/src/tabs/tab-panel.ts +23 -0
- package/src/tabs/tab.ts +62 -0
- package/src/tabs/tabs.ts +134 -0
- package/src/tag/defines.ts +12 -0
- package/src/tag/index.ts +3 -0
- package/src/tag/tag.ts +85 -0
- package/src/text-input/index.ts +2 -0
- package/src/text-input/text-input.ts +75 -0
- package/src/utils.ts +6 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import AeicoField from '../aeico-field';
|
|
2
|
+
import type { InferProps } from 'aeico';
|
|
3
|
+
import { html, tags } from 'aeico';
|
|
4
|
+
import { t } from 'aeico-localize';
|
|
5
|
+
import type {
|
|
6
|
+
SelectOptionValue,
|
|
7
|
+
SelectOption,
|
|
8
|
+
SelectOptions,
|
|
9
|
+
SelectPosition,
|
|
10
|
+
SelectMultiValue,
|
|
11
|
+
} from './defines';
|
|
12
|
+
import style from '../styles/components/select.css?inline';
|
|
13
|
+
import variables from '../styles/variables.css?inline';
|
|
14
|
+
import sizeCSS from '../styles/size.css?inline';
|
|
15
|
+
import SelectOptionElement from './select-option';
|
|
16
|
+
import '../tag/tag';
|
|
17
|
+
import { prop } from 'aeico';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Select component supporting single and multi-select modes, with options provided via both props and slots.
|
|
21
|
+
* - `options` prop accepts an array of strings or objects with `value` and `label` for programmatic options.
|
|
22
|
+
* - Slot content allows for declarative options using `<ae-select-option>` elements.
|
|
23
|
+
* @example
|
|
24
|
+
* <ae-select placeholder="Choose an option" position="bottom">
|
|
25
|
+
* <ae-select-option value="1" label="Option 1">Option 1</ae-select-option>
|
|
26
|
+
* <ae-select-option value="2" label="Option 2">Option 2</ae-select-option>
|
|
27
|
+
* </ae-select>
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
|
|
31
|
+
protected fieldElement = null;
|
|
32
|
+
private _isOpen = false;
|
|
33
|
+
private _triggerEl: HTMLElement | null = null;
|
|
34
|
+
private _dropdownEl: HTMLElement | null = null;
|
|
35
|
+
private _slotEl: HTMLSlotElement | null = null;
|
|
36
|
+
private _slotOptionData: Array<{ value: string; label: string }> = [];
|
|
37
|
+
private _selectedListEl: HTMLElement | null = null;
|
|
38
|
+
|
|
39
|
+
static tagName = 'select';
|
|
40
|
+
|
|
41
|
+
@prop({ type: Boolean, observe: false, reflect: false })
|
|
42
|
+
accessor _expanded: boolean = false;
|
|
43
|
+
|
|
44
|
+
@prop({ type: Array })
|
|
45
|
+
accessor options: SelectOptions | undefined;
|
|
46
|
+
|
|
47
|
+
@prop({ type: String })
|
|
48
|
+
accessor position: SelectPosition | undefined;
|
|
49
|
+
|
|
50
|
+
@prop({ type: String })
|
|
51
|
+
accessor placeholder: string | undefined;
|
|
52
|
+
|
|
53
|
+
@prop({ type: Boolean })
|
|
54
|
+
accessor multiple: boolean = false;
|
|
55
|
+
|
|
56
|
+
@prop({ type: Boolean })
|
|
57
|
+
accessor expandable: boolean = false;
|
|
58
|
+
|
|
59
|
+
// Override base class value prop to support both string and array (multi-select).
|
|
60
|
+
// Uses field decorator (not accessor) because TypeScript TS2611 disallows overriding
|
|
61
|
+
// a parent class data property (declare value?) with an accessor in a subclass.
|
|
62
|
+
@prop({
|
|
63
|
+
type: String,
|
|
64
|
+
parser: (v) => {
|
|
65
|
+
if (v === null || v === undefined) return undefined;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(v);
|
|
68
|
+
} catch {
|
|
69
|
+
return v;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
formatter: (v) => {
|
|
73
|
+
if (v === null || v === undefined) return '';
|
|
74
|
+
if (Array.isArray(v)) return JSON.stringify(v);
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
76
|
+
return String(v);
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
override value: SelectOptionValue | SelectMultiValue | undefined = undefined;
|
|
80
|
+
|
|
81
|
+
// Override base class defaultValue so arrays are JSON-serialized to the attribute,
|
|
82
|
+
// matching the value prop's parser/formatter. Without this override, setting
|
|
83
|
+
// defaultValue = ['a', 'b'] would be serialized as String(['a','b']) = "a,b",
|
|
84
|
+
// and reset() would restore a single string "a,b" instead of the array.
|
|
85
|
+
@prop({
|
|
86
|
+
type: String,
|
|
87
|
+
parser: (v) => {
|
|
88
|
+
if (v === null || v === undefined) return undefined;
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(v);
|
|
91
|
+
} catch {
|
|
92
|
+
return v;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
formatter: (v) => {
|
|
96
|
+
if (v === null || v === undefined) return '';
|
|
97
|
+
if (Array.isArray(v)) return JSON.stringify(v);
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
99
|
+
return String(v);
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
override defaultValue: SelectOptionValue | SelectMultiValue | undefined = undefined;
|
|
103
|
+
|
|
104
|
+
protected static styles = [variables, sizeCSS, style];
|
|
105
|
+
|
|
106
|
+
protected writeValue(_value: SelectOptionValue | SelectMultiValue): void {
|
|
107
|
+
// Reactive re-render via this.value prop change handles the display update
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected getValue(): any {
|
|
111
|
+
if (this.multiple) return this._getMultiValues();
|
|
112
|
+
|
|
113
|
+
return this.value || '';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private _getMultiValues(): SelectMultiValue {
|
|
117
|
+
if (Array.isArray(this.value)) return this.value;
|
|
118
|
+
if (this.value != null && this.value !== '') return [this.value];
|
|
119
|
+
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected onDisabledChanged(_newValue: boolean): void {
|
|
124
|
+
// disabled is a reactive prop — render() already picks it up automatically
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected onUpdated(_changedProps: Map<string, unknown>): void {
|
|
128
|
+
if (!this.multiple || this.expandable) {
|
|
129
|
+
if (this._expanded) this._expanded = false;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const list = this._selectedListEl;
|
|
133
|
+
if (!list) return;
|
|
134
|
+
const overflowing = list.scrollWidth > list.clientWidth + 1;
|
|
135
|
+
if (overflowing !== this._expanded) this._expanded = overflowing;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private _findLabel(value: SelectOptionValue): string {
|
|
139
|
+
const strVal = String(value);
|
|
140
|
+
if (Array.isArray(this.options)) {
|
|
141
|
+
for (const opt of this.options) {
|
|
142
|
+
if (this._isSelectOption(opt)) {
|
|
143
|
+
if (String(opt.value) === strVal) return t(opt.label, opt.label);
|
|
144
|
+
} else {
|
|
145
|
+
if (String(opt) === strVal) return strVal;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const opt of this._slotOptionData) {
|
|
151
|
+
if (opt.value === strVal) return opt.label;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return strVal;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private _onSlotChange(): void {
|
|
158
|
+
if (!this._slotEl) return;
|
|
159
|
+
const data: Array<{ value: string; label: string }> = [];
|
|
160
|
+
for (const el of this._slotEl.assignedElements({ flatten: true })) {
|
|
161
|
+
if (el.tagName.toLowerCase() !== 'ae-select-option') continue;
|
|
162
|
+
const optEl = el as SelectOptionElement;
|
|
163
|
+
data.push({
|
|
164
|
+
value: optEl.value ?? el.getAttribute('value') ?? '',
|
|
165
|
+
label: optEl.label || el.textContent?.trim() || '',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
this._slotOptionData = data;
|
|
169
|
+
this.update();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private _toggleDropdown(): void {
|
|
173
|
+
if (this._isOpen) {
|
|
174
|
+
this._closeDropdown();
|
|
175
|
+
} else {
|
|
176
|
+
this._openDropdown();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private _openDropdown(): void {
|
|
181
|
+
this._isOpen = true;
|
|
182
|
+
this._syncOpenState();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private _closeDropdown(): void {
|
|
186
|
+
this._isOpen = false;
|
|
187
|
+
this._syncOpenState();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private _syncOpenState(): void {
|
|
191
|
+
this._triggerEl?.classList.toggle('open', this._isOpen);
|
|
192
|
+
this._dropdownEl?.classList.toggle('open', this._isOpen);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private readonly _handleOutsideClick = (e: Event): void => {
|
|
196
|
+
if (!e.composedPath().includes(this)) {
|
|
197
|
+
this._closeDropdown();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
private readonly _handleOptionSelect = (e: Event): void => {
|
|
202
|
+
const { value, label } = (e as CustomEvent<{ value: string; label: string }>).detail;
|
|
203
|
+
if (!this._slotOptionData.find((o) => o.value === value)) {
|
|
204
|
+
this._slotOptionData = [
|
|
205
|
+
...this._slotOptionData.filter((o) => o.value !== value),
|
|
206
|
+
{ value, label },
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
if (this.multiple) {
|
|
210
|
+
const current = this._getMultiValues();
|
|
211
|
+
const idx = current.findIndex((v) => String(v) === value);
|
|
212
|
+
const next: SelectMultiValue =
|
|
213
|
+
idx >= 0 ? current.filter((_, i) => i !== idx) : [...current, value];
|
|
214
|
+
|
|
215
|
+
this.setValue(next, { silent: false, action: 'change' });
|
|
216
|
+
} else {
|
|
217
|
+
this.setValue(value, { silent: false, action: 'change' });
|
|
218
|
+
this._closeDropdown();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
connectedCallback() {
|
|
223
|
+
super.connectedCallback();
|
|
224
|
+
document.addEventListener('click', this._handleOutsideClick);
|
|
225
|
+
this.addEventListener('selectoption', this._handleOptionSelect);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
disconnectedCallback() {
|
|
229
|
+
super.disconnectedCallback();
|
|
230
|
+
document.removeEventListener('click', this._handleOutsideClick);
|
|
231
|
+
this.removeEventListener('selectoption', this._handleOptionSelect);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private _syncSlotOptionsSelected(): void {
|
|
235
|
+
if (!this._slotEl) return;
|
|
236
|
+
const multiValues = this._getMultiValues();
|
|
237
|
+
for (const el of this._slotEl.assignedElements({ flatten: true })) {
|
|
238
|
+
if (el.tagName.toLowerCase() !== 'ae-select-option') continue;
|
|
239
|
+
const optEl = el as SelectOptionElement;
|
|
240
|
+
const optVal = optEl.value ?? el.getAttribute('value') ?? '';
|
|
241
|
+
const isSelected = this.multiple
|
|
242
|
+
? multiValues.some((v) => String(v) === optVal)
|
|
243
|
+
: this.value != null && this.value !== '' && String(this.value) === optVal;
|
|
244
|
+
// undefined triggers removeAttribute via reactive setter
|
|
245
|
+
// (null would work too but undefined is type-safe for boolean | undefined)
|
|
246
|
+
optEl.selected = isSelected ? true : undefined;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
render() {
|
|
251
|
+
const position = this.position || 'bottom';
|
|
252
|
+
const multiValues = this.multiple ? this._getMultiValues() : [];
|
|
253
|
+
const hasMultiSelection = this.multiple && multiValues.length > 0;
|
|
254
|
+
const selectedLabel =
|
|
255
|
+
!this.multiple && this.value != null && this.value !== ''
|
|
256
|
+
? this._findLabel(this.value as SelectOptionValue)
|
|
257
|
+
: '';
|
|
258
|
+
const isDisabled = Boolean(this.disabled);
|
|
259
|
+
this._selectedListEl = null;
|
|
260
|
+
|
|
261
|
+
this._syncSlotOptionsSelected();
|
|
262
|
+
|
|
263
|
+
return html(({ div, span, slot }) => {
|
|
264
|
+
div({ className: 'container' }, () => {
|
|
265
|
+
this._triggerEl = div(
|
|
266
|
+
{
|
|
267
|
+
className: `trigger${this._isOpen ? ' open' : ''}${isDisabled ? ' disabled' : ''}`,
|
|
268
|
+
'@click': () => {
|
|
269
|
+
if (isDisabled) return;
|
|
270
|
+
|
|
271
|
+
this._toggleDropdown();
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
() => {
|
|
275
|
+
if (this.multiple) {
|
|
276
|
+
if (hasMultiSelection) {
|
|
277
|
+
this._selectedListEl = div(
|
|
278
|
+
{
|
|
279
|
+
className: `selected-list${!this.expandable ? ' selected-list--clipped' : ''}`,
|
|
280
|
+
},
|
|
281
|
+
() => {
|
|
282
|
+
for (const v of multiValues) {
|
|
283
|
+
const lbl = this._findLabel(v);
|
|
284
|
+
tags.aeTag({
|
|
285
|
+
key: `sel-${v}`,
|
|
286
|
+
color: 'default',
|
|
287
|
+
variant: 'faint',
|
|
288
|
+
dismissible: true,
|
|
289
|
+
disabled: isDisabled,
|
|
290
|
+
textContent: lbl,
|
|
291
|
+
'@dismiss': (e: Event) => {
|
|
292
|
+
e.stopPropagation();
|
|
293
|
+
if (isDisabled) return;
|
|
294
|
+
|
|
295
|
+
const next = multiValues.filter((item) => String(item) !== String(v));
|
|
296
|
+
this.setValue(next, { silent: false, action: 'change' });
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
);
|
|
302
|
+
if (!this.expandable && this._expanded) {
|
|
303
|
+
span({ className: 'overflow-indicator', textContent: '…' });
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
span({ className: 'value placeholder', textContent: this.placeholder || '' });
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
if (selectedLabel) {
|
|
310
|
+
span({ className: 'value', textContent: selectedLabel });
|
|
311
|
+
} else {
|
|
312
|
+
span({ className: 'value placeholder', textContent: this.placeholder || '' });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
span({ className: 'arrow', textContent: '▾' });
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
this._dropdownEl = div(
|
|
320
|
+
{
|
|
321
|
+
className: `dropdown position-${position}${this._isOpen ? ' open' : ''}`,
|
|
322
|
+
},
|
|
323
|
+
() => {
|
|
324
|
+
this._renderProgrammaticOptions();
|
|
325
|
+
this._slotEl = slot({
|
|
326
|
+
'@slotchange': () => this._onSlotChange(),
|
|
327
|
+
});
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
this.renderActionButtons();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private _renderProgrammaticOptions(): void {
|
|
337
|
+
if (!Array.isArray(this.options)) return;
|
|
338
|
+
|
|
339
|
+
const { aeSelectOption } = tags;
|
|
340
|
+
const multiValues = this.multiple ? this._getMultiValues() : [];
|
|
341
|
+
for (const opt of this.options) {
|
|
342
|
+
if (this._isSelectOption(opt)) {
|
|
343
|
+
const isSelected = this.multiple
|
|
344
|
+
? multiValues.some((v) => String(v) === String(opt.value))
|
|
345
|
+
: this.value != null && String(opt.value) === String(this.value);
|
|
346
|
+
aeSelectOption({
|
|
347
|
+
key: `opt-${opt.value}`,
|
|
348
|
+
value: String(opt.value),
|
|
349
|
+
label: opt.label,
|
|
350
|
+
textContent: t(opt.label, opt.label),
|
|
351
|
+
selected: isSelected ? true : undefined,
|
|
352
|
+
});
|
|
353
|
+
} else {
|
|
354
|
+
const isSelected = this.multiple
|
|
355
|
+
? multiValues.some((v) => String(v) === String(opt))
|
|
356
|
+
: this.value != null && String(opt) === String(this.value);
|
|
357
|
+
aeSelectOption({
|
|
358
|
+
key: `opt-${opt}`,
|
|
359
|
+
value: String(opt),
|
|
360
|
+
textContent: String(opt),
|
|
361
|
+
selected: isSelected ? true : undefined,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private _isSelectOption(option: unknown): option is SelectOption {
|
|
368
|
+
return (
|
|
369
|
+
option !== null &&
|
|
370
|
+
typeof option === 'object' &&
|
|
371
|
+
typeof (option as SelectOption).label === 'string' &&
|
|
372
|
+
(typeof (option as SelectOption).value === 'string' ||
|
|
373
|
+
typeof (option as SelectOption).value === 'number')
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
Select.register();
|
|
379
|
+
|
|
380
|
+
declare global {
|
|
381
|
+
interface HTMLElementTagNameMap {
|
|
382
|
+
'ae-select': Select;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export default Select;
|
|
387
|
+
export type SelectProps = InferProps<typeof Select>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type SliderOptionValue = string | number;
|
|
2
|
+
|
|
3
|
+
export type SliderOption = {
|
|
4
|
+
label: string;
|
|
5
|
+
value: SliderOptionValue;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SliderOptions = SliderOptionValue[] | SliderOption[];
|
|
9
|
+
|
|
10
|
+
export type NormalizedOption = {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string; // stored as string for consistency with this.value
|
|
13
|
+
rangeValue: number; // numeric value used by the range input
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A single mark on the slider track.
|
|
18
|
+
* - `number` — mark at that numeric position, no label
|
|
19
|
+
* - `{ value, label? }` — mark at `value` with optional label
|
|
20
|
+
*
|
|
21
|
+
* Marks are purely visual; they do NOT constrain snapping.
|
|
22
|
+
* The slider still snaps according to `step` / `options`.
|
|
23
|
+
* Marks outside [min, max] are silently ignored.
|
|
24
|
+
*/
|
|
25
|
+
export type MarkItem = number | { value: number; label?: string };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The `marks` prop accepts:
|
|
29
|
+
* - `true` — auto-generate marks (at option positions, or min+max in free mode)
|
|
30
|
+
* - `MarkItem[]` — custom marks at the given positions
|
|
31
|
+
* - `false` / omitted — no marks
|
|
32
|
+
*/
|
|
33
|
+
export type SliderMarks = boolean | MarkItem[];
|