@sondor-technology/sondor-components 1.0.1-develop.10 → 1.0.1-develop.12

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 (35) 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/code-block/code-block.d.ts +5 -0
  4. package/dist/components/code-block/code-block.d.ts.map +1 -1
  5. package/dist/components/form/form-step.d.ts +1 -0
  6. package/dist/components/form/form-step.d.ts.map +1 -1
  7. package/dist/components/form/form-step.js +3 -0
  8. package/dist/components/form/form.d.ts +7 -2
  9. package/dist/components/form/form.d.ts.map +1 -1
  10. package/dist/components/form/form.js +134 -42
  11. package/dist/components/input/input.d.ts +5 -0
  12. package/dist/components/input/input.d.ts.map +1 -1
  13. package/dist/components/page-layout/page-layout.js +1 -1
  14. package/dist/components/select/select.d.ts +42 -11
  15. package/dist/components/select/select.d.ts.map +1 -1
  16. package/dist/components/select/select.js +458 -150
  17. package/dist/components/tabs/tab-panel.d.ts +5 -0
  18. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  19. package/dist/components/tabs/tab.d.ts +5 -0
  20. package/dist/components/tabs/tab.d.ts.map +1 -1
  21. package/dist/components/tabs/tabs.d.ts +5 -0
  22. package/dist/components/tabs/tabs.d.ts.map +1 -1
  23. package/dist/styles/index.css +2 -0
  24. package/package.json +1 -1
  25. package/src/components/button-group/button-group.ts +6 -0
  26. package/src/components/code-block/code-block.ts +6 -0
  27. package/src/components/form/form-step.ts +5 -1
  28. package/src/components/form/form.ts +153 -46
  29. package/src/components/input/input.ts +6 -0
  30. package/src/components/page-layout/page-layout.ts +1 -1
  31. package/src/components/select/select.ts +499 -133
  32. package/src/components/tabs/tab-panel.ts +5 -0
  33. package/src/components/tabs/tab.ts +6 -0
  34. package/src/components/tabs/tabs.ts +6 -0
  35. package/src/styles/theme.css +2 -0
@@ -1,162 +1,528 @@
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);
263
+ this.internals = this.attachInternals();
264
+ this.handleClickOutside = this.handleClickOutside.bind(this);
19
265
  }
20
266
 
21
- static styles = [
22
- resetStyles,
23
- css`
24
- :host {
25
- display: inline-block;
26
- width: 100%;
27
- position: relative;
28
- }
267
+ // 2. Public API methods expected by st-form
268
+ public checkValidity(): boolean {
269
+ return this.internals.checkValidity();
270
+ }
29
271
 
30
- .select-wrapper {
31
- position: relative;
32
- display: flex;
33
- align-items: center;
34
- width: 100%;
35
- }
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
+ }
36
278
 
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
- }
279
+ // 3. Override native focus to target the internal combobox button
280
+ public override focus(options?: FocusOptions) {
281
+ this.buttonElement?.focus(options);
282
+ }
53
283
 
54
- ::slotted(select:disabled) {
55
- opacity: 0.6;
56
- cursor: not-allowed;
57
- }
284
+ connectedCallback() {
285
+ super.connectedCallback();
286
+ document.addEventListener("click", this.handleClickOutside);
58
287
 
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
- }
288
+ this.addEventListener("invalid", () => {
289
+ this.touched = true;
290
+ this.invalid = true;
291
+ });
292
+ }
77
293
 
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
- }
294
+ disconnectedCallback() {
295
+ document.removeEventListener("click", this.handleClickOutside);
296
+ super.disconnectedCallback();
297
+ }
83
298
 
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
- }
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
+ }
94
308
 
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
- }
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
+ }
106
316
 
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
- }
317
+ private validate() {
318
+ this.internals.setFormValue(this.value);
114
319
 
115
- /* Primary Color State */
116
- :host([color="primary"]) ::slotted(select) {
117
- border-color: var(--st-color-primary);
118
- }
320
+ const isMissing = this.required && this.selectedValues.length === 0;
119
321
 
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
- }
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
+ }
132
335
 
133
- /* Animate chevron when the wrapper detects focus */
134
- :host([focused]) .chevron {
135
- transform: rotate(180deg);
136
- color: var(--st-color-primary);
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;
349
+
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;
138
372
 
139
- /* Hide chevron if the user slots a native multiple select */
140
- ::slotted(select[multiple]) ~ .chevron {
141
- display: none;
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
-
158
- declare global {
159
- interface HTMLElementTagNameMap {
160
- 'st-select': StSelect;
161
- }
162
- }
@@ -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
+ }