@sondor-technology/sondor-components 1.0.1-develop.13 → 1.0.1-develop.14
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 +1 -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 +7 -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 +109 -69
- 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.d.ts +161 -86
- package/dist/components/icon/icon-paths.d.ts.map +1 -1
- package/dist/components/icon/icon-paths.js +176 -106
- package/dist/components/icon/icon-paths.js.map +1 -0
- package/dist/components/icon/icon.d.ts +1 -1
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +6 -5
- package/dist/components/icon/icon.js.map +1 -0
- package/dist/components/input/input.d.ts +9 -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 +95 -68
- 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 +1 -3
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +14 -5
- 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 +3 -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 +5 -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 +4 -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 +48 -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 +18 -22
- 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 +151 -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 +121 -84
- package/src/components/grid/grid-row.ts +45 -32
- package/src/components/grid/grid.ts +7 -4
- package/src/components/icon/icon-paths.ts +175 -106
- package/src/components/icon/icon.ts +6 -6
- package/src/components/input/input.ts +32 -42
- 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 +97 -72
- 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 +18 -19
- 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 +25 -13
- package/src/components/tabs/tab.ts +58 -33
- package/src/components/tabs/tabs.ts +77 -15
- 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 +38 -32
- 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,14 +1,14 @@
|
|
|
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 DrawerPlacement =
|
|
5
|
+
export type DrawerPlacement = "left" | "right";
|
|
6
6
|
|
|
7
|
-
@customElement(
|
|
7
|
+
@customElement("st-drawer")
|
|
8
8
|
export class StDrawer extends LitElement {
|
|
9
|
-
@property({
|
|
10
|
-
@property({
|
|
11
|
-
@property({
|
|
9
|
+
@property({type: Boolean, reflect: true}) open = false;
|
|
10
|
+
@property({type: String, reflect: true}) placement: DrawerPlacement = "left";
|
|
11
|
+
@property({type: Boolean, attribute: "mobile-only", reflect: true}) mobileOnly = false;
|
|
12
12
|
|
|
13
13
|
private _touchStartX = 0;
|
|
14
14
|
private _currentTranslateX = 0;
|
|
@@ -24,6 +24,7 @@ export class StDrawer extends LitElement {
|
|
|
24
24
|
:host([mobile-only]) {
|
|
25
25
|
display: block;
|
|
26
26
|
}
|
|
27
|
+
|
|
27
28
|
@media (min-width: 768px) {
|
|
28
29
|
:host([mobile-only]) {
|
|
29
30
|
display: none !important;
|
|
@@ -41,12 +42,17 @@ export class StDrawer extends LitElement {
|
|
|
41
42
|
z-index: 1000;
|
|
42
43
|
display: flex;
|
|
43
44
|
opacity: 0;
|
|
45
|
+
/* Delay visibility hidden until opacity finishes transitioning */
|
|
46
|
+
visibility: hidden;
|
|
44
47
|
pointer-events: none;
|
|
45
|
-
transition:
|
|
48
|
+
transition:
|
|
49
|
+
opacity 0.25s ease-in-out,
|
|
50
|
+
visibility 0.25s;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
:host([open]) .overlay {
|
|
49
54
|
opacity: 1;
|
|
55
|
+
visibility: visible;
|
|
50
56
|
pointer-events: auto;
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -54,18 +60,28 @@ export class StDrawer extends LitElement {
|
|
|
54
60
|
position: fixed;
|
|
55
61
|
top: 0;
|
|
56
62
|
bottom: 0;
|
|
57
|
-
width:
|
|
63
|
+
width: var(--st-drawer-width, 320px);
|
|
58
64
|
max-width: 90vw;
|
|
59
65
|
background-color: var(--st-color-surface, #ffffff);
|
|
60
|
-
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
|
|
61
66
|
display: flex;
|
|
62
67
|
flex-direction: column;
|
|
63
68
|
z-index: 1001;
|
|
64
69
|
overflow-y: auto;
|
|
65
|
-
|
|
70
|
+
visibility: hidden;
|
|
71
|
+
/* Remove box shadow when closed to prevent viewport bleeding */
|
|
72
|
+
box-shadow: none;
|
|
73
|
+
transition:
|
|
74
|
+
transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
|
|
75
|
+
visibility 0.3s,
|
|
76
|
+
box-shadow 0.3s;
|
|
66
77
|
touch-action: pan-y;
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
:host([open]) .panel {
|
|
81
|
+
visibility: visible;
|
|
82
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
|
|
83
|
+
}
|
|
84
|
+
|
|
69
85
|
/* Slide from Left */
|
|
70
86
|
:host([placement="left"]) .panel {
|
|
71
87
|
left: 0;
|
|
@@ -131,35 +147,63 @@ export class StDrawer extends LitElement {
|
|
|
131
147
|
padding: var(--st-spacing-lg);
|
|
132
148
|
flex: 1;
|
|
133
149
|
}
|
|
134
|
-
|
|
150
|
+
`,
|
|
135
151
|
];
|
|
136
152
|
|
|
153
|
+
updated(changedProperties: Map<string, any>) {
|
|
154
|
+
super.updated(changedProperties);
|
|
155
|
+
|
|
156
|
+
// Manage side-effects when the drawer opens or closes
|
|
157
|
+
if (changedProperties.has("open")) {
|
|
158
|
+
if (this.open) {
|
|
159
|
+
document.addEventListener("keydown", this._handleKeyDown);
|
|
160
|
+
document.body.style.overflow = "hidden"; // Lock background scroll
|
|
161
|
+
} else {
|
|
162
|
+
document.removeEventListener("keydown", this._handleKeyDown);
|
|
163
|
+
document.body.style.overflow = ""; // Restore background scroll
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
disconnectedCallback() {
|
|
169
|
+
super.disconnectedCallback();
|
|
170
|
+
// Safety cleanup in case the component is destroyed while open
|
|
171
|
+
document.removeEventListener("keydown", this._handleKeyDown);
|
|
172
|
+
document.body.style.overflow = "";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private _handleKeyDown = (e: KeyboardEvent) => {
|
|
176
|
+
if (e.key === "Escape" && this.open) {
|
|
177
|
+
this._close();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
137
181
|
private _close() {
|
|
138
182
|
this.open = false;
|
|
139
|
-
const panel = this.shadowRoot?.querySelector(
|
|
140
|
-
if (panel) panel.style.transform =
|
|
141
|
-
this.dispatchEvent(new CustomEvent(
|
|
183
|
+
const panel = this.shadowRoot?.querySelector(".panel") as HTMLElement;
|
|
184
|
+
if (panel) panel.style.transform = "";
|
|
185
|
+
this.dispatchEvent(new CustomEvent("st-close", {bubbles: true, composed: true}));
|
|
142
186
|
}
|
|
143
187
|
|
|
144
188
|
private _handleTouchStart(e: TouchEvent) {
|
|
145
189
|
this._touchStartX = e.touches[0].clientX;
|
|
146
190
|
this._isDragging = true;
|
|
147
|
-
const panel = this.shadowRoot?.querySelector(
|
|
148
|
-
if (panel) panel.style.transition =
|
|
191
|
+
const panel = this.shadowRoot?.querySelector(".panel") as HTMLElement;
|
|
192
|
+
if (panel) panel.style.transition = "none"; // Disable animation for 1:1 finger tracking
|
|
149
193
|
}
|
|
150
194
|
|
|
151
195
|
private _handleTouchMove(e: TouchEvent) {
|
|
152
196
|
if (!this._isDragging) return;
|
|
153
197
|
const currentX = e.touches[0].clientX;
|
|
154
198
|
const diff = currentX - this._touchStartX;
|
|
155
|
-
const panel = this.shadowRoot?.querySelector(
|
|
199
|
+
const panel = this.shadowRoot?.querySelector(".panel") as HTMLElement;
|
|
156
200
|
|
|
157
201
|
if (!panel) return;
|
|
158
202
|
|
|
159
|
-
if (this.placement ===
|
|
203
|
+
if (this.placement === "left" && diff < 0) {
|
|
160
204
|
this._currentTranslateX = diff;
|
|
161
205
|
panel.style.transform = `translateX(${diff}px)`;
|
|
162
|
-
} else if (this.placement ===
|
|
206
|
+
} else if (this.placement === "right" && diff > 0) {
|
|
163
207
|
this._currentTranslateX = diff;
|
|
164
208
|
panel.style.transform = `translateX(${diff}px)`;
|
|
165
209
|
}
|
|
@@ -168,37 +212,38 @@ export class StDrawer extends LitElement {
|
|
|
168
212
|
private _handleTouchEnd() {
|
|
169
213
|
if (!this._isDragging) return;
|
|
170
214
|
this._isDragging = false;
|
|
171
|
-
const panel = this.shadowRoot?.querySelector(
|
|
172
|
-
if (panel) panel.style.transition =
|
|
215
|
+
const panel = this.shadowRoot?.querySelector(".panel") as HTMLElement;
|
|
216
|
+
if (panel) panel.style.transition = ""; // Restore CSS animations
|
|
173
217
|
|
|
174
218
|
const threshold = 70;
|
|
175
219
|
if (
|
|
176
|
-
(this.placement ===
|
|
177
|
-
(this.placement ===
|
|
220
|
+
(this.placement === "left" && this._currentTranslateX < -threshold) ||
|
|
221
|
+
(this.placement === "right" && this._currentTranslateX > threshold)
|
|
178
222
|
) {
|
|
179
223
|
this._close();
|
|
180
224
|
} else if (panel) {
|
|
181
|
-
|
|
225
|
+
// Snap back if swipe wasn't far enough
|
|
226
|
+
panel.style.transform = "";
|
|
182
227
|
}
|
|
183
228
|
this._currentTranslateX = 0;
|
|
184
229
|
}
|
|
185
230
|
|
|
186
231
|
render() {
|
|
187
|
-
if (!this.open) return nothing;
|
|
188
|
-
|
|
189
232
|
return html`
|
|
190
233
|
<div class="overlay" @click=${this._close}></div>
|
|
191
|
-
<div
|
|
192
|
-
class="panel"
|
|
193
|
-
role="dialog"
|
|
234
|
+
<div
|
|
235
|
+
class="panel"
|
|
236
|
+
role="dialog"
|
|
194
237
|
aria-modal="true"
|
|
238
|
+
aria-labelledby="drawer-title"
|
|
195
239
|
@click=${(e: Event) => e.stopPropagation()}
|
|
196
240
|
@touchstart=${this._handleTouchStart}
|
|
197
241
|
@touchmove=${this._handleTouchMove}
|
|
198
242
|
@touchend=${this._handleTouchEnd}
|
|
199
243
|
>
|
|
200
244
|
<div class="header">
|
|
201
|
-
|
|
245
|
+
<!-- Added id="drawer-title" to link the slot content to the dialog name -->
|
|
246
|
+
<span class="title" id="drawer-title"><slot name="header">Menu</slot></span>
|
|
202
247
|
<button class="close-btn" @click=${this._close} aria-label="Close Drawer">
|
|
203
248
|
<svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"></path></svg>
|
|
204
249
|
</button>
|
|
@@ -213,6 +258,6 @@ export class StDrawer extends LitElement {
|
|
|
213
258
|
|
|
214
259
|
declare global {
|
|
215
260
|
interface HTMLElementTagNameMap {
|
|
216
|
-
|
|
261
|
+
"st-drawer": StDrawer;
|
|
217
262
|
}
|
|
218
|
-
}
|
|
263
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {LitElement, html, css} from "lit";
|
|
2
|
+
import {customElement} from "lit/decorators.js";
|
|
3
|
+
import {resetStyles} from "../../styles/reset.js";
|
|
4
4
|
|
|
5
|
-
@customElement(
|
|
5
|
+
@customElement("st-field")
|
|
6
6
|
export class StField extends LitElement {
|
|
7
7
|
static styles = [
|
|
8
8
|
resetStyles,
|
|
@@ -12,20 +12,18 @@ export class StField extends LitElement {
|
|
|
12
12
|
flex-direction: column;
|
|
13
13
|
gap: var(--st-spacing-xs, 0.25rem);
|
|
14
14
|
width: 100%;
|
|
15
|
-
margin-bottom: var(--st-spacing-md,
|
|
15
|
+
margin-bottom: var(--st-spacing-md, 1rem);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
`,
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
render() {
|
|
21
|
-
return html`
|
|
22
|
-
<slot></slot>
|
|
23
|
-
`;
|
|
21
|
+
return html` <slot></slot> `;
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
declare global {
|
|
28
26
|
interface HTMLElementTagNameMap {
|
|
29
|
-
|
|
27
|
+
"st-field": StField;
|
|
30
28
|
}
|
|
31
|
-
}
|
|
29
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {LitElement, html, css} from
|
|
2
|
-
import {customElement} from
|
|
3
|
-
import {resetStyles} from
|
|
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
|
-
@customElement(
|
|
5
|
+
@customElement('st-footer')
|
|
6
6
|
export class StFooter extends LitElement {
|
|
7
|
+
@property({ type: String }) company = 'Sondor';
|
|
8
|
+
|
|
7
9
|
static styles = [
|
|
8
10
|
resetStyles,
|
|
9
11
|
css`
|
|
@@ -12,10 +14,11 @@ export class StFooter extends LitElement {
|
|
|
12
14
|
border-top: 1px solid var(--st-color-border);
|
|
13
15
|
background-color: var(--st-color-surface);
|
|
14
16
|
padding: var(--st-spacing-lg, 2rem) 0;
|
|
15
|
-
margin-top: auto;
|
|
17
|
+
margin-top: auto;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
footer
|
|
20
|
+
/* 🚀 Changed from 'footer' to a class */
|
|
21
|
+
.footer-container {
|
|
19
22
|
display: flex;
|
|
20
23
|
flex-direction: column;
|
|
21
24
|
align-items: center;
|
|
@@ -27,10 +30,15 @@ export class StFooter extends LitElement {
|
|
|
27
30
|
font-size: 0.875rem;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
/* Wrapper for footer links */
|
|
31
33
|
.links {
|
|
32
34
|
display: flex;
|
|
33
35
|
gap: var(--st-spacing-md, 1rem);
|
|
36
|
+
flex-wrap: wrap;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.copyright {
|
|
41
|
+
text-align: center;
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
::slotted(a) {
|
|
@@ -42,17 +50,40 @@ export class StFooter extends LitElement {
|
|
|
42
50
|
::slotted(a:hover) {
|
|
43
51
|
color: var(--st-color-primary);
|
|
44
52
|
}
|
|
45
|
-
|
|
53
|
+
|
|
54
|
+
::slotted(a:focus-visible) {
|
|
55
|
+
color: var(--st-color-primary);
|
|
56
|
+
outline: 2px solid var(--st-color-primary);
|
|
57
|
+
outline-offset: 4px;
|
|
58
|
+
border-radius: 2px;
|
|
59
|
+
}
|
|
60
|
+
`
|
|
46
61
|
];
|
|
47
62
|
|
|
63
|
+
connectedCallback() {
|
|
64
|
+
super.connectedCallback();
|
|
65
|
+
if (!this.hasAttribute('role')) {
|
|
66
|
+
this.setAttribute('role', 'contentinfo');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
render() {
|
|
49
71
|
return html`
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
<!-- 🚀 Changed to a standard div to avoid duplicate landmarks -->
|
|
73
|
+
<div class="footer-container">
|
|
52
74
|
<div class="links">
|
|
53
|
-
<
|
|
75
|
+
<slot></slot>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="copyright">
|
|
78
|
+
<span>© ${new Date().getFullYear()} ${this.company}.</span>
|
|
54
79
|
</div>
|
|
55
|
-
</
|
|
80
|
+
</div>
|
|
56
81
|
`;
|
|
57
82
|
}
|
|
58
83
|
}
|
|
84
|
+
|
|
85
|
+
declare global {
|
|
86
|
+
interface HTMLElementTagNameMap {
|
|
87
|
+
'st-footer': StFooter;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -16,6 +16,8 @@ export class StForm extends LitElement {
|
|
|
16
16
|
@query("form") private _formEl!: HTMLFormElement;
|
|
17
17
|
|
|
18
18
|
private _modalFooterEl: HTMLElement | null = null;
|
|
19
|
+
private _modalBackBtn: HTMLElement | null = null;
|
|
20
|
+
private _modalSubmitBtn: HTMLElement | null = null;
|
|
19
21
|
|
|
20
22
|
private get _steps(): StFormStep[] {
|
|
21
23
|
return Array.from(this.querySelectorAll("st-form-step"));
|
|
@@ -55,15 +57,22 @@ export class StForm extends LitElement {
|
|
|
55
57
|
flex-direction: column;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
/* Accessibility: Reset lists for the nav */
|
|
58
61
|
.progress-nav {
|
|
59
|
-
display: flex;
|
|
60
|
-
align-items: center;
|
|
61
62
|
margin-bottom: var(--st-spacing-xl);
|
|
62
63
|
padding-bottom: var(--st-spacing-md);
|
|
63
64
|
overflow-x: auto;
|
|
64
65
|
scrollbar-width: none;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
.step-list {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
list-style: none;
|
|
72
|
+
padding: 0;
|
|
73
|
+
margin: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
.step-indicator {
|
|
68
77
|
display: flex;
|
|
69
78
|
align-items: center;
|
|
@@ -158,6 +167,8 @@ export class StForm extends LitElement {
|
|
|
158
167
|
|
|
159
168
|
override connectedCallback() {
|
|
160
169
|
super.connectedCallback();
|
|
170
|
+
// Catch Enter key from Light DOM slotted elements
|
|
171
|
+
this.addEventListener("keydown", this._handleGlobalKeyDown);
|
|
161
172
|
this.updateComplete.then(() => {
|
|
162
173
|
this._updateStepVisibility();
|
|
163
174
|
this._updateModalFooter();
|
|
@@ -166,6 +177,7 @@ export class StForm extends LitElement {
|
|
|
166
177
|
|
|
167
178
|
override disconnectedCallback() {
|
|
168
179
|
super.disconnectedCallback();
|
|
180
|
+
this.removeEventListener("keydown", this._handleGlobalKeyDown);
|
|
169
181
|
if (this._modalFooterEl) {
|
|
170
182
|
this._modalFooterEl.remove();
|
|
171
183
|
this._modalFooterEl = null;
|
|
@@ -177,54 +189,76 @@ export class StForm extends LitElement {
|
|
|
177
189
|
this._updateModalFooter();
|
|
178
190
|
}
|
|
179
191
|
|
|
192
|
+
private _handleGlobalKeyDown = (e: KeyboardEvent) => {
|
|
193
|
+
if (e.key === "Enter") {
|
|
194
|
+
const target = e.target as HTMLElement;
|
|
195
|
+
// Allow multi-line typing in textareas
|
|
196
|
+
if (target.tagName !== "TEXTAREA" && target.tagName !== "ST-TEXTAREA") {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
this._triggerSubmission();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
180
203
|
private _updateModalFooter() {
|
|
181
204
|
const modal = this.closest("st-modal");
|
|
182
|
-
if (modal) {
|
|
183
|
-
if (
|
|
184
|
-
this._modalFooterEl
|
|
185
|
-
this._modalFooterEl
|
|
186
|
-
this._modalFooterEl.style.display = "flex";
|
|
187
|
-
this._modalFooterEl.style.gap = "var(--st-spacing-md)";
|
|
188
|
-
this._modalFooterEl.style.width = "100%";
|
|
189
|
-
this._modalFooterEl.style.justifyContent = "flex-end";
|
|
190
|
-
this._modalFooterEl.style.alignItems = "center";
|
|
191
|
-
modal.appendChild(this._modalFooterEl);
|
|
205
|
+
if (!modal) {
|
|
206
|
+
if (this._modalFooterEl) {
|
|
207
|
+
this._modalFooterEl.remove();
|
|
208
|
+
this._modalFooterEl = null;
|
|
192
209
|
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this._modalFooterEl.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
});
|
|
211
|
-
this._modalFooterEl.appendChild(backBtn);
|
|
212
|
-
}
|
|
213
|
+
// Create the footer wrapper only once to prevent memory leaks
|
|
214
|
+
if (!this._modalFooterEl) {
|
|
215
|
+
this._modalFooterEl = document.createElement("div");
|
|
216
|
+
this._modalFooterEl.setAttribute("slot", "footer");
|
|
217
|
+
this._modalFooterEl.style.display = "flex";
|
|
218
|
+
this._modalFooterEl.style.gap = "var(--st-spacing-md)";
|
|
219
|
+
this._modalFooterEl.style.width = "100%";
|
|
220
|
+
this._modalFooterEl.style.justifyContent = "flex-end";
|
|
221
|
+
this._modalFooterEl.style.alignItems = "center";
|
|
222
|
+
|
|
223
|
+
this._modalBackBtn = document.createElement("st-button");
|
|
224
|
+
this._modalBackBtn.setAttribute("variant", "secondary");
|
|
225
|
+
this._modalBackBtn.addEventListener("click", (e) => {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
this._prevStep();
|
|
228
|
+
});
|
|
213
229
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (this.loading) submitBtn.setAttribute("disabled", "");
|
|
218
|
-
submitBtn.textContent = this.loading ? "Submitting..." : (isLastStep ? "Submit" : "Continue");
|
|
219
|
-
submitBtn.addEventListener("click", (e) => {
|
|
230
|
+
this._modalSubmitBtn = document.createElement("st-button");
|
|
231
|
+
this._modalSubmitBtn.setAttribute("color", "primary");
|
|
232
|
+
this._modalSubmitBtn.addEventListener("click", (e) => {
|
|
220
233
|
e.preventDefault();
|
|
221
234
|
this._triggerSubmission();
|
|
222
235
|
});
|
|
223
|
-
this._modalFooterEl.appendChild(submitBtn);
|
|
224
236
|
|
|
225
|
-
|
|
226
|
-
this._modalFooterEl.
|
|
227
|
-
this._modalFooterEl
|
|
237
|
+
this._modalFooterEl.appendChild(this._modalBackBtn);
|
|
238
|
+
this._modalFooterEl.appendChild(this._modalSubmitBtn);
|
|
239
|
+
modal.appendChild(this._modalFooterEl);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const steps = this._steps;
|
|
243
|
+
const hasSteps = steps.length > 0;
|
|
244
|
+
const isLastStep = hasSteps ? this._currentStepIndex === steps.length - 1 : true;
|
|
245
|
+
|
|
246
|
+
// Mutate existing elements instead of trashing the DOM
|
|
247
|
+
if (this._modalBackBtn) {
|
|
248
|
+
this._modalBackBtn.style.display = hasSteps && this._currentStepIndex > 0 ? "block" : "none";
|
|
249
|
+
if (this.loading) this._modalBackBtn.setAttribute("disabled", "");
|
|
250
|
+
else this._modalBackBtn.removeAttribute("disabled");
|
|
251
|
+
this._modalBackBtn.textContent = "Back";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (this._modalSubmitBtn) {
|
|
255
|
+
if (this.loading) this._modalSubmitBtn.setAttribute("disabled", "");
|
|
256
|
+
else this._modalSubmitBtn.removeAttribute("disabled");
|
|
257
|
+
this._modalSubmitBtn.textContent = this.loading
|
|
258
|
+
? "Submitting..."
|
|
259
|
+
: isLastStep
|
|
260
|
+
? "Submit"
|
|
261
|
+
: "Continue";
|
|
228
262
|
}
|
|
229
263
|
}
|
|
230
264
|
|
|
@@ -243,12 +277,9 @@ export class StForm extends LitElement {
|
|
|
243
277
|
|
|
244
278
|
private async _validateCurrentContext(): Promise<boolean> {
|
|
245
279
|
const steps = this._steps;
|
|
246
|
-
const scope: ParentNode = steps.length > 0
|
|
247
|
-
? steps[this._currentStepIndex]
|
|
248
|
-
: this;
|
|
280
|
+
const scope: ParentNode = steps.length > 0 ? steps[this._currentStepIndex] : this;
|
|
249
281
|
|
|
250
282
|
let isValid = true;
|
|
251
|
-
|
|
252
283
|
const inputs = Array.from(
|
|
253
284
|
scope.querySelectorAll<
|
|
254
285
|
HTMLElement & {
|
|
@@ -261,7 +292,7 @@ export class StForm extends LitElement {
|
|
|
261
292
|
}
|
|
262
293
|
>("st-input, st-select, st-textarea, st-checkbox, st-radio, input, select, textarea"),
|
|
263
294
|
);
|
|
264
|
-
|
|
295
|
+
|
|
265
296
|
for (const input of inputs) {
|
|
266
297
|
if (input.hasAttribute("disabled") || (input as any).disabled) continue;
|
|
267
298
|
|
|
@@ -293,10 +324,9 @@ export class StForm extends LitElement {
|
|
|
293
324
|
}
|
|
294
325
|
}
|
|
295
326
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
327
|
+
// If there are no custom steps, ensure the native form validates as a fallback
|
|
328
|
+
if (steps.length === 0 && this._formEl && typeof this._formEl.checkValidity === "function") {
|
|
329
|
+
if (!this._formEl.checkValidity()) isValid = false;
|
|
300
330
|
}
|
|
301
331
|
|
|
302
332
|
if (isValid && steps.length > 0) {
|
|
@@ -308,9 +338,7 @@ export class StForm extends LitElement {
|
|
|
308
338
|
try {
|
|
309
339
|
this.loading = true;
|
|
310
340
|
const stepPassed = await currentStep.validate();
|
|
311
|
-
if (!stepPassed)
|
|
312
|
-
isValid = false;
|
|
313
|
-
}
|
|
341
|
+
if (!stepPassed) isValid = false;
|
|
314
342
|
} catch {
|
|
315
343
|
isValid = false;
|
|
316
344
|
} finally {
|
|
@@ -335,7 +363,6 @@ export class StForm extends LitElement {
|
|
|
335
363
|
|
|
336
364
|
private _extractFormData(): FormData {
|
|
337
365
|
const formData = new FormData();
|
|
338
|
-
|
|
339
366
|
const fields = this.querySelectorAll<
|
|
340
367
|
HTMLElement & {name: string; value: any; checked?: boolean; files?: FileList}
|
|
341
368
|
>("[name]");
|
|
@@ -412,38 +439,43 @@ export class StForm extends LitElement {
|
|
|
412
439
|
: ""}
|
|
413
440
|
${hasSteps
|
|
414
441
|
? html`
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
: ""}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
442
|
+
<!-- Accessibility: Refactored to a semantic ordered list -->
|
|
443
|
+
<nav class="progress-nav" aria-label="Progress">
|
|
444
|
+
<ol class="step-list">
|
|
445
|
+
${steps.map((step, index) => {
|
|
446
|
+
const isActive = index === this._currentStepIndex;
|
|
447
|
+
const isCompleted = index < this._currentStepIndex;
|
|
448
|
+
|
|
449
|
+
return html`
|
|
450
|
+
<li
|
|
451
|
+
class="step-indicator ${isActive ? "active" : ""} ${isCompleted
|
|
452
|
+
? "completed"
|
|
453
|
+
: ""}"
|
|
454
|
+
aria-current=${isActive ? "step" : "false"}
|
|
455
|
+
>
|
|
456
|
+
<div class="step-circle">
|
|
457
|
+
${isCompleted
|
|
458
|
+
? html`<st-icon name="circle-check" size="sm"></st-icon>`
|
|
459
|
+
: html`${index + 1}`}
|
|
460
|
+
</div>
|
|
461
|
+
<span>${step.title || `Step ${index + 1}`}</span>
|
|
462
|
+
</li>
|
|
463
|
+
${index < steps.length - 1
|
|
464
|
+
? html`<li
|
|
465
|
+
class="step-connector ${isCompleted ? "completed" : ""}"
|
|
466
|
+
aria-hidden="true"
|
|
467
|
+
></li>`
|
|
468
|
+
: ""}
|
|
469
|
+
`;
|
|
470
|
+
})}
|
|
471
|
+
</ol>
|
|
472
|
+
</nav>
|
|
441
473
|
`
|
|
442
474
|
: ""}
|
|
443
475
|
${this.error
|
|
444
476
|
? html`
|
|
445
477
|
<st-alert color="error" style="margin-bottom: var(--st-spacing-lg);">
|
|
446
|
-
<st-icon slot="icon" name="
|
|
478
|
+
<st-icon slot="icon" name="triangle-exclamation"></st-icon>
|
|
447
479
|
${this.error}
|
|
448
480
|
</st-alert>
|
|
449
481
|
`
|
|
@@ -467,14 +499,19 @@ export class StForm extends LitElement {
|
|
|
467
499
|
</st-button>
|
|
468
500
|
`
|
|
469
501
|
: ""}
|
|
470
|
-
|
|
471
502
|
<st-button
|
|
472
503
|
color="primary"
|
|
473
504
|
type="button"
|
|
474
505
|
@click=${this._triggerSubmission}
|
|
475
506
|
?disabled=${this.loading}
|
|
476
507
|
>
|
|
477
|
-
${this.loading
|
|
508
|
+
${this.loading
|
|
509
|
+
? "Submitting..."
|
|
510
|
+
: hasSteps && this._currentStepIndex === steps.length - 1
|
|
511
|
+
? "Submit"
|
|
512
|
+
: hasSteps
|
|
513
|
+
? "Continue"
|
|
514
|
+
: "Submit"}
|
|
478
515
|
</st-button>
|
|
479
516
|
</div>
|
|
480
517
|
`
|
|
@@ -489,4 +526,4 @@ declare global {
|
|
|
489
526
|
interface HTMLElementTagNameMap {
|
|
490
527
|
"st-form": StForm;
|
|
491
528
|
}
|
|
492
|
-
}
|
|
529
|
+
}
|