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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/components/button-group/button-group.d.ts +5 -0
  2. package/dist/components/button-group/button-group.d.ts.map +1 -1
  3. package/dist/components/code-block/code-block.d.ts +5 -0
  4. package/dist/components/code-block/code-block.d.ts.map +1 -1
  5. package/dist/components/form/form-step.d.ts +1 -0
  6. package/dist/components/form/form-step.d.ts.map +1 -1
  7. package/dist/components/form/form-step.js +3 -0
  8. package/dist/components/form/form.d.ts +7 -2
  9. package/dist/components/form/form.d.ts.map +1 -1
  10. package/dist/components/form/form.js +134 -42
  11. package/dist/components/input/input.d.ts +5 -0
  12. package/dist/components/input/input.d.ts.map +1 -1
  13. package/dist/components/page-layout/page-layout.js +1 -1
  14. package/dist/components/select/select.d.ts +42 -11
  15. package/dist/components/select/select.d.ts.map +1 -1
  16. package/dist/components/select/select.js +458 -150
  17. package/dist/components/tabs/tab-panel.d.ts +5 -0
  18. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  19. package/dist/components/tabs/tab.d.ts +5 -0
  20. package/dist/components/tabs/tab.d.ts.map +1 -1
  21. package/dist/components/tabs/tabs.d.ts +5 -0
  22. package/dist/components/tabs/tabs.d.ts.map +1 -1
  23. package/dist/styles/index.css +2 -0
  24. package/package.json +1 -1
  25. package/src/components/button-group/button-group.ts +6 -0
  26. package/src/components/code-block/code-block.ts +6 -0
  27. package/src/components/form/form-step.ts +5 -1
  28. package/src/components/form/form.ts +153 -46
  29. package/src/components/input/input.ts +6 -0
  30. package/src/components/page-layout/page-layout.ts +1 -1
  31. package/src/components/select/select.ts +499 -133
  32. package/src/components/tabs/tab-panel.ts +5 -0
  33. package/src/components/tabs/tab.ts +6 -0
  34. package/src/components/tabs/tabs.ts +6 -0
  35. package/src/styles/theme.css +2 -0
@@ -69,6 +69,8 @@
69
69
  :root[data-theme=light] {
70
70
  --st-color-bg: var(--st-white);
71
71
  --st-color-surface: var(--st-gray-50);
72
+ --st-color-surface-muted: color-mix(in srgb, var(--st-color-surface), black 15%);
73
+ --st-color-surface-hover: color-mix(in srgb, var(--st-color-surface), white 10%);
72
74
  --st-color-border: var(--st-gray-200);
73
75
  --st-color-text: var(--st-gray-900);
74
76
  --st-color-text-muted: var(--st-gray-700);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sondor-technology/sondor-components",
3
- "version": "1.0.1-develop.10",
3
+ "version": "1.0.1-develop.12",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,3 +65,9 @@ export class StButtonGroup extends LitElement {
65
65
  `;
66
66
  }
67
67
  }
68
+
69
+ declare global {
70
+ interface HTMLElementTagNameMap {
71
+ "st-button-group": StButtonGroup;
72
+ }
73
+ }
@@ -254,4 +254,10 @@ export class StCodeBlock extends LitElement {
254
254
  </div>
255
255
  `;
256
256
  }
257
+ }
258
+
259
+ declare global {
260
+ interface HTMLElementTagNameMap {
261
+ "st-code-block": StCodeBlock;
262
+ }
257
263
  }
@@ -6,6 +6,10 @@ import {resetStyles} from "../../styles/reset.js";
6
6
  export class StFormStep extends LitElement {
7
7
  @property({type: String}) title = "";
8
8
  @property({type: Boolean, reflect: true}) active = false;
9
+
10
+ // Declare the validate function property (attribute: false prevents reflection of functions to DOM attributes)
11
+ @property({attribute: false})
12
+ validate?: () => Promise<boolean> | boolean;
9
13
 
10
14
  static styles = [
11
15
  resetStyles,
@@ -29,4 +33,4 @@ declare global {
29
33
  interface HTMLElementTagNameMap {
30
34
  "st-form-step": StFormStep;
31
35
  }
32
- }
36
+ }
@@ -1,5 +1,5 @@
1
- import {LitElement, html, css} from "lit";
2
- import {customElement, property, state, queryAssignedElements, query} from "lit/decorators.js";
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property, state, query} from "lit/decorators.js";
3
3
  import {resetStyles} from "../../styles/reset.js";
4
4
  import type {StFormStep} from "./form-step.js";
5
5
 
@@ -13,11 +13,14 @@ export class StForm extends LitElement {
13
13
 
14
14
  @state() private _currentStepIndex = 0;
15
15
 
16
- @queryAssignedElements({selector: "st-form-step"})
17
- private _stepElements!: StFormStep[];
18
-
19
16
  @query("form") private _formEl!: HTMLFormElement;
20
17
 
18
+ private _modalFooterEl: HTMLElement | null = null;
19
+
20
+ private get _steps(): StFormStep[] {
21
+ return Array.from(this.querySelectorAll("st-form-step"));
22
+ }
23
+
21
24
  static styles = [
22
25
  resetStyles,
23
26
  css`
@@ -153,53 +156,151 @@ export class StForm extends LitElement {
153
156
  `,
154
157
  ];
155
158
 
156
- private _handleSlotChange() {
157
- if (this._stepElements.length > 0) {
159
+ override connectedCallback() {
160
+ super.connectedCallback();
161
+ this.updateComplete.then(() => {
158
162
  this._updateStepVisibility();
163
+ this._updateModalFooter();
164
+ });
165
+ }
166
+
167
+ override disconnectedCallback() {
168
+ super.disconnectedCallback();
169
+ if (this._modalFooterEl) {
170
+ this._modalFooterEl.remove();
171
+ this._modalFooterEl = null;
172
+ }
173
+ }
174
+
175
+ override updated(changedProperties: PropertyValues<this>) {
176
+ super.updated(changedProperties);
177
+ this._updateModalFooter();
178
+ }
179
+
180
+ private _updateModalFooter() {
181
+ 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);
192
+ }
193
+
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
+
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) => {
220
+ e.preventDefault();
221
+ this._triggerSubmission();
222
+ });
223
+ this._modalFooterEl.appendChild(submitBtn);
224
+
225
+ } else if (this._modalFooterEl) {
226
+ this._modalFooterEl.remove();
227
+ this._modalFooterEl = null;
159
228
  }
160
229
  }
161
230
 
231
+ private _handleSlotChange() {
232
+ this._updateStepVisibility();
233
+ this._updateModalFooter();
234
+ }
235
+
162
236
  private _updateStepVisibility() {
163
- this._stepElements.forEach((step, index) => {
237
+ const steps = this._steps;
238
+ steps.forEach((step, index) => {
164
239
  step.active = index === this._currentStepIndex;
165
240
  });
166
241
  this.requestUpdate();
167
242
  }
168
243
 
169
244
  private async _validateCurrentContext(): Promise<boolean> {
170
- const scope: ParentNode = this._stepElements.length > 0
171
- ? this._stepElements[this._currentStepIndex]
245
+ const steps = this._steps;
246
+ const scope: ParentNode = steps.length > 0
247
+ ? steps[this._currentStepIndex]
172
248
  : this;
173
249
 
174
250
  let isValid = true;
175
251
 
176
- // 1. Validate standard DOM input components
177
252
  const inputs = Array.from(
178
253
  scope.querySelectorAll<
179
- HTMLElement & {checkValidity?: () => boolean; reportValidity?: () => boolean}
180
- >("*"),
254
+ HTMLElement & {
255
+ checkValidity?: () => boolean;
256
+ reportValidity?: () => boolean;
257
+ required?: boolean;
258
+ value?: any;
259
+ checked?: boolean;
260
+ invalid?: boolean;
261
+ }
262
+ >("st-input, st-select, st-textarea, st-checkbox, st-radio, input, select, textarea"),
181
263
  );
182
264
 
183
265
  for (const input of inputs) {
266
+ if (input.hasAttribute("disabled") || (input as any).disabled) continue;
267
+
268
+ let fieldValid = true;
269
+
184
270
  if (typeof input.checkValidity === "function") {
185
- if (!input.checkValidity()) {
186
- if (isValid && typeof input.reportValidity === "function") {
187
- input.reportValidity();
271
+ fieldValid = input.checkValidity();
272
+ } else {
273
+ const isRequired = input.hasAttribute("required") || input.required;
274
+ if (isRequired) {
275
+ const isToggle = input.tagName === "ST-CHECKBOX" || input.tagName === "ST-RADIO";
276
+ const val = isToggle ? input.checked : input.value;
277
+ if (val === undefined || val === null || val === "" || val === false) {
278
+ fieldValid = false;
188
279
  }
189
- isValid = false;
190
280
  }
191
281
  }
282
+
283
+ if (!fieldValid) {
284
+ if (typeof input.reportValidity === "function") {
285
+ input.reportValidity();
286
+ } else if ("invalid" in input) {
287
+ input.invalid = true;
288
+ }
289
+ if (isValid && typeof (input as any).focus === "function") {
290
+ (input as any).focus();
291
+ }
292
+ isValid = false;
293
+ }
192
294
  }
193
295
 
194
- if (this._stepElements.length === 0 && this._formEl) {
296
+ if (steps.length === 0 && this._formEl) {
195
297
  if (!this._formEl.checkValidity()) {
196
298
  isValid = false;
197
299
  }
198
300
  }
199
301
 
200
- // 2. Execute step-level async/sync validation hook if defined
201
- if (isValid && this._stepElements.length > 0) {
202
- const currentStep = this._stepElements[this._currentStepIndex] as StFormStep & {
302
+ if (isValid && steps.length > 0) {
303
+ const currentStep = steps[this._currentStepIndex] as StFormStep & {
203
304
  validate?: () => Promise<boolean> | boolean;
204
305
  };
205
306
 
@@ -210,7 +311,7 @@ export class StForm extends LitElement {
210
311
  if (!stepPassed) {
211
312
  isValid = false;
212
313
  }
213
- } catch (err) {
314
+ } catch {
214
315
  isValid = false;
215
316
  } finally {
216
317
  this.loading = false;
@@ -273,8 +374,9 @@ export class StForm extends LitElement {
273
374
  const isValid = await this._validateCurrentContext();
274
375
  if (!isValid) return;
275
376
 
276
- const hasSteps = this._stepElements.length > 0;
277
- const isLastStep = hasSteps ? this._currentStepIndex === this._stepElements.length - 1 : true;
377
+ const steps = this._steps;
378
+ const hasSteps = steps.length > 0;
379
+ const isLastStep = hasSteps ? this._currentStepIndex === steps.length - 1 : true;
278
380
 
279
381
  if (hasSteps && !isLastStep) {
280
382
  this._currentStepIndex++;
@@ -295,8 +397,9 @@ export class StForm extends LitElement {
295
397
  }
296
398
 
297
399
  render() {
298
- const hasSteps = this._stepElements?.length > 0;
299
- const isLastStep = hasSteps ? this._currentStepIndex === this._stepElements.length - 1 : true;
400
+ const steps = this._steps;
401
+ const hasSteps = steps.length > 0;
402
+ const isInModal = !!this.closest("st-modal");
300
403
 
301
404
  return html`
302
405
  <div class="${hasSteps ? "has-steps" : ""}" aria-busy=${this.loading}>
@@ -310,7 +413,7 @@ export class StForm extends LitElement {
310
413
  ${hasSteps
311
414
  ? html`
312
415
  <div class="progress-nav" role="tablist">
313
- ${this._stepElements.map((step, index) => {
416
+ ${steps.map((step, index) => {
314
417
  const isActive = index === this._currentStepIndex;
315
418
  const isCompleted = index < this._currentStepIndex;
316
419
 
@@ -329,7 +432,7 @@ export class StForm extends LitElement {
329
432
  </div>
330
433
  <span>${step.title || `Step ${index + 1}`}</span>
331
434
  </div>
332
- ${index < this._stepElements.length - 1
435
+ ${index < steps.length - 1
333
436
  ? html` <div class="step-connector ${isCompleted ? "completed" : ""}"></div> `
334
437
  : ""}
335
438
  `;
@@ -349,29 +452,33 @@ export class StForm extends LitElement {
349
452
  <form @submit=${this._handleSubmit}>
350
453
  <slot @slotchange=${this._handleSlotChange}></slot>
351
454
 
352
- <div class="form-actions">
353
- ${hasSteps && this._currentStepIndex > 0
354
- ? html`
455
+ ${!isInModal
456
+ ? html`
457
+ <div class="form-actions">
458
+ ${hasSteps && this._currentStepIndex > 0
459
+ ? html`
460
+ <st-button
461
+ variant="secondary"
462
+ type="button"
463
+ @click=${this._prevStep}
464
+ ?disabled=${this.loading}
465
+ >
466
+ Back
467
+ </st-button>
468
+ `
469
+ : ""}
470
+
355
471
  <st-button
356
- variant="secondary"
472
+ color="primary"
357
473
  type="button"
358
- @click=${this._prevStep}
474
+ @click=${this._triggerSubmission}
359
475
  ?disabled=${this.loading}
360
476
  >
361
- Back
477
+ ${this.loading ? "Submitting..." : hasSteps && this._currentStepIndex === steps.length - 1 ? "Submit" : hasSteps ? "Continue" : "Submit"}
362
478
  </st-button>
363
- `
364
- : ""}
365
-
366
- <st-button
367
- color="primary"
368
- type="button"
369
- @click=${this._triggerSubmission}
370
- ?disabled=${this.loading}
371
- >
372
- ${this.loading ? "Submitting..." : isLastStep ? "Submit" : "Continue"}
373
- </st-button>
374
- </div>
479
+ </div>
480
+ `
481
+ : ""}
375
482
  </form>
376
483
  </div>
377
484
  `;
@@ -246,3 +246,9 @@ export class StInput extends LitElement {
246
246
  `;
247
247
  }
248
248
  }
249
+
250
+ declare global {
251
+ interface HTMLElementTagNameMap {
252
+ "st-input": StInput;
253
+ }
254
+ }
@@ -153,7 +153,7 @@ export class StPageLayout extends LitElement {
153
153
  --layout-surface-color: var(--st-color-surface, #f8fafc);
154
154
  --layout-border-color: var(--st-color-border, #e2e8f0);
155
155
  --layout-sidebar-width: 16rem;
156
- --layout-right-sidebar-width: 14rem;
156
+ --layout-right-sidebar-width: 16rem;
157
157
  --layout-main-max-width: none;
158
158
  --layout-nav-height: 4rem;
159
159
  }