@sondor-technology/sondor-components 1.0.3 → 1.0.5

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 (42) hide show
  1. package/dist/components/button-group/button-group.d.ts +5 -0
  2. package/dist/components/button-group/button-group.d.ts.map +1 -1
  3. package/dist/components/checkbox/checkbox.d.ts +3 -0
  4. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  5. package/dist/components/checkbox/checkbox.js +38 -20
  6. package/dist/components/code-block/code-block.d.ts +5 -0
  7. package/dist/components/code-block/code-block.d.ts.map +1 -1
  8. package/dist/components/form/form-step.d.ts +1 -0
  9. package/dist/components/form/form-step.d.ts.map +1 -1
  10. package/dist/components/form/form-step.js +3 -0
  11. package/dist/components/form/form.d.ts +7 -2
  12. package/dist/components/form/form.d.ts.map +1 -1
  13. package/dist/components/form/form.js +157 -59
  14. package/dist/components/icon/icon.d.ts +2 -1
  15. package/dist/components/icon/icon.d.ts.map +1 -1
  16. package/dist/components/input/input.d.ts +5 -0
  17. package/dist/components/input/input.d.ts.map +1 -1
  18. package/dist/components/page-layout/page-layout.js +1 -1
  19. package/dist/components/select/select.d.ts +43 -7
  20. package/dist/components/select/select.d.ts.map +1 -1
  21. package/dist/components/select/select.js +458 -150
  22. package/dist/components/tabs/tab-panel.d.ts +5 -0
  23. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  24. package/dist/components/tabs/tab.d.ts +5 -0
  25. package/dist/components/tabs/tab.d.ts.map +1 -1
  26. package/dist/components/tabs/tabs.d.ts +5 -0
  27. package/dist/components/tabs/tabs.d.ts.map +1 -1
  28. package/dist/styles/index.css +2 -0
  29. package/package.json +1 -1
  30. package/src/components/button-group/button-group.ts +6 -0
  31. package/src/components/checkbox/checkbox.ts +54 -26
  32. package/src/components/code-block/code-block.ts +6 -0
  33. package/src/components/form/form-step.ts +5 -1
  34. package/src/components/form/form.ts +182 -66
  35. package/src/components/icon/icon.ts +3 -1
  36. package/src/components/input/input.ts +6 -0
  37. package/src/components/page-layout/page-layout.ts +1 -1
  38. package/src/components/select/select.ts +502 -130
  39. package/src/components/tabs/tab-panel.ts +5 -0
  40. package/src/components/tabs/tab.ts +6 -0
  41. package/src/components/tabs/tabs.ts +6 -0
  42. package/src/styles/theme.css +2 -0
@@ -1,162 +1,534 @@
1
- import { LitElement, html, css } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { resetStyles } from '../../styles/reset.js';
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property, state, query} from "lit/decorators.js";
3
+ import {classMap} from "lit/directives/class-map.js";
4
4
 
5
- @customElement('st-select')
5
+ export interface SelectOption {
6
+ label: string;
7
+ value: string;
8
+ }
9
+
10
+ /**
11
+ * Standard WHATWG Autocomplete tokens.
12
+ */
13
+ export type SelectAutocomplete =
14
+ | "on"
15
+ | "off"
16
+ // Names
17
+ | "name"
18
+ | "honorific-prefix"
19
+ | "given-name"
20
+ | "additional-name"
21
+ | "family-name"
22
+ | "honorific-suffix"
23
+ | "nickname"
24
+ // Authentication & Identity
25
+ | "email"
26
+ | "username"
27
+ | "new-password"
28
+ | "current-password"
29
+ | "one-time-code"
30
+ | "organization"
31
+ | "organization-title"
32
+ | "url"
33
+ | "photo"
34
+ | "webauthn"
35
+ // Addresses
36
+ | "street-address"
37
+ | "address-line1"
38
+ | "address-line2"
39
+ | "address-line3"
40
+ | "address-level1" // State / Province
41
+ | "address-level2" // City
42
+ | "address-level3"
43
+ | "address-level4"
44
+ | "country"
45
+ | "country-name"
46
+ | "postal-code"
47
+ // Payment
48
+ | "cc-name"
49
+ | "cc-given-name"
50
+ | "cc-additional-name"
51
+ | "cc-family-name"
52
+ | "cc-number"
53
+ | "cc-exp"
54
+ | "cc-exp-month"
55
+ | "cc-exp-year"
56
+ | "cc-csc"
57
+ | "cc-type"
58
+ | "transaction-currency"
59
+ | "transaction-amount"
60
+ // Personal
61
+ | "language"
62
+ | "bday"
63
+ | "bday-day"
64
+ | "bday-month"
65
+ | "bday-year"
66
+ | "sex"
67
+ // Telephone & Messaging
68
+ | "tel"
69
+ | "tel-country-code"
70
+ | "tel-national"
71
+ | "tel-area-code"
72
+ | "tel-local"
73
+ | "tel-local-prefix"
74
+ | "tel-local-suffix"
75
+ | "tel-extension"
76
+ | "impp"
77
+ // Preserves IDE autocomplete while allowing space-separated modifiers (e.g., "shipping postal-code")
78
+ | (string & {});
79
+
80
+ @customElement("st-select")
6
81
  export class StSelect extends LitElement {
7
- @property({ type: String, reflect: true }) size: 'sm' | 'md' | 'lg' = 'md';
8
- @property({ type: String, reflect: true }) variant: 'outline' | 'filled' = 'outline';
9
- @property({ type: String, reflect: true }) color: 'default' | 'primary' | 'error' = 'default';
10
-
11
- // Internal state to handle the chevron animation when the slotted select is focused
12
- @property({ type: Boolean, reflect: true }) focused = false;
82
+ static formAssociated = true;
83
+ private internals: ElementInternals;
84
+
85
+ @property({type: String}) name = "";
86
+ @property({type: String}) label = "";
87
+ @property({type: String}) value = "";
88
+ @property({type: Boolean}) required = false;
89
+ @property({type: Boolean}) multiple = false;
90
+ @property({type: Boolean}) searchable = false;
91
+ @property({type: Array}) options: SelectOption[] = [];
92
+ @property({type: String}) error = "";
93
+
94
+ @property({type: Boolean, reflect: true}) disabled = false;
95
+ @property({type: Boolean, reflect: true}) invalid = false;
96
+ @property({type: String}) autocomplete: SelectAutocomplete = "off";
97
+
98
+ @state() private open = false;
99
+ @state() private filterText = "";
100
+ @state() private nativeError = "";
101
+ @state() private touched = false;
102
+
103
+ @query(".combobox") private buttonElement!: HTMLElement;
104
+ @query(".search-input") private searchInputEl?: HTMLInputElement;
105
+
106
+ static styles = css`
107
+ :host {
108
+ display: block;
109
+ font-family: inherit;
110
+ position: relative;
111
+ }
112
+
113
+ label {
114
+ display: block;
115
+ font-size: var(--st-font-size-sm);
116
+ font-weight: 500;
117
+ color: var(--st-color-text-muted);
118
+ margin-bottom: var(--st-spacing-xs);
119
+ }
120
+
121
+ .combobox {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ align-items: center;
125
+ width: 100%;
126
+ min-height: 42px;
127
+ padding: var(--st-spacing-xs) var(--st-spacing-md);
128
+ background: var(--st-color-surface);
129
+ border: 1px solid var(--st-color-border);
130
+ border-radius: var(--st-radius-md);
131
+ font-size: var(--st-font-size-base);
132
+ color: var(--st-color-text);
133
+ cursor: pointer;
134
+ text-align: left;
135
+ box-sizing: border-box;
136
+ transition:
137
+ border-color 0.2s,
138
+ box-shadow 0.2s,
139
+ background-color 0.2s;
140
+ }
141
+
142
+ .combobox:disabled {
143
+ background: var(--st-color-surface-muted);
144
+ cursor: not-allowed;
145
+ opacity: 0.7;
146
+ }
147
+
148
+ .combobox.is-invalid {
149
+ border-color: var(--st-color-danger, #dc2626);
150
+ }
151
+
152
+ .combobox:focus-visible {
153
+ outline: none;
154
+ border-color: var(--st-color-primary);
155
+ box-shadow: 0 0 0 2px var(--st-color-primary-muted);
156
+ }
157
+
158
+ .combobox.is-placeholder {
159
+ color: var(--st-color-text-muted);
160
+ }
161
+
162
+ .selection-container {
163
+ display: flex;
164
+ flex-wrap: wrap;
165
+ gap: 4px;
166
+ align-items: center;
167
+ flex: 1;
168
+ overflow: hidden;
169
+ }
170
+
171
+ .chip {
172
+ display: inline-flex;
173
+ align-items: center;
174
+ background: var(--st-color-surface-muted);
175
+ border: 1px solid var(--st-color-border);
176
+ border-radius: var(--st-radius-sm);
177
+ padding: 2px 6px;
178
+ font-size: var(--st-font-size-xs);
179
+ color: var(--st-color-text);
180
+ }
181
+
182
+ .listbox-container {
183
+ position: absolute;
184
+ top: calc(100% + 4px);
185
+ left: 0;
186
+ right: 0;
187
+ z-index: 1;
188
+ background: var(--st-color-surface);
189
+ border: 1px solid var(--st-color-border);
190
+ border-radius: var(--st-radius-md);
191
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
192
+ overflow: hidden;
193
+ }
194
+
195
+ .search-wrapper {
196
+ padding: var(--st-spacing-xs);
197
+ border-bottom: 1px solid var(--st-color-border);
198
+ }
199
+
200
+ .search-input {
201
+ width: 100%;
202
+ padding: var(--st-spacing-xs) var(--st-spacing-sm);
203
+ background: var(--st-color-surface-muted);
204
+ border: 1px solid var(--st-color-border);
205
+ border-radius: var(--st-radius-sm);
206
+ font-size: var(--st-font-size-sm);
207
+ color: var(--st-color-text);
208
+ box-sizing: border-box;
209
+ }
210
+
211
+ .search-input:focus {
212
+ outline: none;
213
+ border-color: var(--st-color-primary);
214
+ }
215
+
216
+ .listbox {
217
+ margin: 0;
218
+ padding: var(--st-spacing-xs) 0;
219
+ max-height: 220px;
220
+ overflow-y: auto;
221
+ list-style: none;
222
+ }
223
+
224
+ .option {
225
+ padding: var(--st-spacing-sm) var(--st-spacing-md);
226
+ cursor: pointer;
227
+ color: var(--st-color-text);
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: center;
231
+ }
232
+
233
+ .option:hover {
234
+ background: var(--st-color-surface-muted);
235
+ }
236
+
237
+ .option[aria-selected="true"] {
238
+ font-weight: 500;
239
+ color: var(--st-color-primary);
240
+ }
241
+
242
+ .error-msg {
243
+ display: block;
244
+ margin-top: var(--st-spacing-xs);
245
+ font-size: var(--st-font-size-xs);
246
+ color: var(--st-color-danger, #dc2626);
247
+ }
248
+
249
+ .hidden-native {
250
+ position: absolute;
251
+ width: 1px;
252
+ height: 1px;
253
+ padding: 0;
254
+ margin: -1px;
255
+ overflow: hidden;
256
+ clip: rect(0, 0, 0, 0);
257
+ border: 0;
258
+ }
259
+ `;
13
260
 
14
261
  constructor() {
15
262
  super();
16
- // Track focus states to animate the chevron
17
- this.addEventListener('focusin', () => this.focused = true);
18
- this.addEventListener('focusout', () => this.focused = false);
19
- }
20
-
21
- static styles = [
22
- resetStyles,
23
- css`
24
- :host {
25
- display: inline-block;
26
- width: 100%;
27
- position: relative;
28
- }
263
+ this.internals = this.attachInternals();
264
+ this.handleClickOutside = this.handleClickOutside.bind(this);
265
+ }
29
266
 
30
- .select-wrapper {
31
- position: relative;
32
- display: flex;
33
- align-items: center;
34
- width: 100%;
35
- }
267
+ // 2. Public API methods expected by st-form
268
+ public checkValidity(): boolean {
269
+ return this.internals.checkValidity();
270
+ }
36
271
 
37
- /*
38
- Target the native select provided by the user in the Light DOM.
39
- This completely bypasses the browser rendering bug.
40
- */
41
- ::slotted(select) {
42
- appearance: none;
43
- -webkit-appearance: none;
44
- width: 100%;
45
- font-family: inherit;
46
- background-color: transparent;
47
- border-radius: var(--st-radius-md, 0.5rem);
48
- color: var(--st-color-text);
49
- cursor: pointer;
50
- outline: none;
51
- transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
52
- }
272
+ public reportValidity(): boolean {
273
+ const isValid = this.checkValidity();
274
+ this.touched = true; // Force UI to reveal the error
275
+ this.invalid = !isValid;
276
+ return isValid;
277
+ }
53
278
 
54
- ::slotted(select:disabled) {
55
- opacity: 0.6;
56
- cursor: not-allowed;
57
- }
279
+ // 3. Override native focus to target the internal combobox button
280
+ public override focus(options?: FocusOptions) {
281
+ this.buttonElement?.focus(options);
282
+ }
58
283
 
59
- /* ==========================================================
60
- Sizes
61
- ========================================================== */
62
- :host([size="sm"]) ::slotted(select) {
63
- padding: 0.25rem 2rem 0.25rem 0.75rem;
64
- font-size: 0.875rem;
65
- height: 2rem;
66
- }
67
- :host([size="md"]) ::slotted(select) {
68
- padding: 0.5rem 2.5rem 0.5rem 1rem;
69
- font-size: 1rem;
70
- height: 2.5rem;
71
- }
72
- :host([size="lg"]) ::slotted(select) {
73
- padding: 0.75rem 3rem 0.75rem 1.25rem;
74
- font-size: 1.125rem;
75
- height: 3rem;
76
- }
284
+ connectedCallback() {
285
+ super.connectedCallback();
286
+ document.addEventListener("click", this.handleClickOutside);
77
287
 
78
- /* Multiple select formatting (No fixed height, no right padding for chevron) */
79
- ::slotted(select[multiple]) {
80
- height: auto !important;
81
- padding-right: 1rem !important;
82
- }
288
+ this.addEventListener("invalid", () => {
289
+ this.touched = true;
290
+ this.invalid = true;
291
+ });
292
+ }
83
293
 
84
- /* ==========================================================
85
- Variants & Colors
86
- ========================================================== */
87
- :host([variant="outline"]) ::slotted(select) {
88
- border: 1px solid var(--st-color-border);
89
- }
90
- :host([variant="outline"][color="default"]) ::slotted(select:focus-visible) {
91
- border-color: var(--st-color-primary);
92
- box-shadow: 0 0 0 2px rgba(var(--st-color-primary-rgb, 99, 102, 241), 0.2);
93
- }
294
+ disconnectedCallback() {
295
+ document.removeEventListener("click", this.handleClickOutside);
296
+ super.disconnectedCallback();
297
+ }
94
298
 
95
- :host([variant="filled"]) ::slotted(select) {
96
- border: 1px solid transparent;
97
- background-color: var(--st-color-surface-hover, rgba(0, 0, 0, 0.05));
98
- }
99
- :host([variant="filled"]) ::slotted(select:not(:disabled):hover) {
100
- background-color: var(--st-color-surface-active, rgba(0, 0, 0, 0.1));
101
- }
102
- :host([variant="filled"][color="default"]) ::slotted(select:focus-visible) {
103
- border-color: var(--st-color-primary);
104
- background-color: transparent;
105
- }
299
+ override willUpdate(changedProperties: PropertyValues) {
300
+ if (
301
+ changedProperties.has("value") ||
302
+ changedProperties.has("required") ||
303
+ changedProperties.has("error")
304
+ ) {
305
+ this.validate();
306
+ }
307
+ }
106
308
 
107
- /* Error Color State */
108
- :host([color="error"]) ::slotted(select) {
109
- border-color: var(--st-color-error, #ef4444);
110
- }
111
- :host([color="error"]) ::slotted(select:focus-visible) {
112
- box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
113
- }
309
+ private get selectedValues(): string[] {
310
+ if (!this.value) return [];
311
+ return this.value
312
+ .split(",")
313
+ .map((v) => v.trim())
314
+ .filter(Boolean);
315
+ }
114
316
 
115
- /* Primary Color State */
116
- :host([color="primary"]) ::slotted(select) {
117
- border-color: var(--st-color-primary);
118
- }
317
+ private validate() {
318
+ this.internals.setFormValue(this.value);
119
319
 
120
- /* ==========================================================
121
- Chevron Icon
122
- ========================================================== */
123
- .chevron {
124
- position: absolute;
125
- right: 0.75rem;
126
- width: 1.25rem;
127
- height: 1.25rem;
128
- pointer-events: none;
129
- color: var(--st-color-text-muted);
130
- transition: transform 0.2s ease, color 0.2s ease;
131
- }
320
+ const isMissing = this.required && this.selectedValues.length === 0;
132
321
 
133
- /* Animate chevron when the wrapper detects focus */
134
- :host([focused]) .chevron {
135
- transform: rotate(180deg);
136
- color: var(--st-color-primary);
322
+ if (this.error) {
323
+ this.internals.setValidity({customError: true}, this.error);
324
+ this.nativeError = this.error;
325
+ } else if (isMissing) {
326
+ const msg = "Please make a selection.";
327
+ this.internals.setValidity({valueMissing: true}, msg);
328
+ this.nativeError = msg;
329
+ } else {
330
+ this.internals.setValidity({});
331
+ this.nativeError = "";
332
+ this.invalid = false;
333
+ }
334
+ }
335
+
336
+ private handleClickOutside(e: Event) {
337
+ const path = e.composedPath();
338
+ if (!path.includes(this)) {
339
+ if (this.open) {
340
+ this.touched = true;
137
341
  }
342
+ this.open = false;
343
+ this.filterText = "";
344
+ }
345
+ }
346
+
347
+ private toggleOpen() {
348
+ if (this.disabled) return;
138
349
 
139
- /* Hide chevron if the user slots a native multiple select */
140
- ::slotted(select[multiple]) ~ .chevron {
141
- display: none;
350
+ if (this.open) {
351
+ this.touched = true;
352
+ }
353
+
354
+ this.open = !this.open;
355
+ if (this.open) {
356
+ setTimeout(() => {
357
+ if (this.searchable && this.searchInputEl) {
358
+ this.searchInputEl.focus();
359
+ } else {
360
+ const first = this.shadowRoot?.querySelector(".option") as HTMLElement;
361
+ first?.focus();
362
+ }
363
+ }, 0);
364
+ } else {
365
+ this.filterText = "";
366
+ }
367
+ }
368
+
369
+ private handleOptionClick(optValue: string, e: MouseEvent) {
370
+ e.stopPropagation();
371
+ this.touched = true;
372
+
373
+ if (this.multiple) {
374
+ const current = this.selectedValues;
375
+ const index = current.indexOf(optValue);
376
+ let updated: string[];
377
+ if (index > -1) {
378
+ updated = current.filter((v) => v !== optValue);
379
+ } else {
380
+ updated = [...current, optValue];
142
381
  }
143
- `
144
- ];
382
+ this.value = updated.join(",");
383
+ } else {
384
+ this.value = optValue;
385
+ this.open = false;
386
+ this.buttonElement?.focus();
387
+ }
388
+
389
+ this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
390
+ }
391
+
392
+ private handleSearchInput(e: InputEvent) {
393
+ this.filterText = (e.target as HTMLInputElement).value;
394
+ }
395
+
396
+ private handleButtonKeydown(e: KeyboardEvent) {
397
+ if (this.disabled) return;
398
+
399
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
400
+ e.preventDefault();
401
+ this.toggleOpen();
402
+ }
403
+ }
145
404
 
146
405
  render() {
406
+ const currentValues = this.selectedValues;
407
+ const selectedOptions = this.options.filter((opt) => currentValues.includes(opt.value));
408
+
409
+ // 4. Update isInvalid to also respect the new property fallback
410
+ const isInvalid =
411
+ this.invalid || (this.touched && !this.internals.validity.valid) || !!this.error;
412
+ const displayError = isInvalid ? this.error || this.nativeError : "";
413
+
414
+ const filteredOptions = this.searchable
415
+ ? this.options.filter((opt) =>
416
+ opt.label.toLowerCase().includes(this.filterText.toLowerCase()),
417
+ )
418
+ : this.options;
419
+
147
420
  return html`
148
- <div class="select-wrapper">
149
- <slot></slot>
150
- <svg class="chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
421
+ ${this.label ? html`<label id="select-label">${this.label}</label>` : ""}
422
+
423
+ <button
424
+ type="button"
425
+ ?disabled=${this.disabled}
426
+ class=${classMap({
427
+ combobox: true,
428
+ "is-placeholder": selectedOptions.length === 0,
429
+ "is-invalid": isInvalid,
430
+ })}
431
+ role="combobox"
432
+ aria-haspopup="listbox"
433
+ aria-expanded="${this.open}"
434
+ aria-labelledby="select-label"
435
+ aria-controls="select-listbox"
436
+ @click=${this.toggleOpen}
437
+ @keydown=${this.handleButtonKeydown}
438
+ >
439
+ <div class="selection-container">
440
+ ${selectedOptions.length > 0
441
+ ? this.multiple
442
+ ? selectedOptions.map((opt) => html`<span class="chip">${opt.label}</span>`)
443
+ : html`<span>${selectedOptions[0].label}</span>`
444
+ : html`<span>Select option...</span>`}
445
+ </div>
446
+ <svg
447
+ style="width:16px;height:16px;flex-shrink:0"
448
+ viewBox="0 0 24 24"
449
+ fill="none"
450
+ stroke="currentColor"
451
+ stroke-width="2"
452
+ >
151
453
  <polyline points="6 9 12 15 18 9"></polyline>
152
454
  </svg>
153
- </div>
455
+ </button>
456
+
457
+ ${this.open
458
+ ? html`
459
+ <div class="listbox-container">
460
+ ${this.searchable
461
+ ? html`
462
+ <div class="search-wrapper" @click=${(e: Event) => e.stopPropagation()}>
463
+ <input
464
+ type="text"
465
+ class="search-input"
466
+ placeholder="Search options..."
467
+ autocomplete="off"
468
+ .value=${this.filterText}
469
+ @input=${this.handleSearchInput}
470
+ />
471
+ </div>
472
+ `
473
+ : ""}
474
+
475
+ <ul
476
+ id="select-listbox"
477
+ class="listbox"
478
+ role="listbox"
479
+ aria-multiselectable="${this.multiple}"
480
+ >
481
+ ${filteredOptions.length > 0
482
+ ? filteredOptions.map((opt) => {
483
+ const isSelected = currentValues.includes(opt.value);
484
+ return html`
485
+ <li
486
+ class="option"
487
+ role="option"
488
+ aria-selected="${isSelected}"
489
+ @click=${(e: MouseEvent) => this.handleOptionClick(opt.value, e)}
490
+ >
491
+ <span>${opt.label}</span>
492
+ ${this.multiple && isSelected ? html`<span>✓</span>` : ""}
493
+ </li>
494
+ `;
495
+ })
496
+ : html`<li
497
+ class="option"
498
+ style="color:var(--st-color-text-muted); cursor:default;"
499
+ >
500
+ No options found
501
+ </li>`}
502
+ </ul>
503
+ </div>
504
+ `
505
+ : ""}
506
+ ${displayError ? html`<span class="error-msg">${displayError}</span>` : ""}
507
+
508
+ <select
509
+ class="hidden-native"
510
+ name="${this.name}"
511
+ .autocomplete="${this.autocomplete}"
512
+ ?multiple=${this.multiple}
513
+ tabindex="-1"
514
+ aria-hidden="true"
515
+ ?disabled=${this.disabled}
516
+ >
517
+ <option value=""></option>
518
+ ${this.options.map(
519
+ (opt) => html`
520
+ <option value="${opt.value}" ?selected=${currentValues.includes(opt.value)}>
521
+ ${opt.label}
522
+ </option>
523
+ `,
524
+ )}
525
+ </select>
154
526
  `;
155
527
  }
156
528
  }
157
529
 
158
530
  declare global {
159
531
  interface HTMLElementTagNameMap {
160
- 'st-select': StSelect;
532
+ "st-select": StSelect;
161
533
  }
162
534
  }
@@ -28,4 +28,9 @@ export class StTabPanel extends LitElement {
28
28
  </div>
29
29
  `;
30
30
  }
31
+ }
32
+ declare global {
33
+ interface HTMLElementTagNameMap {
34
+ "st-tab-panel": StTabPanel;
35
+ }
31
36
  }
@@ -74,4 +74,10 @@ export class StTab extends LitElement {
74
74
  </button>
75
75
  `;
76
76
  }
77
+ }
78
+
79
+ declare global {
80
+ interface HTMLElementTagNameMap {
81
+ "st-tab": StTab;
82
+ }
77
83
  }
@@ -86,3 +86,9 @@ export class StTabs extends LitElement {
86
86
  `;
87
87
  }
88
88
  }
89
+
90
+ declare global {
91
+ interface HTMLElementTagNameMap {
92
+ "st-tabs": StTabs;
93
+ }
94
+ }