@sondor-technology/sondor-components 1.0.0 → 1.0.1-develop.2

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 (168) hide show
  1. package/README.md +8 -0
  2. package/dist/components/alert/alert.d.ts +13 -0
  3. package/dist/components/alert/alert.d.ts.map +1 -0
  4. package/dist/components/alert/alert.js +105 -0
  5. package/dist/components/badge/badge.d.ts +13 -0
  6. package/dist/components/badge/badge.d.ts.map +1 -0
  7. package/dist/components/badge/badge.js +101 -0
  8. package/dist/components/breadcrumb/breadcrumb.d.ts +19 -0
  9. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  10. package/dist/components/breadcrumb/breadcrumb.js +99 -0
  11. package/dist/components/button/button.d.ts +23 -0
  12. package/dist/components/button/button.d.ts.map +1 -0
  13. package/dist/components/button/button.js +304 -0
  14. package/dist/components/button-group/button-group.d.ts +6 -0
  15. package/dist/components/button-group/button-group.d.ts.map +1 -0
  16. package/dist/components/button-group/button-group.js +74 -0
  17. package/dist/components/card/card.d.ts +11 -0
  18. package/dist/components/card/card.d.ts.map +1 -0
  19. package/dist/components/card/card.js +54 -0
  20. package/dist/components/checkbox/checkbox.d.ts +28 -0
  21. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  22. package/dist/components/checkbox/checkbox.js +247 -0
  23. package/dist/components/code-block/code-block.d.ts +20 -0
  24. package/dist/components/code-block/code-block.d.ts.map +1 -0
  25. package/dist/components/code-block/code-block.js +260 -0
  26. package/dist/components/content-card/content-card.d.ts +13 -0
  27. package/dist/components/content-card/content-card.d.ts.map +1 -0
  28. package/dist/components/content-card/content-card.js +140 -0
  29. package/dist/components/divider/divider.d.ts +16 -0
  30. package/dist/components/divider/divider.d.ts.map +1 -0
  31. package/dist/components/divider/divider.js +82 -0
  32. package/dist/components/drawer/drawer.d.ts +22 -0
  33. package/dist/components/drawer/drawer.d.ts.map +1 -0
  34. package/dist/components/drawer/drawer.js +226 -0
  35. package/dist/components/field/field.d.ts +11 -0
  36. package/dist/components/field/field.d.ts.map +1 -0
  37. package/dist/components/field/field.js +32 -0
  38. package/dist/components/footer/footer.d.ts +6 -0
  39. package/dist/components/footer/footer.d.ts.map +1 -0
  40. package/dist/components/footer/footer.js +65 -0
  41. package/dist/components/form/form-step.d.ts +13 -0
  42. package/dist/components/form/form-step.d.ts.map +1 -0
  43. package/dist/components/form/form-step.js +41 -0
  44. package/dist/components/form/form.d.ts +25 -0
  45. package/dist/components/form/form.d.ts.map +1 -0
  46. package/dist/components/form/form.js +358 -0
  47. package/dist/components/grid/grid-row.d.ts +17 -0
  48. package/dist/components/grid/grid-row.d.ts.map +1 -0
  49. package/dist/components/grid/grid-row.js +92 -0
  50. package/dist/components/grid/grid.d.ts +13 -0
  51. package/dist/components/grid/grid.d.ts.map +1 -0
  52. package/dist/components/grid/grid.js +42 -0
  53. package/dist/components/icon/icon-paths.d.ts +116 -0
  54. package/dist/components/icon/icon-paths.d.ts.map +1 -0
  55. package/dist/components/icon/icon-paths.js +121 -0
  56. package/dist/components/icon/icon.d.ts +16 -0
  57. package/dist/components/icon/icon.d.ts.map +1 -0
  58. package/dist/components/icon/icon.js +95 -0
  59. package/dist/components/input/input.d.ts +31 -0
  60. package/dist/components/input/input.d.ts.map +1 -0
  61. package/dist/components/input/input.js +268 -0
  62. package/dist/components/link/link.d.ts +16 -0
  63. package/dist/components/link/link.d.ts.map +1 -0
  64. package/dist/components/link/link.js +116 -0
  65. package/dist/components/metadata/metadata.d.ts +18 -0
  66. package/dist/components/metadata/metadata.d.ts.map +1 -0
  67. package/dist/components/metadata/metadata.js +71 -0
  68. package/dist/components/modal/modal.d.ts +24 -0
  69. package/dist/components/modal/modal.d.ts.map +1 -0
  70. package/dist/components/modal/modal.js +163 -0
  71. package/dist/components/page-layout/page-layout.d.ts +46 -0
  72. package/dist/components/page-layout/page-layout.d.ts.map +1 -0
  73. package/dist/components/page-layout/page-layout.js +697 -0
  74. package/dist/components/radio/radio.d.ts +16 -0
  75. package/dist/components/radio/radio.d.ts.map +1 -0
  76. package/dist/components/radio/radio.js +152 -0
  77. package/dist/components/radio-group/radio-group.d.ts +27 -0
  78. package/dist/components/radio-group/radio-group.d.ts.map +1 -0
  79. package/dist/components/radio-group/radio-group.js +161 -0
  80. package/dist/components/router/router.d.ts +7 -0
  81. package/dist/components/router/router.d.ts.map +1 -0
  82. package/dist/components/router/router.js +37 -0
  83. package/dist/components/select/select.d.ts +16 -0
  84. package/dist/components/select/select.d.ts.map +1 -0
  85. package/dist/components/select/select.js +172 -0
  86. package/dist/components/skeleton/skeleton.d.ts +17 -0
  87. package/dist/components/skeleton/skeleton.d.ts.map +1 -0
  88. package/dist/components/skeleton/skeleton.js +78 -0
  89. package/dist/components/switch/switch.d.ts +25 -0
  90. package/dist/components/switch/switch.d.ts.map +1 -0
  91. package/dist/components/switch/switch.js +185 -0
  92. package/dist/components/table/data-table.d.ts +72 -0
  93. package/dist/components/table/data-table.d.ts.map +1 -0
  94. package/dist/components/table/data-table.js +551 -0
  95. package/dist/components/table/table.d.ts +22 -0
  96. package/dist/components/table/table.d.ts.map +1 -0
  97. package/dist/components/table/table.js +167 -0
  98. package/dist/components/tabs/tab-panel.d.ts +8 -0
  99. package/dist/components/tabs/tab-panel.d.ts.map +1 -0
  100. package/dist/components/tabs/tab-panel.js +46 -0
  101. package/dist/components/tabs/tab.d.ts +9 -0
  102. package/dist/components/tabs/tab.d.ts.map +1 -0
  103. package/dist/components/tabs/tab.js +91 -0
  104. package/dist/components/tabs/tabs.d.ts +14 -0
  105. package/dist/components/tabs/tabs.d.ts.map +1 -0
  106. package/dist/components/tabs/tabs.js +85 -0
  107. package/dist/components/textarea/textarea.d.ts +36 -0
  108. package/dist/components/textarea/textarea.d.ts.map +1 -0
  109. package/dist/components/textarea/textarea.js +231 -0
  110. package/dist/components/theme-toggle/theme-toggle.d.ts +15 -0
  111. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -0
  112. package/dist/components/theme-toggle/theme-toggle.js +130 -0
  113. package/dist/components/vertical-nav/vertical-nav-group.d.ts +9 -0
  114. package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -0
  115. package/dist/components/vertical-nav/vertical-nav-group.js +111 -0
  116. package/dist/components/vertical-nav/vertical-nav-item.d.ts +8 -0
  117. package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -0
  118. package/dist/components/vertical-nav/vertical-nav-item.js +65 -0
  119. package/dist/styles/index.css +171 -0
  120. package/dist/styles/reset.d.ts +2 -0
  121. package/dist/styles/reset.d.ts.map +1 -0
  122. package/dist/styles/reset.js +50 -0
  123. package/package.json +3 -2
  124. package/src/components/alert/alert.ts +100 -100
  125. package/src/components/badge/badge.ts +96 -96
  126. package/src/components/breadcrumb/breadcrumb.ts +98 -98
  127. package/src/components/button/button.ts +281 -281
  128. package/src/components/button-group/button-group.ts +67 -67
  129. package/src/components/card/card.ts +52 -52
  130. package/src/components/checkbox/checkbox.ts +238 -238
  131. package/src/components/code-block/code-block.ts +256 -256
  132. package/src/components/content-card/content-card.ts +133 -133
  133. package/src/components/divider/divider.ts +68 -68
  134. package/src/components/drawer/drawer.ts +217 -217
  135. package/src/components/field/field.ts +30 -30
  136. package/src/components/footer/footer.ts +58 -58
  137. package/src/components/form/form-step.ts +32 -32
  138. package/src/components/form/form.ts +376 -376
  139. package/src/components/grid/grid-row.ts +75 -75
  140. package/src/components/grid/grid.ts +36 -36
  141. package/src/components/icon/icon-paths.ts +124 -124
  142. package/src/components/icon/icon.ts +82 -82
  143. package/src/components/input/input.ts +248 -248
  144. package/src/components/link/link.ts +97 -97
  145. package/src/components/metadata/metadata.ts +66 -66
  146. package/src/components/modal/modal.ts +158 -158
  147. package/src/components/page-layout/page-layout.ts +704 -704
  148. package/src/components/radio/radio.ts +142 -142
  149. package/src/components/radio-group/radio-group.ts +157 -157
  150. package/src/components/router/router.ts +46 -46
  151. package/src/components/select/select.ts +161 -161
  152. package/src/components/skeleton/skeleton.ts +67 -67
  153. package/src/components/switch/switch.ts +181 -181
  154. package/src/components/table/data-table.ts +556 -556
  155. package/src/components/table/table.ts +153 -153
  156. package/src/components/tabs/tab-panel.ts +30 -30
  157. package/src/components/tabs/tab.ts +76 -76
  158. package/src/components/tabs/tabs.ts +88 -88
  159. package/src/components/textarea/textarea.ts +215 -215
  160. package/src/components/theme-toggle/theme-toggle.ts +131 -131
  161. package/src/components/vertical-nav/vertical-nav-group.ts +96 -96
  162. package/src/components/vertical-nav/vertical-nav-item.ts +49 -49
  163. package/src/styles/globals.css +58 -58
  164. package/src/styles/index.css +80 -80
  165. package/src/styles/reset.ts +50 -50
  166. package/src/styles/theme.css +58 -58
  167. package/src/styles/typography.css +25 -25
  168. package/tsconfig.json +15 -15
@@ -1,142 +1,142 @@
1
- import {LitElement, html, css} from "lit";
2
- import {customElement, property} from "lit/decorators.js";
3
- import {resetStyles} from "../../styles/reset.js";
4
-
5
- @customElement("st-radio")
6
- export class StRadio extends LitElement {
7
- @property({type: String}) value = "";
8
- @property({type: Boolean, reflect: true}) checked = false;
9
- @property({type: Boolean, reflect: true}) disabled = false;
10
-
11
- @property({attribute: "aria-label"}) ariaLabel = "";
12
-
13
- static styles = [
14
- resetStyles,
15
- css`
16
- :host {
17
- display: inline-block;
18
- outline: none;
19
- user-select: none;
20
- }
21
-
22
- label {
23
- display: inline-flex;
24
- align-items: center;
25
- gap: var(--st-spacing-sm);
26
- cursor: pointer;
27
- color: var(--st-color-text);
28
- font-size: var(--st-font-size-base);
29
- }
30
-
31
- /* Hide the native input visually, but keep it accessible for screen readers */
32
- input {
33
- position: absolute;
34
- width: 1px;
35
- height: 1px;
36
- padding: 0;
37
- margin: -1px;
38
- overflow: hidden;
39
- clip: rect(0, 0, 0, 0);
40
- white-space: nowrap;
41
- border-width: 0;
42
- }
43
-
44
- /* Custom Radio Circle */
45
- .control {
46
- display: flex;
47
- align-items: center;
48
- justify-content: center;
49
- width: 1.25rem;
50
- height: 1.25rem;
51
- border: 1px solid var(--st-color-border);
52
- border-radius: 50%;
53
- background-color: var(--st-color-bg);
54
- transition: all 0.2s ease;
55
- flex-shrink: 0;
56
- }
57
-
58
- /* Inner Dot (Hidden by default) */
59
- .control::after {
60
- content: "";
61
- width: 0.625rem;
62
- height: 0.625rem;
63
- border-radius: 50%;
64
- background-color: var(--st-color-bg); /* Matches the dot color to background initially */
65
- transition:
66
- transform 0.2s ease,
67
- background-color 0.2s ease;
68
- transform: scale(0);
69
- }
70
-
71
- /* Hover State */
72
- label:hover .control {
73
- border-color: var(--st-color-primary-hover);
74
- }
75
-
76
- /* Focus State (Driven by the hidden input) */
77
- input:focus-visible + .control {
78
- box-shadow:
79
- 0 0 0 2px var(--st-color-bg),
80
- 0 0 0 4px var(--st-color-primary);
81
- }
82
-
83
- /* Checked State */
84
- :host([checked]) .control {
85
- border-color: var(--st-color-primary);
86
- background-color: var(--st-color-primary);
87
- }
88
- :host([checked]) .control::after {
89
- background-color: var(--st-white); /* White dot inside the primary circle */
90
- transform: scale(1);
91
- }
92
-
93
- /* Disabled State */
94
- :host([disabled]) {
95
- pointer-events: none;
96
- }
97
- :host([disabled]) label {
98
- opacity: 0.5;
99
- cursor: not-allowed;
100
- }
101
- `,
102
- ];
103
-
104
- private _handleChange(e: Event) {
105
- // Prevent the native change event from leaking out
106
- e.stopPropagation();
107
-
108
- if (!this.disabled) {
109
- // Dispatch a custom event to tell the parent group this radio was selected
110
- this.dispatchEvent(
111
- new CustomEvent("st-radio-select", {
112
- detail: {value: this.value},
113
- bubbles: true,
114
- composed: true,
115
- }),
116
- );
117
- }
118
- }
119
-
120
- render() {
121
- return html`
122
- <label>
123
- <input
124
- type="radio"
125
- aria-label=${this.ariaLabel || ""}
126
- .value=${this.value}
127
- .checked=${this.checked}
128
- ?disabled=${this.disabled}
129
- @change=${this._handleChange}
130
- />
131
- <span class="control"></span>
132
- <span class="label"><slot></slot></span>
133
- </label>
134
- `;
135
- }
136
- }
137
-
138
- declare global {
139
- interface HTMLElementTagNameMap {
140
- "st-radio": StRadio;
141
- }
142
- }
1
+ import {LitElement, html, css} from "lit";
2
+ import {customElement, property} from "lit/decorators.js";
3
+ import {resetStyles} from "../../styles/reset.js";
4
+
5
+ @customElement("st-radio")
6
+ export class StRadio extends LitElement {
7
+ @property({type: String}) value = "";
8
+ @property({type: Boolean, reflect: true}) checked = false;
9
+ @property({type: Boolean, reflect: true}) disabled = false;
10
+
11
+ @property({attribute: "aria-label"}) ariaLabel = "";
12
+
13
+ static styles = [
14
+ resetStyles,
15
+ css`
16
+ :host {
17
+ display: inline-block;
18
+ outline: none;
19
+ user-select: none;
20
+ }
21
+
22
+ label {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ gap: var(--st-spacing-sm);
26
+ cursor: pointer;
27
+ color: var(--st-color-text);
28
+ font-size: var(--st-font-size-base);
29
+ }
30
+
31
+ /* Hide the native input visually, but keep it accessible for screen readers */
32
+ input {
33
+ position: absolute;
34
+ width: 1px;
35
+ height: 1px;
36
+ padding: 0;
37
+ margin: -1px;
38
+ overflow: hidden;
39
+ clip: rect(0, 0, 0, 0);
40
+ white-space: nowrap;
41
+ border-width: 0;
42
+ }
43
+
44
+ /* Custom Radio Circle */
45
+ .control {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ width: 1.25rem;
50
+ height: 1.25rem;
51
+ border: 1px solid var(--st-color-border);
52
+ border-radius: 50%;
53
+ background-color: var(--st-color-bg);
54
+ transition: all 0.2s ease;
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ /* Inner Dot (Hidden by default) */
59
+ .control::after {
60
+ content: "";
61
+ width: 0.625rem;
62
+ height: 0.625rem;
63
+ border-radius: 50%;
64
+ background-color: var(--st-color-bg); /* Matches the dot color to background initially */
65
+ transition:
66
+ transform 0.2s ease,
67
+ background-color 0.2s ease;
68
+ transform: scale(0);
69
+ }
70
+
71
+ /* Hover State */
72
+ label:hover .control {
73
+ border-color: var(--st-color-primary-hover);
74
+ }
75
+
76
+ /* Focus State (Driven by the hidden input) */
77
+ input:focus-visible + .control {
78
+ box-shadow:
79
+ 0 0 0 2px var(--st-color-bg),
80
+ 0 0 0 4px var(--st-color-primary);
81
+ }
82
+
83
+ /* Checked State */
84
+ :host([checked]) .control {
85
+ border-color: var(--st-color-primary);
86
+ background-color: var(--st-color-primary);
87
+ }
88
+ :host([checked]) .control::after {
89
+ background-color: var(--st-white); /* White dot inside the primary circle */
90
+ transform: scale(1);
91
+ }
92
+
93
+ /* Disabled State */
94
+ :host([disabled]) {
95
+ pointer-events: none;
96
+ }
97
+ :host([disabled]) label {
98
+ opacity: 0.5;
99
+ cursor: not-allowed;
100
+ }
101
+ `,
102
+ ];
103
+
104
+ private _handleChange(e: Event) {
105
+ // Prevent the native change event from leaking out
106
+ e.stopPropagation();
107
+
108
+ if (!this.disabled) {
109
+ // Dispatch a custom event to tell the parent group this radio was selected
110
+ this.dispatchEvent(
111
+ new CustomEvent("st-radio-select", {
112
+ detail: {value: this.value},
113
+ bubbles: true,
114
+ composed: true,
115
+ }),
116
+ );
117
+ }
118
+ }
119
+
120
+ render() {
121
+ return html`
122
+ <label>
123
+ <input
124
+ type="radio"
125
+ aria-label=${this.ariaLabel || ""}
126
+ .value=${this.value}
127
+ .checked=${this.checked}
128
+ ?disabled=${this.disabled}
129
+ @change=${this._handleChange}
130
+ />
131
+ <span class="control"></span>
132
+ <span class="label"><slot></slot></span>
133
+ </label>
134
+ `;
135
+ }
136
+ }
137
+
138
+ declare global {
139
+ interface HTMLElementTagNameMap {
140
+ "st-radio": StRadio;
141
+ }
142
+ }
@@ -1,157 +1,157 @@
1
- import {LitElement, html, css, PropertyValues} from "lit";
2
- import {customElement, property, queryAssignedElements} from "lit/decorators.js";
3
- import {resetStyles} from "../../styles/reset.js";
4
- import type {StRadio} from "../radio/radio.js";
5
-
6
- @customElement("st-radio-group")
7
- export class StRadioGroup extends LitElement {
8
- static formAssociated = true;
9
- private _internals: ElementInternals;
10
-
11
- @property({type: String, reflect: true}) name = "";
12
- @property({type: String}) value = "";
13
- @property({type: String}) label = "";
14
- @property({attribute: "help-text"}) helpText = "";
15
- @property({type: Boolean, reflect: true}) disabled = false;
16
- @property({type: Boolean, reflect: true}) invalid = false;
17
-
18
- // Grabs all slotted st-radio components automatically
19
- @queryAssignedElements({selector: "st-radio"})
20
- private _radios!: StRadio[];
21
-
22
- constructor() {
23
- super();
24
- this._internals = this.attachInternals();
25
-
26
- // 1. Trigger error state if the user clicks away and the field is invalid
27
- this.addEventListener("change", () => {
28
- this.invalid = !this._internals.validity.valid;
29
- });
30
-
31
- // 2. Trigger error state if the st-form tries to submit and fails
32
- this.addEventListener("invalid", () => {
33
- this.invalid = true;
34
- });
35
- }
36
-
37
- static styles = [
38
- resetStyles,
39
- css`
40
- :host {
41
- display: block;
42
- }
43
-
44
- fieldset {
45
- border: none;
46
- padding: 0;
47
- margin: 0;
48
- display: flex;
49
- flex-direction: column;
50
- gap: var(--st-spacing-xs);
51
- }
52
-
53
- legend {
54
- font-size: var(--st-font-size-sm);
55
- font-weight: var(--st-font-weight-medium);
56
- color: var(--st-color-text);
57
- padding: 0;
58
- margin-bottom: var(--st-spacing-xs);
59
- }
60
-
61
- .options {
62
- display: flex;
63
- flex-direction: column;
64
- gap: var(--st-spacing-sm);
65
- }
66
-
67
- /* Horizontal layout modifier */
68
- :host([layout="horizontal"]) .options {
69
- flex-direction: row;
70
- gap: var(--st-spacing-md);
71
- }
72
-
73
- .help-text {
74
- font-size: var(--st-font-size-xs);
75
- color: var(--st-color-text-muted);
76
- margin: 0;
77
- }
78
-
79
- :host([invalid]) .help-text {
80
- color: var(--st-color-error);
81
- }
82
- :host([invalid]) legend {
83
- color: var(--st-color-error);
84
- }
85
- `,
86
- ];
87
-
88
- connectedCallback() {
89
- super.connectedCallback();
90
- // Listen for selections from child radios
91
- this.addEventListener("st-radio-select", this._handleRadioSelect as EventListener);
92
- }
93
-
94
- disconnectedCallback() {
95
- super.disconnectedCallback();
96
- this.removeEventListener("st-radio-select", this._handleRadioSelect as EventListener);
97
- }
98
-
99
- private _handleRadioSelect(e: CustomEvent<{value: string}>) {
100
- if (this.disabled) return;
101
- this.value = e.detail.value;
102
- this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
103
- }
104
-
105
- override updated(changedProperties: PropertyValues<this>) {
106
- super.updated(changedProperties);
107
-
108
- // When the group's value changes, sync the form and update all child radios
109
- if (changedProperties.has("value")) {
110
- this._internals.setFormValue(this.value);
111
- this._syncRadios();
112
- }
113
-
114
- if (changedProperties.has("disabled")) {
115
- this._syncRadios();
116
- }
117
- }
118
-
119
- // Loops through children and unchecks/checks them based on the group's current value
120
- private _syncRadios() {
121
- if (!this._radios) return;
122
-
123
- this._radios.forEach((radio) => {
124
- radio.checked = radio.value === this.value;
125
- if (this.disabled) {
126
- radio.disabled = true;
127
- }
128
- });
129
- }
130
-
131
- // Fired natively by the browser if the parent <form> is reset
132
- formResetCallback() {
133
- this.value = "";
134
- this._internals.setFormValue("");
135
- this._syncRadios();
136
- }
137
-
138
- render() {
139
- return html`
140
- <fieldset ?disabled=${this.disabled}>
141
- ${this.label ? html`<legend>${this.label}</legend>` : ""}
142
-
143
- <div class="options">
144
- <slot @slotchange=${this._syncRadios}></slot>
145
- </div>
146
-
147
- ${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
148
- </fieldset>
149
- `;
150
- }
151
- }
152
-
153
- declare global {
154
- interface HTMLElementTagNameMap {
155
- "st-radio-group": StRadioGroup;
156
- }
157
- }
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property, queryAssignedElements} from "lit/decorators.js";
3
+ import {resetStyles} from "../../styles/reset.js";
4
+ import type {StRadio} from "../radio/radio.js";
5
+
6
+ @customElement("st-radio-group")
7
+ export class StRadioGroup extends LitElement {
8
+ static formAssociated = true;
9
+ private _internals: ElementInternals;
10
+
11
+ @property({type: String, reflect: true}) name = "";
12
+ @property({type: String}) value = "";
13
+ @property({type: String}) label = "";
14
+ @property({attribute: "help-text"}) helpText = "";
15
+ @property({type: Boolean, reflect: true}) disabled = false;
16
+ @property({type: Boolean, reflect: true}) invalid = false;
17
+
18
+ // Grabs all slotted st-radio components automatically
19
+ @queryAssignedElements({selector: "st-radio"})
20
+ private _radios!: StRadio[];
21
+
22
+ constructor() {
23
+ super();
24
+ this._internals = this.attachInternals();
25
+
26
+ // 1. Trigger error state if the user clicks away and the field is invalid
27
+ this.addEventListener("change", () => {
28
+ this.invalid = !this._internals.validity.valid;
29
+ });
30
+
31
+ // 2. Trigger error state if the st-form tries to submit and fails
32
+ this.addEventListener("invalid", () => {
33
+ this.invalid = true;
34
+ });
35
+ }
36
+
37
+ static styles = [
38
+ resetStyles,
39
+ css`
40
+ :host {
41
+ display: block;
42
+ }
43
+
44
+ fieldset {
45
+ border: none;
46
+ padding: 0;
47
+ margin: 0;
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--st-spacing-xs);
51
+ }
52
+
53
+ legend {
54
+ font-size: var(--st-font-size-sm);
55
+ font-weight: var(--st-font-weight-medium);
56
+ color: var(--st-color-text);
57
+ padding: 0;
58
+ margin-bottom: var(--st-spacing-xs);
59
+ }
60
+
61
+ .options {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: var(--st-spacing-sm);
65
+ }
66
+
67
+ /* Horizontal layout modifier */
68
+ :host([layout="horizontal"]) .options {
69
+ flex-direction: row;
70
+ gap: var(--st-spacing-md);
71
+ }
72
+
73
+ .help-text {
74
+ font-size: var(--st-font-size-xs);
75
+ color: var(--st-color-text-muted);
76
+ margin: 0;
77
+ }
78
+
79
+ :host([invalid]) .help-text {
80
+ color: var(--st-color-error);
81
+ }
82
+ :host([invalid]) legend {
83
+ color: var(--st-color-error);
84
+ }
85
+ `,
86
+ ];
87
+
88
+ connectedCallback() {
89
+ super.connectedCallback();
90
+ // Listen for selections from child radios
91
+ this.addEventListener("st-radio-select", this._handleRadioSelect as EventListener);
92
+ }
93
+
94
+ disconnectedCallback() {
95
+ super.disconnectedCallback();
96
+ this.removeEventListener("st-radio-select", this._handleRadioSelect as EventListener);
97
+ }
98
+
99
+ private _handleRadioSelect(e: CustomEvent<{value: string}>) {
100
+ if (this.disabled) return;
101
+ this.value = e.detail.value;
102
+ this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
103
+ }
104
+
105
+ override updated(changedProperties: PropertyValues<this>) {
106
+ super.updated(changedProperties);
107
+
108
+ // When the group's value changes, sync the form and update all child radios
109
+ if (changedProperties.has("value")) {
110
+ this._internals.setFormValue(this.value);
111
+ this._syncRadios();
112
+ }
113
+
114
+ if (changedProperties.has("disabled")) {
115
+ this._syncRadios();
116
+ }
117
+ }
118
+
119
+ // Loops through children and unchecks/checks them based on the group's current value
120
+ private _syncRadios() {
121
+ if (!this._radios) return;
122
+
123
+ this._radios.forEach((radio) => {
124
+ radio.checked = radio.value === this.value;
125
+ if (this.disabled) {
126
+ radio.disabled = true;
127
+ }
128
+ });
129
+ }
130
+
131
+ // Fired natively by the browser if the parent <form> is reset
132
+ formResetCallback() {
133
+ this.value = "";
134
+ this._internals.setFormValue("");
135
+ this._syncRadios();
136
+ }
137
+
138
+ render() {
139
+ return html`
140
+ <fieldset ?disabled=${this.disabled}>
141
+ ${this.label ? html`<legend>${this.label}</legend>` : ""}
142
+
143
+ <div class="options">
144
+ <slot @slotchange=${this._syncRadios}></slot>
145
+ </div>
146
+
147
+ ${this.helpText ? html`<div class="help-text">${this.helpText}</div>` : ""}
148
+ </fieldset>
149
+ `;
150
+ }
151
+ }
152
+
153
+ declare global {
154
+ interface HTMLElementTagNameMap {
155
+ "st-radio-group": StRadioGroup;
156
+ }
157
+ }