@momentum-design/components 0.49.1 → 0.49.3
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/dist/browser/index.js +802 -606
- package/dist/browser/index.js.map +4 -4
- package/dist/components/formfieldwrapper/formfieldwrapper.component.js +1 -1
- package/dist/components/list/list.component.js +2 -1
- package/dist/components/list/list.constants.d.ts +1 -7
- package/dist/components/list/list.constants.js +1 -7
- package/dist/components/listitem/listitem.component.d.ts +2 -1
- package/dist/components/listitem/listitem.component.js +2 -1
- package/dist/components/listitem/listitem.styles.js +4 -3
- package/dist/components/option/index.d.ts +2 -0
- package/dist/components/option/index.js +2 -0
- package/dist/components/option/option.component.d.ts +17 -0
- package/dist/components/option/option.component.js +64 -9
- package/dist/components/option/option.constants.d.ts +2 -1
- package/dist/components/option/option.constants.js +2 -1
- package/dist/components/option/option.styles.js +22 -0
- package/dist/components/option/option.types.d.ts +7 -0
- package/dist/components/option/option.types.js +1 -0
- package/dist/components/popover/popover.component.d.ts +2 -0
- package/dist/components/popover/popover.component.js +2 -0
- package/dist/components/popover/popover.styles.js +4 -0
- package/dist/components/select/index.d.ts +10 -0
- package/dist/components/select/index.js +7 -0
- package/dist/components/select/select.component.d.ts +191 -0
- package/dist/components/select/select.component.js +544 -0
- package/dist/components/select/select.constants.d.ts +6 -0
- package/dist/components/select/select.constants.js +7 -0
- package/dist/components/select/select.styles.d.ts +2 -0
- package/dist/components/select/select.styles.js +103 -0
- package/dist/components/select/select.types.d.ts +7 -0
- package/dist/components/select/select.types.js +1 -0
- package/dist/custom-elements.json +7279 -6307
- package/dist/index.d.ts +14 -13
- package/dist/index.js +11 -10
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +3 -2
- package/dist/react/listitem/index.d.ts +2 -1
- package/dist/react/listitem/index.js +2 -1
- package/dist/react/option/index.d.ts +4 -0
- package/dist/react/option/index.js +4 -0
- package/dist/react/popover/index.d.ts +2 -0
- package/dist/react/popover/index.js +2 -0
- package/dist/react/select/index.d.ts +28 -0
- package/dist/react/select/index.js +36 -0
- package/dist/utils/keys.d.ts +12 -0
- package/dist/utils/keys.js +12 -0
- package/dist/utils/styles/index.js +3 -5
- package/package.json +1 -1
@@ -0,0 +1,544 @@
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
|
+
};
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
9
|
+
};
|
10
|
+
import { html, nothing } from 'lit';
|
11
|
+
import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
|
12
|
+
import { KEYS } from '../../utils/keys';
|
13
|
+
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
|
14
|
+
import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
|
15
|
+
import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
|
16
|
+
import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
|
17
|
+
import { TAG_NAME as OPTION_GROUP_TAG_NAME } from '../optgroup/optgroup.constants';
|
18
|
+
import { TAG_NAME as OPTION_TAG_NAME } from '../option/option.constants';
|
19
|
+
import { POPOVER_PLACEMENT } from '../popover/popover.constants';
|
20
|
+
import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
|
21
|
+
import { ARROW_ICON } from './select.constants';
|
22
|
+
import styles from './select.styles';
|
23
|
+
/**
|
24
|
+
* The mdc-select component is a dropdown selection control that allows users to pick an option from a predefined list.
|
25
|
+
* It is designed to work with `mdc-option` for individual options and `mdc-optgroup` for grouping related options.
|
26
|
+
* The component ensures accessibility and usability while handling various use cases,
|
27
|
+
* including long text truncation with tooltip support.
|
28
|
+
*
|
29
|
+
* @dependency mdc-icon
|
30
|
+
* @dependency mdc-popover
|
31
|
+
* @dependency mdc-text
|
32
|
+
*
|
33
|
+
* @tagname mdc-select
|
34
|
+
*
|
35
|
+
* @slot default - This is a default/unnamed slot for options and/or option group.
|
36
|
+
*
|
37
|
+
* @event click - (React: onClick) This event is dispatched when the select is clicked.
|
38
|
+
* @event change - (React: onChange) This event is dispatched when the select is changed.
|
39
|
+
* @event keydown - (React: onKeyDown) This event is dispatched when a key is pressed down on the select.
|
40
|
+
* @event focus - (React: onFocus) This event is dispatched when the select receives focus.
|
41
|
+
*/
|
42
|
+
class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
|
43
|
+
constructor() {
|
44
|
+
super(...arguments);
|
45
|
+
/**
|
46
|
+
* readonly attribute of the select field. If true, the select is read-only.
|
47
|
+
* @default false
|
48
|
+
*/
|
49
|
+
this.readonly = false;
|
50
|
+
/**
|
51
|
+
* height attribute of the select field. If set,
|
52
|
+
* then a scroll bar will be visible when there more options than the adjusted height.
|
53
|
+
* @default 'auto'
|
54
|
+
*/
|
55
|
+
this.height = 'auto';
|
56
|
+
/** @internal */
|
57
|
+
this.baseIconName = ARROW_ICON.ARROW_DOWN;
|
58
|
+
/** @internal */
|
59
|
+
this.selectedValue = '';
|
60
|
+
/** @internal */
|
61
|
+
this.showPopover = false;
|
62
|
+
/** @internal */
|
63
|
+
this.activeDescendant = '';
|
64
|
+
}
|
65
|
+
connectedCallback() {
|
66
|
+
super.connectedCallback();
|
67
|
+
// select will only contain name and value will be defined in the options.
|
68
|
+
this.value = undefined;
|
69
|
+
this.addEventListener('keydown', this.handleKeydown);
|
70
|
+
}
|
71
|
+
disconnectedCallback() {
|
72
|
+
super.disconnectedCallback();
|
73
|
+
this.removeEventListener('keydown', this.handleKeydown);
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* A helper function which returns a flattened array of all valid options from the assigned slot.
|
77
|
+
* It takes care of the edge cases where the option is either a direct child or a
|
78
|
+
* child of an option group.
|
79
|
+
*/
|
80
|
+
getAllValidOptions() {
|
81
|
+
var _a;
|
82
|
+
return ((_a = this.optionsList) === null || _a === void 0 ? void 0 : _a.map((option) => {
|
83
|
+
if (option.tagName.toLowerCase() === OPTION_TAG_NAME) {
|
84
|
+
return option;
|
85
|
+
}
|
86
|
+
if (option.tagName.toLowerCase() === OPTION_GROUP_TAG_NAME) {
|
87
|
+
return Array.from(option.children).filter((optgroup) => optgroup.tagName.toLowerCase() === OPTION_TAG_NAME);
|
88
|
+
}
|
89
|
+
return [];
|
90
|
+
}).flat()) || [];
|
91
|
+
}
|
92
|
+
handlePopoverOpen() {
|
93
|
+
this.baseIconName = ARROW_ICON.ARROW_UP;
|
94
|
+
}
|
95
|
+
handlePopoverClose() {
|
96
|
+
this.baseIconName = ARROW_ICON.ARROW_DOWN;
|
97
|
+
}
|
98
|
+
/**
|
99
|
+
* Updates the tabindex and selected attribute of the options.
|
100
|
+
* If selectedOption is provided, it will be set as the selected option.
|
101
|
+
* Otherwise, it will set the first option as the selected option.
|
102
|
+
* @param selectedOption - The option which should be selected.
|
103
|
+
*/
|
104
|
+
updateTabIndexForAllOptions(selectedOption) {
|
105
|
+
var _a;
|
106
|
+
let isTabIndexSet = false;
|
107
|
+
this.getAllValidOptions().forEach((option) => {
|
108
|
+
if (option === selectedOption) {
|
109
|
+
this.setSelectedValue(option);
|
110
|
+
isTabIndexSet = true;
|
111
|
+
option.setAttribute('selected', '');
|
112
|
+
option.setAttribute('tabindex', '0');
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
option === null || option === void 0 ? void 0 : option.setAttribute('tabindex', '-1');
|
116
|
+
option === null || option === void 0 ? void 0 : option.removeAttribute('selected');
|
117
|
+
}
|
118
|
+
});
|
119
|
+
if (isTabIndexSet) {
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
// if no option is selected, set the first option as focused
|
123
|
+
(_a = this.getAllValidOptions()[0]) === null || _a === void 0 ? void 0 : _a.setAttribute('tabindex', '0');
|
124
|
+
}
|
125
|
+
/**
|
126
|
+
* A private method which is called when an option is clicked.
|
127
|
+
* It is used to update the tabindex and selected attribute of the options.
|
128
|
+
* @param event - The event which triggered this function.
|
129
|
+
*/
|
130
|
+
handleOptionsClick(event) {
|
131
|
+
this.updateTabIndexForAllOptions(event.target);
|
132
|
+
}
|
133
|
+
/**
|
134
|
+
* Sets the selected value based on the provided option element.
|
135
|
+
* It retrieves the 'label' attribute of the option, if present,
|
136
|
+
* otherwise it falls back to the option's text content.
|
137
|
+
* @param option - The option element from which to set the selected value.
|
138
|
+
*/
|
139
|
+
setSelectedValue(option) {
|
140
|
+
var _a, _b, _c, _d;
|
141
|
+
this.selectedValueText = (_b = (_a = option === null || option === void 0 ? void 0 : option.getAttribute('label')) !== null && _a !== void 0 ? _a : option === null || option === void 0 ? void 0 : option.textContent) !== null && _b !== void 0 ? _b : '';
|
142
|
+
this.selectedValue = (_d = (_c = option === null || option === void 0 ? void 0 : option.getAttribute('value')) !== null && _c !== void 0 ? _c : option === null || option === void 0 ? void 0 : option.textContent) !== null && _d !== void 0 ? _d : '';
|
143
|
+
// Set form value
|
144
|
+
this.internals.setFormValue(this.selectedValue);
|
145
|
+
this.manageRequired();
|
146
|
+
// dispatch a change event when a value is selected
|
147
|
+
this.dispatchChange(this.selectedValue);
|
148
|
+
}
|
149
|
+
/**
|
150
|
+
* Manages the required state of the select.
|
151
|
+
* If the value is not set and the requiredLabel property is set,
|
152
|
+
* then the select is invalid.
|
153
|
+
*/
|
154
|
+
manageRequired() {
|
155
|
+
if (!this.selectedValue && this.requiredLabel) {
|
156
|
+
if (this.validationMessage) {
|
157
|
+
this.inputElement.setCustomValidity(this.validationMessage);
|
158
|
+
}
|
159
|
+
else {
|
160
|
+
this.inputElement.setCustomValidity('');
|
161
|
+
}
|
162
|
+
this.setValidity();
|
163
|
+
}
|
164
|
+
else {
|
165
|
+
this.internals.setValidity({});
|
166
|
+
}
|
167
|
+
}
|
168
|
+
/**
|
169
|
+
* @internal
|
170
|
+
* Resets the select to its initial state.
|
171
|
+
*/
|
172
|
+
formResetCallback() {
|
173
|
+
this.selectedValue = '';
|
174
|
+
this.selectedValueText = undefined;
|
175
|
+
this.internals.setFormValue(this.selectedValue);
|
176
|
+
this.updateTabIndexForAllOptions();
|
177
|
+
}
|
178
|
+
/** @internal */
|
179
|
+
formStateRestoreCallback(state) {
|
180
|
+
this.selectedValue = state;
|
181
|
+
this.selectedValueText = state;
|
182
|
+
}
|
183
|
+
dispatchChange(value) {
|
184
|
+
if (!value) {
|
185
|
+
return;
|
186
|
+
}
|
187
|
+
this.dispatchEvent(new CustomEvent('change', {
|
188
|
+
detail: { value },
|
189
|
+
composed: true,
|
190
|
+
bubbles: true,
|
191
|
+
}));
|
192
|
+
}
|
193
|
+
/**
|
194
|
+
* Handles the keydown event on the select element.
|
195
|
+
* If the popover is open, then it calls `handlePopoverOnOpen` with the event.
|
196
|
+
* If the popover is closed, then it calls `handlePopoverOnClose` with the event.
|
197
|
+
* @param event - The keyboard event.
|
198
|
+
*/
|
199
|
+
handleKeydown(event) {
|
200
|
+
if (this.showPopover) {
|
201
|
+
this.handlePopoverOnOpen(event);
|
202
|
+
}
|
203
|
+
else {
|
204
|
+
this.handlePopoverOnClose(event);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
/**
|
208
|
+
* Handles the keydown event on the select element when the popover is open.
|
209
|
+
* The options are as follows:
|
210
|
+
* - SPACE or ENTER: Selects the currently active option and closes the popover.
|
211
|
+
* - ESCAPE: Closes the popover.
|
212
|
+
* - HOME: Sets focus and tabindex on the first option.
|
213
|
+
* - END: Sets focus and tabindex on the last option.
|
214
|
+
* - ARROW_DOWN, ARROW_UP, PAGE_DOWN, PAGE_UP: Handles navigation between options.
|
215
|
+
* @param event - The keyboard event.
|
216
|
+
*/
|
217
|
+
handlePopoverOnOpen(event) {
|
218
|
+
var _a;
|
219
|
+
switch (event.key) {
|
220
|
+
case KEYS.SPACE:
|
221
|
+
this.updateTabIndexForAllOptions(event.target);
|
222
|
+
this.closePopover();
|
223
|
+
break;
|
224
|
+
case KEYS.ENTER:
|
225
|
+
this.updateTabIndexForAllOptions(event.target);
|
226
|
+
this.closePopover();
|
227
|
+
// if the popover is closed, then we submit the form.
|
228
|
+
(_a = this.form) === null || _a === void 0 ? void 0 : _a.requestSubmit();
|
229
|
+
break;
|
230
|
+
case KEYS.ESCAPE:
|
231
|
+
this.closePopover();
|
232
|
+
break;
|
233
|
+
case KEYS.HOME:
|
234
|
+
this.setFocusAndTabIndex(0);
|
235
|
+
break;
|
236
|
+
case KEYS.END:
|
237
|
+
this.setFocusAndTabIndex(this.getAllValidOptions().length - 1);
|
238
|
+
break;
|
239
|
+
case KEYS.ARROW_DOWN:
|
240
|
+
case KEYS.ARROW_UP:
|
241
|
+
case KEYS.PAGE_DOWN:
|
242
|
+
case KEYS.PAGE_UP:
|
243
|
+
this.handleOptionsNavigation(event);
|
244
|
+
this.updateActivedescendant(event.target);
|
245
|
+
break;
|
246
|
+
default:
|
247
|
+
break;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
/**
|
251
|
+
* Handles the keydown event on the select element when the popover is closed.
|
252
|
+
* The options are as follows:
|
253
|
+
* - ARROW_DOWN, ARROW_UP, SPACE: Opens the popover and prevents the default scrolling behavior.
|
254
|
+
* - ENTER: Opens the popover, prevents default scrolling, and submits the form if the popover is closed.
|
255
|
+
* - HOME: Opens the popover and sets focus and tabindex on the first option.
|
256
|
+
* - END: Opens the popover and sets focus and tabindex on the last option.
|
257
|
+
* @param event - The keyboard event.
|
258
|
+
*/
|
259
|
+
handlePopoverOnClose(event) {
|
260
|
+
switch (event.key) {
|
261
|
+
case KEYS.ARROW_DOWN:
|
262
|
+
case KEYS.ARROW_UP:
|
263
|
+
case KEYS.ENTER:
|
264
|
+
case KEYS.SPACE:
|
265
|
+
this.openPopover();
|
266
|
+
// Prevent the default browser behavior of scrolling down
|
267
|
+
event.preventDefault();
|
268
|
+
break;
|
269
|
+
case KEYS.HOME:
|
270
|
+
this.openPopover();
|
271
|
+
this.setFocusAndTabIndex(0);
|
272
|
+
break;
|
273
|
+
case KEYS.END:
|
274
|
+
this.openPopover();
|
275
|
+
this.setFocusAndTabIndex(this.getAllValidOptions().length - 1);
|
276
|
+
break;
|
277
|
+
default:
|
278
|
+
break;
|
279
|
+
}
|
280
|
+
}
|
281
|
+
/**
|
282
|
+
* Handles the navigation of options when the user presses
|
283
|
+
* ArrowUp, ArrowDown, PageUp, or PageDown keys.
|
284
|
+
* @param event - The keyboard event that triggered the navigation.
|
285
|
+
*/
|
286
|
+
handleOptionsNavigation(event) {
|
287
|
+
const options = this.getAllValidOptions();
|
288
|
+
const currentIndex = options.findIndex((option) => option === event.target);
|
289
|
+
const newIndex = this.getNewIndexBasedOnKey(event.key, currentIndex, options.length);
|
290
|
+
if (newIndex !== -1) {
|
291
|
+
this.setFocusAndTabIndex(newIndex);
|
292
|
+
// Prevent the default browser behavior of scrolling down
|
293
|
+
// when pressing ArrowUp, ArrowDown, PageUp, or PageDown keys
|
294
|
+
event.preventDefault();
|
295
|
+
}
|
296
|
+
}
|
297
|
+
/**
|
298
|
+
* Calculates the new index based on the pressed navigation key.
|
299
|
+
* Supports ArrowUp, ArrowDown, PageUp, and PageDown keys for navigating options.
|
300
|
+
* - ArrowDown: Moves focus to the next option, if available.
|
301
|
+
* - ArrowUp: Moves focus to the previous option, if available.
|
302
|
+
* - PageDown: Moves focus 10 options down or to the last option.
|
303
|
+
* - PageUp: Moves focus 10 options up or to the first option.
|
304
|
+
*
|
305
|
+
* @param key - The navigation key that was pressed.
|
306
|
+
* @param currentIndex - The current index of the focused option.
|
307
|
+
* @param optionsLength - The total number of options.
|
308
|
+
* @returns The new index to focus on, or -1 if no movement is possible.
|
309
|
+
*/
|
310
|
+
getNewIndexBasedOnKey(key, currentIndex, optionsLength) {
|
311
|
+
if (key === KEYS.ARROW_DOWN && currentIndex !== optionsLength - 1) {
|
312
|
+
return currentIndex + 1;
|
313
|
+
}
|
314
|
+
if (key === KEYS.ARROW_UP && currentIndex > 0) {
|
315
|
+
return currentIndex - 1;
|
316
|
+
}
|
317
|
+
if (key === KEYS.PAGE_DOWN) {
|
318
|
+
// Jumps visual focus down 10 options (or to last option).
|
319
|
+
return (currentIndex + 10) > optionsLength ? optionsLength - 1 : currentIndex + 10;
|
320
|
+
}
|
321
|
+
if (key === KEYS.PAGE_UP) {
|
322
|
+
// Jumps visual focus up 10 options (or to first option).
|
323
|
+
return (currentIndex - 10) < 0 ? 0 : currentIndex - 10;
|
324
|
+
}
|
325
|
+
return -1;
|
326
|
+
}
|
327
|
+
updateActivedescendant(target) {
|
328
|
+
var _a, _b;
|
329
|
+
const currentIndex = this.getAllValidOptions().findIndex((option) => option === target);
|
330
|
+
this.activeDescendant = (_b = (_a = this.getAllValidOptions()[currentIndex]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
|
331
|
+
}
|
332
|
+
resetActivedescendant() {
|
333
|
+
this.activeDescendant = '';
|
334
|
+
}
|
335
|
+
setFocusAndTabIndex(newIndex) {
|
336
|
+
var _a;
|
337
|
+
(_a = this.getAllValidOptions()[newIndex]) === null || _a === void 0 ? void 0 : _a.focus();
|
338
|
+
this.getAllValidOptions().forEach((node, index) => {
|
339
|
+
const newTabindex = newIndex === index ? '0' : '-1';
|
340
|
+
node === null || node === void 0 ? void 0 : node.setAttribute('tabindex', newTabindex);
|
341
|
+
});
|
342
|
+
}
|
343
|
+
openPopover() {
|
344
|
+
this.showPopover = true;
|
345
|
+
this.resetActivedescendant();
|
346
|
+
}
|
347
|
+
closePopover() {
|
348
|
+
this.showPopover = false;
|
349
|
+
this.resetActivedescendant();
|
350
|
+
}
|
351
|
+
/**
|
352
|
+
* Handles the first updated lifecycle event.
|
353
|
+
* If an option is selected, use that as the value.
|
354
|
+
* If not, use the placeholder if it exists, otherwise use the first option.
|
355
|
+
*/
|
356
|
+
firstUpdated() {
|
357
|
+
const options = this.getAllValidOptions();
|
358
|
+
const selectedOptionIndex = options.findIndex((option) => option === null || option === void 0 ? void 0 : option.hasAttribute('selected'));
|
359
|
+
if (selectedOptionIndex !== -1) {
|
360
|
+
this.setSelectedValue(options[selectedOptionIndex]);
|
361
|
+
this.updateTabIndexForAllOptions(options[selectedOptionIndex]);
|
362
|
+
}
|
363
|
+
else if (!this.placeholder) {
|
364
|
+
// We will show the first option as selected.
|
365
|
+
this.setSelectedValue(options[0]);
|
366
|
+
this.updateTabIndexForAllOptions();
|
367
|
+
}
|
368
|
+
else if (this.placeholder) {
|
369
|
+
// If there is no default selected option
|
370
|
+
// then we set the placeholder and call the native validity
|
371
|
+
this.manageRequired();
|
372
|
+
}
|
373
|
+
}
|
374
|
+
/**
|
375
|
+
* Generates the native select element.
|
376
|
+
* The native select element is not rendered directly and is not visible on the UI.
|
377
|
+
* It's rendered only on the DOM for accessibility purposes.
|
378
|
+
* Instead, the overlay uses the native select element to generate the list of options.
|
379
|
+
* @returns A TemplateResult representing the native select element.
|
380
|
+
*/
|
381
|
+
getNativeSelect() {
|
382
|
+
var _a;
|
383
|
+
return html `
|
384
|
+
<select
|
385
|
+
part="native-select"
|
386
|
+
id="${this.id}"
|
387
|
+
tabindex="-1"
|
388
|
+
name="${this.name}"
|
389
|
+
size="1"
|
390
|
+
.value="${this.selectedValue}"
|
391
|
+
?autofocus="${this.autofocus}"
|
392
|
+
?disabled="${this.disabled}"
|
393
|
+
?required="${!!this.requiredLabel}"
|
394
|
+
aria-activedescendant="${this.activeDescendant}"
|
395
|
+
aria-expanded="${this.showPopover}"
|
396
|
+
aria-haspopup="listbox"
|
397
|
+
aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
|
398
|
+
aria-labelledby="${this.label ? FORMFIELD_DEFAULTS.HEADING_ID : ''}"
|
399
|
+
@mousedown="${(event) => event.preventDefault()}"
|
400
|
+
>
|
401
|
+
${this.getOptionsContentFromSlot()}
|
402
|
+
</select>
|
403
|
+
`;
|
404
|
+
}
|
405
|
+
/**
|
406
|
+
* This method maps over all valid options and constructs their corresponding
|
407
|
+
* HTML `<option>` elements. The attributes such as `value`, `label`, `disabled`,
|
408
|
+
* and `selected` are extracted from the respective option elements.
|
409
|
+
* If the attribute is not present, a default value or fallback is used.
|
410
|
+
* The content of each `<option>` is set to the text content of the option element.
|
411
|
+
* @returns An array of `TemplateResult` representing the option elements.
|
412
|
+
*/
|
413
|
+
getOptionsContentFromSlot() {
|
414
|
+
return this.getAllValidOptions()
|
415
|
+
.map((option) => {
|
416
|
+
var _a, _b;
|
417
|
+
return html `
|
418
|
+
<option
|
419
|
+
part="native-select"
|
420
|
+
value="${(_a = option.getAttribute('value')) !== null && _a !== void 0 ? _a : ''}"
|
421
|
+
label="${(_b = option.getAttribute('label')) !== null && _b !== void 0 ? _b : ''}"
|
422
|
+
?disabled="${!!option.hasAttribute('disabled')}"
|
423
|
+
?selected="${!!option.hasAttribute('selected')}"
|
424
|
+
>
|
425
|
+
${option.textContent}
|
426
|
+
</option>
|
427
|
+
`;
|
428
|
+
});
|
429
|
+
}
|
430
|
+
/**
|
431
|
+
* Generates the content for the popover associated with the select component.
|
432
|
+
* If the component is disabled or readonly, returns `nothing`.
|
433
|
+
* Otherwise, returns a `TemplateResult` that renders a popover with various configurations
|
434
|
+
* such as visibility, interaction, and event handlers.
|
435
|
+
* The popover acts as a dropdown list with options, allowing user interaction.
|
436
|
+
*/
|
437
|
+
getPopoverContent() {
|
438
|
+
if (this.disabled || this.readonly) {
|
439
|
+
return nothing;
|
440
|
+
}
|
441
|
+
return html `
|
442
|
+
<mdc-popover
|
443
|
+
id="options-popover"
|
444
|
+
triggerid="select-base-triggerid"
|
445
|
+
interactive
|
446
|
+
?visible="${this.showPopover}"
|
447
|
+
hide-on-outside-click
|
448
|
+
focus-back-to-trigger
|
449
|
+
focus-trap
|
450
|
+
prevent-scroll
|
451
|
+
role="listbox"
|
452
|
+
placement="${POPOVER_PLACEMENT.BOTTOM_START}"
|
453
|
+
@shown="${this.handlePopoverOpen}"
|
454
|
+
@hidden="${this.handlePopoverClose}"
|
455
|
+
style="--mdc-popover-max-width: 100%; --mdc-popover-max-height: ${this.height};"
|
456
|
+
>
|
457
|
+
<slot @click="${this.handleOptionsClick}"></slot>
|
458
|
+
</mdc-popover>
|
459
|
+
`;
|
460
|
+
}
|
461
|
+
shouldFocusSelect() {
|
462
|
+
if (this.disabled || this.readonly) {
|
463
|
+
return false;
|
464
|
+
}
|
465
|
+
return true;
|
466
|
+
}
|
467
|
+
updated(changedProperties) {
|
468
|
+
super.updated(changedProperties);
|
469
|
+
if (changedProperties.has('disabled') || changedProperties.has('readonly')) {
|
470
|
+
this.closePopover();
|
471
|
+
this.handlePopoverClose();
|
472
|
+
}
|
473
|
+
}
|
474
|
+
render() {
|
475
|
+
var _a;
|
476
|
+
return html `
|
477
|
+
${this.renderLabel()}
|
478
|
+
<div part="container">
|
479
|
+
<div
|
480
|
+
id="select-base-triggerid"
|
481
|
+
part="base-container"
|
482
|
+
tabindex="${this.shouldFocusSelect() ? '0' : '-1'}"
|
483
|
+
class="${this.shouldFocusSelect() ? 'mdc-focus-ring' : ''}"
|
484
|
+
>
|
485
|
+
<mdc-text
|
486
|
+
part="base-text ${this.selectedValueText ? 'selected' : ''}"
|
487
|
+
type="${TYPE.BODY_MIDSIZE_REGULAR}"
|
488
|
+
tagname="${VALID_TEXT_TAGS.SPAN}"
|
489
|
+
>
|
490
|
+
${(_a = this.selectedValueText) !== null && _a !== void 0 ? _a : this.placeholder}
|
491
|
+
</mdc-text>
|
492
|
+
<div part="icon-container">
|
493
|
+
<mdc-icon size="1" length-unit="rem" name="${this.baseIconName}"></mdc-icon>
|
494
|
+
</div>
|
495
|
+
</div>
|
496
|
+
${this.getNativeSelect()}
|
497
|
+
${this.getPopoverContent()}
|
498
|
+
</div>
|
499
|
+
${this.renderHelperText()}
|
500
|
+
`;
|
501
|
+
}
|
502
|
+
}
|
503
|
+
Select.styles = [...FormfieldWrapper.styles, ...styles];
|
504
|
+
__decorate([
|
505
|
+
property({ type: String }),
|
506
|
+
__metadata("design:type", String)
|
507
|
+
], Select.prototype, "placeholder", void 0);
|
508
|
+
__decorate([
|
509
|
+
property({ type: Boolean }),
|
510
|
+
__metadata("design:type", Object)
|
511
|
+
], Select.prototype, "readonly", void 0);
|
512
|
+
__decorate([
|
513
|
+
property({ type: String, attribute: 'height' }),
|
514
|
+
__metadata("design:type", Object)
|
515
|
+
], Select.prototype, "height", void 0);
|
516
|
+
__decorate([
|
517
|
+
queryAssignedElements(),
|
518
|
+
__metadata("design:type", Array)
|
519
|
+
], Select.prototype, "optionsList", void 0);
|
520
|
+
__decorate([
|
521
|
+
state(),
|
522
|
+
__metadata("design:type", String)
|
523
|
+
], Select.prototype, "baseIconName", void 0);
|
524
|
+
__decorate([
|
525
|
+
state(),
|
526
|
+
__metadata("design:type", String)
|
527
|
+
], Select.prototype, "selectedValueText", void 0);
|
528
|
+
__decorate([
|
529
|
+
state(),
|
530
|
+
__metadata("design:type", Object)
|
531
|
+
], Select.prototype, "selectedValue", void 0);
|
532
|
+
__decorate([
|
533
|
+
state(),
|
534
|
+
__metadata("design:type", Object)
|
535
|
+
], Select.prototype, "showPopover", void 0);
|
536
|
+
__decorate([
|
537
|
+
state(),
|
538
|
+
__metadata("design:type", Object)
|
539
|
+
], Select.prototype, "activeDescendant", void 0);
|
540
|
+
__decorate([
|
541
|
+
query('select'),
|
542
|
+
__metadata("design:type", HTMLInputElement)
|
543
|
+
], Select.prototype, "inputElement", void 0);
|
544
|
+
export default Select;
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import { css } from 'lit';
|
2
|
+
import { hostFocusRingStyles } from '../../utils/styles';
|
3
|
+
const styles = css `
|
4
|
+
:host {
|
5
|
+
--mdc-select-background-color: var(--mds-color-theme-background-primary-ghost);
|
6
|
+
--mdc-select-icon-border-color: var(--mds-color-theme-outline-input-normal);
|
7
|
+
--mdc-select-base-text-color: var(--mds-color-theme-text-secondary-normal);
|
8
|
+
--mdc-select-selected-text-color: var(--mds-color-theme-text-primary-normal);
|
9
|
+
--mdc-select-disabled-color: var(--mds-color-theme-outline-primary-disabled);
|
10
|
+
--mdc-select-disabled-text-color: var(--mds-color-theme-text-primary-disabled);
|
11
|
+
--mdc-select-error-border-color: var(--mds-color-theme-text-error-normal);
|
12
|
+
--mdc-select-warning-border-color: var(--mds-color-theme-text-warning-normal);
|
13
|
+
--mdc-select-success-border-color: var(--mds-color-theme-text-success-normal);
|
14
|
+
--mdc-select-background-hover: var(--mds-color-theme-background-primary-hover);
|
15
|
+
--mdc-select-background-active: var(--mds-color-theme-background-primary-active);
|
16
|
+
|
17
|
+
display: flex;
|
18
|
+
flex-direction: column;
|
19
|
+
row-gap: 0.5rem;
|
20
|
+
align-items: unset;
|
21
|
+
width: unset;
|
22
|
+
}
|
23
|
+
:host::part(native-select) {
|
24
|
+
margin: 0;
|
25
|
+
opacity: 0.1%;
|
26
|
+
overflow: visible;
|
27
|
+
padding: 0;
|
28
|
+
position: absolute;
|
29
|
+
width: 100%;
|
30
|
+
height: 1px;
|
31
|
+
z-index: 1;
|
32
|
+
}
|
33
|
+
:host::part(container) {
|
34
|
+
width: 100%;
|
35
|
+
position: relative;
|
36
|
+
}
|
37
|
+
:host::part(base-container) {
|
38
|
+
border-radius: 0.5rem;
|
39
|
+
padding: 5.5px 6px 5.5px 12px;
|
40
|
+
border: 1px solid var(--mdc-select-icon-border-color);
|
41
|
+
background: var(--mdc-select-background-color);
|
42
|
+
display: flex;
|
43
|
+
gap: 0.375rem;
|
44
|
+
}
|
45
|
+
:host::part(base-container):hover {
|
46
|
+
background-color: var(--mdc-select-background-hover);
|
47
|
+
}
|
48
|
+
:host::part(base-container):active {
|
49
|
+
background-color: var(--mdc-select-background-active);
|
50
|
+
}
|
51
|
+
:host::part(base-text) {
|
52
|
+
height: 1.3125rem;
|
53
|
+
width: 100%;
|
54
|
+
color: var(--mdc-select-base-text-color);
|
55
|
+
overflow: hidden;
|
56
|
+
text-overflow: ellipsis;
|
57
|
+
white-space: nowrap;
|
58
|
+
}
|
59
|
+
:host::part(selected) {
|
60
|
+
color: var(--mdc-select-selected-text-color);
|
61
|
+
}
|
62
|
+
:host::part(icon-container) {
|
63
|
+
margin-left: auto;
|
64
|
+
display: flex;
|
65
|
+
padding: 2px;
|
66
|
+
}
|
67
|
+
:host([readonly])::part(icon-container) {
|
68
|
+
color: var(--mdc-select-disabled-text-color);
|
69
|
+
}
|
70
|
+
:host::part(popover-content) {
|
71
|
+
min-width: auto;
|
72
|
+
}
|
73
|
+
:host([disabled])::part(base-container),
|
74
|
+
:host([readonly])::part(base-container),
|
75
|
+
:host([help-text-type="success"][disabled])::part(base-container),
|
76
|
+
:host([help-text-type="error"][disabled])::part(base-container),
|
77
|
+
:host([help-text-type="warning"][disabled])::part(base-container),
|
78
|
+
:host([help-text-type="success"][readonly])::part(base-container),
|
79
|
+
:host([help-text-type="error"][readonly])::part(base-container),
|
80
|
+
:host([help-text-type="warning"][readonly])::part(base-container) {
|
81
|
+
border-color: var(--mdc-select-disabled-color);
|
82
|
+
}
|
83
|
+
:host([disabled]:hover)::part(base-container),
|
84
|
+
:host([readonly]:hover)::part(base-container) {
|
85
|
+
background-color: unset;
|
86
|
+
}
|
87
|
+
:host([readonly])::part(base-text) {
|
88
|
+
color: var(--mdc-select-selected-text-color);
|
89
|
+
}
|
90
|
+
:host([disabled])::part(base-text) {
|
91
|
+
color: var(--mdc-select-disabled-text-color);
|
92
|
+
}
|
93
|
+
:host([help-text-type="success"])::part(base-container) {
|
94
|
+
border-color: var(--mdc-select-success-border-color);
|
95
|
+
}
|
96
|
+
:host([help-text-type="error"])::part(base-container) {
|
97
|
+
border-color: var(--mdc-select-error-border-color);
|
98
|
+
}
|
99
|
+
:host([help-text-type="warning"])::part(base-container) {
|
100
|
+
border-color: var(--mdc-select-warning-border-color);
|
101
|
+
}
|
102
|
+
`;
|
103
|
+
export default [styles, ...hostFocusRingStyles(true)];
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|