@sondor-technology/sondor-components 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) 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 +6 -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 +12 -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 +107 -67
  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.js +1 -0
  82. package/dist/components/icon/icon-paths.js.map +1 -0
  83. package/dist/components/icon/icon.js +1 -0
  84. package/dist/components/icon/icon.js.map +1 -0
  85. package/dist/components/input/input.d.ts +14 -0
  86. package/dist/components/input/input.d.ts.map +1 -1
  87. package/dist/components/input/input.js +30 -37
  88. package/dist/components/input/input.js.map +1 -0
  89. package/dist/components/link/link.d.ts +6 -6
  90. package/dist/components/link/link.d.ts.map +1 -1
  91. package/dist/components/link/link.js +42 -19
  92. package/dist/components/link/link.js.map +1 -0
  93. package/dist/components/metadata/metadata.d.ts +3 -3
  94. package/dist/components/metadata/metadata.d.ts.map +1 -1
  95. package/dist/components/metadata/metadata.js +23 -20
  96. package/dist/components/metadata/metadata.js.map +1 -0
  97. package/dist/components/modal/modal.d.ts +4 -16
  98. package/dist/components/modal/modal.d.ts.map +1 -1
  99. package/dist/components/modal/modal.js +99 -62
  100. package/dist/components/modal/modal.js.map +1 -0
  101. package/dist/components/page-layout/page-layout.d.ts +4 -3
  102. package/dist/components/page-layout/page-layout.d.ts.map +1 -1
  103. package/dist/components/page-layout/page-layout.js +96 -69
  104. package/dist/components/page-layout/page-layout.js.map +1 -0
  105. package/dist/components/radio/radio.d.ts +8 -0
  106. package/dist/components/radio/radio.d.ts.map +1 -1
  107. package/dist/components/radio/radio.js +8 -12
  108. package/dist/components/radio/radio.js.map +1 -0
  109. package/dist/components/radio-group/radio-group.d.ts +6 -1
  110. package/dist/components/radio-group/radio-group.d.ts.map +1 -1
  111. package/dist/components/radio-group/radio-group.js +44 -18
  112. package/dist/components/radio-group/radio-group.js.map +1 -0
  113. package/dist/components/router/router.d.ts +5 -7
  114. package/dist/components/router/router.d.ts.map +1 -1
  115. package/dist/components/router/router.js +54 -38
  116. package/dist/components/router/router.js.map +1 -0
  117. package/dist/components/select/select.d.ts +41 -7
  118. package/dist/components/select/select.d.ts.map +1 -1
  119. package/dist/components/select/select.js +467 -150
  120. package/dist/components/select/select.js.map +1 -0
  121. package/dist/components/skeleton/skeleton.d.ts +3 -2
  122. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  123. package/dist/components/skeleton/skeleton.js +10 -13
  124. package/dist/components/skeleton/skeleton.js.map +1 -0
  125. package/dist/components/switch/switch.d.ts +12 -0
  126. package/dist/components/switch/switch.d.ts.map +1 -1
  127. package/dist/components/switch/switch.js +28 -22
  128. package/dist/components/switch/switch.js.map +1 -0
  129. package/dist/components/table/data-table.d.ts +3 -25
  130. package/dist/components/table/data-table.d.ts.map +1 -1
  131. package/dist/components/table/data-table.js +211 -146
  132. package/dist/components/table/data-table.js.map +1 -0
  133. package/dist/components/table/table.d.ts +3 -3
  134. package/dist/components/table/table.d.ts.map +1 -1
  135. package/dist/components/table/table.js +39 -20
  136. package/dist/components/table/table.js.map +1 -0
  137. package/dist/components/tabs/tab-panel.d.ts +8 -1
  138. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  139. package/dist/components/tabs/tab-panel.js +21 -11
  140. package/dist/components/tabs/tab-panel.js.map +1 -0
  141. package/dist/components/tabs/tab.d.ts +10 -1
  142. package/dist/components/tabs/tab.d.ts.map +1 -1
  143. package/dist/components/tabs/tab.js +46 -28
  144. package/dist/components/tabs/tab.js.map +1 -0
  145. package/dist/components/tabs/tabs.d.ts +9 -0
  146. package/dist/components/tabs/tabs.d.ts.map +1 -1
  147. package/dist/components/tabs/tabs.js +76 -13
  148. package/dist/components/tabs/tabs.js.map +1 -0
  149. package/dist/components/textarea/textarea.d.ts +13 -0
  150. package/dist/components/textarea/textarea.d.ts.map +1 -1
  151. package/dist/components/textarea/textarea.js +59 -13
  152. package/dist/components/textarea/textarea.js.map +1 -0
  153. package/dist/components/theme-toggle/theme-toggle.constants.d.ts +3 -0
  154. package/dist/components/theme-toggle/theme-toggle.constants.d.ts.map +1 -0
  155. package/dist/components/theme-toggle/theme-toggle.constants.js +2 -0
  156. package/dist/components/theme-toggle/theme-toggle.constants.js.map +1 -0
  157. package/dist/components/theme-toggle/theme-toggle.d.ts +3 -2
  158. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  159. package/dist/components/theme-toggle/theme-toggle.js +46 -19
  160. package/dist/components/theme-toggle/theme-toggle.js.map +1 -0
  161. package/dist/components/vertical-nav/vertical-nav-group.d.ts +5 -2
  162. package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -1
  163. package/dist/components/vertical-nav/vertical-nav-group.js +50 -14
  164. package/dist/components/vertical-nav/vertical-nav-group.js.map +1 -0
  165. package/dist/components/vertical-nav/vertical-nav-item.d.ts +3 -2
  166. package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -1
  167. package/dist/components/vertical-nav/vertical-nav-item.js +30 -8
  168. package/dist/components/vertical-nav/vertical-nav-item.js.map +1 -0
  169. package/dist/styles/index.css +50 -17
  170. package/dist/styles/reset.js +1 -0
  171. package/dist/styles/reset.js.map +1 -0
  172. package/package.json +2 -1
  173. package/src/components/alert/alert.constants.ts +2 -0
  174. package/src/components/alert/alert.ts +24 -15
  175. package/src/components/badge/badge.constants.ts +2 -0
  176. package/src/components/badge/badge.ts +3 -4
  177. package/src/components/breadcrumb/breadcrumb.ts +2 -2
  178. package/src/components/button/button.constants.ts +8 -0
  179. package/src/components/button/button.ts +184 -144
  180. package/src/components/button-group/button-group.ts +23 -21
  181. package/src/components/checkbox/checkbox.constants.ts +2 -0
  182. package/src/components/checkbox/checkbox.ts +33 -18
  183. package/src/components/code-block/code-block.ts +157 -84
  184. package/src/components/content-card/content-card.ts +10 -16
  185. package/src/components/divider/divider.constants.ts +5 -0
  186. package/src/components/divider/divider.ts +22 -11
  187. package/src/components/drawer/drawer.ts +79 -34
  188. package/src/components/field/field.ts +9 -11
  189. package/src/components/footer/footer.ts +43 -12
  190. package/src/components/form/form.ts +120 -83
  191. package/src/components/grid/grid-row.ts +45 -32
  192. package/src/components/grid/grid.ts +7 -4
  193. package/src/components/input/input.ts +37 -41
  194. package/src/components/link/link.ts +45 -21
  195. package/src/components/metadata/metadata.ts +26 -24
  196. package/src/components/modal/modal.ts +108 -74
  197. package/src/components/page-layout/page-layout.ts +98 -73
  198. package/src/components/radio/radio.ts +8 -13
  199. package/src/components/radio-group/radio-group.ts +45 -21
  200. package/src/components/router/router.ts +73 -50
  201. package/src/components/select/select.ts +502 -131
  202. package/src/components/skeleton/skeleton.ts +14 -14
  203. package/src/components/switch/switch.ts +31 -25
  204. package/src/components/table/data-table.ts +248 -177
  205. package/src/components/table/table.ts +52 -29
  206. package/src/components/tabs/tab-panel.ts +30 -13
  207. package/src/components/tabs/tab.ts +64 -33
  208. package/src/components/tabs/tabs.ts +82 -14
  209. package/src/components/textarea/textarea.ts +63 -15
  210. package/src/components/theme-toggle/theme-toggle.constants.ts +2 -0
  211. package/src/components/theme-toggle/theme-toggle.ts +47 -22
  212. package/src/components/vertical-nav/vertical-nav-group.ts +57 -18
  213. package/src/components/vertical-nav/vertical-nav-item.ts +35 -12
  214. package/src/styles/globals.css +27 -4
  215. package/src/styles/theme.css +39 -31
  216. package/tsconfig.json +2 -1
  217. package/dist/components/card/card.d.ts +0 -11
  218. package/dist/components/card/card.d.ts.map +0 -1
  219. package/dist/components/card/card.js +0 -54
  220. package/src/components/card/card.ts +0 -53
@@ -1,14 +1,14 @@
1
- import { LitElement, html, css } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { resetStyles } from '../../styles/reset.js';
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property} from "lit/decorators.js";
3
+ import {resetStyles} from "../../styles/reset.js";
4
4
 
5
- @customElement('st-grid-row')
5
+ @customElement("st-grid-row")
6
6
  export class StGridRow extends LitElement {
7
- @property({ type: Number }) xs = 1;
8
- @property({ type: Number }) sm?: number;
9
- @property({ type: Number }) md?: number;
10
- @property({ type: Number }) lg?: number;
11
- @property({ type: Number }) xl?: number;
7
+ @property({type: Number}) xs = 1;
8
+ @property({type: Number}) sm?: number;
9
+ @property({type: Number}) md?: number;
10
+ @property({type: Number}) lg?: number;
11
+ @property({type: Number}) xl?: number;
12
12
 
13
13
  static styles = [
14
14
  resetStyles,
@@ -22,46 +22,59 @@ export class StGridRow extends LitElement {
22
22
  }
23
23
 
24
24
  /* Mobile First (xs) */
25
- :host { --_cols: var(--grid-xs, 1); }
25
+ :host {
26
+ --_cols: var(--grid-xs, 1);
27
+ }
26
28
 
27
29
  /* Small (sm: >= 640px) */
28
30
  @media (min-width: 640px) {
29
- :host { --_cols: var(--grid-sm, var(--grid-xs, 1)); }
31
+ :host {
32
+ --_cols: var(--grid-sm, var(--grid-xs, 1));
33
+ }
30
34
  }
31
-
35
+
32
36
  /* Medium (md: >= 768px) */
33
37
  @media (min-width: 768px) {
34
- :host { --_cols: var(--grid-md, var(--grid-sm, var(--grid-xs, 1))); }
38
+ :host {
39
+ --_cols: var(--grid-md, var(--grid-sm, var(--grid-xs, 1)));
40
+ }
35
41
  }
36
-
42
+
37
43
  /* Large (lg: >= 1024px) */
38
44
  @media (min-width: 1024px) {
39
- :host { --_cols: var(--grid-lg, var(--grid-md, var(--grid-sm, var(--grid-xs, 1)))); }
45
+ :host {
46
+ --_cols: var(--grid-lg, var(--grid-md, var(--grid-sm, var(--grid-xs, 1))));
47
+ }
40
48
  }
41
-
49
+
42
50
  /* Extra Large (xl: >= 1280px) */
43
51
  @media (min-width: 1280px) {
44
- :host { --_cols: var(--grid-xl, var(--grid-lg, var(--grid-md, var(--grid-sm, var(--grid-xs, 1))))); }
52
+ :host {
53
+ --_cols: var(
54
+ --grid-xl,
55
+ var(--grid-lg, var(--grid-md, var(--grid-sm, var(--grid-xs, 1))))
56
+ );
57
+ }
45
58
  }
46
- `
59
+ `,
47
60
  ];
48
61
 
49
- updated(changedProperties: Map<string, any>) {
62
+ updated(changedProperties: PropertyValues<this>) {
50
63
  super.updated(changedProperties);
51
-
52
- this.style.setProperty('--grid-xs', this.xs.toString());
53
-
54
- if (this.sm !== undefined) this.style.setProperty('--grid-sm', this.sm.toString());
55
- else this.style.removeProperty('--grid-sm');
56
64
 
57
- if (this.md !== undefined) this.style.setProperty('--grid-md', this.md.toString());
58
- else this.style.removeProperty('--grid-md');
65
+ this.style.setProperty("--grid-xs", this.xs.toString());
66
+
67
+ if (this.sm !== undefined) this.style.setProperty("--grid-sm", this.sm.toString());
68
+ else this.style.removeProperty("--grid-sm");
59
69
 
60
- if (this.lg !== undefined) this.style.setProperty('--grid-lg', this.lg.toString());
61
- else this.style.removeProperty('--grid-lg');
70
+ if (this.md !== undefined) this.style.setProperty("--grid-md", this.md.toString());
71
+ else this.style.removeProperty("--grid-md");
62
72
 
63
- if (this.xl !== undefined) this.style.setProperty('--grid-xl', this.xl.toString());
64
- else this.style.removeProperty('--grid-xl');
73
+ if (this.lg !== undefined) this.style.setProperty("--grid-lg", this.lg.toString());
74
+ else this.style.removeProperty("--grid-lg");
75
+
76
+ if (this.xl !== undefined) this.style.setProperty("--grid-xl", this.xl.toString());
77
+ else this.style.removeProperty("--grid-xl");
65
78
  }
66
79
 
67
80
  render() {
@@ -71,6 +84,6 @@ export class StGridRow extends LitElement {
71
84
 
72
85
  declare global {
73
86
  interface HTMLElementTagNameMap {
74
- 'st-grid-row': StGridRow;
87
+ "st-grid-row": StGridRow;
75
88
  }
76
- }
89
+ }
@@ -1,4 +1,4 @@
1
- import { LitElement, html, css } from 'lit';
1
+ import { LitElement, html, css, PropertyValues } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
  import { resetStyles } from '../../styles/reset.js';
4
4
 
@@ -19,10 +19,13 @@ export class StGrid extends LitElement {
19
19
  `
20
20
  ];
21
21
 
22
- updated(changedProperties: Map<string, any>) {
22
+ updated(changedProperties: PropertyValues<this>) {
23
23
  super.updated(changedProperties);
24
- // This CSS variable will automatically cascade down the DOM tree
25
- this.style.setProperty('--grid-gap', this.gap);
24
+
25
+ // Performance optimization: Only touch the DOM if the gap property actually changed
26
+ if (changedProperties.has('gap')) {
27
+ this.style.setProperty('--grid-gap', this.gap);
28
+ }
26
29
  }
27
30
 
28
31
  render() {
@@ -1,6 +1,6 @@
1
1
  import {LitElement, html, css, PropertyValues} from "lit";
2
2
  import {customElement, property, query} from "lit/decorators.js";
3
- import {ifDefined} from "lit/directives/if-defined.js"; // Helper for optional attributes
3
+ import {ifDefined} from "lit/directives/if-defined.js";
4
4
  import {resetStyles} from "../../styles/reset.js";
5
5
 
6
6
  export type InputType = "text" | "password" | "email" | "number" | "search" | "url";
@@ -8,6 +8,7 @@ export type InputType = "text" | "password" | "email" | "number" | "search" | "u
8
8
  @customElement("st-input")
9
9
  export class StInput extends LitElement {
10
10
  static formAssociated = true;
11
+ static shadowRootOptions = {...LitElement.shadowRootOptions, delegatesFocus: true};
11
12
  private _internals: ElementInternals;
12
13
 
13
14
  @query("input") private _inputEl!: HTMLInputElement;
@@ -17,8 +18,8 @@ export class StInput extends LitElement {
17
18
  @property({type: String}) placeholder = "";
18
19
  @property({type: String}) type: InputType = "text";
19
20
  @property({attribute: "help-text"}) helpText = "";
21
+ @property({ type: String }) autocomplete?: string;
20
22
 
21
- // Validation Properties
22
23
  @property({type: Boolean, reflect: true}) required = false;
23
24
  @property({type: Number}) minlength?: number;
24
25
  @property({type: Number}) maxlength?: number;
@@ -32,12 +33,10 @@ export class StInput extends LitElement {
32
33
  super();
33
34
  this._internals = this.attachInternals();
34
35
 
35
- // 1. Trigger error state if the user clicks away and the field is invalid
36
36
  this.addEventListener("focusout", () => {
37
37
  this.invalid = !this._inputEl.validity.valid;
38
38
  });
39
39
 
40
- // 2. Trigger error state if the st-form tries to submit and fails
41
40
  this.addEventListener("invalid", () => {
42
41
  this.invalid = true;
43
42
  });
@@ -61,41 +60,34 @@ export class StInput extends LitElement {
61
60
  font-weight: var(--st-font-weight-medium);
62
61
  color: var(--st-color-text);
63
62
  margin-bottom: 0.5em;
63
+ cursor: pointer;
64
64
  }
65
65
 
66
- /* 1. The Wrapper acts as the visual input field */
67
66
  .input-wrapper {
68
67
  display: flex;
69
68
  align-items: center;
70
69
  gap: var(--st-spacing-sm);
71
70
  width: 100%;
72
-
73
71
  background-color: var(--st-color-bg);
74
72
  border: 1px solid var(--st-color-border);
75
73
  border-radius: 4px;
76
-
77
- /* Horizontal padding on the wrapper, vertical on the input */
78
74
  padding: 0 var(--st-spacing-md);
79
75
  transition:
80
76
  border-color 0.2s ease,
81
77
  box-shadow 0.2s ease;
82
78
  }
83
79
 
84
- /* 2. Apply focus ring to the wrapper when the internal input is focused */
85
80
  .input-wrapper:focus-within {
86
81
  border-color: var(--st-color-primary);
87
82
  box-shadow: 0 0 0 1px var(--st-color-primary);
88
83
  }
89
84
 
90
- /* 3. The actual input becomes transparent and fills the wrapper */
91
85
  input {
92
86
  flex: 1;
93
87
  width: 100%;
94
88
  border: none;
95
89
  background: transparent;
96
90
  outline: none;
97
-
98
- /* Vertical padding stretches the wrapper */
99
91
  padding: var(--st-spacing-sm) 0;
100
92
  font-size: var(--st-font-size-base);
101
93
  color: var(--st-color-text);
@@ -105,77 +97,63 @@ export class StInput extends LitElement {
105
97
  color: var(--st-color-text-muted);
106
98
  }
107
99
 
108
- /* Slotted Icons */
109
100
  ::slotted(svg) {
110
101
  width: 1.25em;
111
102
  height: 1.25em;
112
- /* Default icons to the muted text color */
113
103
  color: var(--st-color-text-muted);
114
104
  fill: currentColor;
115
105
  flex-shrink: 0;
116
106
  }
117
107
 
118
- /* Interactive Slotted Elements (like clear buttons) */
119
108
  ::slotted(st-button) {
120
- /* Pull the button back slightly so it aligns nicely with the input padding */
121
109
  margin-right: calc(var(--st-spacing-sm) * -1);
122
110
  }
123
111
 
124
- /* Invalid State */
112
+ .help-text {
113
+ font-size: var(--st-font-size-xs);
114
+ color: var(--st-color-text-muted);
115
+ margin: 0;
116
+ }
117
+
125
118
  :host([invalid]) .input-wrapper {
126
119
  border-color: var(--st-color-error);
127
120
  }
121
+
128
122
  :host([invalid]) .input-wrapper:focus-within {
129
123
  box-shadow: 0 0 0 1px var(--st-color-error);
130
124
  }
125
+
131
126
  :host([invalid]) .help-text {
132
127
  color: var(--st-color-error);
133
128
  }
134
129
 
135
- /* Disabled & Readonly States */
136
130
  :host([disabled]) .input-wrapper {
137
131
  opacity: 0.5;
138
132
  cursor: not-allowed;
139
133
  background-color: var(--st-color-surface);
140
134
  }
135
+
141
136
  :host([disabled]) input {
142
137
  cursor: not-allowed;
143
138
  }
139
+
144
140
  :host([readonly]) .input-wrapper {
145
141
  background-color: var(--st-color-surface);
146
142
  }
147
-
148
- .help-text {
149
- font-size: var(--st-font-size-xs);
150
- color: var(--st-color-text-muted);
151
- margin: 0;
152
- }
153
-
154
- :host([invalid]) .input-wrapper {
155
- border-color: var(--st-color-error);
156
- }
157
- :host([invalid]) .help-text {
158
- color: var(--st-color-error);
159
- }
160
143
  `,
161
144
  ];
162
145
 
163
146
  override updated(changedProperties: PropertyValues<this>) {
164
147
  super.updated(changedProperties);
165
148
 
166
- // Sync the value to the form engine
167
149
  if (changedProperties.has("value")) {
168
150
  this._internals.setFormValue(this.value);
169
151
  }
170
-
171
- // Sync validity whenever properties change (like value or required status)
172
152
  this._syncValidity();
173
153
  }
174
154
 
175
155
  private _syncValidity() {
176
156
  if (!this._inputEl) return;
177
-
178
- // setValidity arguments: (ValidityState object, Custom Error Message, Anchor Element for Tooltip)
179
157
  this._internals.setValidity(
180
158
  this._inputEl.validity,
181
159
  this._inputEl.validationMessage,
@@ -186,11 +164,9 @@ export class StInput extends LitElement {
186
164
  formResetCallback() {
187
165
  this.value = "";
188
166
  this._internals.setFormValue("");
189
- // Need to wait for the render cycle to finish before checking validity on reset
190
167
  this.updateComplete.then(() => this._syncValidity());
191
168
  }
192
169
 
193
- // Expose standard native methods so external scripts can call them on the component
194
170
  checkValidity() {
195
171
  return this._internals.checkValidity();
196
172
  }
@@ -200,7 +176,7 @@ export class StInput extends LitElement {
200
176
  }
201
177
 
202
178
  private _handleInput(e: Event) {
203
- const target = e.target as HTMLTextAreaElement;
179
+ const target = e.target as HTMLInputElement;
204
180
  this.value = target.value;
205
181
 
206
182
  if (this.invalid && target.validity.valid) {
@@ -214,7 +190,14 @@ export class StInput extends LitElement {
214
190
  this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
215
191
  }
216
192
 
217
- render() {
193
+ render() {
194
+ const helpTextId = this.helpText ? "help-text" : undefined;
195
+
196
+ // Security: Prevent OS/Extensions from logging sensitive data via spellcheck
197
+ const isSensitive = this.type === "password" || this.type === "email";
198
+ const safeSpellcheck = isSensitive ? "false" : undefined;
199
+ const safeAutocapitalize = isSensitive ? "none" : undefined;
200
+
218
201
  return html`
219
202
  <div class="form-control">
220
203
  ${this.label ? html`<label for="input">${this.label}</label>` : ""}
@@ -234,6 +217,11 @@ export class StInput extends LitElement {
234
217
  minlength=${ifDefined(this.minlength)}
235
218
  maxlength=${ifDefined(this.maxlength)}
236
219
  pattern=${ifDefined(this.pattern)}
220
+ autocomplete=${ifDefined(this.autocomplete as any)}
221
+ spellcheck=${ifDefined(safeSpellcheck)}
222
+ autocapitalize=${ifDefined(safeAutocapitalize)}
223
+ aria-invalid=${this.invalid ? "true" : "false"}
224
+ aria-describedby=${ifDefined(helpTextId)}
237
225
  @input=${this._handleInput}
238
226
  @change=${this._handleChange}
239
227
  />
@@ -241,8 +229,16 @@ export class StInput extends LitElement {
241
229
  <slot name="suffix"></slot>
242
230
  </div>
243
231
 
244
- ${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
232
+ ${this.helpText
233
+ ? html`<div id="help-text" class="help-text">${this.helpText}</div>`
234
+ : ""}
245
235
  </div>
246
236
  `;
247
237
  }
248
238
  }
239
+
240
+ declare global {
241
+ interface HTMLElementTagNameMap {
242
+ "st-input": StInput;
243
+ }
244
+ }
@@ -1,21 +1,23 @@
1
- import { LitElement, html, css } 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 {ifDefined} from "lit/directives/if-defined.js";
4
+ import {resetStyles} from "../../styles/reset.js";
4
5
 
5
- @customElement('st-link')
6
+ @customElement("st-link")
6
7
  export class StLink extends LitElement {
7
- @property({ type: String }) href = '#';
8
- @property({ type: String }) target: '_blank' | '_self' = '_self';
9
- @property({ type: String, reflect: true }) size: 'sm' | 'md' | 'lg' = 'md';
10
- @property({ type: String, reflect: true }) variant: 'inline' | 'standalone' = 'inline';
11
- @property({ type: String, reflect: true }) color: 'default' | 'primary' | 'muted' = 'primary';
8
+ @property({type: String}) href = "#";
9
+ @property({type: String}) target: "_blank" | "_self" = "_self";
10
+ @property({type: String, reflect: true}) size: "sm" | "md" | "lg" = "md";
11
+ @property({type: String, reflect: true}) variant: "inline" | "standalone" = "inline";
12
+ @property({type: String, reflect: true}) color: "default" | "primary" | "muted" = "primary";
12
13
 
13
14
  static styles = [
14
15
  resetStyles,
15
16
  css`
16
17
  :host {
17
18
  display: inline-flex;
18
- align-items: center;
19
+ align-items: baseline; /* Ensures it sits perfectly inside a line of text */
20
+ vertical-align: baseline;
19
21
  }
20
22
 
21
23
  a {
@@ -24,7 +26,17 @@ export class StLink extends LitElement {
24
26
  gap: 0.25em;
25
27
  text-decoration: none;
26
28
  font-weight: 500;
27
- transition: color 0.2s ease, opacity 0.2s ease;
29
+ transition:
30
+ color 0.2s ease,
31
+ opacity 0.2s ease,
32
+ text-decoration-thickness 0.2s ease;
33
+ }
34
+
35
+ /* Accessibility: Clear focus indicators for keyboard navigation */
36
+ a:focus-visible {
37
+ outline: 2px solid var(--st-color-primary);
38
+ outline-offset: 2px;
39
+ border-radius: 2px;
28
40
  }
29
41
 
30
42
  /* ==========================================================
@@ -54,24 +66,28 @@ export class StLink extends LitElement {
54
66
  /* ==========================================================
55
67
  Sizes
56
68
  ========================================================== */
57
- :host([size="sm"]) a { font-size: 0.875rem; }
58
- :host([size="md"]) a { font-size: 1rem; }
59
- :host([size="lg"]) a { font-size: 1.125rem; }
69
+ :host([size="sm"]) a {
70
+ font-size: 0.875rem;
71
+ }
72
+ :host([size="md"]) a {
73
+ font-size: 1rem;
74
+ }
75
+ :host([size="lg"]) a {
76
+ font-size: 1.125rem;
77
+ }
60
78
 
61
79
  /* ==========================================================
62
80
  Variants
63
81
  ========================================================== */
64
- /* inline links live inside paragraphs and underline on hover */
65
82
  :host([variant="inline"]) a {
66
83
  text-decoration: underline;
67
84
  text-underline-offset: 4px;
68
- font-weight: inherit; /* Inherits from surrounding paragraph text */
85
+ font-weight: inherit;
69
86
  }
70
87
  :host([variant="inline"]) a:hover {
71
88
  text-decoration-thickness: 2px;
72
89
  }
73
90
 
74
- /* standalone links (e.g., footers, sidebars) have no underline by default */
75
91
  :host([variant="standalone"]) a {
76
92
  text-decoration: none;
77
93
  }
@@ -79,12 +95,20 @@ export class StLink extends LitElement {
79
95
  text-decoration: underline;
80
96
  text-underline-offset: 4px;
81
97
  }
82
- `
98
+ `,
83
99
  ];
84
100
 
85
101
  render() {
102
+ // SECURITY: Prevent XSS via javascript: URI injection
103
+ const sanitizedHref = this.href?.trim().toLowerCase().startsWith("javascript:")
104
+ ? "#blocked"
105
+ : this.href;
106
+
107
+ // SECURITY: Prevent Reverse Tabnabbing for external links
108
+ const secureRel = this.target === "_blank" ? "noopener noreferrer" : undefined;
109
+
86
110
  return html`
87
- <a href="${this.href}" target="${this.target}">
111
+ <a href="${sanitizedHref}" target="${this.target}" rel="${ifDefined(secureRel)}">
88
112
  <slot></slot>
89
113
  </a>
90
114
  `;
@@ -93,6 +117,6 @@ export class StLink extends LitElement {
93
117
 
94
118
  declare global {
95
119
  interface HTMLElementTagNameMap {
96
- 'st-link': StLink;
120
+ "st-link": StLink;
97
121
  }
98
- }
122
+ }
@@ -1,58 +1,60 @@
1
- import { LitElement, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import {LitElement, nothing, PropertyValues} from "lit";
2
+ import {customElement, property} from "lit/decorators.js";
3
3
 
4
- @customElement('st-metadata')
4
+ @customElement("st-metadata")
5
5
  export class StMetadata extends LitElement {
6
- @property({ type: String }) pageTitle = '';
7
- @property({ type: String }) description = '';
8
- @property({ type: String }) image = '';
6
+ @property({type: String}) pageTitle = "";
7
+ @property({type: String}) description = "";
8
+ @property({type: String}) image = "";
9
9
 
10
- protected createRenderRoot() { return this; }
10
+ protected createRenderRoot() {
11
+ return this;
12
+ }
11
13
 
12
- updated() {
14
+ // 🚀 Added strict PropertyValues typing and super call
15
+ override updated(changedProperties: PropertyValues<this>) {
16
+ super.updated(changedProperties);
13
17
  this._updateMetadata();
14
18
  }
15
19
 
16
20
  private _updateMetadata() {
17
- // Read site name from environment variable (e.g., VITE_SITE_NAME)
18
- const siteName = (import.meta as any).env?.VITE_SITE_NAME || '';
21
+ const siteName = (import.meta as any).env?.VITE_SITE_NAME || "";
19
22
 
20
23
  if (this.pageTitle) {
21
- // If site name is configured, append it; otherwise use the title standalone
22
24
  document.title = siteName ? `${this.pageTitle} - ${siteName}` : this.pageTitle;
23
- this._setMeta('property', 'og:title', this.pageTitle);
25
+ this._setMeta("property", "og:title", this.pageTitle);
24
26
  }
25
27
 
26
28
  if (this.description) {
27
- this._setMeta('name', 'description', this.description);
28
- this._setMeta('property', 'og:description', this.description);
29
+ this._setMeta("name", "description", this.description);
30
+ this._setMeta("property", "og:description", this.description);
29
31
  }
30
32
 
31
33
  if (this.image) {
32
- this._setMeta('property', 'og:image', this.image);
34
+ this._setMeta("property", "og:image", this.image);
33
35
  }
34
-
36
+
35
37
  this._setCanonical(window.location.href);
36
38
  }
37
39
 
38
- private _setMeta(attrName: 'name' | 'property', attrValue: string, content: string) {
40
+ private _setMeta(attrName: "name" | "property", attrValue: string, content: string) {
39
41
  let meta = document.head.querySelector(`meta[${attrName}="${attrValue}"]`);
40
42
  if (!meta) {
41
- meta = document.createElement('meta');
43
+ meta = document.createElement("meta");
42
44
  meta.setAttribute(attrName, attrValue);
43
45
  document.head.appendChild(meta);
44
46
  }
45
- meta.setAttribute('content', content);
47
+ meta.setAttribute("content", content);
46
48
  }
47
49
 
48
50
  private _setCanonical(url: string) {
49
51
  let link = document.head.querySelector('link[rel="canonical"]');
50
52
  if (!link) {
51
- link = document.createElement('link');
52
- link.setAttribute('rel', 'canonical');
53
+ link = document.createElement("link");
54
+ link.setAttribute("rel", "canonical");
53
55
  document.head.appendChild(link);
54
56
  }
55
- link.setAttribute('href', url);
57
+ link.setAttribute("href", url);
56
58
  }
57
59
 
58
60
  render() {
@@ -62,6 +64,6 @@ export class StMetadata extends LitElement {
62
64
 
63
65
  declare global {
64
66
  interface HTMLElementTagNameMap {
65
- 'st-metadata': StMetadata;
67
+ "st-metadata": StMetadata;
66
68
  }
67
- }
69
+ }