@reliability-design/web 1.4.6 → 1.4.8

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 (148) hide show
  1. package/CHANGELOG.md +246 -49
  2. package/dist/components/rel-accordion/README.md +133 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +172 -61
  5. package/dist/components/rel-avatar/README.md +118 -130
  6. package/dist/components/rel-badge/README.md +97 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +190 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +186 -166
  13. package/dist/components/rel-checkbox/README.md +96 -75
  14. package/dist/components/rel-chip/README.md +138 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +88 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-layout/README.md +34 -0
  21. package/dist/components/rel-menu/README.md +180 -151
  22. package/dist/components/rel-menubar/README.md +218 -329
  23. package/dist/components/rel-otp/README.md +171 -440
  24. package/dist/components/rel-pagination/README.md +100 -424
  25. package/dist/components/rel-popover/README.md +203 -568
  26. package/dist/components/rel-progress/README.md +88 -110
  27. package/dist/components/rel-radio/README.md +131 -138
  28. package/dist/components/rel-range-picker/README.md +165 -0
  29. package/dist/components/rel-resizable/README.md +199 -140
  30. package/dist/components/rel-segmented-button/README.md +156 -141
  31. package/dist/components/rel-select/README.md +156 -93
  32. package/dist/components/rel-sheet/README.md +168 -224
  33. package/dist/components/rel-skeleton/README.md +104 -0
  34. package/dist/components/rel-slider/README.md +131 -135
  35. package/dist/components/rel-snackbar/README.md +168 -134
  36. package/dist/components/rel-stepper/README.md +162 -160
  37. package/dist/components/rel-switch/README.md +117 -102
  38. package/dist/components/rel-tab/README.md +173 -163
  39. package/dist/components/rel-textfield/README.md +176 -69
  40. package/dist/components/rel-time-picker/README.md +122 -157
  41. package/dist/components/rel-toast/README.md +165 -148
  42. package/dist/components/rel-tooltip/README.md +119 -111
  43. package/dist/components/rel-upload/README.md +220 -0
  44. package/dist/index.d.mts +1726 -447
  45. package/dist/index.d.ts +1726 -447
  46. package/dist/index.js +13715 -10870
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +14018 -11163
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/styles/focus-ring.ts +84 -0
  51. package/dist/styles/vars.css +892 -866
  52. package/dist/{vars-XNMTSRVN.css → vars-DXX6QOAG.css} +892 -866
  53. package/package.json +3 -2
  54. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  55. package/dist/components/rel-alert/rel-alert.ts +0 -305
  56. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  57. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  58. package/dist/components/rel-badge/rel-badge.ts +0 -175
  59. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  60. package/dist/components/rel-button/rel-button.ts +0 -560
  61. package/dist/components/rel-calendar/index.ts +0 -1
  62. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  63. package/dist/components/rel-card/rel-card.ts +0 -88
  64. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  65. package/dist/components/rel-carousel/index.ts +0 -1
  66. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  67. package/dist/components/rel-chart/README.md +0 -286
  68. package/dist/components/rel-chart/index.ts +0 -1
  69. package/dist/components/rel-chart/rel-chart.ts +0 -754
  70. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  71. package/dist/components/rel-chip/rel-chip.ts +0 -483
  72. package/dist/components/rel-container/rel-container.ts +0 -95
  73. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  74. package/dist/components/rel-date-picker/date-format.ts +0 -47
  75. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  76. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  77. package/dist/components/rel-date-picker/index.ts +0 -1
  78. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  79. package/dist/components/rel-divider/rel-divider.ts +0 -54
  80. package/dist/components/rel-drawer/index.ts +0 -1
  81. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  82. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  83. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  84. package/dist/components/rel-layout/rel-layout.ts +0 -49
  85. package/dist/components/rel-menu/rel-menu.ts +0 -541
  86. package/dist/components/rel-menubar/index.ts +0 -2
  87. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  88. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  89. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  90. package/dist/components/rel-nav-menu/README.md +0 -289
  91. package/dist/components/rel-nav-menu/index.ts +0 -3
  92. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  93. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  94. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  95. package/dist/components/rel-navbar/README.md +0 -126
  96. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  97. package/dist/components/rel-otp/index.ts +0 -1
  98. package/dist/components/rel-otp/rel-otp.ts +0 -527
  99. package/dist/components/rel-pagination/index.ts +0 -1
  100. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  101. package/dist/components/rel-popover/index.ts +0 -2
  102. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  103. package/dist/components/rel-popover/rel-popover.ts +0 -475
  104. package/dist/components/rel-progress/rel-progress.ts +0 -234
  105. package/dist/components/rel-radio/rel-radio.ts +0 -481
  106. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  107. package/dist/components/rel-resizable/index.ts +0 -3
  108. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  109. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  110. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  111. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  112. package/dist/components/rel-select/rel-select.ts +0 -1416
  113. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  114. package/dist/components/rel-sidebar/README.md +0 -209
  115. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  116. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  117. package/dist/components/rel-slider/rel-slider.ts +0 -673
  118. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  119. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  120. package/dist/components/rel-switch/rel-switch.ts +0 -199
  121. package/dist/components/rel-tab/rel-tab.ts +0 -264
  122. package/dist/components/rel-table/README.md +0 -178
  123. package/dist/components/rel-table/rel-table-body.ts +0 -22
  124. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  125. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  126. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  127. package/dist/components/rel-table/rel-table-head.ts +0 -47
  128. package/dist/components/rel-table/rel-table-header.ts +0 -22
  129. package/dist/components/rel-table/rel-table-row.ts +0 -37
  130. package/dist/components/rel-table/rel-table.ts +0 -62
  131. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  132. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  133. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  134. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  135. package/dist/components/rel-toast/rel-toast.ts +0 -206
  136. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  137. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  138. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  139. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  140. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  141. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  142. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  144. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  145. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  146. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  147. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  148. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -1,481 +0,0 @@
1
- import { LitElement, html, css, nothing, type PropertyValues } from "lit";
2
- import { customElement, property } from "lit/decorators.js";
3
- import "@reliability-design/icons/rel-icon";
4
- import type { Orientation } from "../rel-stepper/rel-stepper.js";
5
-
6
- export interface RadioOption {
7
- value: string;
8
- label: string;
9
- supportingText?: string;
10
- disabled?: boolean;
11
- /** rel-icon name rendered before the label (Figma Has Icon/Avatar, Type=Icon). */
12
- icon?: string;
13
- /** 1–2 initials rendered as a circular chip before the label (Type=Avatar).
14
- * Ignored when `icon` is also set. */
15
- avatar?: string;
16
- }
17
-
18
- type RadioSize = "small" | "medium" | "large";
19
- type RadioDensity = "compact" | "comfortable";
20
- type RadioAlignment = "leading" | "trailing";
21
-
22
- @customElement("rel-radio")
23
- export class RelRadio extends LitElement {
24
- @property({ type: Array }) options: RadioOption[] = [];
25
-
26
- @property({ type: String }) name = "radio-group";
27
-
28
- /** Layout axis of the option list. `"vertical"` (default) stacks options in
29
- * a column; `"horizontal"` lays them out in a wrapping row. */
30
- @property({ type: String, reflect: true }) orientation: Orientation = "vertical";
31
-
32
- /** Control scale (Figma Size). `"medium"` (default, Figma M) = 20px control
33
- * with body-lg labels; `"large"` (Figma L) = 24px control with body-xl
34
- * labels; `"small"` (Figma S) = 20px control with body-md labels — S and M
35
- * share the control size in the spec, only typography steps down. */
36
- @property({ type: String, reflect: true }) size: RadioSize = "medium";
37
-
38
- /** Supporting-text layout (Figma Density). `"compact"` (default) puts the
39
- * supporting text on its own line under the row, indented past the control;
40
- * `"comfortable"` stacks label + supporting text beside the control. */
41
- @property({ type: String, reflect: true }) density: RadioDensity = "compact";
42
-
43
- /** Control position (Figma Layout). `"leading"` (default) renders the
44
- * control before the label; `"trailing"` pushes it to the far edge of the
45
- * row. Trailing has no compact layout in the spec, so it always renders
46
- * with comfortable density. Meant for vertical orientation — trailing
47
- * options stretch to the group's full width. */
48
- @property({ type: String, reflect: true }) alignment: RadioAlignment = "leading";
49
-
50
- @property({ type: String, reflect: true })
51
- selectedValue: string | null = null;
52
-
53
- @property({ type: Boolean })
54
- disabled = false;
55
-
56
- @property({ type: Boolean }) error = false;
57
-
58
- @property({ type: String }) errorMessage = "";
59
-
60
- /** Accessible name for the radiogroup (it renders no visible group label). */
61
- @property({ type: String, attribute: "field-aria-label" }) fieldAriaLabel?: string;
62
-
63
- @property({ type: String }) value = "";
64
-
65
- checked = false;
66
-
67
- private _onChange: (value: string) => void = () => {};
68
- private _onTouched: () => void = () => {};
69
-
70
- static styles = css`
71
- :host {
72
- color: var(--rel-text-default);
73
- display: block;
74
- font-family: var(--rel-fonts-font-family);
75
- }
76
-
77
- .rel-radio-group {
78
- display: flex;
79
- flex-direction: column;
80
- align-items: flex-start;
81
- gap: var(--rel-spacing-xs);
82
- }
83
-
84
- /* Horizontal orientation — options flow in a wrapping row. Wider column
85
- gap than the vertical rhythm so adjacent option labels read as separate
86
- choices; row gap keeps the vertical value for wrapped lines. */
87
- :host([orientation="horizontal"]) .rel-radio-group {
88
- flex-direction: row;
89
- flex-wrap: wrap;
90
- gap: var(--rel-spacing-xs) var(--rel-spacing-xl);
91
- }
92
-
93
- /* Trailing alignment — options stretch so the control can sit on the far
94
- edge of the group's width. */
95
- :host([alignment="trailing"]) .rel-radio-group {
96
- align-items: stretch;
97
- }
98
-
99
- .rel-radio-option {
100
- display: inline-flex;
101
- flex-direction: column;
102
- gap: var(--rel-spacing-3xs);
103
- cursor: pointer;
104
- }
105
-
106
- .rel-radio-option.disabled {
107
- /* Figma State=Disabled: whole option at 40% opacity, colors unchanged. */
108
- opacity: 0.4;
109
- cursor: not-allowed !important;
110
- }
111
-
112
- .rel-radio-option:focus-within:has(input:focus-visible) {
113
- outline: 1.5px solid var(--rel-border-focus);
114
- outline-offset: 1px;
115
- }
116
-
117
- .radio-main {
118
- /* Containing block for the hidden input below — without this the
119
- absolutely-positioned input resolves against the document (or any
120
- positioned ancestor in the consumer page), where it neither moves
121
- with intermediate scroll containers nor stays aligned with the
122
- visible radio. Tabbing to it then makes the browser scroll the
123
- WRONG scroller, shoving the whole page up (same bug as
124
- rel-checkbox — keep the two fixes in sync). */
125
- position: relative;
126
- display: flex;
127
- align-items: center;
128
- /* Compact row rhythm (control ↔ label). Comfortable widens to 12px. */
129
- gap: var(--rel-spacing-xs);
130
- }
131
-
132
- .rel-radio-option.comfortable .radio-main {
133
- gap: var(--rel-spacing-sm);
134
- }
135
-
136
- :host([alignment="trailing"]) .radio-main {
137
- flex-direction: row-reverse;
138
- justify-content: space-between;
139
- }
140
-
141
- input {
142
- position: absolute;
143
- inset: 0;
144
- width: 100%;
145
- height: 100%;
146
- margin: 0;
147
- opacity: 0;
148
- pointer-events: none;
149
- }
150
-
151
- /* Control box (Figma .Radio Control) — the drawn ring is inset inside a
152
- larger hit/spacing box: 16px ring in a 20px box (M/S), 20px ring in a
153
- 24px box (L). Indent math below uses the BOX size. */
154
- .radio-custom {
155
- width: 20px;
156
- height: 20px;
157
- display: inline-flex;
158
- align-items: center;
159
- justify-content: center;
160
- position: relative;
161
- flex-shrink: 0;
162
- }
163
-
164
- .radio-custom::before {
165
- content: "";
166
- box-sizing: border-box;
167
- width: 16px;
168
- height: 16px;
169
- border: 2px solid var(--rel-text-default);
170
- border-radius: 50%;
171
- }
172
-
173
- .radio-custom::after {
174
- content: "";
175
- position: absolute;
176
- width: 8px;
177
- height: 8px;
178
- border-radius: 50%;
179
- background: var(--rel-text-default);
180
- display: none;
181
- }
182
-
183
- input:checked + .radio-custom::after {
184
- display: block;
185
- }
186
-
187
- /* Comfortable density — label and supporting text stack beside the control. */
188
- .stack {
189
- display: flex;
190
- flex-direction: column;
191
- min-width: 0;
192
- }
193
-
194
- .label-line {
195
- display: flex;
196
- align-items: center;
197
- gap: var(--rel-spacing-xs);
198
- min-width: 0;
199
- }
200
-
201
- /* Optional label prefix — icon tracks the label's color in every state
202
- (rel-icon inherits currentColor). */
203
- .radio-prefix {
204
- flex-shrink: 0;
205
- color: var(--rel-text-default);
206
- }
207
-
208
- .radio-avatar {
209
- flex-shrink: 0;
210
- width: 20px;
211
- height: 20px;
212
- border-radius: 50%;
213
- display: inline-flex;
214
- align-items: center;
215
- justify-content: center;
216
- background: var(--rel-bg-neutral-inverse-2);
217
- color: var(--rel-text-inverse);
218
- font-family: var(--rel-body-xs-semibold-font-family);
219
- font-weight: var(--rel-body-xs-semibold-font-weight);
220
- font-size: var(--rel-body-xs-semibold-font-size);
221
- line-height: 1;
222
- letter-spacing: var(--rel-body-xs-semibold-letter-spacing);
223
- }
224
-
225
- .radio-label {
226
- color: var(--rel-text-default);
227
- font-family: var(--rel-body-lg-regular-font-family);
228
- font-weight: var(--rel-body-lg-regular-font-weight);
229
- font-size: var(--rel-body-lg-regular-font-size);
230
- line-height: var(--rel-body-lg-regular-line-height);
231
- letter-spacing: var(--rel-body-lg-regular-letter-spacing);
232
- }
233
-
234
- .radio-supporting {
235
- color: var(--rel-text-caption);
236
- font-family: var(--rel-body-sm-regular-font-family);
237
- font-weight: var(--rel-body-sm-regular-font-weight);
238
- font-size: var(--rel-body-sm-regular-font-size);
239
- line-height: var(--rel-body-sm-regular-line-height);
240
- letter-spacing: var(--rel-body-sm-regular-letter-spacing);
241
- }
242
-
243
- /* Compact density — supporting text sits under the whole row, indented
244
- past the control box (box width + row gap). */
245
- .radio-supporting.indent {
246
- padding-left: 28px;
247
- }
248
-
249
- /* Hover (Figma State=Hover) — ring, dot, label, and prefix icon shift
250
- text-default → text-body; supporting text stays caption. Scoped away
251
- from disabled and error so those keep their own colors. */
252
- .rel-radio-option:not(.disabled):not(.error-unchecked):not(.error-checked):hover .radio-custom::before {
253
- border-color: var(--rel-text-body);
254
- }
255
-
256
- .rel-radio-option:not(.disabled):not(.error-unchecked):not(.error-checked):hover .radio-custom::after {
257
- background: var(--rel-text-body);
258
- }
259
-
260
- .rel-radio-option:not(.disabled):not(.error-unchecked):not(.error-checked):hover .radio-label,
261
- .rel-radio-option:not(.disabled):not(.error-unchecked):not(.error-checked):hover .radio-prefix {
262
- color: var(--rel-text-body);
263
- }
264
-
265
- /* Size variants (medium is the base scale above). */
266
- :host([size="large"]) .radio-custom {
267
- width: 24px;
268
- height: 24px;
269
- }
270
-
271
- :host([size="large"]) .radio-custom::before {
272
- width: 20px;
273
- height: 20px;
274
- }
275
-
276
- :host([size="large"]) .radio-custom::after {
277
- width: 10px;
278
- height: 10px;
279
- }
280
-
281
- :host([size="large"]) .radio-label {
282
- font-family: var(--rel-body-xl-regular-font-family);
283
- font-weight: var(--rel-body-xl-regular-font-weight);
284
- font-size: var(--rel-body-xl-regular-font-size);
285
- line-height: var(--rel-body-xl-regular-line-height);
286
- letter-spacing: var(--rel-body-xl-regular-letter-spacing);
287
- }
288
-
289
- :host([size="large"]) .radio-supporting {
290
- font-family: var(--rel-body-md-regular-font-family);
291
- font-weight: var(--rel-body-md-regular-font-weight);
292
- font-size: var(--rel-body-md-regular-font-size);
293
- line-height: var(--rel-body-md-regular-line-height);
294
- letter-spacing: var(--rel-body-md-regular-letter-spacing);
295
- }
296
-
297
- :host([size="large"]) .radio-supporting.indent {
298
- padding-left: 32px;
299
- }
300
-
301
- :host([size="large"]) .radio-avatar {
302
- width: 24px;
303
- height: 24px;
304
- font-family: var(--rel-body-sm-semibold-font-family);
305
- font-weight: var(--rel-body-sm-semibold-font-weight);
306
- font-size: var(--rel-body-sm-semibold-font-size);
307
- letter-spacing: var(--rel-body-sm-semibold-letter-spacing);
308
- }
309
-
310
- :host([size="small"]) .radio-label {
311
- font-family: var(--rel-body-md-regular-font-family);
312
- font-weight: var(--rel-body-md-regular-font-weight);
313
- font-size: var(--rel-body-md-regular-font-size);
314
- line-height: var(--rel-body-md-regular-line-height);
315
- letter-spacing: var(--rel-body-md-regular-letter-spacing);
316
- }
317
-
318
- :host([size="small"]) .radio-supporting {
319
- font-family: var(--rel-body-xs-regular-font-family);
320
- font-weight: var(--rel-body-xs-regular-font-weight);
321
- font-size: var(--rel-body-xs-regular-font-size);
322
- line-height: var(--rel-body-xs-regular-line-height);
323
- letter-spacing: var(--rel-body-xs-regular-letter-spacing);
324
- }
325
-
326
- /* Error state (Figma State=Error) — ring, dot, and supporting text go
327
- danger; the label (and prefix) keep their default color. */
328
- .rel-radio-option.error-unchecked .radio-custom::before,
329
- .rel-radio-option.error-checked .radio-custom::before {
330
- border-color: var(--rel-border-danger);
331
- }
332
-
333
- .rel-radio-option.error-checked .radio-custom::after {
334
- background: var(--rel-text-danger);
335
- }
336
-
337
- .rel-radio-option.error-unchecked .radio-supporting,
338
- .rel-radio-option.error-checked .radio-supporting {
339
- color: var(--rel-text-danger);
340
- }
341
-
342
- /* Error message below group */
343
- .error-message {
344
- color: var(--rel-text-danger);
345
- margin-top: var(--rel-spacing-2xs, 4px);
346
-
347
- font-family: var(--rel-body-sm-regular-font-family);
348
-
349
- font-weight: var(--rel-body-sm-regular-font-weight);
350
-
351
- font-size: var(--rel-body-sm-regular-font-size);
352
-
353
- line-height: var(--rel-body-sm-regular-line-height);
354
-
355
- letter-spacing: var(--rel-body-sm-regular-letter-spacing);
356
-
357
- }
358
-
359
- .error-message.empty {
360
- margin-top: 0;
361
- }
362
- `;
363
-
364
- protected willUpdate(changed: PropertyValues<this>) {
365
- if (
366
- (changed.has("alignment") || changed.has("density")) &&
367
- this.alignment === "trailing" &&
368
- this.density === "compact"
369
- ) {
370
- console.warn(
371
- '[rel-radio] density="compact" has no trailing layout; rendering comfortable density.'
372
- );
373
- }
374
- }
375
-
376
- /** Trailing has no compact layout in the spec — trailing wins. */
377
- private get effectiveDensity(): RadioDensity {
378
- return this.alignment === "trailing" ? "comfortable" : this.density;
379
- }
380
-
381
- private onChange(value: string) {
382
- if (this.disabled) return;
383
-
384
- this.selectedValue = value;
385
- this.dispatchEvent(
386
- new CustomEvent("rel-change", {
387
- detail: { value, checked: true },
388
- bubbles: true,
389
- composed: true,
390
- })
391
- );
392
- this._onChange(this.value);
393
- }
394
-
395
- registerOnChange(fn: (value: string) => void): void { this._onChange = fn; }
396
- registerOnTouched(fn: () => void): void { this._onTouched = fn; }
397
- writeValue(value: string): void { this.checked = value === this.value; }
398
-
399
- private get hasSelection(): boolean {
400
- return this.selectedValue !== null && this.selectedValue !== "";
401
- }
402
-
403
- private renderPrefix(option: RadioOption) {
404
- if (option.icon) {
405
- return html`<rel-icon
406
- name="${option.icon}"
407
- size="${this.size === "large" ? "lg" : "md"}"
408
- class="radio-prefix"
409
- ></rel-icon>`;
410
- }
411
- if (option.avatar) {
412
- return html`<span class="radio-avatar" aria-hidden="true">${option.avatar}</span>`;
413
- }
414
- return null;
415
- }
416
-
417
- private renderOption(option: RadioOption) {
418
- const isChecked = this.selectedValue === option.value;
419
- const isDisabled = this.disabled || option.disabled;
420
- const comfortable = this.effectiveDensity === "comfortable";
421
- const classes = [
422
- "rel-radio-option",
423
- comfortable ? "comfortable" : "compact",
424
- isDisabled ? "disabled" : "",
425
- this.error && !this.hasSelection ? "error-unchecked" : "",
426
- this.error && isChecked ? "error-checked" : "",
427
- ]
428
- .filter(Boolean)
429
- .join(" ");
430
-
431
- const labelLine = html`
432
- <span class="label-line">
433
- ${this.renderPrefix(option)}
434
- <span part="label" class="radio-label">${option.label}</span>
435
- </span>
436
- `;
437
-
438
- const supporting = option.supportingText
439
- ? html`<div class="radio-supporting ${comfortable ? "" : "indent"}">
440
- ${option.supportingText}
441
- </div>`
442
- : null;
443
-
444
- return html`
445
- <label class="${classes}">
446
- <div class="radio-main">
447
- <input
448
- type="radio"
449
- name="${this.name}"
450
- .checked="${isChecked}"
451
- ?disabled="${isDisabled}"
452
- @change="${() => this.onChange(option.value)}"
453
- @blur="${() => this._onTouched()}"
454
- />
455
- <span class="radio-custom"></span>
456
- ${comfortable
457
- ? html`<span class="stack">${labelLine}${supporting}</span>`
458
- : labelLine}
459
- </div>
460
-
461
- ${comfortable ? null : supporting}
462
- </label>
463
- `;
464
- }
465
-
466
- render() {
467
- return html`
468
- <div
469
- part="base"
470
- class="rel-radio-group"
471
- role="radiogroup"
472
- aria-orientation="${this.orientation}"
473
- aria-label="${this.fieldAriaLabel ?? nothing}"
474
- aria-describedby="radio-error"
475
- >
476
- ${this.options.map((o) => this.renderOption(o))}
477
- </div>
478
- <div class="${this.error && this.errorMessage ? "error-message" : "error-message empty"}" id="radio-error" aria-live="polite">${this.error && this.errorMessage ? this.errorMessage : ""}</div>
479
- `;
480
- }
481
- }