@sondor-technology/sondor-components 1.0.0 → 1.0.1-develop.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/README.md +8 -0
- package/dist/components/alert/alert.d.ts +13 -0
- package/dist/components/alert/alert.d.ts.map +1 -0
- package/dist/components/alert/alert.js +105 -0
- package/dist/components/badge/badge.d.ts +13 -0
- package/dist/components/badge/badge.d.ts.map +1 -0
- package/dist/components/badge/badge.js +101 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +19 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.js +99 -0
- package/dist/components/button/button.d.ts +23 -0
- package/dist/components/button/button.d.ts.map +1 -0
- package/dist/components/button/button.js +304 -0
- package/dist/components/button-group/button-group.d.ts +6 -0
- package/dist/components/button-group/button-group.d.ts.map +1 -0
- package/dist/components/button-group/button-group.js +74 -0
- package/dist/components/card/card.d.ts +11 -0
- package/dist/components/card/card.d.ts.map +1 -0
- package/dist/components/card/card.js +54 -0
- package/dist/components/checkbox/checkbox.d.ts +28 -0
- package/dist/components/checkbox/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox.js +247 -0
- package/dist/components/code-block/code-block.d.ts +20 -0
- package/dist/components/code-block/code-block.d.ts.map +1 -0
- package/dist/components/code-block/code-block.js +260 -0
- package/dist/components/content-card/content-card.d.ts +13 -0
- package/dist/components/content-card/content-card.d.ts.map +1 -0
- package/dist/components/content-card/content-card.js +140 -0
- package/dist/components/divider/divider.d.ts +16 -0
- package/dist/components/divider/divider.d.ts.map +1 -0
- package/dist/components/divider/divider.js +82 -0
- package/dist/components/drawer/drawer.d.ts +22 -0
- package/dist/components/drawer/drawer.d.ts.map +1 -0
- package/dist/components/drawer/drawer.js +226 -0
- package/dist/components/field/field.d.ts +11 -0
- package/dist/components/field/field.d.ts.map +1 -0
- package/dist/components/field/field.js +32 -0
- package/dist/components/footer/footer.d.ts +6 -0
- package/dist/components/footer/footer.d.ts.map +1 -0
- package/dist/components/footer/footer.js +65 -0
- package/dist/components/form/form-step.d.ts +13 -0
- package/dist/components/form/form-step.d.ts.map +1 -0
- package/dist/components/form/form-step.js +41 -0
- package/dist/components/form/form.d.ts +25 -0
- package/dist/components/form/form.d.ts.map +1 -0
- package/dist/components/form/form.js +358 -0
- package/dist/components/grid/grid-row.d.ts +17 -0
- package/dist/components/grid/grid-row.d.ts.map +1 -0
- package/dist/components/grid/grid-row.js +92 -0
- package/dist/components/grid/grid.d.ts +13 -0
- package/dist/components/grid/grid.d.ts.map +1 -0
- package/dist/components/grid/grid.js +42 -0
- package/dist/components/icon/icon-paths.d.ts +132 -0
- package/dist/components/icon/icon-paths.d.ts.map +1 -0
- package/dist/components/icon/icon-paths.js +137 -0
- package/dist/components/icon/icon.d.ts +16 -0
- package/dist/components/icon/icon.d.ts.map +1 -0
- package/dist/components/icon/icon.js +95 -0
- package/dist/components/input/input.d.ts +31 -0
- package/dist/components/input/input.d.ts.map +1 -0
- package/dist/components/input/input.js +268 -0
- package/dist/components/link/link.d.ts +16 -0
- package/dist/components/link/link.d.ts.map +1 -0
- package/dist/components/link/link.js +116 -0
- package/dist/components/metadata/metadata.d.ts +18 -0
- package/dist/components/metadata/metadata.d.ts.map +1 -0
- package/dist/components/metadata/metadata.js +71 -0
- package/dist/components/modal/modal.d.ts +24 -0
- package/dist/components/modal/modal.d.ts.map +1 -0
- package/dist/components/modal/modal.js +163 -0
- package/dist/components/page-layout/page-layout.d.ts +46 -0
- package/dist/components/page-layout/page-layout.d.ts.map +1 -0
- package/dist/components/page-layout/page-layout.js +697 -0
- package/dist/components/radio/radio.d.ts +16 -0
- package/dist/components/radio/radio.d.ts.map +1 -0
- package/dist/components/radio/radio.js +152 -0
- package/dist/components/radio-group/radio-group.d.ts +27 -0
- package/dist/components/radio-group/radio-group.d.ts.map +1 -0
- package/dist/components/radio-group/radio-group.js +161 -0
- package/dist/components/router/router.d.ts +7 -0
- package/dist/components/router/router.d.ts.map +1 -0
- package/dist/components/router/router.js +37 -0
- package/dist/components/select/select.d.ts +16 -0
- package/dist/components/select/select.d.ts.map +1 -0
- package/dist/components/select/select.js +172 -0
- package/dist/components/skeleton/skeleton.d.ts +17 -0
- package/dist/components/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.js +78 -0
- package/dist/components/switch/switch.d.ts +25 -0
- package/dist/components/switch/switch.d.ts.map +1 -0
- package/dist/components/switch/switch.js +185 -0
- package/dist/components/table/data-table.d.ts +72 -0
- package/dist/components/table/data-table.d.ts.map +1 -0
- package/dist/components/table/data-table.js +551 -0
- package/dist/components/table/table.d.ts +22 -0
- package/dist/components/table/table.d.ts.map +1 -0
- package/dist/components/table/table.js +167 -0
- package/dist/components/tabs/tab-panel.d.ts +8 -0
- package/dist/components/tabs/tab-panel.d.ts.map +1 -0
- package/dist/components/tabs/tab-panel.js +46 -0
- package/dist/components/tabs/tab.d.ts +9 -0
- package/dist/components/tabs/tab.d.ts.map +1 -0
- package/dist/components/tabs/tab.js +91 -0
- package/dist/components/tabs/tabs.d.ts +14 -0
- package/dist/components/tabs/tabs.d.ts.map +1 -0
- package/dist/components/tabs/tabs.js +85 -0
- package/dist/components/textarea/textarea.d.ts +36 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -0
- package/dist/components/textarea/textarea.js +231 -0
- package/dist/components/theme-toggle/theme-toggle.d.ts +15 -0
- package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -0
- package/dist/components/theme-toggle/theme-toggle.js +130 -0
- package/dist/components/vertical-nav/vertical-nav-group.d.ts +10 -0
- package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-group.js +127 -0
- package/dist/components/vertical-nav/vertical-nav-item.d.ts +9 -0
- package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -0
- package/dist/components/vertical-nav/vertical-nav-item.js +85 -0
- package/dist/styles/index.css +171 -0
- package/dist/styles/reset.d.ts +2 -0
- package/dist/styles/reset.d.ts.map +1 -0
- package/dist/styles/reset.js +50 -0
- package/package.json +7 -2
- package/src/components/alert/alert.ts +100 -100
- package/src/components/badge/badge.ts +96 -96
- package/src/components/breadcrumb/breadcrumb.ts +98 -98
- package/src/components/button/button.ts +281 -281
- package/src/components/button-group/button-group.ts +67 -67
- package/src/components/card/card.ts +52 -52
- package/src/components/checkbox/checkbox.ts +238 -238
- package/src/components/code-block/code-block.ts +256 -256
- package/src/components/content-card/content-card.ts +133 -133
- package/src/components/divider/divider.ts +68 -68
- package/src/components/drawer/drawer.ts +217 -217
- package/src/components/field/field.ts +30 -30
- package/src/components/footer/footer.ts +58 -58
- package/src/components/form/form-step.ts +32 -32
- package/src/components/form/form.ts +376 -376
- package/src/components/grid/grid-row.ts +75 -75
- package/src/components/grid/grid.ts +36 -36
- package/src/components/icon/icon-paths.ts +140 -124
- package/src/components/icon/icon.ts +82 -82
- package/src/components/input/input.ts +248 -248
- package/src/components/link/link.ts +97 -97
- package/src/components/metadata/metadata.ts +66 -66
- package/src/components/modal/modal.ts +158 -158
- package/src/components/page-layout/page-layout.ts +704 -704
- package/src/components/radio/radio.ts +142 -142
- package/src/components/radio-group/radio-group.ts +157 -157
- package/src/components/router/router.ts +46 -46
- package/src/components/select/select.ts +161 -161
- package/src/components/skeleton/skeleton.ts +67 -67
- package/src/components/switch/switch.ts +181 -181
- package/src/components/table/data-table.ts +556 -556
- package/src/components/table/table.ts +153 -153
- package/src/components/tabs/tab-panel.ts +30 -30
- package/src/components/tabs/tab.ts +76 -76
- package/src/components/tabs/tabs.ts +88 -88
- package/src/components/textarea/textarea.ts +215 -215
- package/src/components/theme-toggle/theme-toggle.ts +131 -131
- package/src/components/vertical-nav/vertical-nav-group.ts +109 -96
- package/src/components/vertical-nav/vertical-nav-item.ts +66 -49
- package/src/styles/globals.css +58 -58
- package/src/styles/index.css +80 -80
- package/src/styles/reset.ts +50 -50
- package/src/styles/theme.css +58 -58
- package/src/styles/typography.css +25 -25
- package/tsconfig.json +15 -15
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import "./tab.js";
|
|
2
|
-
import "./tab-panel.js";
|
|
3
|
-
|
|
4
|
-
import {LitElement, html, css} from "lit";
|
|
5
|
-
import {customElement, queryAssignedElements} from "lit/decorators.js";
|
|
6
|
-
import {resetStyles} from "../../styles/reset.js";
|
|
7
|
-
import type {StTab} from "./tab.js";
|
|
8
|
-
import type {StTabPanel} from "./tab-panel.js";
|
|
9
|
-
|
|
10
|
-
@customElement("st-tabs")
|
|
11
|
-
export class StTabs extends LitElement {
|
|
12
|
-
@queryAssignedElements({slot: "nav", selector: "st-tab"})
|
|
13
|
-
private _tabs!: StTab[];
|
|
14
|
-
|
|
15
|
-
@queryAssignedElements({selector: "st-tab-panel"})
|
|
16
|
-
private _panels!: StTabPanel[];
|
|
17
|
-
|
|
18
|
-
static styles = [
|
|
19
|
-
resetStyles,
|
|
20
|
-
css`
|
|
21
|
-
:host {
|
|
22
|
-
display: block;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.nav-container {
|
|
26
|
-
display: flex;
|
|
27
|
-
/* Creates the track line underneath the tabs */
|
|
28
|
-
border-bottom: 1px solid var(--st-color-border);
|
|
29
|
-
gap: var(--st-spacing-xs);
|
|
30
|
-
overflow-x: auto; /* Allows tabs to scroll horizontally on small screens */
|
|
31
|
-
scrollbar-width: none; /* Hides scrollbar on Firefox */
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.nav-container::-webkit-scrollbar {
|
|
35
|
-
display: none; /* Hides scrollbar on Chrome/Safari */
|
|
36
|
-
}
|
|
37
|
-
`,
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
connectedCallback() {
|
|
41
|
-
super.connectedCallback();
|
|
42
|
-
this.addEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
disconnectedCallback() {
|
|
46
|
-
super.disconnectedCallback();
|
|
47
|
-
this.removeEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private _handleTabSelect(e: CustomEvent<{panel: string}>) {
|
|
51
|
-
// 1. Stop the event here so parent <st-tabs> instances don't react to it
|
|
52
|
-
e.stopPropagation();
|
|
53
|
-
|
|
54
|
-
const targetPanel = e.detail.panel;
|
|
55
|
-
|
|
56
|
-
// Update active states on all tabs
|
|
57
|
-
this._tabs.forEach((tab) => {
|
|
58
|
-
tab.active = tab.panel === targetPanel;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Update active states on all panels
|
|
62
|
-
this._panels.forEach((panel) => {
|
|
63
|
-
panel.active = panel.name === targetPanel;
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Ensures the first tab is active by default if none are explicitly set
|
|
68
|
-
private _handleSlotChange() {
|
|
69
|
-
if (!this._tabs.length || !this._panels.length) return;
|
|
70
|
-
|
|
71
|
-
const hasActiveTab = this._tabs.some((tab) => tab.active);
|
|
72
|
-
if (!hasActiveTab) {
|
|
73
|
-
this._tabs[0].active = true;
|
|
74
|
-
this._panels[0].active = true;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
render() {
|
|
79
|
-
return html`
|
|
80
|
-
<div class="nav-container" role="tablist">
|
|
81
|
-
<slot name="nav" @slotchange=${this._handleSlotChange}></slot>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="panels-container">
|
|
84
|
-
<slot></slot>
|
|
85
|
-
</div>
|
|
86
|
-
`;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
1
|
+
import "./tab.js";
|
|
2
|
+
import "./tab-panel.js";
|
|
3
|
+
|
|
4
|
+
import {LitElement, html, css} from "lit";
|
|
5
|
+
import {customElement, queryAssignedElements} from "lit/decorators.js";
|
|
6
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
7
|
+
import type {StTab} from "./tab.js";
|
|
8
|
+
import type {StTabPanel} from "./tab-panel.js";
|
|
9
|
+
|
|
10
|
+
@customElement("st-tabs")
|
|
11
|
+
export class StTabs extends LitElement {
|
|
12
|
+
@queryAssignedElements({slot: "nav", selector: "st-tab"})
|
|
13
|
+
private _tabs!: StTab[];
|
|
14
|
+
|
|
15
|
+
@queryAssignedElements({selector: "st-tab-panel"})
|
|
16
|
+
private _panels!: StTabPanel[];
|
|
17
|
+
|
|
18
|
+
static styles = [
|
|
19
|
+
resetStyles,
|
|
20
|
+
css`
|
|
21
|
+
:host {
|
|
22
|
+
display: block;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.nav-container {
|
|
26
|
+
display: flex;
|
|
27
|
+
/* Creates the track line underneath the tabs */
|
|
28
|
+
border-bottom: 1px solid var(--st-color-border);
|
|
29
|
+
gap: var(--st-spacing-xs);
|
|
30
|
+
overflow-x: auto; /* Allows tabs to scroll horizontally on small screens */
|
|
31
|
+
scrollbar-width: none; /* Hides scrollbar on Firefox */
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.nav-container::-webkit-scrollbar {
|
|
35
|
+
display: none; /* Hides scrollbar on Chrome/Safari */
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
connectedCallback() {
|
|
41
|
+
super.connectedCallback();
|
|
42
|
+
this.addEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
disconnectedCallback() {
|
|
46
|
+
super.disconnectedCallback();
|
|
47
|
+
this.removeEventListener("st-tab-select", this._handleTabSelect as EventListener);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private _handleTabSelect(e: CustomEvent<{panel: string}>) {
|
|
51
|
+
// 1. Stop the event here so parent <st-tabs> instances don't react to it
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
|
|
54
|
+
const targetPanel = e.detail.panel;
|
|
55
|
+
|
|
56
|
+
// Update active states on all tabs
|
|
57
|
+
this._tabs.forEach((tab) => {
|
|
58
|
+
tab.active = tab.panel === targetPanel;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Update active states on all panels
|
|
62
|
+
this._panels.forEach((panel) => {
|
|
63
|
+
panel.active = panel.name === targetPanel;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Ensures the first tab is active by default if none are explicitly set
|
|
68
|
+
private _handleSlotChange() {
|
|
69
|
+
if (!this._tabs.length || !this._panels.length) return;
|
|
70
|
+
|
|
71
|
+
const hasActiveTab = this._tabs.some((tab) => tab.active);
|
|
72
|
+
if (!hasActiveTab) {
|
|
73
|
+
this._tabs[0].active = true;
|
|
74
|
+
this._panels[0].active = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
render() {
|
|
79
|
+
return html`
|
|
80
|
+
<div class="nav-container" role="tablist">
|
|
81
|
+
<slot name="nav" @slotchange=${this._handleSlotChange}></slot>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="panels-container">
|
|
84
|
+
<slot></slot>
|
|
85
|
+
</div>
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
import {LitElement, html, css, PropertyValues} from "lit";
|
|
2
|
-
import {customElement, property, query} from "lit/decorators.js";
|
|
3
|
-
import {ifDefined} from "lit/directives/if-defined.js";
|
|
4
|
-
import {resetStyles} from "../../styles/reset.js";
|
|
5
|
-
|
|
6
|
-
export type TextareaResize = "none" | "vertical" | "horizontal" | "both";
|
|
7
|
-
|
|
8
|
-
@customElement("st-textarea")
|
|
9
|
-
export class StTextarea extends LitElement {
|
|
10
|
-
static formAssociated = true;
|
|
11
|
-
private _internals: ElementInternals;
|
|
12
|
-
|
|
13
|
-
@query("textarea") private _textareaEl!: HTMLTextAreaElement;
|
|
14
|
-
|
|
15
|
-
@property({type: String, reflect: true}) name = "";
|
|
16
|
-
@property({type: String}) label = "";
|
|
17
|
-
@property({type: String}) value = "";
|
|
18
|
-
@property({type: String}) placeholder = "";
|
|
19
|
-
@property({attribute: "help-text"}) helpText = "";
|
|
20
|
-
|
|
21
|
-
// Textarea specific properties
|
|
22
|
-
@property({type: Number}) rows = 4;
|
|
23
|
-
@property({type: String, reflect: true}) resize: TextareaResize = "vertical";
|
|
24
|
-
|
|
25
|
-
// Validation
|
|
26
|
-
@property({type: Boolean, reflect: true}) required = false;
|
|
27
|
-
@property({type: Number}) minlength?: number;
|
|
28
|
-
@property({type: Number}) maxlength?: number;
|
|
29
|
-
|
|
30
|
-
// States
|
|
31
|
-
@property({type: Boolean, reflect: true}) disabled = false;
|
|
32
|
-
@property({type: Boolean, reflect: true}) invalid = false;
|
|
33
|
-
@property({type: Boolean, reflect: true}) readonly = false;
|
|
34
|
-
|
|
35
|
-
constructor() {
|
|
36
|
-
super();
|
|
37
|
-
this._internals = this.attachInternals();
|
|
38
|
-
|
|
39
|
-
// 1. Trigger error state if the user clicks away and the field is invalid
|
|
40
|
-
this.addEventListener("focusout", () => {
|
|
41
|
-
this.invalid = !this._textareaEl.validity.valid;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 2. Trigger error state if the st-form tries to submit and fails
|
|
45
|
-
this.addEventListener("invalid", () => {
|
|
46
|
-
this.invalid = true;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
static styles = [
|
|
51
|
-
resetStyles,
|
|
52
|
-
css`
|
|
53
|
-
:host {
|
|
54
|
-
display: block;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.form-control {
|
|
58
|
-
display: flex;
|
|
59
|
-
flex-direction: column;
|
|
60
|
-
gap: var(--st-spacing-xs);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
label {
|
|
64
|
-
font-size: var(--st-font-size-sm);
|
|
65
|
-
font-weight: var(--st-font-weight-medium);
|
|
66
|
-
color: var(--st-color-text);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
textarea {
|
|
70
|
-
width: 100%;
|
|
71
|
-
padding: var(--st-spacing-sm) var(--st-spacing-md);
|
|
72
|
-
font-size: var(--st-font-size-base);
|
|
73
|
-
font-family: inherit;
|
|
74
|
-
color: var(--st-color-text);
|
|
75
|
-
background-color: var(--st-color-bg);
|
|
76
|
-
border: 1px solid var(--st-color-border);
|
|
77
|
-
border-radius: 4px;
|
|
78
|
-
transition:
|
|
79
|
-
border-color 0.2s ease,
|
|
80
|
-
box-shadow 0.2s ease;
|
|
81
|
-
|
|
82
|
-
/* Map the resize property */
|
|
83
|
-
resize: vertical;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Dynamically handle resize constraints based on the host attribute */
|
|
87
|
-
:host([resize="none"]) textarea {
|
|
88
|
-
resize: none;
|
|
89
|
-
}
|
|
90
|
-
:host([resize="horizontal"]) textarea {
|
|
91
|
-
resize: horizontal;
|
|
92
|
-
}
|
|
93
|
-
:host([resize="both"]) textarea {
|
|
94
|
-
resize: both;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
textarea::placeholder {
|
|
98
|
-
color: var(--st-color-text-muted);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
textarea:focus {
|
|
102
|
-
outline: none;
|
|
103
|
-
border-color: var(--st-color-primary);
|
|
104
|
-
box-shadow: 0 0 0 1px var(--st-color-primary);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/* Validation States */
|
|
108
|
-
:host([invalid]) textarea {
|
|
109
|
-
border-color: var(--st-color-error);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
:host([invalid]) textarea:focus {
|
|
113
|
-
box-shadow: 0 0 0 1px var(--st-color-error);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
:host([invalid]) .help-text {
|
|
117
|
-
color: var(--st-color-error);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/* Disabled & Readonly */
|
|
121
|
-
:host([disabled]) textarea {
|
|
122
|
-
opacity: 0.5;
|
|
123
|
-
cursor: not-allowed;
|
|
124
|
-
background-color: var(--st-color-surface);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
:host([readonly]) textarea {
|
|
128
|
-
background-color: var(--st-color-surface);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.help-text {
|
|
132
|
-
font-size: var(--st-font-size-xs);
|
|
133
|
-
color: var(--st-color-text-muted);
|
|
134
|
-
margin: 0;
|
|
135
|
-
}
|
|
136
|
-
`,
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
override updated(changedProperties: PropertyValues<this>) {
|
|
140
|
-
super.updated(changedProperties);
|
|
141
|
-
|
|
142
|
-
if (changedProperties.has("value")) {
|
|
143
|
-
this._internals.setFormValue(this.value);
|
|
144
|
-
}
|
|
145
|
-
this._syncValidity();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private _syncValidity() {
|
|
149
|
-
if (!this._textareaEl) return;
|
|
150
|
-
this._internals.setValidity(
|
|
151
|
-
this._textareaEl.validity,
|
|
152
|
-
this._textareaEl.validationMessage,
|
|
153
|
-
this._textareaEl,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
formResetCallback() {
|
|
158
|
-
this.value = "";
|
|
159
|
-
this._internals.setFormValue("");
|
|
160
|
-
this.updateComplete.then(() => this._syncValidity());
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
checkValidity() {
|
|
164
|
-
return this._internals.checkValidity();
|
|
165
|
-
}
|
|
166
|
-
reportValidity() {
|
|
167
|
-
return this._internals.reportValidity();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private _handleInput(e: Event) {
|
|
171
|
-
const target = e.target as HTMLTextAreaElement;
|
|
172
|
-
this.value = target.value;
|
|
173
|
-
|
|
174
|
-
if (this.invalid && target.validity.valid) {
|
|
175
|
-
this.invalid = false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
this.dispatchEvent(new Event("input", {bubbles: true, composed: true}));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private _handleChange(e: Event) {
|
|
182
|
-
this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
render() {
|
|
186
|
-
return html`
|
|
187
|
-
<div class="form-control">
|
|
188
|
-
${this.label ? html`<label for="textarea">${this.label}</label>` : ""}
|
|
189
|
-
|
|
190
|
-
<textarea
|
|
191
|
-
id="textarea"
|
|
192
|
-
.name=${this.name}
|
|
193
|
-
.value=${this.value}
|
|
194
|
-
placeholder=${this.placeholder}
|
|
195
|
-
rows=${this.rows}
|
|
196
|
-
?disabled=${this.disabled}
|
|
197
|
-
?readonly=${this.readonly}
|
|
198
|
-
?required=${this.required}
|
|
199
|
-
minlength=${ifDefined(this.minlength)}
|
|
200
|
-
maxlength=${ifDefined(this.maxlength)}
|
|
201
|
-
@input=${this._handleInput}
|
|
202
|
-
@change=${this._handleChange}
|
|
203
|
-
></textarea>
|
|
204
|
-
|
|
205
|
-
${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
|
|
206
|
-
</div>
|
|
207
|
-
`;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
declare global {
|
|
212
|
-
interface HTMLElementTagNameMap {
|
|
213
|
-
"st-textarea": StTextarea;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
1
|
+
import {LitElement, html, css, PropertyValues} from "lit";
|
|
2
|
+
import {customElement, property, query} from "lit/decorators.js";
|
|
3
|
+
import {ifDefined} from "lit/directives/if-defined.js";
|
|
4
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
5
|
+
|
|
6
|
+
export type TextareaResize = "none" | "vertical" | "horizontal" | "both";
|
|
7
|
+
|
|
8
|
+
@customElement("st-textarea")
|
|
9
|
+
export class StTextarea extends LitElement {
|
|
10
|
+
static formAssociated = true;
|
|
11
|
+
private _internals: ElementInternals;
|
|
12
|
+
|
|
13
|
+
@query("textarea") private _textareaEl!: HTMLTextAreaElement;
|
|
14
|
+
|
|
15
|
+
@property({type: String, reflect: true}) name = "";
|
|
16
|
+
@property({type: String}) label = "";
|
|
17
|
+
@property({type: String}) value = "";
|
|
18
|
+
@property({type: String}) placeholder = "";
|
|
19
|
+
@property({attribute: "help-text"}) helpText = "";
|
|
20
|
+
|
|
21
|
+
// Textarea specific properties
|
|
22
|
+
@property({type: Number}) rows = 4;
|
|
23
|
+
@property({type: String, reflect: true}) resize: TextareaResize = "vertical";
|
|
24
|
+
|
|
25
|
+
// Validation
|
|
26
|
+
@property({type: Boolean, reflect: true}) required = false;
|
|
27
|
+
@property({type: Number}) minlength?: number;
|
|
28
|
+
@property({type: Number}) maxlength?: number;
|
|
29
|
+
|
|
30
|
+
// States
|
|
31
|
+
@property({type: Boolean, reflect: true}) disabled = false;
|
|
32
|
+
@property({type: Boolean, reflect: true}) invalid = false;
|
|
33
|
+
@property({type: Boolean, reflect: true}) readonly = false;
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
super();
|
|
37
|
+
this._internals = this.attachInternals();
|
|
38
|
+
|
|
39
|
+
// 1. Trigger error state if the user clicks away and the field is invalid
|
|
40
|
+
this.addEventListener("focusout", () => {
|
|
41
|
+
this.invalid = !this._textareaEl.validity.valid;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 2. Trigger error state if the st-form tries to submit and fails
|
|
45
|
+
this.addEventListener("invalid", () => {
|
|
46
|
+
this.invalid = true;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static styles = [
|
|
51
|
+
resetStyles,
|
|
52
|
+
css`
|
|
53
|
+
:host {
|
|
54
|
+
display: block;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.form-control {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: var(--st-spacing-xs);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
label {
|
|
64
|
+
font-size: var(--st-font-size-sm);
|
|
65
|
+
font-weight: var(--st-font-weight-medium);
|
|
66
|
+
color: var(--st-color-text);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
textarea {
|
|
70
|
+
width: 100%;
|
|
71
|
+
padding: var(--st-spacing-sm) var(--st-spacing-md);
|
|
72
|
+
font-size: var(--st-font-size-base);
|
|
73
|
+
font-family: inherit;
|
|
74
|
+
color: var(--st-color-text);
|
|
75
|
+
background-color: var(--st-color-bg);
|
|
76
|
+
border: 1px solid var(--st-color-border);
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
transition:
|
|
79
|
+
border-color 0.2s ease,
|
|
80
|
+
box-shadow 0.2s ease;
|
|
81
|
+
|
|
82
|
+
/* Map the resize property */
|
|
83
|
+
resize: vertical;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Dynamically handle resize constraints based on the host attribute */
|
|
87
|
+
:host([resize="none"]) textarea {
|
|
88
|
+
resize: none;
|
|
89
|
+
}
|
|
90
|
+
:host([resize="horizontal"]) textarea {
|
|
91
|
+
resize: horizontal;
|
|
92
|
+
}
|
|
93
|
+
:host([resize="both"]) textarea {
|
|
94
|
+
resize: both;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
textarea::placeholder {
|
|
98
|
+
color: var(--st-color-text-muted);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
textarea:focus {
|
|
102
|
+
outline: none;
|
|
103
|
+
border-color: var(--st-color-primary);
|
|
104
|
+
box-shadow: 0 0 0 1px var(--st-color-primary);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Validation States */
|
|
108
|
+
:host([invalid]) textarea {
|
|
109
|
+
border-color: var(--st-color-error);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
:host([invalid]) textarea:focus {
|
|
113
|
+
box-shadow: 0 0 0 1px var(--st-color-error);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:host([invalid]) .help-text {
|
|
117
|
+
color: var(--st-color-error);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Disabled & Readonly */
|
|
121
|
+
:host([disabled]) textarea {
|
|
122
|
+
opacity: 0.5;
|
|
123
|
+
cursor: not-allowed;
|
|
124
|
+
background-color: var(--st-color-surface);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:host([readonly]) textarea {
|
|
128
|
+
background-color: var(--st-color-surface);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.help-text {
|
|
132
|
+
font-size: var(--st-font-size-xs);
|
|
133
|
+
color: var(--st-color-text-muted);
|
|
134
|
+
margin: 0;
|
|
135
|
+
}
|
|
136
|
+
`,
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
override updated(changedProperties: PropertyValues<this>) {
|
|
140
|
+
super.updated(changedProperties);
|
|
141
|
+
|
|
142
|
+
if (changedProperties.has("value")) {
|
|
143
|
+
this._internals.setFormValue(this.value);
|
|
144
|
+
}
|
|
145
|
+
this._syncValidity();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private _syncValidity() {
|
|
149
|
+
if (!this._textareaEl) return;
|
|
150
|
+
this._internals.setValidity(
|
|
151
|
+
this._textareaEl.validity,
|
|
152
|
+
this._textareaEl.validationMessage,
|
|
153
|
+
this._textareaEl,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
formResetCallback() {
|
|
158
|
+
this.value = "";
|
|
159
|
+
this._internals.setFormValue("");
|
|
160
|
+
this.updateComplete.then(() => this._syncValidity());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
checkValidity() {
|
|
164
|
+
return this._internals.checkValidity();
|
|
165
|
+
}
|
|
166
|
+
reportValidity() {
|
|
167
|
+
return this._internals.reportValidity();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private _handleInput(e: Event) {
|
|
171
|
+
const target = e.target as HTMLTextAreaElement;
|
|
172
|
+
this.value = target.value;
|
|
173
|
+
|
|
174
|
+
if (this.invalid && target.validity.valid) {
|
|
175
|
+
this.invalid = false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.dispatchEvent(new Event("input", {bubbles: true, composed: true}));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private _handleChange(e: Event) {
|
|
182
|
+
this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
render() {
|
|
186
|
+
return html`
|
|
187
|
+
<div class="form-control">
|
|
188
|
+
${this.label ? html`<label for="textarea">${this.label}</label>` : ""}
|
|
189
|
+
|
|
190
|
+
<textarea
|
|
191
|
+
id="textarea"
|
|
192
|
+
.name=${this.name}
|
|
193
|
+
.value=${this.value}
|
|
194
|
+
placeholder=${this.placeholder}
|
|
195
|
+
rows=${this.rows}
|
|
196
|
+
?disabled=${this.disabled}
|
|
197
|
+
?readonly=${this.readonly}
|
|
198
|
+
?required=${this.required}
|
|
199
|
+
minlength=${ifDefined(this.minlength)}
|
|
200
|
+
maxlength=${ifDefined(this.maxlength)}
|
|
201
|
+
@input=${this._handleInput}
|
|
202
|
+
@change=${this._handleChange}
|
|
203
|
+
></textarea>
|
|
204
|
+
|
|
205
|
+
${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
|
|
206
|
+
</div>
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
declare global {
|
|
212
|
+
interface HTMLElementTagNameMap {
|
|
213
|
+
"st-textarea": StTextarea;
|
|
214
|
+
}
|
|
215
|
+
}
|