@sondor-technology/sondor-components 1.0.4 → 1.0.6
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/components/alert/alert.constants.d.ts +3 -0
- package/dist/components/alert/alert.constants.d.ts.map +1 -0
- package/dist/components/alert/alert.constants.js +2 -0
- package/dist/components/alert/alert.constants.js.map +1 -0
- package/dist/components/alert/alert.d.ts +4 -3
- package/dist/components/alert/alert.d.ts.map +1 -1
- package/dist/components/alert/alert.js +21 -11
- package/dist/components/alert/alert.js.map +1 -0
- package/dist/components/badge/badge.constants.d.ts +3 -0
- package/dist/components/badge/badge.constants.d.ts.map +1 -0
- package/dist/components/badge/badge.constants.js +2 -0
- package/dist/components/badge/badge.constants.js.map +1 -0
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/badge.js +3 -2
- package/dist/components/badge/badge.js.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +2 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/button/button.constants.d.ts +7 -0
- package/dist/components/button/button.constants.d.ts.map +1 -0
- package/dist/components/button/button.constants.js +4 -0
- package/dist/components/button/button.constants.js.map +1 -0
- package/dist/components/button/button.d.ts +8 -4
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +181 -137
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/button-group/button-group.d.ts +6 -0
- package/dist/components/button-group/button-group.d.ts.map +1 -1
- package/dist/components/button-group/button-group.js +17 -21
- package/dist/components/button-group/button-group.js.map +1 -0
- package/dist/components/checkbox/checkbox.constants.d.ts +3 -0
- package/dist/components/checkbox/checkbox.constants.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox.constants.js +2 -0
- package/dist/components/checkbox/checkbox.constants.js.map +1 -0
- package/dist/components/checkbox/checkbox.d.ts +5 -1
- package/dist/components/checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox/checkbox.js +21 -5
- package/dist/components/checkbox/checkbox.js.map +1 -0
- package/dist/components/code-block/code-block.d.ts +12 -7
- package/dist/components/code-block/code-block.d.ts.map +1 -1
- package/dist/components/code-block/code-block.js +130 -66
- package/dist/components/code-block/code-block.js.map +1 -0
- package/dist/components/content-card/content-card.d.ts.map +1 -1
- package/dist/components/content-card/content-card.js +10 -15
- package/dist/components/content-card/content-card.js.map +1 -0
- package/dist/components/divider/divider.constants.d.ts +5 -0
- package/dist/components/divider/divider.constants.d.ts.map +1 -0
- package/dist/components/divider/divider.constants.js +3 -0
- package/dist/components/divider/divider.constants.js.map +1 -0
- package/dist/components/divider/divider.d.ts +4 -2
- package/dist/components/divider/divider.d.ts.map +1 -1
- package/dist/components/divider/divider.js +18 -9
- package/dist/components/divider/divider.js.map +1 -0
- package/dist/components/drawer/drawer.d.ts +7 -4
- package/dist/components/drawer/drawer.d.ts.map +1 -1
- package/dist/components/drawer/drawer.js +73 -30
- package/dist/components/drawer/drawer.js.map +1 -0
- package/dist/components/field/field.d.ts +2 -2
- package/dist/components/field/field.d.ts.map +1 -1
- package/dist/components/field/field.js +8 -9
- package/dist/components/field/field.js.map +1 -0
- package/dist/components/footer/footer.d.ts +8 -1
- package/dist/components/footer/footer.d.ts.map +1 -1
- package/dist/components/footer/footer.js +42 -12
- package/dist/components/footer/footer.js.map +1 -0
- package/dist/components/form/form-step.js +1 -0
- package/dist/components/form/form-step.js.map +1 -0
- package/dist/components/form/form.d.ts +3 -0
- package/dist/components/form/form.d.ts.map +1 -1
- package/dist/components/form/form.js +107 -67
- package/dist/components/form/form.js.map +1 -0
- package/dist/components/grid/grid-row.d.ts +3 -3
- package/dist/components/grid/grid-row.d.ts.map +1 -1
- package/dist/components/grid/grid-row.js +36 -22
- package/dist/components/grid/grid-row.js.map +1 -0
- package/dist/components/grid/grid.d.ts +2 -2
- package/dist/components/grid/grid.d.ts.map +1 -1
- package/dist/components/grid/grid.js +5 -2
- package/dist/components/grid/grid.js.map +1 -0
- package/dist/components/icon/icon-paths.js +1 -0
- package/dist/components/icon/icon-paths.js.map +1 -0
- package/dist/components/icon/icon.js +1 -0
- package/dist/components/icon/icon.js.map +1 -0
- package/dist/components/input/input.d.ts +14 -0
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +30 -37
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/link/link.d.ts +6 -6
- package/dist/components/link/link.d.ts.map +1 -1
- package/dist/components/link/link.js +42 -19
- package/dist/components/link/link.js.map +1 -0
- package/dist/components/metadata/metadata.d.ts +3 -3
- package/dist/components/metadata/metadata.d.ts.map +1 -1
- package/dist/components/metadata/metadata.js +23 -20
- package/dist/components/metadata/metadata.js.map +1 -0
- package/dist/components/modal/modal.d.ts +4 -16
- package/dist/components/modal/modal.d.ts.map +1 -1
- package/dist/components/modal/modal.js +99 -62
- package/dist/components/modal/modal.js.map +1 -0
- package/dist/components/page-layout/page-layout.d.ts +4 -3
- package/dist/components/page-layout/page-layout.d.ts.map +1 -1
- package/dist/components/page-layout/page-layout.js +96 -69
- package/dist/components/page-layout/page-layout.js.map +1 -0
- package/dist/components/radio/radio.d.ts +8 -0
- package/dist/components/radio/radio.d.ts.map +1 -1
- package/dist/components/radio/radio.js +8 -12
- package/dist/components/radio/radio.js.map +1 -0
- package/dist/components/radio-group/radio-group.d.ts +6 -1
- package/dist/components/radio-group/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group/radio-group.js +44 -18
- package/dist/components/radio-group/radio-group.js.map +1 -0
- package/dist/components/router/router.d.ts +5 -7
- package/dist/components/router/router.d.ts.map +1 -1
- package/dist/components/router/router.js +54 -38
- package/dist/components/router/router.js.map +1 -0
- package/dist/components/select/select.d.ts +41 -7
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +467 -150
- package/dist/components/select/select.js.map +1 -0
- package/dist/components/skeleton/skeleton.d.ts +3 -2
- package/dist/components/skeleton/skeleton.d.ts.map +1 -1
- package/dist/components/skeleton/skeleton.js +10 -13
- package/dist/components/skeleton/skeleton.js.map +1 -0
- package/dist/components/switch/switch.d.ts +12 -0
- package/dist/components/switch/switch.d.ts.map +1 -1
- package/dist/components/switch/switch.js +28 -22
- package/dist/components/switch/switch.js.map +1 -0
- package/dist/components/table/data-table.d.ts +3 -25
- package/dist/components/table/data-table.d.ts.map +1 -1
- package/dist/components/table/data-table.js +211 -146
- package/dist/components/table/data-table.js.map +1 -0
- package/dist/components/table/table.d.ts +3 -3
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +39 -20
- package/dist/components/table/table.js.map +1 -0
- package/dist/components/tabs/tab-panel.d.ts +8 -1
- package/dist/components/tabs/tab-panel.d.ts.map +1 -1
- package/dist/components/tabs/tab-panel.js +21 -11
- package/dist/components/tabs/tab-panel.js.map +1 -0
- package/dist/components/tabs/tab.d.ts +10 -1
- package/dist/components/tabs/tab.d.ts.map +1 -1
- package/dist/components/tabs/tab.js +46 -28
- package/dist/components/tabs/tab.js.map +1 -0
- package/dist/components/tabs/tabs.d.ts +9 -0
- package/dist/components/tabs/tabs.d.ts.map +1 -1
- package/dist/components/tabs/tabs.js +76 -13
- package/dist/components/tabs/tabs.js.map +1 -0
- package/dist/components/textarea/textarea.d.ts +13 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +59 -13
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.constants.d.ts +3 -0
- package/dist/components/theme-toggle/theme-toggle.constants.d.ts.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.constants.js +2 -0
- package/dist/components/theme-toggle/theme-toggle.constants.js.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.d.ts +3 -2
- package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -1
- package/dist/components/theme-toggle/theme-toggle.js +46 -19
- package/dist/components/theme-toggle/theme-toggle.js.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-group.d.ts +5 -2
- package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -1
- package/dist/components/vertical-nav/vertical-nav-group.js +50 -14
- package/dist/components/vertical-nav/vertical-nav-group.js.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-item.d.ts +3 -2
- package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -1
- package/dist/components/vertical-nav/vertical-nav-item.js +30 -8
- package/dist/components/vertical-nav/vertical-nav-item.js.map +1 -0
- package/dist/styles/index.css +50 -17
- package/dist/styles/reset.js +1 -0
- package/dist/styles/reset.js.map +1 -0
- package/package.json +2 -1
- package/src/components/alert/alert.constants.ts +2 -0
- package/src/components/alert/alert.ts +24 -15
- package/src/components/badge/badge.constants.ts +2 -0
- package/src/components/badge/badge.ts +3 -4
- package/src/components/breadcrumb/breadcrumb.ts +2 -2
- package/src/components/button/button.constants.ts +8 -0
- package/src/components/button/button.ts +184 -144
- package/src/components/button-group/button-group.ts +23 -21
- package/src/components/checkbox/checkbox.constants.ts +2 -0
- package/src/components/checkbox/checkbox.ts +33 -18
- package/src/components/code-block/code-block.ts +157 -84
- package/src/components/content-card/content-card.ts +10 -16
- package/src/components/divider/divider.constants.ts +5 -0
- package/src/components/divider/divider.ts +22 -11
- package/src/components/drawer/drawer.ts +79 -34
- package/src/components/field/field.ts +9 -11
- package/src/components/footer/footer.ts +43 -12
- package/src/components/form/form.ts +120 -83
- package/src/components/grid/grid-row.ts +45 -32
- package/src/components/grid/grid.ts +7 -4
- package/src/components/input/input.ts +37 -41
- package/src/components/link/link.ts +45 -21
- package/src/components/metadata/metadata.ts +26 -24
- package/src/components/modal/modal.ts +108 -74
- package/src/components/page-layout/page-layout.ts +98 -73
- package/src/components/radio/radio.ts +8 -13
- package/src/components/radio-group/radio-group.ts +45 -21
- package/src/components/router/router.ts +73 -50
- package/src/components/select/select.ts +502 -131
- package/src/components/skeleton/skeleton.ts +14 -14
- package/src/components/switch/switch.ts +31 -25
- package/src/components/table/data-table.ts +248 -177
- package/src/components/table/table.ts +52 -29
- package/src/components/tabs/tab-panel.ts +30 -13
- package/src/components/tabs/tab.ts +64 -33
- package/src/components/tabs/tabs.ts +82 -14
- package/src/components/textarea/textarea.ts +63 -15
- package/src/components/theme-toggle/theme-toggle.constants.ts +2 -0
- package/src/components/theme-toggle/theme-toggle.ts +47 -22
- package/src/components/vertical-nav/vertical-nav-group.ts +57 -18
- package/src/components/vertical-nav/vertical-nav-item.ts +35 -12
- package/src/styles/globals.css +27 -4
- package/src/styles/theme.css +39 -31
- package/tsconfig.json +2 -1
- package/dist/components/card/card.d.ts +0 -11
- package/dist/components/card/card.d.ts.map +0 -1
- package/dist/components/card/card.js +0 -54
- package/src/components/card/card.ts +0 -53
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {LitElement, html, css} from "lit";
|
|
2
|
+
import {customElement, property} from "lit/decorators.js";
|
|
3
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
4
4
|
|
|
5
|
-
export type TableMode =
|
|
5
|
+
export type TableMode = "horizontal" | "vertical";
|
|
6
6
|
|
|
7
7
|
export interface TableColumn {
|
|
8
8
|
header: string;
|
|
9
9
|
key: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
@customElement(
|
|
12
|
+
@customElement("st-table")
|
|
13
13
|
export class StTable extends LitElement {
|
|
14
|
-
@property({
|
|
15
|
-
@property({
|
|
16
|
-
|
|
17
|
-
@property({
|
|
18
|
-
@property({
|
|
19
|
-
@property({
|
|
20
|
-
@property({
|
|
14
|
+
@property({type: Array}) columns: TableColumn[] = [];
|
|
15
|
+
@property({type: Array}) data: Record<string, any>[] = [];
|
|
16
|
+
|
|
17
|
+
@property({type: String, reflect: true}) mode: TableMode = "horizontal";
|
|
18
|
+
@property({type: Boolean, reflect: true}) striped = false;
|
|
19
|
+
@property({type: Boolean, reflect: true}) hoverable = false;
|
|
20
|
+
@property({type: Boolean, reflect: true}) dense = false;
|
|
21
21
|
|
|
22
22
|
static styles = [
|
|
23
23
|
resetStyles,
|
|
@@ -61,11 +61,11 @@ export class StTable extends LitElement {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
:host([striped]) tbody tr:nth-of-type(odd) {
|
|
64
|
-
background-color: var(--st-color-surface-muted);
|
|
64
|
+
background-color: var(--st-color-surface-muted, #f8fafc);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
:host([hoverable]) tbody tr:hover {
|
|
68
|
-
background-color: var(--st-color-surface-hover);
|
|
68
|
+
background-color: var(--st-color-surface-hover, #f1f5f9);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/* ==========================================================
|
|
@@ -120,26 +120,49 @@ export class StTable extends LitElement {
|
|
|
120
120
|
padding-right: var(--st-spacing-md);
|
|
121
121
|
color: var(--st-color-text-muted);
|
|
122
122
|
}
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
.empty-state {
|
|
125
|
+
text-align: center;
|
|
126
|
+
padding: 2rem !important;
|
|
127
|
+
color: var(--st-color-text-muted);
|
|
128
|
+
font-style: italic;
|
|
129
|
+
}
|
|
130
|
+
`,
|
|
124
131
|
];
|
|
125
132
|
|
|
126
133
|
render() {
|
|
127
134
|
return html`
|
|
128
135
|
<div class="table-wrapper">
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
<!-- 🚀 FIX: Explicit ARIA roles map to preserve semantics when CSS 'display' changes -->
|
|
137
|
+
<table role="table">
|
|
138
|
+
<thead role="rowgroup">
|
|
139
|
+
<tr role="row">
|
|
140
|
+
${this.columns.map(
|
|
141
|
+
(col) => html`<th role="columnheader" scope="col">${col.header}</th>`,
|
|
142
|
+
)}
|
|
133
143
|
</tr>
|
|
134
144
|
</thead>
|
|
135
|
-
<tbody>
|
|
136
|
-
${this.data.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
<tbody role="rowgroup">
|
|
146
|
+
${this.data.length === 0
|
|
147
|
+
? html`
|
|
148
|
+
<tr role="row">
|
|
149
|
+
<td role="cell" colspan=${this.columns.length} class="empty-state">
|
|
150
|
+
No data available.
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
`
|
|
154
|
+
: this.data.map(
|
|
155
|
+
(row) => html`
|
|
156
|
+
<tr role="row">
|
|
157
|
+
${this.columns.map(
|
|
158
|
+
// 🚀 FIX: Nullish coalescing prevents printing "undefined" for sparse data objects
|
|
159
|
+
(col) => html`
|
|
160
|
+
<td role="cell" data-label=${col.header}>${row[col.key] ?? ""}</td>
|
|
161
|
+
`,
|
|
162
|
+
)}
|
|
163
|
+
</tr>
|
|
164
|
+
`,
|
|
165
|
+
)}
|
|
143
166
|
</tbody>
|
|
144
167
|
</table>
|
|
145
168
|
</div>
|
|
@@ -149,6 +172,6 @@ export class StTable extends LitElement {
|
|
|
149
172
|
|
|
150
173
|
declare global {
|
|
151
174
|
interface HTMLElementTagNameMap {
|
|
152
|
-
|
|
175
|
+
"st-table": StTable;
|
|
153
176
|
}
|
|
154
|
-
}
|
|
177
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {LitElement, html, css, PropertyValues} from "lit";
|
|
2
|
+
import {customElement, property} from "lit/decorators.js";
|
|
3
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
4
4
|
|
|
5
|
-
@customElement(
|
|
5
|
+
@customElement("st-tab-panel")
|
|
6
6
|
export class StTabPanel extends LitElement {
|
|
7
|
-
@property({
|
|
8
|
-
@property({
|
|
7
|
+
@property({type: String, reflect: true}) name = "";
|
|
8
|
+
@property({type: Boolean, reflect: true}) active = false;
|
|
9
9
|
|
|
10
10
|
static styles = [
|
|
11
11
|
resetStyles,
|
|
@@ -18,14 +18,31 @@ export class StTabPanel extends LitElement {
|
|
|
18
18
|
:host(:not([active])) {
|
|
19
19
|
display: none;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
`,
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
+
connectedCallback() {
|
|
25
|
+
super.connectedCallback();
|
|
26
|
+
// 🚀 FIX: Expose role and ID on the host to link perfectly with the orchestrator
|
|
27
|
+
this.id = this.id || `panel-${this.name}`;
|
|
28
|
+
this.setAttribute("role", "tabpanel");
|
|
29
|
+
this.setAttribute("aria-hidden", this.active ? "false" : "true");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override updated(changedProperties: PropertyValues) {
|
|
33
|
+
super.updated(changedProperties);
|
|
34
|
+
if (changedProperties.has("active")) {
|
|
35
|
+
this.setAttribute("aria-hidden", this.active ? "false" : "true");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
render() {
|
|
25
|
-
return html
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
return html`<slot></slot>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare global {
|
|
45
|
+
interface HTMLElementTagNameMap {
|
|
46
|
+
"st-tab-panel": StTabPanel;
|
|
30
47
|
}
|
|
31
|
-
}
|
|
48
|
+
}
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {LitElement, html, css, PropertyValues} from "lit";
|
|
2
|
+
import {customElement, property} from "lit/decorators.js";
|
|
3
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
4
4
|
|
|
5
|
-
@customElement(
|
|
5
|
+
@customElement("st-tab")
|
|
6
6
|
export class StTab extends LitElement {
|
|
7
|
-
@property({
|
|
8
|
-
@property({
|
|
7
|
+
@property({type: String, reflect: true}) panel = "";
|
|
8
|
+
@property({type: Boolean, reflect: true}) active = false;
|
|
9
9
|
|
|
10
10
|
static styles = [
|
|
11
11
|
resetStyles,
|
|
12
12
|
css`
|
|
13
|
+
/* 🚀 FIX: Style the host element directly to act as the interactive tab */
|
|
13
14
|
:host {
|
|
14
|
-
display: inline-block;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
button {
|
|
18
15
|
display: inline-flex;
|
|
19
16
|
align-items: center;
|
|
20
17
|
gap: var(--st-spacing-sm);
|
|
21
18
|
background: transparent;
|
|
22
|
-
border: none;
|
|
23
19
|
border-bottom: 2px solid transparent;
|
|
24
20
|
padding: var(--st-spacing-sm) var(--st-spacing-md);
|
|
25
21
|
color: var(--st-color-text-muted);
|
|
@@ -28,20 +24,19 @@ export class StTab extends LitElement {
|
|
|
28
24
|
cursor: pointer;
|
|
29
25
|
transition: all 0.2s ease;
|
|
30
26
|
white-space: nowrap;
|
|
27
|
+
outline: none; /* Focus outline handled below */
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
:host(:hover) {
|
|
34
31
|
color: var(--st-color-text);
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
outline: none;
|
|
34
|
+
:host(:focus-visible) {
|
|
39
35
|
background-color: var(--st-color-surface);
|
|
40
36
|
border-radius: 4px 4px 0 0;
|
|
41
37
|
}
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
:host([active]) button {
|
|
39
|
+
:host([active]) {
|
|
45
40
|
color: var(--st-color-primary);
|
|
46
41
|
border-bottom-color: var(--st-color-primary);
|
|
47
42
|
}
|
|
@@ -51,27 +46,63 @@ export class StTab extends LitElement {
|
|
|
51
46
|
height: 1.125rem;
|
|
52
47
|
fill: currentColor;
|
|
53
48
|
}
|
|
54
|
-
|
|
49
|
+
`,
|
|
55
50
|
];
|
|
56
51
|
|
|
52
|
+
connectedCallback() {
|
|
53
|
+
super.connectedCallback();
|
|
54
|
+
|
|
55
|
+
// 🚀 FIX: Apply the ARIA role and attributes to the Host element
|
|
56
|
+
this.id = this.id || `tab-${this.panel}`;
|
|
57
|
+
this.setAttribute("role", "tab");
|
|
58
|
+
this.setAttribute("aria-selected", this.active ? "true" : "false");
|
|
59
|
+
this.setAttribute("tabindex", this.active ? "0" : "-1");
|
|
60
|
+
|
|
61
|
+
// Attach native event listeners since we are no longer using a button
|
|
62
|
+
this.addEventListener("click", this._handleClick);
|
|
63
|
+
this.addEventListener("keydown", this._handleKeyDown);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
disconnectedCallback() {
|
|
67
|
+
super.disconnectedCallback();
|
|
68
|
+
this.removeEventListener("click", this._handleClick);
|
|
69
|
+
this.removeEventListener("keydown", this._handleKeyDown);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override updated(changedProperties: PropertyValues) {
|
|
73
|
+
super.updated(changedProperties);
|
|
74
|
+
if (changedProperties.has("active")) {
|
|
75
|
+
// Sync ARIA states when active changes
|
|
76
|
+
this.setAttribute("aria-selected", this.active ? "true" : "false");
|
|
77
|
+
this.setAttribute("tabindex", this.active ? "0" : "-1");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
private _handleClick() {
|
|
58
|
-
this.dispatchEvent(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
this.dispatchEvent(
|
|
83
|
+
new CustomEvent("st-tab-select", {
|
|
84
|
+
detail: {panel: this.panel},
|
|
85
|
+
bubbles: true,
|
|
86
|
+
composed: true,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private _handleKeyDown(e: KeyboardEvent) {
|
|
92
|
+
// 🚀 FIX: Ensure Enter and Space keys activate the tab natively
|
|
93
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
this._handleClick();
|
|
96
|
+
}
|
|
63
97
|
}
|
|
64
98
|
|
|
65
99
|
render() {
|
|
66
|
-
return html
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<slot></slot>
|
|
74
|
-
</button>
|
|
75
|
-
`;
|
|
100
|
+
return html`<slot></slot>`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
declare global {
|
|
105
|
+
interface HTMLElementTagNameMap {
|
|
106
|
+
"st-tab": StTab;
|
|
76
107
|
}
|
|
77
|
-
}
|
|
108
|
+
}
|
|
@@ -2,7 +2,7 @@ import "./tab.js";
|
|
|
2
2
|
import "./tab-panel.js";
|
|
3
3
|
|
|
4
4
|
import {LitElement, html, css} from "lit";
|
|
5
|
-
import {customElement, queryAssignedElements} from "lit/decorators.js";
|
|
5
|
+
import {customElement, queryAssignedElements, property} from "lit/decorators.js";
|
|
6
6
|
import {resetStyles} from "../../styles/reset.js";
|
|
7
7
|
import type {StTab} from "./tab.js";
|
|
8
8
|
import type {StTabPanel} from "./tab-panel.js";
|
|
@@ -15,6 +15,9 @@ export class StTabs extends LitElement {
|
|
|
15
15
|
@queryAssignedElements({selector: "st-tab-panel"})
|
|
16
16
|
private _panels!: StTabPanel[];
|
|
17
17
|
|
|
18
|
+
// 🚀 FEATURE: Syncs the active tab with the URL Hash
|
|
19
|
+
@property({type: Boolean, attribute: "sync-url"}) syncUrl = false;
|
|
20
|
+
|
|
18
21
|
static styles = [
|
|
19
22
|
resetStyles,
|
|
20
23
|
css`
|
|
@@ -24,15 +27,14 @@ export class StTabs extends LitElement {
|
|
|
24
27
|
|
|
25
28
|
.nav-container {
|
|
26
29
|
display: flex;
|
|
27
|
-
/* Creates the track line underneath the tabs */
|
|
28
30
|
border-bottom: 1px solid var(--st-color-border);
|
|
29
31
|
gap: var(--st-spacing-xs);
|
|
30
|
-
overflow-x: auto;
|
|
31
|
-
scrollbar-width: none;
|
|
32
|
+
overflow-x: auto;
|
|
33
|
+
scrollbar-width: none;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
.nav-container::-webkit-scrollbar {
|
|
35
|
-
display: none;
|
|
37
|
+
display: none;
|
|
36
38
|
}
|
|
37
39
|
`,
|
|
38
40
|
];
|
|
@@ -40,44 +42,104 @@ export class StTabs extends LitElement {
|
|
|
40
42
|
connectedCallback() {
|
|
41
43
|
super.connectedCallback();
|
|
42
44
|
this.addEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
45
|
+
window.addEventListener("hashchange", this._handleHashChange);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
disconnectedCallback() {
|
|
46
49
|
super.disconnectedCallback();
|
|
47
50
|
this.removeEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
51
|
+
window.removeEventListener("hashchange", this._handleHashChange);
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
private _handleHashChange = () => {
|
|
55
|
+
if (!this.syncUrl) return;
|
|
56
|
+
const hash = window.location.hash.replace("#", "");
|
|
57
|
+
if (hash && this._tabs.some((t) => t.panel === hash)) {
|
|
58
|
+
this._activateTab(hash, false); // Don't push state if we are responding to a history change
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
50
62
|
private _handleTabSelect(e: CustomEvent<{panel: string}>) {
|
|
51
|
-
// 1. Stop the event here so parent <st-tabs> instances don't react to it
|
|
52
63
|
e.stopPropagation();
|
|
64
|
+
this._activateTab(e.detail.panel, true);
|
|
65
|
+
}
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Update active states on all tabs
|
|
67
|
+
private _activateTab(targetPanel: string, updateUrl: boolean) {
|
|
57
68
|
this._tabs.forEach((tab) => {
|
|
58
69
|
tab.active = tab.panel === targetPanel;
|
|
59
70
|
});
|
|
60
71
|
|
|
61
|
-
// Update active states on all panels
|
|
62
72
|
this._panels.forEach((panel) => {
|
|
63
73
|
panel.active = panel.name === targetPanel;
|
|
64
74
|
});
|
|
75
|
+
|
|
76
|
+
if (updateUrl && this.syncUrl) {
|
|
77
|
+
window.history.replaceState(null, "", `#${targetPanel}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.dispatchEvent(
|
|
81
|
+
new CustomEvent("change", {
|
|
82
|
+
detail: {panel: targetPanel},
|
|
83
|
+
bubbles: true,
|
|
84
|
+
composed: true,
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
65
87
|
}
|
|
66
88
|
|
|
67
|
-
// Ensures the first tab is active by default if none are explicitly set
|
|
68
89
|
private _handleSlotChange() {
|
|
69
90
|
if (!this._tabs.length || !this._panels.length) return;
|
|
70
91
|
|
|
92
|
+
// Link ARIA attributes securely
|
|
93
|
+
this._tabs.forEach((t) => {
|
|
94
|
+
t.setAttribute("aria-controls", `panel-${t.panel}`);
|
|
95
|
+
});
|
|
96
|
+
this._panels.forEach((p) => {
|
|
97
|
+
p.setAttribute("aria-labelledby", `tab-${p.name}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 1. If sync-url is on, and hash exists, try to load it
|
|
101
|
+
if (this.syncUrl) {
|
|
102
|
+
const hash = window.location.hash.replace("#", "");
|
|
103
|
+
if (hash && this._tabs.some((t) => t.panel === hash)) {
|
|
104
|
+
this._activateTab(hash, false);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 2. Otherwise fallback to the first active tab, or the very first tab
|
|
71
110
|
const hasActiveTab = this._tabs.some((tab) => tab.active);
|
|
72
111
|
if (!hasActiveTab) {
|
|
73
|
-
this._tabs[0].
|
|
74
|
-
this._panels[0].active = true;
|
|
112
|
+
this._activateTab(this._tabs[0].panel, false);
|
|
75
113
|
}
|
|
76
114
|
}
|
|
77
115
|
|
|
116
|
+
// 🚀 FEATURE: W3C Keyboard Navigation (Arrows, Home, End)
|
|
117
|
+
private _handleKeyDown(e: KeyboardEvent) {
|
|
118
|
+
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
|
|
119
|
+
|
|
120
|
+
const tabs = this._tabs;
|
|
121
|
+
const currentIndex = tabs.findIndex((t) => t.active);
|
|
122
|
+
let nextIndex = currentIndex;
|
|
123
|
+
|
|
124
|
+
if (e.key === "ArrowRight") {
|
|
125
|
+
nextIndex = (currentIndex + 1) % tabs.length;
|
|
126
|
+
} else if (e.key === "ArrowLeft") {
|
|
127
|
+
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
128
|
+
} else if (e.key === "Home") {
|
|
129
|
+
nextIndex = 0;
|
|
130
|
+
} else if (e.key === "End") {
|
|
131
|
+
nextIndex = tabs.length - 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
const targetTab = tabs[nextIndex];
|
|
136
|
+
targetTab.focus();
|
|
137
|
+
this._activateTab(targetTab.panel, true);
|
|
138
|
+
}
|
|
139
|
+
|
|
78
140
|
render() {
|
|
79
141
|
return html`
|
|
80
|
-
<div class="nav-container" role="tablist">
|
|
142
|
+
<div class="nav-container" role="tablist" @keydown=${this._handleKeyDown}>
|
|
81
143
|
<slot name="nav" @slotchange=${this._handleSlotChange}></slot>
|
|
82
144
|
</div>
|
|
83
145
|
<div class="panels-container">
|
|
@@ -86,3 +148,9 @@ export class StTabs extends LitElement {
|
|
|
86
148
|
`;
|
|
87
149
|
}
|
|
88
150
|
}
|
|
151
|
+
|
|
152
|
+
declare global {
|
|
153
|
+
interface HTMLElementTagNameMap {
|
|
154
|
+
"st-tabs": StTabs;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -10,6 +10,9 @@ export class StTextarea extends LitElement {
|
|
|
10
10
|
static formAssociated = true;
|
|
11
11
|
private _internals: ElementInternals;
|
|
12
12
|
|
|
13
|
+
// 🚀 FIX: Forwards host clicks and keyboard tabs to the hidden native input
|
|
14
|
+
static shadowRootOptions = {...LitElement.shadowRootOptions, delegatesFocus: true};
|
|
15
|
+
|
|
13
16
|
@query("textarea") private _textareaEl!: HTMLTextAreaElement;
|
|
14
17
|
|
|
15
18
|
@property({type: String, reflect: true}) name = "";
|
|
@@ -22,6 +25,12 @@ export class StTextarea extends LitElement {
|
|
|
22
25
|
@property({type: Number}) rows = 4;
|
|
23
26
|
@property({type: String, reflect: true}) resize: TextareaResize = "vertical";
|
|
24
27
|
|
|
28
|
+
// 🚀 FEATURE: Auto-resize support
|
|
29
|
+
@property({type: Boolean, attribute: "auto-resize"}) autoResize = false;
|
|
30
|
+
|
|
31
|
+
// Security / Privacy
|
|
32
|
+
@property({type: String, attribute: "spellcheck"}) spellcheckAttr?: "true" | "false";
|
|
33
|
+
|
|
25
34
|
// Validation
|
|
26
35
|
@property({type: Boolean, reflect: true}) required = false;
|
|
27
36
|
@property({type: Number}) minlength?: number;
|
|
@@ -32,16 +41,18 @@ export class StTextarea extends LitElement {
|
|
|
32
41
|
@property({type: Boolean, reflect: true}) invalid = false;
|
|
33
42
|
@property({type: Boolean, reflect: true}) readonly = false;
|
|
34
43
|
|
|
44
|
+
private _defaultValue = "";
|
|
45
|
+
|
|
35
46
|
constructor() {
|
|
36
47
|
super();
|
|
37
48
|
this._internals = this.attachInternals();
|
|
38
49
|
|
|
39
|
-
// 1. Trigger error state if the user clicks away and the field is invalid
|
|
40
50
|
this.addEventListener("focusout", () => {
|
|
41
|
-
|
|
51
|
+
if (this._textareaEl) {
|
|
52
|
+
this.invalid = !this._textareaEl.validity.valid;
|
|
53
|
+
}
|
|
42
54
|
});
|
|
43
55
|
|
|
44
|
-
// 2. Trigger error state if the st-form tries to submit and fails
|
|
45
56
|
this.addEventListener("invalid", () => {
|
|
46
57
|
this.invalid = true;
|
|
47
58
|
});
|
|
@@ -68,6 +79,7 @@ export class StTextarea extends LitElement {
|
|
|
68
79
|
|
|
69
80
|
textarea {
|
|
70
81
|
width: 100%;
|
|
82
|
+
box-sizing: border-box; /* 🚀 FIX: Prevent padding from causing overflow */
|
|
71
83
|
padding: var(--st-spacing-sm) var(--st-spacing-md);
|
|
72
84
|
font-size: var(--st-font-size-base);
|
|
73
85
|
font-family: inherit;
|
|
@@ -78,12 +90,10 @@ export class StTextarea extends LitElement {
|
|
|
78
90
|
transition:
|
|
79
91
|
border-color 0.2s ease,
|
|
80
92
|
box-shadow 0.2s ease;
|
|
81
|
-
|
|
82
|
-
/* Map the resize property */
|
|
83
93
|
resize: vertical;
|
|
84
94
|
}
|
|
85
95
|
|
|
86
|
-
/* Dynamically handle resize constraints
|
|
96
|
+
/* Dynamically handle resize constraints */
|
|
87
97
|
:host([resize="none"]) textarea {
|
|
88
98
|
resize: none;
|
|
89
99
|
}
|
|
@@ -94,6 +104,12 @@ export class StTextarea extends LitElement {
|
|
|
94
104
|
resize: both;
|
|
95
105
|
}
|
|
96
106
|
|
|
107
|
+
/* Auto-resize overrides manual resize */
|
|
108
|
+
:host([auto-resize]) textarea {
|
|
109
|
+
resize: none;
|
|
110
|
+
overflow-y: hidden;
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
textarea::placeholder {
|
|
98
114
|
color: var(--st-color-text-muted);
|
|
99
115
|
}
|
|
@@ -106,15 +122,15 @@ export class StTextarea extends LitElement {
|
|
|
106
122
|
|
|
107
123
|
/* Validation States */
|
|
108
124
|
:host([invalid]) textarea {
|
|
109
|
-
border-color: var(--st-color-error);
|
|
125
|
+
border-color: var(--st-color-error, #dc2626);
|
|
110
126
|
}
|
|
111
127
|
|
|
112
128
|
:host([invalid]) textarea:focus {
|
|
113
|
-
box-shadow: 0 0 0 1px var(--st-color-error);
|
|
129
|
+
box-shadow: 0 0 0 1px var(--st-color-error, #dc2626);
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
:host([invalid]) .help-text {
|
|
117
|
-
color: var(--st-color-error);
|
|
133
|
+
color: var(--st-color-error, #dc2626);
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
/* Disabled & Readonly */
|
|
@@ -136,15 +152,32 @@ export class StTextarea extends LitElement {
|
|
|
136
152
|
`,
|
|
137
153
|
];
|
|
138
154
|
|
|
155
|
+
override firstUpdated(changedProperties: PropertyValues<this>) {
|
|
156
|
+
super.firstUpdated(changedProperties);
|
|
157
|
+
this._defaultValue = this.value;
|
|
158
|
+
if (this.autoResize) this._adjustHeight();
|
|
159
|
+
}
|
|
160
|
+
|
|
139
161
|
override updated(changedProperties: PropertyValues<this>) {
|
|
140
162
|
super.updated(changedProperties);
|
|
141
163
|
|
|
142
164
|
if (changedProperties.has("value")) {
|
|
143
165
|
this._internals.setFormValue(this.value);
|
|
166
|
+
if (this.autoResize) this._adjustHeight();
|
|
144
167
|
}
|
|
168
|
+
|
|
145
169
|
this._syncValidity();
|
|
146
170
|
}
|
|
147
171
|
|
|
172
|
+
private async _adjustHeight() {
|
|
173
|
+
await this.updateComplete;
|
|
174
|
+
if (!this._textareaEl) return;
|
|
175
|
+
|
|
176
|
+
// Reset height to auto to force scrollHeight to shrink if text is deleted
|
|
177
|
+
this._textareaEl.style.height = "auto";
|
|
178
|
+
this._textareaEl.style.height = `${this._textareaEl.scrollHeight}px`;
|
|
179
|
+
}
|
|
180
|
+
|
|
148
181
|
private _syncValidity() {
|
|
149
182
|
if (!this._textareaEl) return;
|
|
150
183
|
this._internals.setValidity(
|
|
@@ -155,15 +188,21 @@ export class StTextarea extends LitElement {
|
|
|
155
188
|
}
|
|
156
189
|
|
|
157
190
|
formResetCallback() {
|
|
158
|
-
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
191
|
+
// 🚀 FIX: Revert to the initial value instead of hardcoding an empty string
|
|
192
|
+
this.value = this._defaultValue;
|
|
193
|
+
this.invalid = false;
|
|
194
|
+
this._internals.setFormValue(this.value);
|
|
195
|
+
this.updateComplete.then(() => {
|
|
196
|
+
this._syncValidity();
|
|
197
|
+
if (this.autoResize) this._adjustHeight();
|
|
198
|
+
});
|
|
161
199
|
}
|
|
162
200
|
|
|
163
|
-
checkValidity() {
|
|
201
|
+
public checkValidity() {
|
|
164
202
|
return this._internals.checkValidity();
|
|
165
203
|
}
|
|
166
|
-
|
|
204
|
+
|
|
205
|
+
public reportValidity() {
|
|
167
206
|
return this._internals.reportValidity();
|
|
168
207
|
}
|
|
169
208
|
|
|
@@ -175,6 +214,10 @@ export class StTextarea extends LitElement {
|
|
|
175
214
|
this.invalid = false;
|
|
176
215
|
}
|
|
177
216
|
|
|
217
|
+
if (this.autoResize) {
|
|
218
|
+
this._adjustHeight();
|
|
219
|
+
}
|
|
220
|
+
|
|
178
221
|
this.dispatchEvent(new Event("input", {bubbles: true, composed: true}));
|
|
179
222
|
}
|
|
180
223
|
|
|
@@ -183,6 +226,8 @@ export class StTextarea extends LitElement {
|
|
|
183
226
|
}
|
|
184
227
|
|
|
185
228
|
render() {
|
|
229
|
+
const helpTextId = "help-text";
|
|
230
|
+
|
|
186
231
|
return html`
|
|
187
232
|
<div class="form-control">
|
|
188
233
|
${this.label ? html`<label for="textarea">${this.label}</label>` : ""}
|
|
@@ -198,11 +243,14 @@ export class StTextarea extends LitElement {
|
|
|
198
243
|
?required=${this.required}
|
|
199
244
|
minlength=${ifDefined(this.minlength)}
|
|
200
245
|
maxlength=${ifDefined(this.maxlength)}
|
|
246
|
+
spellcheck=${ifDefined(this.spellcheck)}
|
|
247
|
+
aria-invalid=${this.invalid ? "true" : "false"}
|
|
248
|
+
aria-describedby=${ifDefined(this.helpText ? helpTextId : undefined)}
|
|
201
249
|
@input=${this._handleInput}
|
|
202
250
|
@change=${this._handleChange}
|
|
203
251
|
></textarea>
|
|
204
252
|
|
|
205
|
-
${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
|
|
253
|
+
${this.helpText ? html`<div id=${helpTextId} class="help-text">${this.helpText}</div>` : ""}
|
|
206
254
|
</div>
|
|
207
255
|
`;
|
|
208
256
|
}
|