@italia/radio 0.1.0-alpha.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.
@@ -0,0 +1,1725 @@
1
+ import { _ as __decorate, a as __metadata, F as FormControl, b as FormControlController } from '../form-control-CdxVvcex.js';
2
+ import { css, html } from 'lit';
3
+ import { property, queryAssignedElements, state, customElement } from 'lit/decorators.js';
4
+ import 'lit/directive.js';
5
+
6
+ /**
7
+ * Roving Tabindex Controller
8
+ *
9
+ * Implements the ARIA roving tabindex pattern for keyboard navigation.
10
+ * See: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
11
+ *
12
+ * Usage:
13
+ * ```ts
14
+ * private rovingTabindex = new RovingTabindexController(this, {
15
+ * getItems: () => Array.from(this.querySelectorAll('my-item')),
16
+ * onSelect: (item) => this.selectItem(item),
17
+ * });
18
+ * ```
19
+ */
20
+ class RovingTabindexController {
21
+ constructor(host, config) {
22
+ this.host = host;
23
+ this.config = {
24
+ wrap: true,
25
+ direction: 'both',
26
+ selectOnFocus: false,
27
+ skipItem: (item) => item.hasAttribute('disabled') || item.disabled === true,
28
+ ...config,
29
+ };
30
+ host.addController(this);
31
+ }
32
+ // eslint-disable-next-line class-methods-use-this
33
+ hostConnected() {
34
+ // Controller is ready when host connects
35
+ }
36
+ // eslint-disable-next-line class-methods-use-this
37
+ hostDisconnected() {
38
+ // Cleanup if needed
39
+ }
40
+ /**
41
+ * Update tabindex values for all items
42
+ * @param activeIndex - Index of the item that should be tabbable (default: 0 or first non-disabled)
43
+ */
44
+ updateTabindices(activeIndex) {
45
+ const items = this.config.getItems();
46
+ if (!items || items.length === 0) {
47
+ return;
48
+ }
49
+ // Find the active index
50
+ let targetIndex = activeIndex ?? 0;
51
+ // If no active index specified, use first non-disabled item
52
+ if (activeIndex === undefined) {
53
+ targetIndex = items.findIndex((item) => !this.config.skipItem(item));
54
+ if (targetIndex === -1) {
55
+ targetIndex = 0; // Fallback to first item if all disabled
56
+ }
57
+ }
58
+ // Set tabindex for all items
59
+ items.forEach((item, index) => {
60
+ const itemElement = item;
61
+ if (this.config.skipItem(itemElement)) {
62
+ // eslint-disable-next-line no-param-reassign
63
+ itemElement.tabIndex = -1;
64
+ }
65
+ else {
66
+ // eslint-disable-next-line no-param-reassign
67
+ itemElement.tabIndex = index === targetIndex ? 0 : -1;
68
+ }
69
+ });
70
+ }
71
+ /**
72
+ * Handle keyboard navigation
73
+ * @param currentItem - The currently focused item
74
+ * @param event - The keyboard event
75
+ * @returns true if the event was handled, false otherwise
76
+ */
77
+ handleKeydown(currentItem, event) {
78
+ const { direction } = this.config;
79
+ const { key } = event;
80
+ // Determine if this key should be handled based on direction
81
+ const isVertical = key === 'ArrowUp' || key === 'ArrowDown';
82
+ const isHorizontal = key === 'ArrowLeft' || key === 'ArrowRight';
83
+ const isHome = key === 'Home';
84
+ const isEnd = key === 'End';
85
+ const shouldHandle = isHome ||
86
+ isEnd ||
87
+ (direction === 'both' && (isVertical || isHorizontal)) ||
88
+ (direction === 'vertical' && isVertical) ||
89
+ (direction === 'horizontal' && isHorizontal);
90
+ if (!shouldHandle) {
91
+ return false;
92
+ }
93
+ // Prevent default behavior (page scrolling)
94
+ event.preventDefault();
95
+ const items = this.config.getItems();
96
+ const currentIndex = items.indexOf(currentItem);
97
+ if (currentIndex === -1) {
98
+ return false;
99
+ }
100
+ let nextIndex = currentIndex;
101
+ // Handle Home/End keys
102
+ if (isHome) {
103
+ nextIndex = 0;
104
+ }
105
+ else if (isEnd) {
106
+ nextIndex = items.length - 1;
107
+ }
108
+ else {
109
+ // Handle arrow keys
110
+ const isNext = key === 'ArrowDown' || key === 'ArrowRight';
111
+ const isPrev = key === 'ArrowUp' || key === 'ArrowLeft';
112
+ if (isNext) {
113
+ nextIndex = this.getNextIndex(items, currentIndex, 1);
114
+ }
115
+ else if (isPrev) {
116
+ nextIndex = this.getNextIndex(items, currentIndex, -1);
117
+ }
118
+ }
119
+ // Skip disabled items
120
+ nextIndex = this.findNextValidIndex(items, nextIndex, nextIndex > currentIndex ? 1 : -1);
121
+ if (nextIndex !== -1 && nextIndex !== currentIndex) {
122
+ const nextItem = items[nextIndex];
123
+ // Update tabindices
124
+ this.updateTabindices(nextIndex);
125
+ // Focus the next item
126
+ nextItem.focus();
127
+ // Optionally select/activate the item
128
+ if (this.config.selectOnFocus && this.config.onSelect) {
129
+ this.config.onSelect(nextItem, event);
130
+ }
131
+ return true;
132
+ }
133
+ return false;
134
+ }
135
+ /**
136
+ * Get the next index based on direction
137
+ */
138
+ getNextIndex(items, currentIndex, direction) {
139
+ const { wrap } = this.config;
140
+ let nextIndex = currentIndex + direction;
141
+ if (wrap) {
142
+ // Wrap around
143
+ if (nextIndex < 0) {
144
+ nextIndex = items.length - 1;
145
+ }
146
+ else if (nextIndex >= items.length) {
147
+ nextIndex = 0;
148
+ }
149
+ }
150
+ else {
151
+ // Clamp to bounds
152
+ nextIndex = Math.max(0, Math.min(items.length - 1, nextIndex));
153
+ }
154
+ return nextIndex;
155
+ }
156
+ /**
157
+ * Find the next valid (non-disabled) index
158
+ */
159
+ findNextValidIndex(items, startIndex, direction) {
160
+ const maxAttempts = items.length;
161
+ let attempts = 0;
162
+ let index = startIndex;
163
+ while (attempts < maxAttempts) {
164
+ if (!this.config.skipItem(items[index])) {
165
+ return index;
166
+ }
167
+ index = this.getNextIndex(items, index, direction);
168
+ attempts += 1;
169
+ }
170
+ // All items are disabled
171
+ return -1;
172
+ }
173
+ /**
174
+ * Set focus to a specific item
175
+ */
176
+ focusItem(item) {
177
+ const items = this.config.getItems();
178
+ const index = items.indexOf(item);
179
+ if (index !== -1) {
180
+ this.updateTabindices(index);
181
+ item.focus();
182
+ }
183
+ }
184
+ /**
185
+ * Set focus to the first non-disabled item
186
+ */
187
+ focusFirst() {
188
+ const items = this.config.getItems();
189
+ const firstValidIndex = this.findNextValidIndex(items, 0, 1);
190
+ if (firstValidIndex !== -1) {
191
+ this.focusItem(items[firstValidIndex]);
192
+ }
193
+ }
194
+ /**
195
+ * Set focus to the last non-disabled item
196
+ */
197
+ focusLast() {
198
+ const items = this.config.getItems();
199
+ const lastValidIndex = this.findNextValidIndex(items, items.length - 1, -1);
200
+ if (lastValidIndex !== -1) {
201
+ this.focusItem(items[lastValidIndex]);
202
+ }
203
+ }
204
+ }
205
+
206
+ var styles = css`/***************************** 1 ****************************************/
207
+ /***************************** 2 ****************************************/
208
+ /***************************** 1 ****************************************/
209
+ /***************************** 2 ****************************************/
210
+ /***************************** 1 ****************************************/
211
+ /***************************** 2 ****************************************/
212
+ /***************************** 3 ****************************************/
213
+ /***************************** 1 ****************************************/
214
+ /***************************** 2 ****************************************/
215
+ /***************************** 3 ****************************************/
216
+ /***************************** NEUTRAL 1 ****************************************/
217
+ /***************************** NEUTRAL 2 ****************************************/
218
+ /***************************** NEUTRAL 2 / 3 ****************************************/
219
+ .form-check [type=checkbox]:focus + label,
220
+ .form-check [type=radio]:focus + label {
221
+ border-color: hsl(0, 0%, 0%) !important;
222
+ box-shadow: 0 0 0 2px var(--bs-color-border-inverse), 0 0 0 5px var(--bs-color-outline-focus) !important;
223
+ outline: 3px solid transparent !important;
224
+ outline-offset: 3px !important;
225
+ }
226
+
227
+ .form-check [type=checkbox]:focus[data-focus-mouse=true] + label,
228
+ .form-check [type=radio]:focus[data-focus-mouse=true] + label {
229
+ border-color: inherit !important;
230
+ box-shadow: none !important;
231
+ outline: none !important;
232
+ }
233
+
234
+ *,
235
+ *::before,
236
+ *::after {
237
+ box-sizing: border-box;
238
+ }
239
+
240
+ @media (prefers-reduced-motion: no-preference) {
241
+ :root {
242
+ scroll-behavior: smooth;
243
+ }
244
+ }
245
+
246
+ body {
247
+ margin: 0;
248
+ -webkit-text-size-adjust: 100%;
249
+ -webkit-tap-highlight-color: hsla(0, 0%, 0%, 0);
250
+ }
251
+
252
+ hr {
253
+ margin: 1rem 0;
254
+ color: inherit;
255
+ border: 0;
256
+ border-top: 1px solid;
257
+ opacity: 0.25;
258
+ }
259
+
260
+ p {
261
+ margin-top: 0;
262
+ margin-bottom: var(--bs-paragraph-spacing);
263
+ }
264
+
265
+ abbr[title] {
266
+ text-decoration: underline dotted;
267
+ cursor: help;
268
+ text-decoration-skip-ink: none;
269
+ }
270
+
271
+ address {
272
+ margin-bottom: var(--bs-spacing-s);
273
+ font-style: normal;
274
+ line-height: inherit;
275
+ }
276
+
277
+ ol,
278
+ ul {
279
+ padding-left: var(--bs-spacing-l);
280
+ }
281
+
282
+ ol,
283
+ ul,
284
+ dl {
285
+ margin-top: 0;
286
+ margin-bottom: var(--bs-spacing-s);
287
+ }
288
+
289
+ ol ol,
290
+ ul ul,
291
+ ol ul,
292
+ ul ol {
293
+ margin-bottom: 0;
294
+ }
295
+
296
+ dt {
297
+ font-weight: var(--bs-font-weight-strong);
298
+ }
299
+
300
+ dd {
301
+ margin-bottom: var(--bs-spacing-xxs);
302
+ margin-left: 0;
303
+ }
304
+
305
+ blockquote {
306
+ margin: 0 0 var(--bs-spacing-s);
307
+ }
308
+
309
+ sub,
310
+ sup {
311
+ position: relative;
312
+ font-size: var(--bs-font-size-1);
313
+ line-height: 0;
314
+ vertical-align: baseline;
315
+ }
316
+
317
+ sub {
318
+ bottom: -0.25em;
319
+ }
320
+
321
+ sup {
322
+ top: -0.5em;
323
+ }
324
+
325
+ a {
326
+ color: var(--bs-color-link);
327
+ text-decoration: underline;
328
+ text-decoration-skip-ink: auto;
329
+ text-underline-offset: 2px;
330
+ }
331
+ a:hover {
332
+ color: var(--bs-color-link-hover);
333
+ }
334
+
335
+ a:not([href]):not([class]), a:not([href]):not([class]):hover {
336
+ color: inherit;
337
+ text-decoration: none;
338
+ }
339
+
340
+ pre,
341
+ code,
342
+ kbd,
343
+ samp {
344
+ font-size: 1em;
345
+ }
346
+
347
+ pre {
348
+ display: block;
349
+ margin-top: 0;
350
+ margin-bottom: var(--bs-paragraph-spacing);
351
+ overflow: auto;
352
+ }
353
+ pre code {
354
+ word-break: normal;
355
+ }
356
+
357
+ a > code {
358
+ color: inherit;
359
+ }
360
+
361
+ figure {
362
+ margin: 0 0 1rem;
363
+ }
364
+
365
+ img,
366
+ svg {
367
+ vertical-align: middle;
368
+ }
369
+
370
+ table {
371
+ caption-side: bottom;
372
+ border-collapse: collapse;
373
+ }
374
+
375
+ caption {
376
+ padding-top: 0.5rem;
377
+ padding-bottom: 0.5rem;
378
+ color: hsl(210, 17%, 44%);
379
+ text-align: left;
380
+ }
381
+
382
+ th {
383
+ text-align: inherit;
384
+ text-align: -webkit-match-parent;
385
+ }
386
+
387
+ thead,
388
+ tbody,
389
+ tfoot,
390
+ tr,
391
+ td,
392
+ th {
393
+ border-color: inherit;
394
+ border-style: solid;
395
+ border-width: 0;
396
+ }
397
+
398
+ label {
399
+ display: inline-block;
400
+ }
401
+
402
+ button {
403
+ border-radius: 0;
404
+ }
405
+
406
+ button:focus:not(:focus-visible) {
407
+ outline: 0;
408
+ }
409
+
410
+ input,
411
+ button,
412
+ select,
413
+ optgroup,
414
+ textarea {
415
+ margin: 0;
416
+ font-family: inherit;
417
+ font-size: inherit;
418
+ line-height: inherit;
419
+ }
420
+
421
+ button,
422
+ select {
423
+ text-transform: none;
424
+ }
425
+
426
+ [role=button] {
427
+ cursor: pointer;
428
+ }
429
+
430
+ select {
431
+ word-wrap: normal;
432
+ }
433
+ select:disabled {
434
+ opacity: 1;
435
+ }
436
+
437
+ [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
438
+ display: none !important;
439
+ }
440
+
441
+ button,
442
+ [type=button],
443
+ [type=reset],
444
+ [type=submit] {
445
+ -webkit-appearance: button;
446
+ }
447
+ button:not(:disabled),
448
+ [type=button]:not(:disabled),
449
+ [type=reset]:not(:disabled),
450
+ [type=submit]:not(:disabled) {
451
+ cursor: pointer;
452
+ }
453
+
454
+ ::-moz-focus-inner {
455
+ padding: 0;
456
+ border-style: none;
457
+ }
458
+
459
+ textarea {
460
+ resize: vertical;
461
+ }
462
+
463
+ fieldset {
464
+ min-width: 0;
465
+ padding: 0;
466
+ margin: 0;
467
+ border: 0;
468
+ }
469
+
470
+ ::-webkit-datetime-edit-fields-wrapper,
471
+ ::-webkit-datetime-edit-text,
472
+ ::-webkit-datetime-edit-minute,
473
+ ::-webkit-datetime-edit-hour-field,
474
+ ::-webkit-datetime-edit-day-field,
475
+ ::-webkit-datetime-edit-month-field,
476
+ ::-webkit-datetime-edit-year-field {
477
+ padding: 0;
478
+ }
479
+
480
+ ::-webkit-inner-spin-button {
481
+ height: auto;
482
+ }
483
+
484
+ [type=search] {
485
+ outline-offset: -2px;
486
+ -webkit-appearance: textfield;
487
+ }
488
+
489
+ /* rtl:raw:
490
+ [type="tel"],
491
+ [type="url"],
492
+ [type="email"],
493
+ [type="number"] {
494
+ direction: ltr;
495
+ }
496
+ */
497
+ ::-webkit-search-decoration {
498
+ -webkit-appearance: none;
499
+ }
500
+
501
+ ::-webkit-color-swatch-wrapper {
502
+ padding: 0;
503
+ }
504
+
505
+ ::file-selector-button {
506
+ font: inherit;
507
+ -webkit-appearance: button;
508
+ }
509
+
510
+ output {
511
+ display: inline-block;
512
+ }
513
+
514
+ iframe {
515
+ border: 0;
516
+ }
517
+
518
+ summary {
519
+ display: list-item;
520
+ cursor: pointer;
521
+ }
522
+
523
+ progress {
524
+ vertical-align: baseline;
525
+ }
526
+
527
+ [hidden] {
528
+ display: none !important;
529
+ }
530
+
531
+ .visually-hidden,
532
+ .visually-hidden-focusable:not(:focus):not(:focus-within) {
533
+ position: absolute !important;
534
+ width: 1px !important;
535
+ height: 1px !important;
536
+ padding: 0 !important;
537
+ margin: -1px !important;
538
+ overflow: hidden !important;
539
+ clip: rect(0, 0, 0, 0) !important;
540
+ white-space: nowrap !important;
541
+ border: 0 !important;
542
+ }
543
+
544
+ :root {
545
+ --bs-form-control-height: 2.5rem;
546
+ --bs-form-control-spacing: var(--bs-spacing-xxs);
547
+ --bs-form-control-background-color: var(--bs-color-background-inverse);
548
+ --bs-form-control-border-color: var(--bs-color-border-secondary);
549
+ --bs-form-control-border-radius: var(--bs-radius-smooth);
550
+ --bs-form-control-placeholder-color: var(--bs-color-text-muted);
551
+ --bs-form-control-label-color: var(--bs-color-text-base);
552
+ --bs-form-control-text-color: var(--bs-color-text-secondary);
553
+ --bs-form-control-font-size: var(--bs-body-font-size);
554
+ --bs-form-group-spacing-y: var(--bs-spacing-m);
555
+ --bs-form-checkbox-border-color: var(--bs-color-border-secondary);
556
+ --bs-form-checkbox-border-radius: var(--bs-radius-smooth);
557
+ --bs-form-checked-color: var(--bs-color-background-primary);
558
+ }
559
+
560
+ input[readonly],
561
+ textarea[readonly],
562
+ select[readonly] {
563
+ --bs-form-control-border-color: var(--bs-color-border-subtle);
564
+ --bs-form-control-background-color: var(--bs-color-background-muted);
565
+ cursor: not-allowed;
566
+ }
567
+
568
+ input,
569
+ textarea,
570
+ select {
571
+ display: block;
572
+ width: 100%;
573
+ padding: var(--bs-form-control-spacing);
574
+ border: 1px solid var(--bs-form-control-border-color);
575
+ border-radius: var(--bs-form-control-border-radius);
576
+ background-color: var(--bs-form-control-background-color);
577
+ color: var(--bs-form-control-text-color);
578
+ font-size: var(--bs-form-control-font-size);
579
+ }
580
+ input.disabled, input:disabled,
581
+ textarea.disabled,
582
+ textarea:disabled,
583
+ select.disabled,
584
+ select:disabled {
585
+ border-color: var(--bs-color-border-disabled);
586
+ opacity: 1;
587
+ background-color: var(--bs-color-background-disabled);
588
+ color: var(--bs-color-text-disabled);
589
+ }
590
+
591
+ input:focus,
592
+ textarea:focus {
593
+ outline: 3px solid transparent;
594
+ outline-offset: 3px;
595
+ box-shadow: 0 0 0 2px var(--bs-color-border-inverse), 0 0 0 5px var(--bs-color-outline-focus) !important;
596
+ }
597
+
598
+ input::file-selector-button {
599
+ margin: -0.375rem -0.75rem;
600
+ padding: 0.375rem 0.75rem;
601
+ border-width: 0;
602
+ border-style: solid;
603
+ border-radius: 0;
604
+ border-color: inherit;
605
+ color: hsl(0, 0%, 10%);
606
+ pointer-events: none;
607
+ margin-inline-end: 0.75rem;
608
+ border-inline-end-width: 0;
609
+ background-color: hsl(0, 0%, 100%);
610
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
611
+ }
612
+ @media (prefers-reduced-motion: reduce) {
613
+ input::file-selector-button {
614
+ transition: none;
615
+ }
616
+ }
617
+ input:hover:not(:disabled):not([readonly])::file-selector-button {
618
+ background-color: rgb(242.25, 242.25, 242.25);
619
+ }
620
+ input[type=file] {
621
+ overflow: hidden;
622
+ }
623
+ input[type=file]:not(:disabled):not([readonly]) {
624
+ cursor: pointer;
625
+ }
626
+ input::-webkit-date-and-time-value {
627
+ height: 1.5em;
628
+ }
629
+
630
+ select {
631
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='hsl%280, 0%, 15%%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
632
+ background-repeat: no-repeat;
633
+ background-position: right var(--bs-form-control-spacing) center;
634
+ background-size: 16px 12px;
635
+ appearance: none;
636
+ }
637
+ select:focus {
638
+ border-color: hsl(210, 17%, 44%);
639
+ outline: 0;
640
+ box-shadow: 0 0 0 0.25rem rgba(0, 102, 204, 0.25);
641
+ }
642
+ select[multiple], select[size]:not([size="1"]) {
643
+ padding-right: 0.75rem;
644
+ background-image: none;
645
+ }
646
+ select:disabled {
647
+ background-color: hsl(0, 0%, 90%);
648
+ }
649
+ select:disabled:hover {
650
+ cursor: not-allowed;
651
+ }
652
+ select:-moz-focusring {
653
+ color: transparent;
654
+ text-shadow: 0 0 0 hsl(0, 0%, 10%);
655
+ }
656
+ select option {
657
+ font-weight: normal;
658
+ }
659
+ select:disabled {
660
+ opacity: 1;
661
+ background-color: hsl(210, 3%, 85%);
662
+ }
663
+
664
+ textarea {
665
+ height: auto;
666
+ font-size: var(--bs-body-font-size);
667
+ line-height: 1.5;
668
+ }
669
+
670
+ label {
671
+ display: inline-block;
672
+ width: auto;
673
+ max-width: 100%;
674
+ margin-bottom: var(--bs-spacing-xxs);
675
+ color: var(--bs-form-control-label-color);
676
+ font-size: var(--bs-label-font-size-s);
677
+ font-weight: var(--bs-font-weight-solid);
678
+ line-height: var(--bs-label-line-height);
679
+ }
680
+
681
+ input,
682
+ textarea {
683
+ outline: 0;
684
+ box-shadow: none;
685
+ transition: none;
686
+ appearance: none;
687
+ }
688
+
689
+ input[type=date],
690
+ input[type=datetime-local],
691
+ input[type=time] {
692
+ display: flex;
693
+ }
694
+
695
+ fieldset legend {
696
+ margin-bottom: var(--bs-spacing-s);
697
+ padding: 0 var(--bs-form-input-spacing-x);
698
+ background-color: transparent;
699
+ color: var(--bs-form-control-text-color);
700
+ font-size: var(--bs-label-sm);
701
+ font-weight: var(--bs-font-weight-solid);
702
+ }
703
+
704
+ ::placeholder {
705
+ color: var(--bs-form-control-placeholder-color);
706
+ }
707
+
708
+ input::-webkit-datetime-edit {
709
+ background-color: var(--bs-color-background-primary-lighter);
710
+ color: var(--bs-form-contro-text-color);
711
+ }
712
+
713
+ .form-group {
714
+ position: relative;
715
+ margin-bottom: var(--bs-form-group-spacing-y);
716
+ }
717
+ .form-group label.input-symbol-label:not(.active) {
718
+ left: 2.25rem;
719
+ }
720
+ .form-group small.form-text {
721
+ margin: 0;
722
+ font-size: 0.875rem;
723
+ }
724
+ .form-group input[type=time] ~ label {
725
+ font-size: 0.875rem;
726
+ }
727
+
728
+ .form-text {
729
+ margin: var(--bs-spacing-xxs) 0;
730
+ color: var(--bs-color-text-secondary);
731
+ }
732
+
733
+ .form-group.active .form-file-name {
734
+ padding-bottom: 1.95rem;
735
+ }
736
+
737
+ .warning-feedback {
738
+ display: none;
739
+ width: 100%;
740
+ margin-top: 0.25rem;
741
+ color: var(--bs-color-text-warning);
742
+ font-size: 0.75rem;
743
+ }
744
+
745
+ .valid-feedback,
746
+ .invalid-feedback,
747
+ .warning-feedback {
748
+ margin-left: 0.5rem;
749
+ }
750
+
751
+ .input-group .input-group-text .btn {
752
+ border-radius: var(--bs-form-control-border-radius) 0 0 var(--bs-form-control-border-radius);
753
+ }
754
+ .input-group .input-group-append {
755
+ margin-left: 0;
756
+ }
757
+ .input-group .input-group-append .btn {
758
+ height: 100%;
759
+ padding-top: 0;
760
+ padding-bottom: 0;
761
+ border-bottom: 1px solid hsl(210, 17%, 44%);
762
+ border-radius: 0 var(--bs-form-control-border-radius) var(--bs-form-control-border-radius) 0;
763
+ }
764
+
765
+ .form-check {
766
+ position: relative;
767
+ padding-left: 0;
768
+ align-items: center;
769
+ }
770
+ .form-check + .form-check {
771
+ margin-top: var(--bs-spacing-s);
772
+ }
773
+ .form-check [type=checkbox],
774
+ .form-check [type=radio] {
775
+ position: absolute;
776
+ height: 100%;
777
+ margin-top: 0;
778
+ margin-left: 0;
779
+ opacity: 0;
780
+ }
781
+ .form-check [type=checkbox] + label,
782
+ .form-check [type=radio] + label {
783
+ position: relative;
784
+ display: flex;
785
+ align-items: center;
786
+ padding-left: 28px;
787
+ font-size: var(--bs-label-font-size);
788
+ font-weight: var(--bs-font-weight-solid);
789
+ cursor: pointer;
790
+ margin-bottom: 0;
791
+ user-select: none;
792
+ }
793
+ @media (min-width: 576px) {
794
+ .form-check [type=checkbox] + label,
795
+ .form-check [type=radio] + label {
796
+ font-size: var(--bs-label-font-size-m);
797
+ }
798
+ }
799
+ .form-check input[type=checkbox] + label::after,
800
+ .form-check input[type=checkbox] + label::before {
801
+ position: absolute;
802
+ left: 0;
803
+ z-index: 1;
804
+ content: "";
805
+ border-width: 2px;
806
+ border-style: solid;
807
+ transition: all var(--bs-transition-instant) ease-out;
808
+ }
809
+ .form-check input[type=checkbox] + label::after {
810
+ top: 0;
811
+ width: 20px;
812
+ height: 20px;
813
+ border-radius: var(--bs-form-control-border-radius);
814
+ }
815
+ .form-check input[type=checkbox]:checked + label::before {
816
+ top: 3px;
817
+ left: 3px;
818
+ width: 6px;
819
+ height: 12px;
820
+ border-width: 2px;
821
+ border-style: solid;
822
+ border-color: transparent var(--bs-color-border-inverse) var(--bs-color-border-inverse) transparent;
823
+ opacity: 1;
824
+ transform: rotate(40deg);
825
+ transform-origin: 100% 100%;
826
+ backface-visibility: hidden;
827
+ }
828
+ .form-check input[type=checkbox]:checked + label::after {
829
+ z-index: 0;
830
+ border-color: var(--bs-form-checked-color);
831
+ background-color: var(--bs-form-checked-color);
832
+ }
833
+ .form-check input[type=checkbox]:not(:checked) + label::after {
834
+ z-index: 0;
835
+ border-color: var(--bs-form-checkbox-border-color);
836
+ background-color: transparent;
837
+ }
838
+ .form-check input[type=checkbox]:not(:checked) + label::before {
839
+ top: 10px;
840
+ left: 6px;
841
+ width: 0;
842
+ height: 0;
843
+ border-color: transparent;
844
+ }
845
+ .form-check input[type=checkbox]:disabled + label {
846
+ opacity: 1;
847
+ cursor: not-allowed;
848
+ }
849
+ .form-check input[type=checkbox]:disabled:not(:checked) + label::after {
850
+ border-color: var(--bs-color-border-disabled);
851
+ background-color: transparent;
852
+ }
853
+ .form-check input[type=checkbox]:disabled:checked + label::after {
854
+ border-color: var(--bs-color-border-disabled);
855
+ background-color: var(--bs-color-border-disabled);
856
+ }
857
+ .form-check input[type=radio] + label::after, .form-check input[type=radio] + label::before {
858
+ position: absolute;
859
+ top: 0;
860
+ left: 0;
861
+ z-index: 0;
862
+ content: "";
863
+ width: 20px;
864
+ height: 20px;
865
+ border-width: 2px;
866
+ border-style: solid;
867
+ border-radius: var(--bs-radius-rounded);
868
+ transition: all var(--bs-transition-instant) ease-out;
869
+ }
870
+ .form-check input[type=radio]:not(:checked) + label::after, .form-check input[type=radio]:not(:checked) + label::before {
871
+ border-color: var(--bs-form-checkbox-border-color);
872
+ }
873
+ .form-check input[type=radio]:not(:checked) + label:after {
874
+ z-index: -1;
875
+ transform: scale(0);
876
+ }
877
+ .form-check input[type=radio]:checked + label::after {
878
+ z-index: 0;
879
+ border-color: var(--bs-form-checked-color);
880
+ background-color: var(--bs-form-checked-color);
881
+ transform: scale(0.64);
882
+ }
883
+ .form-check input[type=radio]:checked + label::before {
884
+ border-color: var(--bs-form-checked-color);
885
+ }
886
+ .form-check input[type=radio]:disabled + label {
887
+ cursor: not-allowed;
888
+ }
889
+ .form-check input[type=radio]:disabled:not(:checked) + label::after, .form-check input[type=radio]:disabled:not(:checked) + label::before {
890
+ border-color: var(--bs-color-border-disabled);
891
+ }
892
+ .form-check input[type=radio]:disabled:checked + label::after {
893
+ border-color: var(--bs-color-border-disabled);
894
+ background-color: var(--bs-color-border-disabled);
895
+ }
896
+ .form-check input[type=radio]:disabled:checked + label::before {
897
+ border-color: var(--bs-color-border-disabled);
898
+ }
899
+ .form-check.form-check-group {
900
+ margin-bottom: 1rem;
901
+ padding: 0 0 1rem 0;
902
+ box-shadow: inset 0 -1px 0 0 rgba(1, 1, 1, 0.1);
903
+ }
904
+ .form-check.form-check-group input[type=checkbox] + label,
905
+ .form-check.form-check-group input[type=radio] + label {
906
+ padding-right: 3.25rem;
907
+ padding-left: 0;
908
+ }
909
+ .form-check.form-check-group input[type=checkbox] + label::after, .form-check.form-check-group input[type=checkbox] + label::before,
910
+ .form-check.form-check-group input[type=radio] + label::after,
911
+ .form-check.form-check-group input[type=radio] + label::before {
912
+ right: 0;
913
+ left: auto;
914
+ }
915
+ .form-check.form-check-group input[type=checkbox]:checked + label::before {
916
+ right: 11px;
917
+ }
918
+ .form-check.form-check-group input[type=radio]:checked + label::before {
919
+ right: 0;
920
+ }
921
+ .form-check.form-check-group .form-text {
922
+ display: block;
923
+ margin-bottom: 0.5rem;
924
+ padding-right: 3.25rem;
925
+ }
926
+ .form-check.form-check-group input.semi-checked:not(:checked) + label::before {
927
+ right: 4px;
928
+ left: auto;
929
+ }
930
+ .form-check input.semi-checked:not(:checked) + label::before {
931
+ top: 9px;
932
+ left: 4px;
933
+ width: 12px;
934
+ height: 2px;
935
+ border-width: 0;
936
+ border-style: none;
937
+ border-color: transparent;
938
+ opacity: 1;
939
+ background: var(--bs-color-background-inverse);
940
+ transform: none;
941
+ backface-visibility: hidden;
942
+ }
943
+ .form-check input.semi-checked:not(:checked) + label::after {
944
+ z-index: 0;
945
+ border-color: var(--bs-form-checked-color);
946
+ background-color: var(--bs-form-checked-color);
947
+ }
948
+
949
+ .form-check-inline {
950
+ display: inline-block;
951
+ }
952
+ .form-check-inline:not(:last-child) {
953
+ margin-right: var(--bs-spacing-m);
954
+ }
955
+
956
+ @media (prefers-reduced-motion: reduce) {
957
+ fieldset legend,
958
+ .form-group label,
959
+ textarea,
960
+ .form-check [type=checkbox],
961
+ .form-check [type=radio],
962
+ .form-check [type=checkbox] + label::after,
963
+ .form-check [type=checkbox] + label::before,
964
+ .form-check [type=radio] + label::after,
965
+ .form-check [type=radio] + label::before,
966
+ .toggles label input[type=checkbox] + .lever::before,
967
+ .toggles label input[type=checkbox] + .lever::after {
968
+ transition: none !important;
969
+ }
970
+ }
971
+ .form-check [type=checkbox]:focus + label,
972
+ .form-check [type=radio]:focus + label {
973
+ border-color: hsl(0, 0%, 0%) !important;
974
+ box-shadow: 0 0 0 2px var(--bs-color-border-inverse), 0 0 0 5px var(--bs-color-outline-focus) !important;
975
+ outline: 3px solid transparent !important;
976
+ outline-offset: 3px !important;
977
+ }
978
+
979
+ .form-check [type=checkbox]:focus[data-focus-mouse=true] + label,
980
+ .form-check [type=radio]:focus[data-focus-mouse=true] + label {
981
+ border-color: inherit !important;
982
+ box-shadow: none !important;
983
+ outline: none !important;
984
+ }
985
+
986
+ .form-control-plaintext {
987
+ border: 0;
988
+ --bs-form-control-border-color: transparent;
989
+ --bs-form-control-border-radius: 0;
990
+ --bs-form-control-background-color: transparent;
991
+ --bs-form-control-spacing: 0;
992
+ }
993
+ .form-control-plaintext:focus {
994
+ outline: 0;
995
+ box-shadow: none !important;
996
+ }
997
+ .form-control-plaintext + label {
998
+ cursor: text;
999
+ }
1000
+
1001
+ .form-control {
1002
+ background-repeat: no-repeat;
1003
+ background-position: center right;
1004
+ background-size: 45px 30%;
1005
+ }
1006
+ .form-control:disabled {
1007
+ cursor: not-allowed;
1008
+ background: var(--bs-color-background-disabled);
1009
+ border: 0;
1010
+ color: var(--bs-color-text-disabled);
1011
+ }
1012
+ .was-validated .form-control:valid, .form-control.is-valid {
1013
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23008055' viewBox='0 0 192 512'%3E%3Cpath d='M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z'/%3E%3C/svg%3E");
1014
+ }
1015
+ .was-validated .form-control:invalid, .form-control.is-invalid {
1016
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23cc334d' viewBox='0 0 384 512'%3E%3Cpath d='M231.6 256l130.1-130.1c4.7-4.7 4.7-12.3 0-17l-22.6-22.6c-4.7-4.7-12.3-4.7-17 0L192 216.4 61.9 86.3c-4.7-4.7-12.3-4.7-17 0l-22.6 22.6c-4.7 4.7-4.7 12.3 0 17L152.4 256 22.3 386.1c-4.7 4.7-4.7 12.3 0 17l22.6 22.6c4.7 4.7 12.3 4.7 17 0L192 295.6l130.1 130.1c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17L231.6 256z'/%3E%3C/svg%3E");
1017
+ border-color: var(--bs-color-border-danger);
1018
+ }
1019
+ .form-control.warning {
1020
+ background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%0A%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M2%2012C2%206.47715%206.47715%202%2012%202C14.6522%202%2017.1957%203.05357%2019.0711%204.92893C20.9464%206.8043%2022%209.34784%2022%2012C22%2017.5228%2017.5228%2022%2012%2022C6.47715%2022%202%2017.5228%202%2012ZM3%2012C3%2016.9706%207.02944%2021%2012%2021C16.9706%2021%2021%2016.9706%2021%2012C21%207.02944%2016.9706%203%2012%203C7.02944%203%203%207.02944%203%2012ZM11.5%2014.2V5.7H12.7V14.2H11.5ZM12.6%2018.3V16.5H11.4V18.3H12.6Z%22/%3E%0A%3C/svg%3E") no-repeat;
1021
+ border-color: var(--bs-color-border-warning);
1022
+ }
1023
+ .form-control.is-valid ~ .warning-feedback {
1024
+ display: block;
1025
+ }
1026
+
1027
+ .form-control-sm {
1028
+ --bs-form-control-spacing: var(--bs-spacing-xxs) var(--bs-spacing-3xs);
1029
+ --bs-form-control-font-size: var(--bs-label-font-size);
1030
+ }
1031
+ .form-control-sm::file-selector-button {
1032
+ padding: 0.25rem 0.5rem;
1033
+ margin: -0.25rem -0.5rem;
1034
+ margin-inline-end: 0.5rem;
1035
+ }
1036
+
1037
+ .form-control-lg {
1038
+ --bs-form-control-font-size: var(--bs-lead-font-size);
1039
+ }
1040
+ .form-control-lg::file-selector-button {
1041
+ padding: 0.5rem 1rem;
1042
+ margin: -0.5rem -1rem;
1043
+ margin-inline-end: 1rem;
1044
+ }
1045
+
1046
+ textarea.form-control {
1047
+ min-height: 2.5rem;
1048
+ border: 1px solid hsl(210, 17%, 44%);
1049
+ }
1050
+ textarea.form-control-sm {
1051
+ min-height: calc(1.5em + 0.5rem);
1052
+ }
1053
+ textarea.form-control-lg {
1054
+ min-height: calc(1.5em + 1rem);
1055
+ }
1056
+
1057
+ .form-control-color {
1058
+ width: 3rem;
1059
+ height: 2.5rem;
1060
+ padding: 0.375rem;
1061
+ }
1062
+ .form-control-color:not(:disabled):not([readonly]) {
1063
+ cursor: pointer;
1064
+ }
1065
+ .form-control-color::-moz-color-swatch {
1066
+ border: 0 !important;
1067
+ border-radius: 0;
1068
+ }
1069
+ .form-control-color::-webkit-color-swatch {
1070
+ border-radius: 0;
1071
+ }
1072
+ .form-control-color.form-control-sm {
1073
+ height: calc(1.5em + 0.5rem);
1074
+ }
1075
+ .form-control-color.form-control-lg {
1076
+ height: calc(1.5em + 1rem);
1077
+ }
1078
+
1079
+ .form-check-reverse {
1080
+ padding-right: 1.5em;
1081
+ padding-left: 0;
1082
+ text-align: right;
1083
+ }
1084
+ .form-check-reverse .form-check-input {
1085
+ float: right;
1086
+ margin-right: -1.5em;
1087
+ margin-left: 0;
1088
+ }
1089
+
1090
+ .form-check-input {
1091
+ width: 1em;
1092
+ height: 1em;
1093
+ margin-top: 0.25em;
1094
+ vertical-align: top;
1095
+ background-color: hsl(0, 0%, 100%);
1096
+ background-repeat: no-repeat;
1097
+ background-position: center;
1098
+ background-size: contain;
1099
+ border: 1px solid rgba(0, 0, 0, 0.25);
1100
+ appearance: none;
1101
+ print-color-adjust: exact;
1102
+ }
1103
+ .form-check-input[type=checkbox] {
1104
+ border-radius: 0.25em;
1105
+ }
1106
+ .form-check-input[type=radio] {
1107
+ border-radius: 50%;
1108
+ }
1109
+ .form-check-input:active {
1110
+ filter: brightness(90%);
1111
+ }
1112
+ .form-check-input:focus {
1113
+ border-color: hsl(210, 17%, 44%);
1114
+ outline: 0;
1115
+ box-shadow: 0 0 0 0.25rem rgba(0, 102, 204, 0.25);
1116
+ }
1117
+ .form-check-input:checked {
1118
+ background-color: hsl(210, 100%, 40%);
1119
+ border-color: hsl(210, 100%, 40%);
1120
+ }
1121
+ .form-check-input:checked[type=checkbox] {
1122
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='hsl%280, 0%, 100%%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
1123
+ }
1124
+ .form-check-input:checked[type=radio] {
1125
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='hsl%280, 0%, 100%%29'/%3e%3c/svg%3e");
1126
+ }
1127
+ .form-check-input[type=checkbox]:indeterminate {
1128
+ background-color: hsl(210, 100%, 40%);
1129
+ border-color: hsl(210, 100%, 40%);
1130
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='hsl%280, 0%, 100%%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
1131
+ }
1132
+ .form-check-input:disabled {
1133
+ pointer-events: none;
1134
+ filter: none;
1135
+ opacity: 0.5;
1136
+ }
1137
+ .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {
1138
+ cursor: default;
1139
+ opacity: 0.5;
1140
+ }
1141
+
1142
+ .form-switch {
1143
+ padding-left: 2.5em;
1144
+ }
1145
+ .form-switch .form-check-input {
1146
+ width: 2em;
1147
+ margin-left: -2.5em;
1148
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");
1149
+ background-position: left center;
1150
+ border-radius: 2em;
1151
+ transition: background-position 0.15s ease-in-out;
1152
+ }
1153
+ @media (prefers-reduced-motion: reduce) {
1154
+ .form-switch .form-check-input {
1155
+ transition: none;
1156
+ }
1157
+ }
1158
+ .form-switch .form-check-input:focus {
1159
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='hsl%28210, 17%, 44%%29'/%3e%3c/svg%3e");
1160
+ }
1161
+ .form-switch .form-check-input:checked {
1162
+ background-position: right center;
1163
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='hsl%280, 0%, 100%%29'/%3e%3c/svg%3e");
1164
+ }
1165
+ .form-switch.form-check-reverse {
1166
+ padding-right: 2.5em;
1167
+ padding-left: 0;
1168
+ }
1169
+ .form-switch.form-check-reverse .form-check-input {
1170
+ margin-right: -2.5em;
1171
+ margin-left: 0;
1172
+ }
1173
+
1174
+ .form-check-inline {
1175
+ display: inline-block;
1176
+ margin-right: 1rem;
1177
+ }
1178
+
1179
+ .btn-check {
1180
+ position: absolute;
1181
+ clip: rect(0, 0, 0, 0);
1182
+ pointer-events: none;
1183
+ }
1184
+ .btn-check[disabled] + .btn, .btn-check:disabled + .btn {
1185
+ pointer-events: none;
1186
+ filter: none;
1187
+ opacity: 0.65;
1188
+ }
1189
+
1190
+ .form-feedback {
1191
+ width: 100%;
1192
+ margin-top: 0.25rem;
1193
+ font-size: 0.75rem;
1194
+ }
1195
+ .form-feedback.just-validate-error-label {
1196
+ color: var(--bs-color-text-danger);
1197
+ }
1198
+
1199
+ .input-group-text:has(~ [data-focus-mouse=true]:not(.btn)),
1200
+ [data-focus-mouse=true]:not(.btn) ~ .input-group-text,
1201
+ button:has(~ [data-focus-mouse=true]:not(.btn)),
1202
+ [data-focus-mouse=true]:not(.btn) + button {
1203
+ border-color: inherit !important;
1204
+ box-shadow: none !important;
1205
+ outline: none !important;
1206
+ }
1207
+
1208
+ .input-group-text:has(~ .is-invalid),
1209
+ .is-invalid ~ .input-group-text,
1210
+ button:has(~ .is-invalid),
1211
+ .is-invalid + button {
1212
+ border-color: var(--bs-color-border-danger) !important;
1213
+ }
1214
+
1215
+ .sr-only-justvalidate-bi {
1216
+ display: none;
1217
+ }
1218
+
1219
+ .just-validate-success-field {
1220
+ border-color: var(--bs-color-border-success) !important;
1221
+ padding-right: calc(1.5em + 0.75rem) !important;
1222
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23008055' viewBox='0 0 192 512'%3E%3Cpath d='M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z'/%3E%3C/svg%3E");
1223
+ }
1224
+
1225
+ .input-group-text:has(~ .just-validate-success-field),
1226
+ .just-validate-success-field ~ .input-group-text,
1227
+ button:has(~ .just-validate-success-field),
1228
+ .just-validate-success-field + button {
1229
+ border-color: var(--bs-color-border-success);
1230
+ }
1231
+
1232
+ .just-validate-success-field + .input-group-text.align-buttons,
1233
+ .is-invalid + .input-group-text.align-buttons {
1234
+ right: 30px;
1235
+ }
1236
+
1237
+ .is-invalid + .input-group-text.align-buttons {
1238
+ bottom: 22px;
1239
+ }
1240
+
1241
+ .autocomplete__wrapper .form-feedback.just-validate-error-label {
1242
+ position: absolute;
1243
+ }
1244
+
1245
+ textarea.form-control {
1246
+ background-position: top 0.3em right 0.3em !important;
1247
+ background-size: 37px 30% !important;
1248
+ }
1249
+ textarea.is-invalid {
1250
+ border-bottom-style: solid;
1251
+ border-bottom-width: 1px;
1252
+ }
1253
+ textarea.just-validate-success-field {
1254
+ border-bottom-style: solid;
1255
+ border-bottom-width: 1px;
1256
+ }
1257
+
1258
+ input[type=checkbox].is-invalid,
1259
+ input[type=radio].is-invalid {
1260
+ --bs-form-checkbox-border-color: var(--bs-color-border-danger);
1261
+ }
1262
+
1263
+ select.is-invalid {
1264
+ border: 1px solid var(--bs-color-border-danger);
1265
+ }
1266
+ select.just-validate-success-field {
1267
+ border: 1px solid var(--bs-color-border-success);
1268
+ }
1269
+
1270
+ ::slotted([slot=label]) {
1271
+ display: block;
1272
+ padding: 0 var(--bs-form-input-spacing-x);
1273
+ margin-bottom: var(--bs-spacing-s);
1274
+ background-color: rgba(0, 0, 0, 0);
1275
+ color: var(--bs-form-control-text-color);
1276
+ font-size: var(--bs-label-sm);
1277
+ font-weight: var(--bs-font-weight-solid);
1278
+ }
1279
+
1280
+ .it-radio-group {
1281
+ margin-bottom: var(--bs-spacing-xs);
1282
+ }
1283
+
1284
+ .it-radio-group-inline {
1285
+ display: flex;
1286
+ flex-wrap: wrap;
1287
+ align-items: center;
1288
+ gap: var(--bs-spacing-s);
1289
+ }
1290
+
1291
+ .just-validate-success-field ::slotted(it-radio) {
1292
+ --it-radio-label-color: var(--bs-color-text-success);
1293
+ }
1294
+
1295
+ .just-validate-success-field {
1296
+ padding: 0 !important;
1297
+ border: none !important;
1298
+ background-image: none !important;
1299
+ }`;
1300
+
1301
+ /**
1302
+ * Radio group component - manages a collection of radio buttons
1303
+ *
1304
+ * @element it-radio-group
1305
+ * @slot - Contains the `<it-radio>` elements
1306
+ *
1307
+ */
1308
+ let ItRadioGroup = class ItRadioGroup extends FormControl {
1309
+ constructor() {
1310
+ super(...arguments);
1311
+ /**
1312
+ * The name of the radio group, used for form submission
1313
+ */
1314
+ this.name = '';
1315
+ /**
1316
+ * The currently selected value
1317
+ */
1318
+ this.value = '';
1319
+ /**
1320
+ * Whether the radio group is disabled
1321
+ */
1322
+ this.disabled = false;
1323
+ /**
1324
+ * Whether a selection is required
1325
+ */
1326
+ this.required = false;
1327
+ /**
1328
+ * Optional prop for visual styling of grouped radios (borders between radios)
1329
+ */
1330
+ this.grouped = false;
1331
+ /**
1332
+ * Whether the radios are displayed inline (horizontally) or stacked (vertically)
1333
+ */
1334
+ this.inline = false;
1335
+ /**
1336
+ * Track if validation has been triggered (via reportValidity or setCustomValidity)
1337
+ * ARIA best practice: don't show aria-invalid until user attempts submission
1338
+ */
1339
+ this._validationTriggered = false;
1340
+ /**
1341
+ * Roving tabindex controller for keyboard navigation
1342
+ */
1343
+ this.rovingTabindex = new RovingTabindexController(this, {
1344
+ getItems: () => this._radios,
1345
+ onSelect: (radio, event) => this.selectRadio(radio, event),
1346
+ direction: 'both',
1347
+ selectOnFocus: true,
1348
+ });
1349
+ /**
1350
+ * Form control integration
1351
+ */
1352
+ this._formController = new FormControlController(this, {
1353
+ value: () => this.value,
1354
+ // disabled: () => this.disabled,
1355
+ setValue: (control, value) => {
1356
+ const radioGroup = control;
1357
+ radioGroup.value = value;
1358
+ },
1359
+ });
1360
+ /**
1361
+ * Handle slot changes (when radios are added/removed)
1362
+ */
1363
+ this._handleSlotChange = () => {
1364
+ this._syncValueFromRadios();
1365
+ this._updateRadiosState();
1366
+ // Sync group state to newly added radios
1367
+ this._syncGroupStateToRadios();
1368
+ };
1369
+ this._handleLabelSlotChange = () => {
1370
+ const labelId = this.generateId('it-radio-group-label');
1371
+ this._label[0]?.setAttribute('id', labelId);
1372
+ this.setAttribute('aria-labelledby', labelId);
1373
+ };
1374
+ }
1375
+ /**
1376
+ * Override: Custom validity check for radio group
1377
+ */
1378
+ get validity() {
1379
+ // Fallback: create a custom ValidityState-like object
1380
+ const valid = !this.required || !!this.value;
1381
+ return {
1382
+ valid,
1383
+ valueMissing: this.required && !this.value,
1384
+ typeMismatch: false,
1385
+ patternMismatch: false,
1386
+ tooLong: false,
1387
+ tooShort: false,
1388
+ rangeUnderflow: false,
1389
+ rangeOverflow: false,
1390
+ stepMismatch: false,
1391
+ badInput: false,
1392
+ customError: false,
1393
+ };
1394
+ }
1395
+ /**
1396
+ * Override: Check validity for radio group
1397
+ */
1398
+ checkValidity() {
1399
+ if (this.required && !this.value) {
1400
+ return false;
1401
+ }
1402
+ return true;
1403
+ }
1404
+ /**
1405
+ * Override: Report validity for radio group
1406
+ */
1407
+ reportValidity() {
1408
+ this._validationTriggered = true; // Mark that validation has been attempted
1409
+ const isValid = this.checkValidity();
1410
+ this.handleValidationMessages();
1411
+ this._updateInvalidState();
1412
+ return isValid;
1413
+ }
1414
+ /** Sets a custom validation message. Pass an empty string to restore validity. */
1415
+ setCustomValidity(message) {
1416
+ // Only trigger validation state if message is non-empty (actual error)
1417
+ // Empty message during init or after correction should not trigger invalid state initially
1418
+ if (message && message.length > 0) {
1419
+ this._validationTriggered = true;
1420
+ }
1421
+ this.validationMessage = message;
1422
+ this.formControlController.updateValidity();
1423
+ // Update aria-invalid on group and children immediately
1424
+ this._updateInvalidState();
1425
+ }
1426
+ handleValidationMessages() {
1427
+ const _v = this.validity;
1428
+ if (!this.customValidation) {
1429
+ if (_v.valueMissing) {
1430
+ this.setCustomValidity(this.$t('validityRequired'));
1431
+ }
1432
+ else
1433
+ this.setCustomValidity('');
1434
+ }
1435
+ }
1436
+ _updateInvalidState() {
1437
+ // Only show aria-invalid if validation has been triggered (ARIA best practice)
1438
+ if (!this._validationTriggered) {
1439
+ return;
1440
+ }
1441
+ const invalid = this.checkValidity() === false || !!this.validationMessage;
1442
+ if (invalid)
1443
+ this.setAttribute('aria-invalid', 'true');
1444
+ else
1445
+ this.removeAttribute('aria-invalid');
1446
+ this._radios?.forEach((r) => {
1447
+ if (invalid)
1448
+ r.setAttribute('aria-invalid', 'true');
1449
+ else
1450
+ r.removeAttribute('aria-invalid');
1451
+ });
1452
+ // Update validation message in light DOM
1453
+ this._updateValidationMessage();
1454
+ }
1455
+ /**
1456
+ * Update validation message in light DOM (for aria-describedby cross-boundary support)
1457
+ */
1458
+ _updateValidationMessage() {
1459
+ const validityMessage = this.validationMessage;
1460
+ const messageId = `invalid-feedback-${this._id}`;
1461
+ // Find or create validation message element in light DOM
1462
+ let messageEl = this.querySelector(`[slot="validation-message"]`);
1463
+ if (validityMessage && validityMessage.length > 0) {
1464
+ // Create message element if it doesn't exist
1465
+ if (!messageEl) {
1466
+ messageEl = document.createElement('div');
1467
+ messageEl.setAttribute('slot', 'validation-message');
1468
+ this.appendChild(messageEl);
1469
+ }
1470
+ // Update message content and attributes
1471
+ messageEl.id = messageId;
1472
+ messageEl.className = 'invalid-feedback form-feedback form-text form-feedback just-validate-error-label';
1473
+ messageEl.setAttribute('role', 'alert');
1474
+ messageEl.removeAttribute('hidden');
1475
+ const labelText = this._label?.[0]?.textContent || '';
1476
+ messageEl.innerHTML = `<span class="visually-hidden">${labelText}: </span>${validityMessage}`;
1477
+ // Update aria-describedby on host
1478
+ const existingDescribedBy = this._ariaAttributes['aria-describedby'] || '';
1479
+ const ariaDescribedBy = existingDescribedBy ? `${existingDescribedBy} ${messageId}` : messageId;
1480
+ this.setAttribute('aria-describedby', ariaDescribedBy);
1481
+ }
1482
+ else {
1483
+ // Remove message element if validation passed
1484
+ if (messageEl) {
1485
+ messageEl.remove();
1486
+ }
1487
+ // Update aria-describedby (remove message id)
1488
+ const existingDescribedBy = this._ariaAttributes['aria-describedby'] || '';
1489
+ if (existingDescribedBy) {
1490
+ this.setAttribute('aria-describedby', existingDescribedBy);
1491
+ }
1492
+ else {
1493
+ this.removeAttribute('aria-describedby');
1494
+ }
1495
+ }
1496
+ }
1497
+ connectedCallback() {
1498
+ super.connectedCallback?.();
1499
+ this._handleReady();
1500
+ this.setAttribute('role', 'radiogroup');
1501
+ }
1502
+ firstUpdated() {
1503
+ const checkedRadio = this.querySelector('it-radio[checked]');
1504
+ if (checkedRadio) {
1505
+ this.value = checkedRadio.value;
1506
+ }
1507
+ this.updateRadios();
1508
+ }
1509
+ async updateRadios() {
1510
+ if (!this.hasUpdated) {
1511
+ // Initial validation has to happen after the initial render to allow
1512
+ // the buttons to be queries from the rendered <slot> element
1513
+ await this.updateComplete;
1514
+ }
1515
+ const radios = this.querySelectorAll('it-radio');
1516
+ // eslint-disable-next-line no-return-assign, no-param-reassign
1517
+ radios.forEach((r) => {
1518
+ // eslint-disable-next-line no-param-reassign
1519
+ r.checked = r.value === this.value;
1520
+ });
1521
+ }
1522
+ disconnectedCallback() {
1523
+ super.disconnectedCallback?.();
1524
+ }
1525
+ /**
1526
+ * Sync group value from checked radio (initialization)
1527
+ */
1528
+ _syncValueFromRadios() {
1529
+ if (!this._radios) {
1530
+ return;
1531
+ }
1532
+ // Find the first checked radio
1533
+ const checkedRadio = this._radios.find((r) => r.checked);
1534
+ if (checkedRadio) {
1535
+ // Sync the group value from the checked radio
1536
+ this.value = checkedRadio.value;
1537
+ }
1538
+ }
1539
+ /**
1540
+ * PUBLIC API: Called by radio buttons to select themselves
1541
+ */
1542
+ selectRadio(radio, event) {
1543
+ if (radio.disabled || this.disabled) {
1544
+ return;
1545
+ }
1546
+ this.value = radio.value;
1547
+ // Uncheck other radios using their public API
1548
+ this._radios.forEach((r) => {
1549
+ if (r !== radio) {
1550
+ // eslint-disable-next-line no-param-reassign
1551
+ r.checked = false;
1552
+ }
1553
+ else {
1554
+ // eslint-disable-next-line no-param-reassign
1555
+ r.checked = true;
1556
+ }
1557
+ });
1558
+ // If validation was already triggered, update state after selection
1559
+ if (this._validationTriggered) {
1560
+ // For native validation, clear error if now valid
1561
+ if (!this.customValidation) {
1562
+ this.handleValidationMessages();
1563
+ this._updateInvalidState();
1564
+ }
1565
+ // For custom validation (JustValidate), the external validator will call setCustomValidity
1566
+ // but we still update the state based on current validationMessage
1567
+ else {
1568
+ this._updateInvalidState();
1569
+ }
1570
+ }
1571
+ if (event.type === 'click' || event.type === 'pointerdown') {
1572
+ this._handleClick(event);
1573
+ }
1574
+ else {
1575
+ this._handleChange(event);
1576
+ }
1577
+ }
1578
+ /**
1579
+ * PUBLIC API: Handle keyboard navigation from radio buttons
1580
+ */
1581
+ handleRadioKeyDown(radio, event) {
1582
+ // Delegate to roving tabindex controller
1583
+ const handled = this.rovingTabindex.handleKeydown(radio, event);
1584
+ if (handled)
1585
+ this._handleFocus(event);
1586
+ else
1587
+ this._handleBlur(event);
1588
+ }
1589
+ /**
1590
+ * Synchronize radio button states with group state
1591
+ */
1592
+ _updateRadiosState() {
1593
+ if (!this._radios || this._radios.length === 0) {
1594
+ return;
1595
+ }
1596
+ this._radios.forEach((radio) => {
1597
+ // Set checked state based on value
1598
+ // eslint-disable-next-line no-param-reassign
1599
+ radio.checked = radio.value === this.value;
1600
+ // Sync disabled state from group to radios
1601
+ // Note: We don't override individual radio's disabled state
1602
+ // If the group is disabled, all radios should be disabled
1603
+ // But individual radios can be disabled independently
1604
+ if (this.disabled) {
1605
+ // eslint-disable-next-line no-param-reassign
1606
+ radio.disabled = true;
1607
+ }
1608
+ });
1609
+ // Update tabindex using roving tabindex controller
1610
+ // Priority: checked and enabled > first enabled > 0
1611
+ let tabbableIndex = 0;
1612
+ // First, try to find a checked radio that is not disabled
1613
+ const checkedEnabledIndex = this._radios.findIndex((r) => r.checked && !r.disabled);
1614
+ if (checkedEnabledIndex >= 0) {
1615
+ tabbableIndex = checkedEnabledIndex;
1616
+ }
1617
+ else {
1618
+ // If no checked enabled radio, find the first enabled radio
1619
+ const firstEnabledIndex = this._radios.findIndex((r) => !r.disabled);
1620
+ if (firstEnabledIndex >= 0) {
1621
+ tabbableIndex = firstEnabledIndex;
1622
+ }
1623
+ // If all radios are disabled, tabbableIndex stays 0 (but controller will set all to -1)
1624
+ }
1625
+ this.rovingTabindex.updateTabindices(tabbableIndex);
1626
+ }
1627
+ /**
1628
+ * Sync group state (name, grouped, inline, required) to child radios
1629
+ * This replaces the need for requestUpdate() calls
1630
+ */
1631
+ _syncGroupStateToRadios() {
1632
+ if (!this._radios || this._radios.length === 0) {
1633
+ return;
1634
+ }
1635
+ const groupState = {
1636
+ name: this.name,
1637
+ grouped: this.grouped,
1638
+ inline: this.inline,
1639
+ required: this.required,
1640
+ };
1641
+ this._radios.forEach((radio) => {
1642
+ radio.syncFromGroup(groupState);
1643
+ });
1644
+ }
1645
+ updated(changed) {
1646
+ super.updated(changed);
1647
+ // Update radios when value or name changes
1648
+ if (changed.has('value')) {
1649
+ this._updateRadiosState();
1650
+ this.dispatchEvent(new Event('change', { bubbles: true }));
1651
+ // Re-validate after value change (for native validation) only if validation was already triggered
1652
+ if (!this.customValidation && this._validationTriggered) {
1653
+ this.handleValidationMessages();
1654
+ this._updateInvalidState();
1655
+ }
1656
+ }
1657
+ // If validation message changed, update aria-invalid on group and children
1658
+ if (changed.has('validationMessage')) {
1659
+ this._updateInvalidState();
1660
+ }
1661
+ // If relevant group properties changed, sync to child radios
1662
+ const relevant = ['grouped', 'inline', 'name', 'required', 'disabled'];
1663
+ const hasChanged = Array.from(changed.keys()).some((k) => relevant.includes(String(k)));
1664
+ if (hasChanged && this._radios?.length) {
1665
+ this._syncGroupStateToRadios();
1666
+ }
1667
+ }
1668
+ /**
1669
+ * Render the component
1670
+ */
1671
+ render() {
1672
+ const validityMessage = this.validationMessage;
1673
+ const invalid = validityMessage?.length > 0 || (!this.customValidation && this?.checkValidity() === false);
1674
+ const groupWrapperClasses = this.composeClass('it-radio-group', 'it-form__control', this.inline && !this.grouped ? 'it-radio-group-inline' : '', this.grouped && !this.inline ? 'it-radio-group-stacked' : '', invalid ? 'is-invalid' : '', !invalid && this._touched ? 'just-validate-success-field' : '');
1675
+ return html `<slot name="label" @slotchange=${this._handleLabelSlotChange}></slot>
1676
+ <div class=${groupWrapperClasses}>
1677
+ <slot @slotchange=${this._handleSlotChange}></slot>
1678
+ </div>
1679
+ <slot name="validation-message"></slot>`;
1680
+ }
1681
+ };
1682
+ ItRadioGroup.styles = styles;
1683
+ ItRadioGroup.formAssociated = true;
1684
+ __decorate([
1685
+ property({ type: String }),
1686
+ __metadata("design:type", Object)
1687
+ ], ItRadioGroup.prototype, "name", void 0);
1688
+ __decorate([
1689
+ property({ type: String, reflect: true }),
1690
+ __metadata("design:type", Object)
1691
+ ], ItRadioGroup.prototype, "value", void 0);
1692
+ __decorate([
1693
+ property({ type: Boolean, reflect: true }),
1694
+ __metadata("design:type", Object)
1695
+ ], ItRadioGroup.prototype, "disabled", void 0);
1696
+ __decorate([
1697
+ property({ type: Boolean, reflect: true }),
1698
+ __metadata("design:type", Object)
1699
+ ], ItRadioGroup.prototype, "required", void 0);
1700
+ __decorate([
1701
+ property({ type: Boolean, reflect: true }),
1702
+ __metadata("design:type", Object)
1703
+ ], ItRadioGroup.prototype, "grouped", void 0);
1704
+ __decorate([
1705
+ property({ type: Boolean, reflect: true }),
1706
+ __metadata("design:type", Object)
1707
+ ], ItRadioGroup.prototype, "inline", void 0);
1708
+ __decorate([
1709
+ queryAssignedElements(),
1710
+ __metadata("design:type", Array)
1711
+ ], ItRadioGroup.prototype, "_radios", void 0);
1712
+ __decorate([
1713
+ queryAssignedElements({ slot: 'label' }),
1714
+ __metadata("design:type", Array)
1715
+ ], ItRadioGroup.prototype, "_label", void 0);
1716
+ __decorate([
1717
+ state(),
1718
+ __metadata("design:type", Object)
1719
+ ], ItRadioGroup.prototype, "_validationTriggered", void 0);
1720
+ ItRadioGroup = __decorate([
1721
+ customElement('it-radio-group')
1722
+ ], ItRadioGroup);
1723
+
1724
+ export { ItRadioGroup };
1725
+ //# sourceMappingURL=it-radio-group.js.map