@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.
Files changed (226) hide show
  1. package/dist/components/alert/alert.constants.d.ts +3 -0
  2. package/dist/components/alert/alert.constants.d.ts.map +1 -0
  3. package/dist/components/alert/alert.constants.js +2 -0
  4. package/dist/components/alert/alert.constants.js.map +1 -0
  5. package/dist/components/alert/alert.d.ts +4 -3
  6. package/dist/components/alert/alert.d.ts.map +1 -1
  7. package/dist/components/alert/alert.js +21 -11
  8. package/dist/components/alert/alert.js.map +1 -0
  9. package/dist/components/badge/badge.constants.d.ts +3 -0
  10. package/dist/components/badge/badge.constants.d.ts.map +1 -0
  11. package/dist/components/badge/badge.constants.js +2 -0
  12. package/dist/components/badge/badge.constants.js.map +1 -0
  13. package/dist/components/badge/badge.d.ts +1 -1
  14. package/dist/components/badge/badge.d.ts.map +1 -1
  15. package/dist/components/badge/badge.js +3 -2
  16. package/dist/components/badge/badge.js.map +1 -0
  17. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
  18. package/dist/components/breadcrumb/breadcrumb.js +2 -1
  19. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  20. package/dist/components/button/button.constants.d.ts +7 -0
  21. package/dist/components/button/button.constants.d.ts.map +1 -0
  22. package/dist/components/button/button.constants.js +4 -0
  23. package/dist/components/button/button.constants.js.map +1 -0
  24. package/dist/components/button/button.d.ts +8 -4
  25. package/dist/components/button/button.d.ts.map +1 -1
  26. package/dist/components/button/button.js +181 -137
  27. package/dist/components/button/button.js.map +1 -0
  28. package/dist/components/button-group/button-group.d.ts +1 -0
  29. package/dist/components/button-group/button-group.d.ts.map +1 -1
  30. package/dist/components/button-group/button-group.js +17 -21
  31. package/dist/components/button-group/button-group.js.map +1 -0
  32. package/dist/components/checkbox/checkbox.constants.d.ts +3 -0
  33. package/dist/components/checkbox/checkbox.constants.d.ts.map +1 -0
  34. package/dist/components/checkbox/checkbox.constants.js +2 -0
  35. package/dist/components/checkbox/checkbox.constants.js.map +1 -0
  36. package/dist/components/checkbox/checkbox.d.ts +5 -1
  37. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  38. package/dist/components/checkbox/checkbox.js +21 -5
  39. package/dist/components/checkbox/checkbox.js.map +1 -0
  40. package/dist/components/code-block/code-block.d.ts +7 -7
  41. package/dist/components/code-block/code-block.d.ts.map +1 -1
  42. package/dist/components/code-block/code-block.js +130 -66
  43. package/dist/components/code-block/code-block.js.map +1 -0
  44. package/dist/components/content-card/content-card.d.ts.map +1 -1
  45. package/dist/components/content-card/content-card.js +10 -15
  46. package/dist/components/content-card/content-card.js.map +1 -0
  47. package/dist/components/divider/divider.constants.d.ts +5 -0
  48. package/dist/components/divider/divider.constants.d.ts.map +1 -0
  49. package/dist/components/divider/divider.constants.js +3 -0
  50. package/dist/components/divider/divider.constants.js.map +1 -0
  51. package/dist/components/divider/divider.d.ts +4 -2
  52. package/dist/components/divider/divider.d.ts.map +1 -1
  53. package/dist/components/divider/divider.js +18 -9
  54. package/dist/components/divider/divider.js.map +1 -0
  55. package/dist/components/drawer/drawer.d.ts +7 -4
  56. package/dist/components/drawer/drawer.d.ts.map +1 -1
  57. package/dist/components/drawer/drawer.js +73 -30
  58. package/dist/components/drawer/drawer.js.map +1 -0
  59. package/dist/components/field/field.d.ts +2 -2
  60. package/dist/components/field/field.d.ts.map +1 -1
  61. package/dist/components/field/field.js +8 -9
  62. package/dist/components/field/field.js.map +1 -0
  63. package/dist/components/footer/footer.d.ts +8 -1
  64. package/dist/components/footer/footer.d.ts.map +1 -1
  65. package/dist/components/footer/footer.js +42 -12
  66. package/dist/components/footer/footer.js.map +1 -0
  67. package/dist/components/form/form-step.js +1 -0
  68. package/dist/components/form/form-step.js.map +1 -0
  69. package/dist/components/form/form.d.ts +3 -0
  70. package/dist/components/form/form.d.ts.map +1 -1
  71. package/dist/components/form/form.js +109 -69
  72. package/dist/components/form/form.js.map +1 -0
  73. package/dist/components/grid/grid-row.d.ts +3 -3
  74. package/dist/components/grid/grid-row.d.ts.map +1 -1
  75. package/dist/components/grid/grid-row.js +36 -22
  76. package/dist/components/grid/grid-row.js.map +1 -0
  77. package/dist/components/grid/grid.d.ts +2 -2
  78. package/dist/components/grid/grid.d.ts.map +1 -1
  79. package/dist/components/grid/grid.js +5 -2
  80. package/dist/components/grid/grid.js.map +1 -0
  81. package/dist/components/icon/icon-paths.d.ts +161 -86
  82. package/dist/components/icon/icon-paths.d.ts.map +1 -1
  83. package/dist/components/icon/icon-paths.js +176 -106
  84. package/dist/components/icon/icon-paths.js.map +1 -0
  85. package/dist/components/icon/icon.d.ts +1 -1
  86. package/dist/components/icon/icon.d.ts.map +1 -1
  87. package/dist/components/icon/icon.js +6 -5
  88. package/dist/components/icon/icon.js.map +1 -0
  89. package/dist/components/input/input.d.ts +9 -0
  90. package/dist/components/input/input.d.ts.map +1 -1
  91. package/dist/components/input/input.js +30 -37
  92. package/dist/components/input/input.js.map +1 -0
  93. package/dist/components/link/link.d.ts +6 -6
  94. package/dist/components/link/link.d.ts.map +1 -1
  95. package/dist/components/link/link.js +42 -19
  96. package/dist/components/link/link.js.map +1 -0
  97. package/dist/components/metadata/metadata.d.ts +3 -3
  98. package/dist/components/metadata/metadata.d.ts.map +1 -1
  99. package/dist/components/metadata/metadata.js +23 -20
  100. package/dist/components/metadata/metadata.js.map +1 -0
  101. package/dist/components/modal/modal.d.ts +4 -16
  102. package/dist/components/modal/modal.d.ts.map +1 -1
  103. package/dist/components/modal/modal.js +99 -62
  104. package/dist/components/modal/modal.js.map +1 -0
  105. package/dist/components/page-layout/page-layout.d.ts +4 -3
  106. package/dist/components/page-layout/page-layout.d.ts.map +1 -1
  107. package/dist/components/page-layout/page-layout.js +95 -68
  108. package/dist/components/page-layout/page-layout.js.map +1 -0
  109. package/dist/components/radio/radio.d.ts +8 -0
  110. package/dist/components/radio/radio.d.ts.map +1 -1
  111. package/dist/components/radio/radio.js +8 -12
  112. package/dist/components/radio/radio.js.map +1 -0
  113. package/dist/components/radio-group/radio-group.d.ts +6 -1
  114. package/dist/components/radio-group/radio-group.d.ts.map +1 -1
  115. package/dist/components/radio-group/radio-group.js +44 -18
  116. package/dist/components/radio-group/radio-group.js.map +1 -0
  117. package/dist/components/router/router.d.ts +5 -7
  118. package/dist/components/router/router.d.ts.map +1 -1
  119. package/dist/components/router/router.js +54 -38
  120. package/dist/components/router/router.js.map +1 -0
  121. package/dist/components/select/select.d.ts +1 -3
  122. package/dist/components/select/select.d.ts.map +1 -1
  123. package/dist/components/select/select.js +14 -5
  124. package/dist/components/select/select.js.map +1 -0
  125. package/dist/components/skeleton/skeleton.d.ts +3 -2
  126. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  127. package/dist/components/skeleton/skeleton.js +10 -13
  128. package/dist/components/skeleton/skeleton.js.map +1 -0
  129. package/dist/components/switch/switch.d.ts +12 -0
  130. package/dist/components/switch/switch.d.ts.map +1 -1
  131. package/dist/components/switch/switch.js +28 -22
  132. package/dist/components/switch/switch.js.map +1 -0
  133. package/dist/components/table/data-table.d.ts +3 -25
  134. package/dist/components/table/data-table.d.ts.map +1 -1
  135. package/dist/components/table/data-table.js +211 -146
  136. package/dist/components/table/data-table.js.map +1 -0
  137. package/dist/components/table/table.d.ts +3 -3
  138. package/dist/components/table/table.d.ts.map +1 -1
  139. package/dist/components/table/table.js +39 -20
  140. package/dist/components/table/table.js.map +1 -0
  141. package/dist/components/tabs/tab-panel.d.ts +3 -1
  142. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  143. package/dist/components/tabs/tab-panel.js +21 -11
  144. package/dist/components/tabs/tab-panel.js.map +1 -0
  145. package/dist/components/tabs/tab.d.ts +5 -1
  146. package/dist/components/tabs/tab.d.ts.map +1 -1
  147. package/dist/components/tabs/tab.js +46 -28
  148. package/dist/components/tabs/tab.js.map +1 -0
  149. package/dist/components/tabs/tabs.d.ts +4 -0
  150. package/dist/components/tabs/tabs.d.ts.map +1 -1
  151. package/dist/components/tabs/tabs.js +76 -13
  152. package/dist/components/tabs/tabs.js.map +1 -0
  153. package/dist/components/textarea/textarea.d.ts +13 -0
  154. package/dist/components/textarea/textarea.d.ts.map +1 -1
  155. package/dist/components/textarea/textarea.js +59 -13
  156. package/dist/components/textarea/textarea.js.map +1 -0
  157. package/dist/components/theme-toggle/theme-toggle.constants.d.ts +3 -0
  158. package/dist/components/theme-toggle/theme-toggle.constants.d.ts.map +1 -0
  159. package/dist/components/theme-toggle/theme-toggle.constants.js +2 -0
  160. package/dist/components/theme-toggle/theme-toggle.constants.js.map +1 -0
  161. package/dist/components/theme-toggle/theme-toggle.d.ts +3 -2
  162. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  163. package/dist/components/theme-toggle/theme-toggle.js +46 -19
  164. package/dist/components/theme-toggle/theme-toggle.js.map +1 -0
  165. package/dist/components/vertical-nav/vertical-nav-group.d.ts +5 -2
  166. package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -1
  167. package/dist/components/vertical-nav/vertical-nav-group.js +50 -14
  168. package/dist/components/vertical-nav/vertical-nav-group.js.map +1 -0
  169. package/dist/components/vertical-nav/vertical-nav-item.d.ts +3 -2
  170. package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -1
  171. package/dist/components/vertical-nav/vertical-nav-item.js +30 -8
  172. package/dist/components/vertical-nav/vertical-nav-item.js.map +1 -0
  173. package/dist/styles/index.css +48 -17
  174. package/dist/styles/reset.js +1 -0
  175. package/dist/styles/reset.js.map +1 -0
  176. package/package.json +2 -1
  177. package/src/components/alert/alert.constants.ts +2 -0
  178. package/src/components/alert/alert.ts +24 -15
  179. package/src/components/badge/badge.constants.ts +2 -0
  180. package/src/components/badge/badge.ts +3 -4
  181. package/src/components/breadcrumb/breadcrumb.ts +2 -2
  182. package/src/components/button/button.constants.ts +8 -0
  183. package/src/components/button/button.ts +184 -144
  184. package/src/components/button-group/button-group.ts +18 -22
  185. package/src/components/checkbox/checkbox.constants.ts +2 -0
  186. package/src/components/checkbox/checkbox.ts +33 -18
  187. package/src/components/code-block/code-block.ts +151 -84
  188. package/src/components/content-card/content-card.ts +10 -16
  189. package/src/components/divider/divider.constants.ts +5 -0
  190. package/src/components/divider/divider.ts +22 -11
  191. package/src/components/drawer/drawer.ts +79 -34
  192. package/src/components/field/field.ts +9 -11
  193. package/src/components/footer/footer.ts +43 -12
  194. package/src/components/form/form.ts +121 -84
  195. package/src/components/grid/grid-row.ts +45 -32
  196. package/src/components/grid/grid.ts +7 -4
  197. package/src/components/icon/icon-paths.ts +175 -106
  198. package/src/components/icon/icon.ts +6 -6
  199. package/src/components/input/input.ts +32 -42
  200. package/src/components/link/link.ts +45 -21
  201. package/src/components/metadata/metadata.ts +26 -24
  202. package/src/components/modal/modal.ts +108 -74
  203. package/src/components/page-layout/page-layout.ts +97 -72
  204. package/src/components/radio/radio.ts +8 -13
  205. package/src/components/radio-group/radio-group.ts +45 -21
  206. package/src/components/router/router.ts +73 -50
  207. package/src/components/select/select.ts +18 -19
  208. package/src/components/skeleton/skeleton.ts +14 -14
  209. package/src/components/switch/switch.ts +31 -25
  210. package/src/components/table/data-table.ts +248 -177
  211. package/src/components/table/table.ts +52 -29
  212. package/src/components/tabs/tab-panel.ts +25 -13
  213. package/src/components/tabs/tab.ts +58 -33
  214. package/src/components/tabs/tabs.ts +77 -15
  215. package/src/components/textarea/textarea.ts +63 -15
  216. package/src/components/theme-toggle/theme-toggle.constants.ts +2 -0
  217. package/src/components/theme-toggle/theme-toggle.ts +47 -22
  218. package/src/components/vertical-nav/vertical-nav-group.ts +57 -18
  219. package/src/components/vertical-nav/vertical-nav-item.ts +35 -12
  220. package/src/styles/globals.css +27 -4
  221. package/src/styles/theme.css +38 -32
  222. package/tsconfig.json +2 -1
  223. package/dist/components/card/card.d.ts +0 -11
  224. package/dist/components/card/card.d.ts.map +0 -1
  225. package/dist/components/card/card.js +0 -54
  226. package/src/components/card/card.ts +0 -53
@@ -1,14 +1,14 @@
1
- import { LitElement, html, css, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { resetStyles } from '../../styles/reset.js';
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 = 'left' | 'right';
5
+ export type DrawerPlacement = "left" | "right";
6
6
 
7
- @customElement('st-drawer')
7
+ @customElement("st-drawer")
8
8
  export class StDrawer extends LitElement {
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;
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: opacity 0.25s ease-in-out;
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: auto;
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
- transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
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('.panel') as HTMLElement;
140
- if (panel) panel.style.transform = '';
141
- this.dispatchEvent(new CustomEvent('st-close', { bubbles: true, composed: true }));
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('.panel') as HTMLElement;
148
- if (panel) panel.style.transition = 'none';
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('.panel') as HTMLElement;
199
+ const panel = this.shadowRoot?.querySelector(".panel") as HTMLElement;
156
200
 
157
201
  if (!panel) return;
158
202
 
159
- if (this.placement === 'left' && diff < 0) {
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 === 'right' && diff > 0) {
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('.panel') as HTMLElement;
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 === 'left' && this._currentTranslateX < -threshold) ||
177
- (this.placement === 'right' && this._currentTranslateX > threshold)
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
- panel.style.transform = '';
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
- <span class="title"><slot name="header">Menu</slot></span>
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
- 'st-drawer': StDrawer;
261
+ "st-drawer": StDrawer;
217
262
  }
218
- }
263
+ }
@@ -1,8 +1,8 @@
1
- import { LitElement, html, css } from 'lit';
2
- import { customElement, } from 'lit/decorators.js';
3
- import { resetStyles } from '../../styles/reset.js';
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('st-field')
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, 0.25rem);
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
- 'st-field': StField;
27
+ "st-field": StField;
30
28
  }
31
- }
29
+ }
@@ -1,9 +1,11 @@
1
- import {LitElement, html, css} from "lit";
2
- import {customElement} from "lit/decorators.js";
3
- import {resetStyles} from "../../styles/reset";
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("st-footer")
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; /* Pushes footer to the bottom of the page */
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
- <footer>
51
- <slot></slot>
72
+ <!-- 🚀 Changed to a standard div to avoid duplicate landmarks -->
73
+ <div class="footer-container">
52
74
  <div class="links">
53
- <span>&copy; ${new Date().getFullYear()} Sondor.</span>
75
+ <slot></slot>
76
+ </div>
77
+ <div class="copyright">
78
+ <span>&copy; ${new Date().getFullYear()} ${this.company}.</span>
54
79
  </div>
55
- </footer>
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 (!this._modalFooterEl) {
184
- this._modalFooterEl = document.createElement("div");
185
- this._modalFooterEl.setAttribute("slot", "footer");
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
- const steps = this._steps;
195
- const hasSteps = steps.length > 0;
196
- const isLastStep = hasSteps ? this._currentStepIndex === steps.length - 1 : true;
197
-
198
- // Imperative DOM management prevents event listener loss and re-rendering conflicts
199
- this._modalFooterEl.innerHTML = "";
200
-
201
- if (hasSteps && this._currentStepIndex > 0) {
202
- const backBtn = document.createElement("st-button");
203
- backBtn.setAttribute("variant", "secondary");
204
- backBtn.setAttribute("type", "button");
205
- if (this.loading) backBtn.setAttribute("disabled", "");
206
- backBtn.textContent = "Back";
207
- backBtn.addEventListener("click", (e) => {
208
- e.preventDefault();
209
- this._prevStep();
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
- const submitBtn = document.createElement("st-button");
215
- submitBtn.setAttribute("color", "primary");
216
- submitBtn.setAttribute("type", "button");
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
- } else if (this._modalFooterEl) {
226
- this._modalFooterEl.remove();
227
- this._modalFooterEl = null;
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
- if (steps.length === 0 && this._formEl) {
297
- if (!this._formEl.checkValidity()) {
298
- isValid = false;
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
- <div class="progress-nav" role="tablist">
416
- ${steps.map((step, index) => {
417
- const isActive = index === this._currentStepIndex;
418
- const isCompleted = index < this._currentStepIndex;
419
-
420
- return html`
421
- <div
422
- class="step-indicator ${isActive ? "active" : ""} ${isCompleted
423
- ? "completed"
424
- : ""}"
425
- role="tab"
426
- aria-selected=${isActive}
427
- >
428
- <div class="step-circle">
429
- ${isCompleted
430
- ? html`<st-icon name="check-circle" size="sm"></st-icon>`
431
- : html`${index + 1}`}
432
- </div>
433
- <span>${step.title || `Step ${index + 1}`}</span>
434
- </div>
435
- ${index < steps.length - 1
436
- ? html` <div class="step-connector ${isCompleted ? "completed" : ""}"></div> `
437
- : ""}
438
- `;
439
- })}
440
- </div>
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="alert-triangle"></st-icon>
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 ? "Submitting..." : hasSteps && this._currentStepIndex === steps.length - 1 ? "Submit" : hasSteps ? "Continue" : "Submit"}
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
+ }