@api-client/ui 0.5.24 → 0.5.26

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.
Files changed (83) hide show
  1. package/.cursor/rules/lit-best-practices.mdc +12 -1
  2. package/.github/instructions/lit-best-practices.instructions.md +2 -0
  3. package/build/src/elements/navigation/internals/AppNavigationElement.d.ts.map +1 -1
  4. package/build/src/elements/navigation/internals/AppNavigationElement.js +1 -6
  5. package/build/src/elements/navigation/internals/AppNavigationElement.js.map +1 -1
  6. package/build/src/elements/navigation/internals/NavigationItem.d.ts +6 -0
  7. package/build/src/elements/navigation/internals/NavigationItem.d.ts.map +1 -1
  8. package/build/src/elements/navigation/internals/NavigationItem.js +26 -7
  9. package/build/src/elements/navigation/internals/NavigationItem.js.map +1 -1
  10. package/build/src/elements/navigation/internals/NavigationItem.styles.d.ts.map +1 -1
  11. package/build/src/elements/navigation/internals/NavigationItem.styles.js +18 -1
  12. package/build/src/elements/navigation/internals/NavigationItem.styles.js.map +1 -1
  13. package/build/src/md/dropdown-list/internals/UiDropdownList.d.ts.map +1 -1
  14. package/build/src/md/dropdown-list/internals/UiDropdownList.js +4 -3
  15. package/build/src/md/dropdown-list/internals/UiDropdownList.js.map +1 -1
  16. package/build/src/md/input/Input.d.ts +8 -4
  17. package/build/src/md/input/Input.d.ts.map +1 -1
  18. package/build/src/md/input/Input.js +8 -36
  19. package/build/src/md/input/Input.js.map +1 -1
  20. package/build/src/md/list/internals/List.d.ts +3 -1
  21. package/build/src/md/list/internals/List.d.ts.map +1 -1
  22. package/build/src/md/list/internals/List.js +9 -4
  23. package/build/src/md/list/internals/List.js.map +1 -1
  24. package/build/src/md/menu/internal/Menu.d.ts +8 -7
  25. package/build/src/md/menu/internal/Menu.d.ts.map +1 -1
  26. package/build/src/md/menu/internal/Menu.js +26 -29
  27. package/build/src/md/menu/internal/Menu.js.map +1 -1
  28. package/build/src/md/select/index.d.ts +4 -0
  29. package/build/src/md/select/index.d.ts.map +1 -0
  30. package/build/src/md/select/index.js +3 -0
  31. package/build/src/md/select/index.js.map +1 -0
  32. package/build/src/md/select/internals/Option.d.ts +125 -0
  33. package/build/src/md/select/internals/Option.d.ts.map +1 -0
  34. package/build/src/md/select/internals/Option.js +242 -0
  35. package/build/src/md/select/internals/Option.js.map +1 -0
  36. package/build/src/md/select/internals/Option.styles.d.ts +3 -0
  37. package/build/src/md/select/internals/Option.styles.d.ts.map +1 -0
  38. package/build/src/md/select/internals/Option.styles.js +139 -0
  39. package/build/src/md/select/internals/Option.styles.js.map +1 -0
  40. package/build/src/md/select/internals/Select.d.ts +250 -0
  41. package/build/src/md/select/internals/Select.d.ts.map +1 -0
  42. package/build/src/md/select/internals/Select.js +606 -0
  43. package/build/src/md/select/internals/Select.js.map +1 -0
  44. package/build/src/md/select/internals/Select.styles.d.ts +3 -0
  45. package/build/src/md/select/internals/Select.styles.d.ts.map +1 -0
  46. package/build/src/md/select/internals/Select.styles.js +22 -0
  47. package/build/src/md/select/internals/Select.styles.js.map +1 -0
  48. package/build/src/md/select/ui-option.d.ts +12 -0
  49. package/build/src/md/select/ui-option.d.ts.map +1 -0
  50. package/build/src/md/select/ui-option.js +29 -0
  51. package/build/src/md/select/ui-option.js.map +1 -0
  52. package/build/src/md/select/ui-select.d.ts +12 -0
  53. package/build/src/md/select/ui-select.d.ts.map +1 -0
  54. package/build/src/md/select/ui-select.js +27 -0
  55. package/build/src/md/select/ui-select.js.map +1 -0
  56. package/build/src/md/text-field/internals/TextField.d.ts.map +1 -1
  57. package/build/src/md/text-field/internals/TextField.js +1 -0
  58. package/build/src/md/text-field/internals/TextField.js.map +1 -1
  59. package/demo/elements/index.html +7 -4
  60. package/demo/elements/navigation/navigation-item.html +45 -0
  61. package/demo/elements/navigation/navigation-item.ts +112 -0
  62. package/demo/md/index.html +2 -0
  63. package/demo/md/inputs/input.ts +4 -0
  64. package/demo/md/select/index.html +16 -0
  65. package/demo/md/select/index.ts +202 -0
  66. package/package.json +1 -1
  67. package/src/elements/navigation/internals/AppNavigationElement.ts +1 -6
  68. package/src/elements/navigation/internals/NavigationItem.styles.ts +18 -1
  69. package/src/elements/navigation/internals/NavigationItem.ts +11 -5
  70. package/src/md/dropdown-list/internals/UiDropdownList.ts +4 -3
  71. package/src/md/input/Input.ts +8 -37
  72. package/src/md/list/internals/List.ts +12 -5
  73. package/src/md/menu/internal/Menu.ts +27 -18
  74. package/src/md/select/index.ts +3 -0
  75. package/src/md/select/internals/Option.styles.ts +139 -0
  76. package/src/md/select/internals/Option.ts +210 -0
  77. package/src/md/select/internals/Select.styles.ts +22 -0
  78. package/src/md/select/internals/Select.ts +534 -0
  79. package/src/md/select/ui-option.ts +18 -0
  80. package/src/md/select/ui-select.ts +17 -0
  81. package/src/md/text-field/internals/TextField.ts +1 -0
  82. package/test/md/menu/SubMenu.test.ts +2 -3
  83. package/test/md/select/Select.test.ts +667 -0
@@ -0,0 +1,606 @@
1
+ import { __esDecorate, __runInitializers } from "tslib";
2
+ import { html, LitElement } from 'lit';
3
+ import { property, query, state } from 'lit/decorators.js';
4
+ import { classMap } from 'lit/directives/class-map.js';
5
+ import { setDisabled } from '../../../lib/disabled.js';
6
+ import '../../text-field/ui-outlined-text-field.js';
7
+ import '../../menu/ui-menu.js';
8
+ import '../../icons/ui-icon.js';
9
+ import '@material/web/focus/md-focus-ring.js';
10
+ let UiSelect = (() => {
11
+ let _classSuper = LitElement;
12
+ let _instanceExtraInitializers = [];
13
+ let _set_value_decorators;
14
+ let _name_decorators;
15
+ let _name_initializers = [];
16
+ let _name_extraInitializers = [];
17
+ let _label_decorators;
18
+ let _label_initializers = [];
19
+ let _label_extraInitializers = [];
20
+ let _required_decorators;
21
+ let _required_initializers = [];
22
+ let _required_extraInitializers = [];
23
+ let _invalid_decorators;
24
+ let _invalid_initializers = [];
25
+ let _invalid_extraInitializers = [];
26
+ let _invalidText_decorators;
27
+ let _invalidText_initializers = [];
28
+ let _invalidText_extraInitializers = [];
29
+ let _disabled_decorators;
30
+ let _disabled_initializers = [];
31
+ let _disabled_extraInitializers = [];
32
+ let _open_decorators;
33
+ let _open_initializers = [];
34
+ let _open_extraInitializers = [];
35
+ let _selectedOption_decorators;
36
+ let _selectedOption_initializers = [];
37
+ let _selectedOption_extraInitializers = [];
38
+ let _ariaActiveDescendant_decorators;
39
+ let _ariaActiveDescendant_initializers = [];
40
+ let _ariaActiveDescendant_extraInitializers = [];
41
+ let _menu_decorators;
42
+ let _menu_initializers = [];
43
+ let _menu_extraInitializers = [];
44
+ return class UiSelect extends _classSuper {
45
+ static {
46
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
47
+ _set_value_decorators = [property({ type: String })];
48
+ _name_decorators = [property({ type: String })];
49
+ _label_decorators = [property({ type: String })];
50
+ _required_decorators = [property({ type: Boolean })];
51
+ _invalid_decorators = [property({ type: Boolean })];
52
+ _invalidText_decorators = [property({ type: String })];
53
+ _disabled_decorators = [property({ type: Boolean, reflect: true })];
54
+ _open_decorators = [property({ type: Boolean, reflect: true })];
55
+ _selectedOption_decorators = [state()];
56
+ _ariaActiveDescendant_decorators = [state()];
57
+ _menu_decorators = [query('.menu')];
58
+ __esDecorate(this, null, _set_value_decorators, { kind: "setter", name: "value", static: false, private: false, access: { has: obj => "value" in obj, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
59
+ __esDecorate(this, null, _name_decorators, { kind: "accessor", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
60
+ __esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
61
+ __esDecorate(this, null, _required_decorators, { kind: "accessor", name: "required", static: false, private: false, access: { has: obj => "required" in obj, get: obj => obj.required, set: (obj, value) => { obj.required = value; } }, metadata: _metadata }, _required_initializers, _required_extraInitializers);
62
+ __esDecorate(this, null, _invalid_decorators, { kind: "accessor", name: "invalid", static: false, private: false, access: { has: obj => "invalid" in obj, get: obj => obj.invalid, set: (obj, value) => { obj.invalid = value; } }, metadata: _metadata }, _invalid_initializers, _invalid_extraInitializers);
63
+ __esDecorate(this, null, _invalidText_decorators, { kind: "accessor", name: "invalidText", static: false, private: false, access: { has: obj => "invalidText" in obj, get: obj => obj.invalidText, set: (obj, value) => { obj.invalidText = value; } }, metadata: _metadata }, _invalidText_initializers, _invalidText_extraInitializers);
64
+ __esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
65
+ __esDecorate(this, null, _open_decorators, { kind: "accessor", name: "open", static: false, private: false, access: { has: obj => "open" in obj, get: obj => obj.open, set: (obj, value) => { obj.open = value; } }, metadata: _metadata }, _open_initializers, _open_extraInitializers);
66
+ __esDecorate(this, null, _selectedOption_decorators, { kind: "accessor", name: "selectedOption", static: false, private: false, access: { has: obj => "selectedOption" in obj, get: obj => obj.selectedOption, set: (obj, value) => { obj.selectedOption = value; } }, metadata: _metadata }, _selectedOption_initializers, _selectedOption_extraInitializers);
67
+ __esDecorate(this, null, _ariaActiveDescendant_decorators, { kind: "accessor", name: "ariaActiveDescendant", static: false, private: false, access: { has: obj => "ariaActiveDescendant" in obj, get: obj => obj.ariaActiveDescendant, set: (obj, value) => { obj.ariaActiveDescendant = value; } }, metadata: _metadata }, _ariaActiveDescendant_initializers, _ariaActiveDescendant_extraInitializers);
68
+ __esDecorate(this, null, _menu_decorators, { kind: "accessor", name: "menu", static: false, private: false, access: { has: obj => "menu" in obj, get: obj => obj.menu, set: (obj, value) => { obj.menu = value; } }, metadata: _metadata }, _menu_initializers, _menu_extraInitializers);
69
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
70
+ }
71
+ static formAssociated = true;
72
+ #internals = (__runInitializers(this, _instanceExtraInitializers), this.attachInternals());
73
+ /**
74
+ * The value has a private member so that we can set the value without triggering
75
+ * the side effects.
76
+ */
77
+ #value;
78
+ /**
79
+ * The currently selected value. Corresponds to the `value` attribute of the selected `ui-option`.
80
+ * When set programmatically, it will update the selected option if a matching option exists.
81
+ *
82
+ * @example
83
+ * ```html
84
+ * <ui-select value="apple">
85
+ * <ui-option value="apple">Apple</ui-option>
86
+ * <ui-option value="banana">Banana</ui-option>
87
+ * </ui-select>
88
+ * ```
89
+ */
90
+ get value() {
91
+ return this.#value;
92
+ }
93
+ set value(newValue) {
94
+ const oldValue = this.#value;
95
+ if (newValue === oldValue)
96
+ return;
97
+ this.#value = newValue;
98
+ this.requestUpdate();
99
+ }
100
+ #name_accessor_storage = __runInitializers(this, _name_initializers, void 0);
101
+ /**
102
+ * The name attribute for form submission. This value will be used as the key
103
+ * when the form is submitted.
104
+ *
105
+ * @example
106
+ * ```html
107
+ * <ui-select name="country" value="us">
108
+ * <ui-option value="us">United States</ui-option>
109
+ * </ui-select>
110
+ * ```
111
+ */
112
+ get name() { return this.#name_accessor_storage; }
113
+ set name(value) { this.#name_accessor_storage = value; }
114
+ #label_accessor_storage = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, _label_initializers, void 0));
115
+ /**
116
+ * The label text displayed in the select field. Provides accessible labeling
117
+ * and is shown as the floating label in the outlined text field.
118
+ *
119
+ * @example
120
+ * ```html
121
+ * <ui-select label="Select a country">
122
+ * <ui-option value="us">United States</ui-option>
123
+ * </ui-select>
124
+ * ```
125
+ */
126
+ get label() { return this.#label_accessor_storage; }
127
+ set label(value) { this.#label_accessor_storage = value; }
128
+ #required_accessor_storage = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _required_initializers, false
129
+ /**
130
+ * Whether the select is in an invalid state. This is typically set automatically
131
+ * during validation, but can be set manually to indicate validation errors.
132
+ *
133
+ * @example
134
+ * ```html
135
+ * <ui-select invalid invalidText="Please select a valid option">
136
+ * <ui-option value="option1">Option 1</ui-option>
137
+ * </ui-select>
138
+ * ```
139
+ */
140
+ ));
141
+ /**
142
+ * Whether the select is required for form validation. When true, the select
143
+ * must have a value selected for the form to be valid.
144
+ *
145
+ * @default false
146
+ * @example
147
+ * ```html
148
+ * <ui-select required label="Required field">
149
+ * <ui-option value="option1">Option 1</ui-option>
150
+ * </ui-select>
151
+ * ```
152
+ */
153
+ get required() { return this.#required_accessor_storage; }
154
+ set required(value) { this.#required_accessor_storage = value; }
155
+ #invalid_accessor_storage = (__runInitializers(this, _required_extraInitializers), __runInitializers(this, _invalid_initializers, void 0));
156
+ /**
157
+ * Whether the select is in an invalid state. This is typically set automatically
158
+ * during validation, but can be set manually to indicate validation errors.
159
+ *
160
+ * @example
161
+ * ```html
162
+ * <ui-select invalid invalidText="Please select a valid option">
163
+ * <ui-option value="option1">Option 1</ui-option>
164
+ * </ui-select>
165
+ * ```
166
+ */
167
+ get invalid() { return this.#invalid_accessor_storage; }
168
+ set invalid(value) { this.#invalid_accessor_storage = value; }
169
+ #invalidText_accessor_storage = (__runInitializers(this, _invalid_extraInitializers), __runInitializers(this, _invalidText_initializers, void 0));
170
+ /**
171
+ * The error message to display when the select is invalid. This text is shown
172
+ * below the select field when `invalid` is true.
173
+ *
174
+ * @example
175
+ * ```html
176
+ * <ui-select invalid invalidText="This field is required">
177
+ * <ui-option value="option1">Option 1</ui-option>
178
+ * </ui-select>
179
+ * ```
180
+ */
181
+ get invalidText() { return this.#invalidText_accessor_storage; }
182
+ set invalidText(value) { this.#invalidText_accessor_storage = value; }
183
+ #disabled_accessor_storage = (__runInitializers(this, _invalidText_extraInitializers), __runInitializers(this, _disabled_initializers, false
184
+ /**
185
+ * Whether the dropdown menu is currently open. This property reflects the
186
+ * current state of the dropdown and can be set programmatically to open/close it.
187
+ *
188
+ * @default false
189
+ * @example
190
+ * ```javascript
191
+ * // Open the dropdown programmatically
192
+ * selectElement.open = true;
193
+ *
194
+ * // Close the dropdown
195
+ * selectElement.open = false;
196
+ * ```
197
+ */
198
+ ));
199
+ /**
200
+ * Whether the select is disabled. When disabled, the select cannot be interacted
201
+ * with and will not receive focus or respond to user input.
202
+ *
203
+ * @default false
204
+ * @example
205
+ * ```html
206
+ * <ui-select disabled label="Disabled select">
207
+ * <ui-option value="option1">Option 1</ui-option>
208
+ * </ui-select>
209
+ * ```
210
+ */
211
+ get disabled() { return this.#disabled_accessor_storage; }
212
+ set disabled(value) { this.#disabled_accessor_storage = value; }
213
+ #open_accessor_storage = (__runInitializers(this, _disabled_extraInitializers), __runInitializers(this, _open_initializers, false));
214
+ /**
215
+ * Whether the dropdown menu is currently open. This property reflects the
216
+ * current state of the dropdown and can be set programmatically to open/close it.
217
+ *
218
+ * @default false
219
+ * @example
220
+ * ```javascript
221
+ * // Open the dropdown programmatically
222
+ * selectElement.open = true;
223
+ *
224
+ * // Close the dropdown
225
+ * selectElement.open = false;
226
+ * ```
227
+ */
228
+ get open() { return this.#open_accessor_storage; }
229
+ set open(value) { this.#open_accessor_storage = value; }
230
+ #selectedOption_accessor_storage = (__runInitializers(this, _open_extraInitializers), __runInitializers(this, _selectedOption_initializers, null));
231
+ get selectedOption() { return this.#selectedOption_accessor_storage; }
232
+ set selectedOption(value) { this.#selectedOption_accessor_storage = value; }
233
+ #ariaActiveDescendant_accessor_storage = (__runInitializers(this, _selectedOption_extraInitializers), __runInitializers(this, _ariaActiveDescendant_initializers, void 0));
234
+ get ariaActiveDescendant() { return this.#ariaActiveDescendant_accessor_storage; }
235
+ set ariaActiveDescendant(value) { this.#ariaActiveDescendant_accessor_storage = value; }
236
+ #menu_accessor_storage = (__runInitializers(this, _ariaActiveDescendant_extraInitializers), __runInitializers(this, _menu_initializers, void 0));
237
+ get menu() { return this.#menu_accessor_storage; }
238
+ set menu(value) { this.#menu_accessor_storage = value; }
239
+ /**
240
+ * Returns the currently selected option element. This provides access to the
241
+ * full `ui-option` element, not just its value.
242
+ *
243
+ * @readonly
244
+ * @example
245
+ * ```javascript
246
+ * const select = document.querySelector('ui-select');
247
+ * const selectedItem = select.selectedItem;
248
+ * if (selectedItem) {
249
+ * console.log('Selected option:', selectedItem.textContent);
250
+ * }
251
+ * ```
252
+ */
253
+ get selectedItem() {
254
+ return this.selectedOption;
255
+ }
256
+ /**
257
+ * Returns the text content that should be displayed in the select field.
258
+ * This is the rendered value of the currently selected option.
259
+ *
260
+ * @readonly
261
+ * @example
262
+ * ```javascript
263
+ * const select = document.querySelector('ui-select');
264
+ * console.log('Display text:', select.renderValue);
265
+ * ```
266
+ */
267
+ get renderValue() {
268
+ const item = this.selectedOption;
269
+ return item ? item.renderValue : '';
270
+ }
271
+ /**
272
+ * Returns the form element that contains this select, if any.
273
+ * Part of the form-associated custom element API.
274
+ *
275
+ * @readonly
276
+ */
277
+ get form() {
278
+ return this.#internals.form;
279
+ }
280
+ /**
281
+ * Returns the validity state of the select element.
282
+ * Part of the form-associated custom element API.
283
+ *
284
+ * @readonly
285
+ */
286
+ get validity() {
287
+ return this.#internals.validity;
288
+ }
289
+ /**
290
+ * Returns the validation message for the select element.
291
+ * Part of the form-associated custom element API.
292
+ *
293
+ * @readonly
294
+ */
295
+ get validationMessage() {
296
+ return this.#internals.validationMessage;
297
+ }
298
+ /**
299
+ * Returns whether the select element will be validated when the form is submitted.
300
+ * Part of the form-associated custom element API.
301
+ *
302
+ * @readonly
303
+ */
304
+ get willValidate() {
305
+ return this.#internals.willValidate;
306
+ }
307
+ /**
308
+ * Checks the validity of the select element and returns true if valid.
309
+ * Part of the form-associated custom element API.
310
+ *
311
+ * @returns {boolean} True if the element is valid, false otherwise
312
+ * @example
313
+ * ```javascript
314
+ * const select = document.querySelector('ui-select');
315
+ * if (!select.checkValidity()) {
316
+ * console.log('Select is invalid:', select.validationMessage);
317
+ * }
318
+ * ```
319
+ */
320
+ checkValidity() {
321
+ return this.#internals.checkValidity();
322
+ }
323
+ constructor() {
324
+ super();
325
+ __runInitializers(this, _menu_extraInitializers);
326
+ this.addEventListener('click', this.handleClick.bind(this));
327
+ this.addEventListener('blur', this.handleBlur.bind(this));
328
+ this.addEventListener('keydown', this.handleKeydown.bind(this));
329
+ }
330
+ connectedCallback() {
331
+ super.connectedCallback();
332
+ this.setAttribute('role', 'combobox');
333
+ this.setAttribute('aria-haspopup', 'listbox');
334
+ this.setAttribute('aria-controls', 'menu');
335
+ if (!this.disabled) {
336
+ this.setAttribute('tabindex', '0');
337
+ }
338
+ }
339
+ /**
340
+ * Resets the select to its initial state. Called automatically when the parent
341
+ * form is reset. Part of the form-associated custom element API.
342
+ *
343
+ * @example
344
+ * ```javascript
345
+ * const select = document.querySelector('ui-select');
346
+ * select.formResetCallback(); // Clears the selection
347
+ * ```
348
+ */
349
+ formResetCallback() {
350
+ this.value = undefined;
351
+ }
352
+ /**
353
+ * Restores the select's state from saved form data. Called automatically when
354
+ * the browser restores form state. Part of the form-associated custom element API.
355
+ *
356
+ * @param {string | null} state - The saved state to restore
357
+ */
358
+ formStateRestoreCallback(state) {
359
+ this.value = state ?? undefined;
360
+ }
361
+ /**
362
+ * Validates the select element and updates its validity state. This is called
363
+ * automatically during property changes, but can be called manually to trigger validation.
364
+ *
365
+ * @example
366
+ * ```javascript
367
+ * const select = document.querySelector('ui-select');
368
+ * select.validate();
369
+ * if (select.invalid) {
370
+ * console.log('Validation failed:', select.invalidText);
371
+ * }
372
+ * ```
373
+ */
374
+ validate() {
375
+ let message = '';
376
+ if (this.required && !this.value) {
377
+ message = 'Please select an item.';
378
+ this.#internals.setValidity({ valueMissing: true }, message);
379
+ }
380
+ else {
381
+ this.#internals.setValidity({});
382
+ }
383
+ this.invalid = !this.#internals.validity.valid;
384
+ this.invalidText = message;
385
+ }
386
+ willUpdate(changedProperties) {
387
+ super.willUpdate(changedProperties);
388
+ if (changedProperties.has('disabled')) {
389
+ setDisabled(this, this.disabled);
390
+ }
391
+ if (changedProperties.has('open')) {
392
+ this.handleOpenChange();
393
+ }
394
+ if (changedProperties.has('value')) {
395
+ this.setCurrentOption();
396
+ this.#internals.setFormValue(this.value ?? null);
397
+ this.validate();
398
+ }
399
+ if (changedProperties.has('label')) {
400
+ if (this.label) {
401
+ this.setAttribute('aria-label', this.label);
402
+ }
403
+ else {
404
+ this.removeAttribute('aria-label');
405
+ }
406
+ }
407
+ }
408
+ async setCurrentOption() {
409
+ await this.updateComplete;
410
+ if (this.value) {
411
+ const options = this.querySelectorAll('ui-option');
412
+ this.selectedOption = Array.from(options).find((option) => option.value === this.value) || null;
413
+ }
414
+ else {
415
+ this.selectedOption = null;
416
+ }
417
+ }
418
+ handleKeydown(e) {
419
+ if (this.disabled || e.defaultPrevented)
420
+ return;
421
+ if (this.open) {
422
+ switch (e.key) {
423
+ case 'Tab': {
424
+ // If menu is open and Tab is pressed, close it and allow normal tab navigation
425
+ if (this.open) {
426
+ this.open = false;
427
+ }
428
+ break;
429
+ }
430
+ case 'Escape': {
431
+ if (this.open) {
432
+ e.preventDefault();
433
+ this.open = false;
434
+ this.focus(); // Return focus to the select element
435
+ }
436
+ break;
437
+ }
438
+ case 'ArrowDown':
439
+ e.preventDefault();
440
+ this.menu.highlightNext();
441
+ return;
442
+ case 'ArrowUp':
443
+ e.preventDefault();
444
+ this.menu.highlightPrevious();
445
+ return;
446
+ case 'Home':
447
+ e.preventDefault();
448
+ this.menu.highlightFirst();
449
+ return;
450
+ case 'End':
451
+ e.preventDefault();
452
+ this.menu.highlightLast();
453
+ return;
454
+ case 'Enter':
455
+ case ' ':
456
+ if (this.menu.highlightListItem) {
457
+ e.preventDefault();
458
+ this.menu.notifySelect(this.menu.highlightListItem);
459
+ }
460
+ return;
461
+ }
462
+ }
463
+ else {
464
+ switch (e.key) {
465
+ case 'Enter':
466
+ case ' ': {
467
+ if (!this.open) {
468
+ e.preventDefault();
469
+ this.open = true;
470
+ }
471
+ break;
472
+ }
473
+ case 'ArrowDown':
474
+ case 'ArrowUp': {
475
+ if (!this.open) {
476
+ e.preventDefault();
477
+ this.open = true;
478
+ }
479
+ // If menu is open, let the menu handle arrow keys
480
+ break;
481
+ }
482
+ }
483
+ }
484
+ }
485
+ handleBlur(e) {
486
+ if (this.disabled)
487
+ return;
488
+ // Check if focus is moving to the menu or one of its children
489
+ const relatedTarget = e.relatedTarget;
490
+ if (relatedTarget && this.contains(relatedTarget)) {
491
+ // Focus is moving to the menu, keep it open
492
+ return;
493
+ }
494
+ // Close the menu when focus leaves the component
495
+ this.open = false;
496
+ }
497
+ handleClick(e) {
498
+ if (this.disabled || e.defaultPrevented)
499
+ return;
500
+ e.preventDefault();
501
+ e.stopPropagation();
502
+ this.open = true;
503
+ }
504
+ async handleOpenChange() {
505
+ const menu = this.menu;
506
+ if (!menu) {
507
+ // The status can be set before the menu is rendered
508
+ return;
509
+ }
510
+ this.setAttribute('aria-expanded', String(this.open));
511
+ if (this.open) {
512
+ menu.showPopover();
513
+ // menu.focus()
514
+ if (this.selectedOption) {
515
+ this.menu.highlightItem(this.selectedOption);
516
+ }
517
+ else {
518
+ this.menu.highlightFirst();
519
+ }
520
+ this.dispatchEvent(new CustomEvent('open', { bubbles: false, composed: true }));
521
+ this.focus();
522
+ }
523
+ else {
524
+ menu.hidePopover();
525
+ this.dispatchEvent(new CustomEvent('close', { bubbles: false, composed: true }));
526
+ }
527
+ }
528
+ handleSelect(e) {
529
+ e.stopPropagation();
530
+ const item = e.detail.item;
531
+ this.selectedOption = item;
532
+ this.#value = item.value;
533
+ this.open = false;
534
+ // Dispatch change event
535
+ const changeEvent = new CustomEvent('change', {
536
+ detail: { value: this.value, item: item },
537
+ bubbles: false,
538
+ composed: true,
539
+ });
540
+ this.dispatchEvent(changeEvent);
541
+ this.focus();
542
+ }
543
+ handleHighlightChange(e) {
544
+ this.ariaActiveDescendant = e.detail.item?.id;
545
+ }
546
+ handleMenuClose() {
547
+ this.open = false;
548
+ this.focus();
549
+ }
550
+ render() {
551
+ const classes = classMap({
552
+ 'ui-select': true,
553
+ 'open': this.open,
554
+ 'disabled': this.disabled,
555
+ });
556
+ return html `${this.renderFocusRing()}
557
+ <div class="${classes}" aria-activedescendant=${this.ariaActiveDescendant || ''}>
558
+ ${this.renderInput()} ${this.renderMenu()}
559
+ </div> `;
560
+ }
561
+ renderInput() {
562
+ return html `<ui-outlined-text-field
563
+ .name=${this.name}
564
+ .label=${this.label}
565
+ .value=${this.renderValue}
566
+ .disabled=${this.disabled}
567
+ .required=${this.required}
568
+ readonly
569
+ tabindex="-1"
570
+ inert
571
+ aria-hidden="true"
572
+ .invalid=${this.invalid}
573
+ .invalidText=${this.invalidText || ''}
574
+ class="input"
575
+ >
576
+ <ui-icon slot="suffix">arrow_drop_down</ui-icon>
577
+ </ui-outlined-text-field>`;
578
+ }
579
+ renderMenu() {
580
+ return html `<ui-menu
581
+ id="menu"
582
+ class="menu"
583
+ popover="auto"
584
+ selector="ui-option"
585
+ @select="${this.handleSelect}"
586
+ @close="${this.handleMenuClose}"
587
+ @highlightchange="${this.handleHighlightChange}"
588
+ >
589
+ <slot></slot>
590
+ </ui-menu>`;
591
+ }
592
+ renderFocusRing() {
593
+ return html `<md-focus-ring part="focus-ring" class="focus-ring" .control="${this}"></md-focus-ring>`;
594
+ }
595
+ };
596
+ })();
597
+ /**
598
+ * Material Design 3 Select component that behaves like an outlined text field with dropdown.
599
+ *
600
+ * @fires change - Dispatched when the selection changes. The event is non-bubbling and non-cancelable.
601
+ * The `event.detail` object contains the `value` and `item` properties.
602
+ * @fires open - Dispatched when the dropdown opens
603
+ * @fires close - Dispatched when the dropdown closes
604
+ */
605
+ export default UiSelect;
606
+ //# sourceMappingURL=Select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.js","sourceRoot":"","sources":["../../../../../src/md/select/internals/Select.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAkC,MAAM,KAAK,CAAA;AACtE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAItD,OAAO,4CAA4C,CAAA;AACnD,OAAO,uBAAuB,CAAA;AAC9B,OAAO,wBAAwB,CAAA;AAC/B,OAAO,sCAAsC,CAAA;;sBAeP,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA3B,QAAS,SAAQ,WAAU;;;qCA0B7C,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gCAmB1B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iCAa1B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oCAc1B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mCAa3B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;uCAa3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oCAc1B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gCAgB1C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;0CAE1C,KAAK,EAAE;gDACP,KAAK,EAAE;gCACP,KAAK,CAAC,OAAO,CAAC;YAzGf,iLAAI,KAAK,wEAKR;YAa2B,iKAAS,IAAI,6BAAJ,IAAI,mFAAoB;YAajC,oKAAS,KAAK,6BAAL,KAAK,qFAAoB;YAcjC,6KAAS,QAAQ,6BAAR,QAAQ,2FAAQ;YAazB,0KAAS,OAAO,6BAAP,OAAO,yFAAqB;YAatC,sLAAS,WAAW,6BAAX,WAAW,iGAAoB;YAcxB,6KAAS,QAAQ,6BAAR,QAAQ,2FAAQ;YAgBzB,iKAAS,IAAI,6BAAJ,IAAI,mFAAQ;YAExD,+LAAS,cAAc,6BAAd,cAAc,uGAAwB;YAC/C,iNAAS,oBAAoB,6BAApB,oBAAoB,mHAAoB;YAC1C,iKAAS,IAAI,6BAAJ,IAAI,mFAAgB;;;QAnI7C,MAAM,CAAU,cAAc,GAAG,IAAI,CAAA;QACrC,UAAU,IAFS,mDAAQ,EAEd,IAAI,CAAC,eAAe,EAAE,EAAA;QAEnC;;;WAGG;QACH,MAAM,CAAoB;QAE1B;;;;;;;;;;;WAWG;QACH,IAAI,KAAK;YACP,OAAO,IAAI,CAAC,MAAM,CAAA;QACpB,CAAC;QAGD,IAAI,KAAK,CAAC,QAA4B;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAA;YAC5B,IAAI,QAAQ,KAAK,QAAQ;gBAAE,OAAM;YACjC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAA;YACtB,IAAI,CAAC,aAAa,EAAE,CAAA;QACtB,CAAC;QAa2B,6EAAiC;QAX7D;;;;;;;;;;WAUG;QACyB,IAAS,IAAI,0CAAoB;QAAjC,IAAS,IAAI,gDAAoB;QAajC,mIAAkC;QAX9D;;;;;;;;;;WAUG;QACyB,IAAS,KAAK,2CAAoB;QAAlC,IAAS,KAAK,iDAAoB;QAcjC,iIAAoB,KAAK;QAEtD;;;;;;;;;;WAUG;WAZmD;QAZtD;;;;;;;;;;;WAWG;QAC0B,IAAS,QAAQ,8CAAQ;QAAzB,IAAS,QAAQ,oDAAQ;QAazB,2IAAqC;QAXlE;;;;;;;;;;WAUG;QAC0B,IAAS,OAAO,6CAAqB;QAArC,IAAS,OAAO,mDAAqB;QAatC,kJAAwC;QAXpE;;;;;;;;;;WAUG;QACyB,IAAS,WAAW,iDAAoB;QAAxC,IAAS,WAAW,uDAAoB;QAcxB,uIAAoB,KAAK;QAErE;;;;;;;;;;;;;WAaG;WAfkE;QAZrE;;;;;;;;;;;WAWG;QACyC,IAAS,QAAQ,8CAAQ;QAAzB,IAAS,QAAQ,oDAAQ;QAgBzB,4HAAgB,KAAK,GAAA;QAdjE;;;;;;;;;;;;;WAaG;QACyC,IAAS,IAAI,0CAAQ;QAArB,IAAS,IAAI,gDAAQ;QAExD,4IAA2C,IAAI,GAAA;QAA/C,IAAS,cAAc,oDAAwB;QAA/C,IAAS,cAAc,0DAAwB;QAC/C,2KAAiD;QAAjD,IAAS,oBAAoB,0DAAoB;QAAjD,IAAS,oBAAoB,gEAAoB;QAC1C,iJAA6B;QAA7B,IAAS,IAAI,0CAAgB;QAA7B,IAAS,IAAI,gDAAgB;QAE7C;;;;;;;;;;;;;WAaG;QACH,IAAI,YAAY;YACd,OAAO,IAAI,CAAC,cAAc,CAAA;QAC5B,CAAC;QAED;;;;;;;;;;WAUG;QACH,IAAI,WAAW;YACb,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAA;YAChC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;QACrC,CAAC;QAED;;;;;WAKG;QACH,IAAI,IAAI;YACN,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAA;QAC7B,CAAC;QAED;;;;;WAKG;QACH,IAAI,QAAQ;YACV,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAA;QACjC,CAAC;QAED;;;;;WAKG;QACH,IAAI,iBAAiB;YACnB,OAAO,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAA;QAC1C,CAAC;QAED;;;;;WAKG;QACH,IAAI,YAAY;YACd,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,CAAA;QACrC,CAAC;QAED;;;;;;;;;;;;WAYG;QACH,aAAa;YACX,OAAO,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAA;QACxC,CAAC;QAED;YACE,KAAK,EAAE,CAAA;;YACP,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC3D,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YACzD,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;SAChE;QAEQ,iBAAiB;YACxB,KAAK,CAAC,iBAAiB,EAAE,CAAA;YACzB,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;YACrC,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,CAAA;YAC7C,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;YAC1C,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;QAED;;;;;;;;;WASG;QACH,iBAAiB;YACf,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACxB,CAAC;QAED;;;;;WAKG;QACH,wBAAwB,CAAC,KAAoB;YAC3C,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,SAAS,CAAA;QACjC,CAAC;QAED;;;;;;;;;;;;WAYG;QACH,QAAQ;YACN,IAAI,OAAO,GAAG,EAAE,CAAA;YAChB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACjC,OAAO,GAAG,wBAAwB,CAAA;gBAClC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAA;YAC9D,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;YACjC,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAA;YAC9C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAA;QAC5B,CAAC;QAEkB,UAAU,CAAC,iBAAuC;YACnE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;YACnC,IAAI,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAClC,CAAC;YACD,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,IAAI,CAAC,gBAAgB,EAAE,CAAA;YACzB,CAAC;YACD,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,gBAAgB,EAAE,CAAA;gBACvB,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;gBAChD,IAAI,CAAC,QAAQ,EAAE,CAAA;YACjB,CAAC;YACD,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC7C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;gBACpC,CAAC;YACH,CAAC;QACH,CAAC;QAES,KAAK,CAAC,gBAAgB;YAC9B,MAAM,IAAI,CAAC,cAAc,CAAA;YACzB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAW,WAAW,CAAC,CAAA;gBAC5D,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAA;YACjG,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC5B,CAAC;QACH,CAAC;QAES,aAAa,CAAC,CAAgB;YACtC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,gBAAgB;gBAAE,OAAM;YAC/C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;oBACd,KAAK,KAAK,CAAC,CAAC,CAAC;wBACX,+EAA+E;wBAC/E,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;4BACd,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;wBACnB,CAAC;wBACD,MAAK;oBACP,CAAC;oBACD,KAAK,QAAQ,CAAC,CAAC,CAAC;wBACd,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;4BACd,CAAC,CAAC,cAAc,EAAE,CAAA;4BAClB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;4BACjB,IAAI,CAAC,KAAK,EAAE,CAAA,CAAC,qCAAqC;wBACpD,CAAC;wBACD,MAAK;oBACP,CAAC;oBACD,KAAK,WAAW;wBACd,CAAC,CAAC,cAAc,EAAE,CAAA;wBAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAA;wBACzB,OAAM;oBACR,KAAK,SAAS;wBACZ,CAAC,CAAC,cAAc,EAAE,CAAA;wBAClB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAA;wBAC7B,OAAM;oBACR,KAAK,MAAM;wBACT,CAAC,CAAC,cAAc,EAAE,CAAA;wBAClB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAA;wBAC1B,OAAM;oBACR,KAAK,KAAK;wBACR,CAAC,CAAC,cAAc,EAAE,CAAA;wBAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAA;wBACzB,OAAM;oBACR,KAAK,OAAO,CAAC;oBACb,KAAK,GAAG;wBACN,IAAI,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;4BAChC,CAAC,CAAC,cAAc,EAAE,CAAA;4BAClB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,iBAA6B,CAAC,CAAA;wBACjE,CAAC;wBACD,OAAM;gBACV,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;oBACd,KAAK,OAAO,CAAC;oBACb,KAAK,GAAG,CAAC,CAAC,CAAC;wBACT,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;4BACf,CAAC,CAAC,cAAc,EAAE,CAAA;4BAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;wBAClB,CAAC;wBACD,MAAK;oBACP,CAAC;oBACD,KAAK,WAAW,CAAC;oBACjB,KAAK,SAAS,CAAC,CAAC,CAAC;wBACf,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;4BACf,CAAC,CAAC,cAAc,EAAE,CAAA;4BAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;wBAClB,CAAC;wBACD,kDAAkD;wBAClD,MAAK;oBACP,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAES,UAAU,CAAC,CAAa;YAChC,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAM;YAEzB,8DAA8D;YAC9D,MAAM,aAAa,GAAG,CAAC,CAAC,aAA4B,CAAA;YAEpD,IAAI,aAAa,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClD,4CAA4C;gBAC5C,OAAM;YACR,CAAC;YAED,iDAAiD;YACjD,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;QACnB,CAAC;QAES,WAAW,CAAC,CAAQ;YAC5B,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,gBAAgB;gBAAE,OAAM;YAC/C,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;QAES,KAAK,CAAC,gBAAgB;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;YACtB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,oDAAoD;gBACpD,OAAM;YACR,CAAC;YACD,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YACrD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,IAAI,CAAC,WAAW,EAAE,CAAA;gBAClB,eAAe;gBACf,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;oBACxB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;gBAC9C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAA;gBAC5B,CAAC;gBACD,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;gBAC/E,IAAI,CAAC,KAAK,EAAE,CAAA;YACd,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,WAAW,EAAE,CAAA;gBAClB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YAClF,CAAC;QACH,CAAC;QAED,YAAY,CAAC,CAAkC;YAC7C,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;YAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAA;YACxB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;YAEjB,wBAAwB;YACxB,MAAM,WAAW,GAAG,IAAI,WAAW,CAAsB,QAAQ,EAAE;gBACjE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE;gBACzC,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,IAAI;aACf,CAAC,CAAA;YACF,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAA;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;QAED,qBAAqB,CAAC,CAAyC;YAC7D,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAA;QAC/C,CAAC;QAED,eAAe;YACb,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;YACjB,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;QAEQ,MAAM;YACb,MAAM,OAAO,GAAG,QAAQ,CAAC;gBACvB,WAAW,EAAE,IAAI;gBACjB,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,UAAU,EAAE,IAAI,CAAC,QAAQ;aAC1B,CAAC,CAAA;YACF,OAAO,IAAI,CAAA,GAAG,IAAI,CAAC,eAAe,EAAE;oBACpB,OAAO,2BAA2B,IAAI,CAAC,oBAAoB,IAAI,EAAE;UAC3E,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE;cACnC,CAAA;QACZ,CAAC;QAES,WAAW;YACnB,OAAO,IAAI,CAAA;cACD,IAAI,CAAC,IAAI;eACR,IAAI,CAAC,KAAK;eACV,IAAI,CAAC,WAAW;kBACb,IAAI,CAAC,QAAQ;kBACb,IAAI,CAAC,QAAQ;;;;;iBAKd,IAAI,CAAC,OAAO;qBACR,IAAI,CAAC,WAAW,IAAI,EAAE;;;;8BAIb,CAAA;QAC5B,CAAC;QAES,UAAU;YAClB,OAAO,IAAI,CAAA;;;;;iBAKE,IAAI,CAAC,YAAY;gBAClB,IAAI,CAAC,eAAe;0BACV,IAAI,CAAC,qBAAqB;;;eAGrC,CAAA;QACb,CAAC;QAES,eAAe;YACvB,OAAO,IAAI,CAAA,iEAAiE,IAAmB,oBAAoB,CAAA;QACrH,CAAC;;;AAngBH;;;;;;;GAOG;AACH","sourcesContent":["import { html, LitElement, PropertyValues, TemplateResult } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { setDisabled } from '../../../lib/disabled.js'\nimport type UiOption from './Option.js'\nimport type { UiMenuElement } from '../../menu/ui-menu.js'\n\nimport '../../text-field/ui-outlined-text-field.js'\nimport '../../menu/ui-menu.js'\nimport '../../icons/ui-icon.js'\nimport '@material/web/focus/md-focus-ring.js'\n\nexport interface UiSelectChangeEvent {\n value: string | undefined\n item: UiOption | null\n}\n\n/**\n * Material Design 3 Select component that behaves like an outlined text field with dropdown.\n *\n * @fires change - Dispatched when the selection changes. The event is non-bubbling and non-cancelable.\n * The `event.detail` object contains the `value` and `item` properties.\n * @fires open - Dispatched when the dropdown opens\n * @fires close - Dispatched when the dropdown closes\n */\nexport default class UiSelect extends LitElement {\n static readonly formAssociated = true\n #internals = this.attachInternals()\n\n /**\n * The value has a private member so that we can set the value without triggering\n * the side effects.\n */\n #value: string | undefined\n\n /**\n * The currently selected value. Corresponds to the `value` attribute of the selected `ui-option`.\n * When set programmatically, it will update the selected option if a matching option exists.\n *\n * @example\n * ```html\n * <ui-select value=\"apple\">\n * <ui-option value=\"apple\">Apple</ui-option>\n * <ui-option value=\"banana\">Banana</ui-option>\n * </ui-select>\n * ```\n */\n get value(): string | undefined {\n return this.#value\n }\n\n @property({ type: String })\n set value(newValue: string | undefined) {\n const oldValue = this.#value\n if (newValue === oldValue) return\n this.#value = newValue\n this.requestUpdate()\n }\n\n /**\n * The name attribute for form submission. This value will be used as the key\n * when the form is submitted.\n *\n * @example\n * ```html\n * <ui-select name=\"country\" value=\"us\">\n * <ui-option value=\"us\">United States</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: String }) accessor name: string | undefined\n\n /**\n * The label text displayed in the select field. Provides accessible labeling\n * and is shown as the floating label in the outlined text field.\n *\n * @example\n * ```html\n * <ui-select label=\"Select a country\">\n * <ui-option value=\"us\">United States</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: String }) accessor label: string | undefined\n\n /**\n * Whether the select is required for form validation. When true, the select\n * must have a value selected for the form to be valid.\n *\n * @default false\n * @example\n * ```html\n * <ui-select required label=\"Required field\">\n * <ui-option value=\"option1\">Option 1</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: Boolean }) accessor required = false\n\n /**\n * Whether the select is in an invalid state. This is typically set automatically\n * during validation, but can be set manually to indicate validation errors.\n *\n * @example\n * ```html\n * <ui-select invalid invalidText=\"Please select a valid option\">\n * <ui-option value=\"option1\">Option 1</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: Boolean }) accessor invalid: boolean | undefined\n\n /**\n * The error message to display when the select is invalid. This text is shown\n * below the select field when `invalid` is true.\n *\n * @example\n * ```html\n * <ui-select invalid invalidText=\"This field is required\">\n * <ui-option value=\"option1\">Option 1</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: String }) accessor invalidText: string | undefined\n\n /**\n * Whether the select is disabled. When disabled, the select cannot be interacted\n * with and will not receive focus or respond to user input.\n *\n * @default false\n * @example\n * ```html\n * <ui-select disabled label=\"Disabled select\">\n * <ui-option value=\"option1\">Option 1</ui-option>\n * </ui-select>\n * ```\n */\n @property({ type: Boolean, reflect: true }) accessor disabled = false\n\n /**\n * Whether the dropdown menu is currently open. This property reflects the\n * current state of the dropdown and can be set programmatically to open/close it.\n *\n * @default false\n * @example\n * ```javascript\n * // Open the dropdown programmatically\n * selectElement.open = true;\n *\n * // Close the dropdown\n * selectElement.open = false;\n * ```\n */\n @property({ type: Boolean, reflect: true }) accessor open = false\n\n @state() accessor selectedOption: UiOption | null = null\n @state() accessor ariaActiveDescendant: string | undefined\n @query('.menu') accessor menu!: UiMenuElement\n\n /**\n * Returns the currently selected option element. This provides access to the\n * full `ui-option` element, not just its value.\n *\n * @readonly\n * @example\n * ```javascript\n * const select = document.querySelector('ui-select');\n * const selectedItem = select.selectedItem;\n * if (selectedItem) {\n * console.log('Selected option:', selectedItem.textContent);\n * }\n * ```\n */\n get selectedItem(): UiOption | null {\n return this.selectedOption\n }\n\n /**\n * Returns the text content that should be displayed in the select field.\n * This is the rendered value of the currently selected option.\n *\n * @readonly\n * @example\n * ```javascript\n * const select = document.querySelector('ui-select');\n * console.log('Display text:', select.renderValue);\n * ```\n */\n get renderValue(): string {\n const item = this.selectedOption\n return item ? item.renderValue : ''\n }\n\n /**\n * Returns the form element that contains this select, if any.\n * Part of the form-associated custom element API.\n *\n * @readonly\n */\n get form(): HTMLFormElement | null {\n return this.#internals.form\n }\n\n /**\n * Returns the validity state of the select element.\n * Part of the form-associated custom element API.\n *\n * @readonly\n */\n get validity(): ValidityState {\n return this.#internals.validity\n }\n\n /**\n * Returns the validation message for the select element.\n * Part of the form-associated custom element API.\n *\n * @readonly\n */\n get validationMessage(): string {\n return this.#internals.validationMessage\n }\n\n /**\n * Returns whether the select element will be validated when the form is submitted.\n * Part of the form-associated custom element API.\n *\n * @readonly\n */\n get willValidate(): boolean {\n return this.#internals.willValidate\n }\n\n /**\n * Checks the validity of the select element and returns true if valid.\n * Part of the form-associated custom element API.\n *\n * @returns {boolean} True if the element is valid, false otherwise\n * @example\n * ```javascript\n * const select = document.querySelector('ui-select');\n * if (!select.checkValidity()) {\n * console.log('Select is invalid:', select.validationMessage);\n * }\n * ```\n */\n checkValidity(): boolean {\n return this.#internals.checkValidity()\n }\n\n constructor() {\n super()\n this.addEventListener('click', this.handleClick.bind(this))\n this.addEventListener('blur', this.handleBlur.bind(this))\n this.addEventListener('keydown', this.handleKeydown.bind(this))\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.setAttribute('role', 'combobox')\n this.setAttribute('aria-haspopup', 'listbox')\n this.setAttribute('aria-controls', 'menu')\n if (!this.disabled) {\n this.setAttribute('tabindex', '0')\n }\n }\n\n /**\n * Resets the select to its initial state. Called automatically when the parent\n * form is reset. Part of the form-associated custom element API.\n *\n * @example\n * ```javascript\n * const select = document.querySelector('ui-select');\n * select.formResetCallback(); // Clears the selection\n * ```\n */\n formResetCallback(): void {\n this.value = undefined\n }\n\n /**\n * Restores the select's state from saved form data. Called automatically when\n * the browser restores form state. Part of the form-associated custom element API.\n *\n * @param {string | null} state - The saved state to restore\n */\n formStateRestoreCallback(state: string | null): void {\n this.value = state ?? undefined\n }\n\n /**\n * Validates the select element and updates its validity state. This is called\n * automatically during property changes, but can be called manually to trigger validation.\n *\n * @example\n * ```javascript\n * const select = document.querySelector('ui-select');\n * select.validate();\n * if (select.invalid) {\n * console.log('Validation failed:', select.invalidText);\n * }\n * ```\n */\n validate(): void {\n let message = ''\n if (this.required && !this.value) {\n message = 'Please select an item.'\n this.#internals.setValidity({ valueMissing: true }, message)\n } else {\n this.#internals.setValidity({})\n }\n this.invalid = !this.#internals.validity.valid\n this.invalidText = message\n }\n\n protected override willUpdate(changedProperties: PropertyValues<this>): void {\n super.willUpdate(changedProperties)\n if (changedProperties.has('disabled')) {\n setDisabled(this, this.disabled)\n }\n if (changedProperties.has('open')) {\n this.handleOpenChange()\n }\n if (changedProperties.has('value')) {\n this.setCurrentOption()\n this.#internals.setFormValue(this.value ?? null)\n this.validate()\n }\n if (changedProperties.has('label')) {\n if (this.label) {\n this.setAttribute('aria-label', this.label)\n } else {\n this.removeAttribute('aria-label')\n }\n }\n }\n\n protected async setCurrentOption(): Promise<void> {\n await this.updateComplete\n if (this.value) {\n const options = this.querySelectorAll<UiOption>('ui-option')\n this.selectedOption = Array.from(options).find((option) => option.value === this.value) || null\n } else {\n this.selectedOption = null\n }\n }\n\n protected handleKeydown(e: KeyboardEvent): void {\n if (this.disabled || e.defaultPrevented) return\n if (this.open) {\n switch (e.key) {\n case 'Tab': {\n // If menu is open and Tab is pressed, close it and allow normal tab navigation\n if (this.open) {\n this.open = false\n }\n break\n }\n case 'Escape': {\n if (this.open) {\n e.preventDefault()\n this.open = false\n this.focus() // Return focus to the select element\n }\n break\n }\n case 'ArrowDown':\n e.preventDefault()\n this.menu.highlightNext()\n return\n case 'ArrowUp':\n e.preventDefault()\n this.menu.highlightPrevious()\n return\n case 'Home':\n e.preventDefault()\n this.menu.highlightFirst()\n return\n case 'End':\n e.preventDefault()\n this.menu.highlightLast()\n return\n case 'Enter':\n case ' ':\n if (this.menu.highlightListItem) {\n e.preventDefault()\n this.menu.notifySelect(this.menu.highlightListItem as UiOption)\n }\n return\n }\n } else {\n switch (e.key) {\n case 'Enter':\n case ' ': {\n if (!this.open) {\n e.preventDefault()\n this.open = true\n }\n break\n }\n case 'ArrowDown':\n case 'ArrowUp': {\n if (!this.open) {\n e.preventDefault()\n this.open = true\n }\n // If menu is open, let the menu handle arrow keys\n break\n }\n }\n }\n }\n\n protected handleBlur(e: FocusEvent): void {\n if (this.disabled) return\n\n // Check if focus is moving to the menu or one of its children\n const relatedTarget = e.relatedTarget as HTMLElement\n\n if (relatedTarget && this.contains(relatedTarget)) {\n // Focus is moving to the menu, keep it open\n return\n }\n\n // Close the menu when focus leaves the component\n this.open = false\n }\n\n protected handleClick(e: Event): void {\n if (this.disabled || e.defaultPrevented) return\n e.preventDefault()\n e.stopPropagation()\n this.open = true\n }\n\n protected async handleOpenChange(): Promise<void> {\n const menu = this.menu\n if (!menu) {\n // The status can be set before the menu is rendered\n return\n }\n this.setAttribute('aria-expanded', String(this.open))\n if (this.open) {\n menu.showPopover()\n // menu.focus()\n if (this.selectedOption) {\n this.menu.highlightItem(this.selectedOption)\n } else {\n this.menu.highlightFirst()\n }\n this.dispatchEvent(new CustomEvent('open', { bubbles: false, composed: true }))\n this.focus()\n } else {\n menu.hidePopover()\n this.dispatchEvent(new CustomEvent('close', { bubbles: false, composed: true }))\n }\n }\n\n handleSelect(e: CustomEvent<{ item: UiOption }>): void {\n e.stopPropagation()\n const item = e.detail.item\n this.selectedOption = item\n this.#value = item.value\n this.open = false\n\n // Dispatch change event\n const changeEvent = new CustomEvent<UiSelectChangeEvent>('change', {\n detail: { value: this.value, item: item },\n bubbles: false,\n composed: true,\n })\n this.dispatchEvent(changeEvent)\n this.focus()\n }\n\n handleHighlightChange(e: CustomEvent<{ item: UiOption | null }>): void {\n this.ariaActiveDescendant = e.detail.item?.id\n }\n\n handleMenuClose(): void {\n this.open = false\n this.focus()\n }\n\n override render(): TemplateResult {\n const classes = classMap({\n 'ui-select': true,\n 'open': this.open,\n 'disabled': this.disabled,\n })\n return html`${this.renderFocusRing()}\n <div class=\"${classes}\" aria-activedescendant=${this.ariaActiveDescendant || ''}>\n ${this.renderInput()} ${this.renderMenu()}\n </div> `\n }\n\n protected renderInput(): TemplateResult {\n return html`<ui-outlined-text-field\n .name=${this.name}\n .label=${this.label}\n .value=${this.renderValue}\n .disabled=${this.disabled}\n .required=${this.required}\n readonly\n tabindex=\"-1\"\n inert\n aria-hidden=\"true\"\n .invalid=${this.invalid}\n .invalidText=${this.invalidText || ''}\n class=\"input\"\n >\n <ui-icon slot=\"suffix\">arrow_drop_down</ui-icon>\n </ui-outlined-text-field>`\n }\n\n protected renderMenu(): TemplateResult {\n return html`<ui-menu\n id=\"menu\"\n class=\"menu\"\n popover=\"auto\"\n selector=\"ui-option\"\n @select=\"${this.handleSelect}\"\n @close=\"${this.handleMenuClose}\"\n @highlightchange=\"${this.handleHighlightChange}\"\n >\n <slot></slot>\n </ui-menu>`\n }\n\n protected renderFocusRing(): TemplateResult {\n return html`<md-focus-ring part=\"focus-ring\" class=\"focus-ring\" .control=\"${this as HTMLElement}\"></md-focus-ring>`\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("lit").CSSResult;
2
+ export default _default;
3
+ //# sourceMappingURL=Select.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.styles.d.ts","sourceRoot":"","sources":["../../../../../src/md/select/internals/Select.styles.ts"],"names":[],"mappings":";AAEA,wBAmBC"}