@urbicon-ui/blocks 6.29.2 → 6.30.0

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.
@@ -379,195 +379,7 @@ declare const blocksTranslations: {
379
379
  };
380
380
  };
381
381
  };
382
- export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<{
383
- readonly accessibility: {
384
- readonly avatar: "Avatar";
385
- readonly breadcrumb: "Breadcrumb";
386
- readonly breadcrumbExpand: "Show all breadcrumb items";
387
- readonly clearInput: "Clear input";
388
- readonly clearSearch: "Clear search";
389
- readonly clearSelection: "Clear selection";
390
- readonly closeDialog: "Close dialog";
391
- readonly closeDrawer: "Close drawer";
392
- readonly dismiss: "Dismiss";
393
- readonly fileUpload: "File upload";
394
- readonly loading: "Loading";
395
- readonly maximum: "Maximum";
396
- readonly minimum: "Minimum";
397
- readonly pagination: "Page navigation";
398
- readonly progress: "Progress";
399
- readonly slider: "Slider";
400
- readonly toggle: "Toggle";
401
- readonly toggleOptions: "Toggle options";
402
- readonly removableBadge: "Removable badge";
403
- readonly removeBadge: "Remove badge";
404
- readonly removeFile: "Remove {{name}}";
405
- readonly removeTag: "Remove {{label}}";
406
- };
407
- readonly button: {
408
- readonly close: "Close";
409
- readonly apply: "Apply";
410
- readonly cancel: "Cancel";
411
- readonly confirm: "Confirm";
412
- };
413
- readonly menu: {
414
- readonly placeholder: "Select an option...";
415
- };
416
- readonly common: {
417
- readonly loading: "Loading...";
418
- };
419
- readonly languages: {
420
- readonly de: "Deutsch";
421
- readonly en: "English";
422
- readonly es: "Español";
423
- readonly fr: "Français";
424
- readonly it: "Italiano";
425
- readonly nl: "Nederlands";
426
- };
427
- readonly localeSwitcher: {
428
- readonly ariaLabel: "Language selection";
429
- readonly placeholder: "Select language...";
430
- };
431
- readonly pagination: {
432
- readonly next: "Next";
433
- readonly first: "First";
434
- readonly last: "Last";
435
- readonly page: "Page";
436
- readonly previous: "Previous";
437
- readonly pageInfo: "{{label}} {{current}} of {{total}}";
438
- readonly rangeInfo: "{{start}}–{{end}} of {{total}}";
439
- };
440
- readonly calendar: {
441
- readonly recurring: "Recurring event";
442
- readonly previousMonth: "Previous month";
443
- readonly nextMonth: "Next month";
444
- readonly previousWeek: "Previous week";
445
- readonly nextWeek: "Next week";
446
- readonly previousDay: "Previous day";
447
- readonly nextDay: "Next day";
448
- readonly previousYear: "Previous year";
449
- readonly nextYear: "Next year";
450
- readonly today: "Today";
451
- readonly calendarGrid: "Calendar";
452
- readonly weekdays: "Weekdays";
453
- readonly weekNumber: "Week number";
454
- readonly events: "Events";
455
- readonly noEvents: "No events";
456
- readonly showMore: "{{count}} more";
457
- readonly legend: "Legend";
458
- readonly yearView: "Year overview";
459
- readonly weekView: "Week view";
460
- readonly viewMonth: "Month";
461
- readonly viewYear: "Year";
462
- readonly viewWeek: "Week";
463
- readonly viewDay: "Day";
464
- readonly multiDayLabel: "Day {{current}} of {{total}}";
465
- readonly agendaView: "Agenda view";
466
- readonly viewAgenda: "Agenda";
467
- readonly viewSwitcher: "View mode";
468
- };
469
- readonly planner: {
470
- readonly previousWeek: "Previous week";
471
- readonly nextWeek: "Next week";
472
- readonly previousMonth: "Previous month";
473
- readonly nextMonth: "Next month";
474
- readonly previousRange: "Previous range";
475
- readonly nextRange: "Next range";
476
- readonly today: "Today";
477
- readonly grid: "Planner";
478
- readonly itemCount: "{{count}} items";
479
- };
480
- readonly commandPalette: {
481
- readonly noResults: "No results found.";
482
- readonly search: "Search...";
483
- readonly hints: {
484
- readonly navigate: "Navigate";
485
- readonly select: "Select";
486
- readonly close: "Close";
487
- };
488
- };
489
- readonly compositionBar: {
490
- readonly total: "Total";
491
- readonly summary: "Composition: {{total}} ({{count}} shares) — {{parts}}";
492
- readonly share: "Share";
493
- readonly value: "Value";
494
- readonly percent: "Percent";
495
- readonly remaining: "remaining";
496
- };
497
- readonly guide: {
498
- readonly next: "Next";
499
- readonly previous: "Back";
500
- readonly skip: "Skip tour";
501
- readonly done: "Done";
502
- readonly step: "Step {{current}} of {{total}}";
503
- readonly tour: "Guided tour";
504
- readonly close: "Close";
505
- readonly openHelp: "Help";
506
- readonly backToList: "All topics";
507
- readonly info: "More information";
508
- readonly infoAbout: "More information about {{label}}";
509
- readonly dismiss: "Dismiss hint";
510
- readonly startTour: "Start the guided tour";
511
- readonly actionRequired: "Complete the highlighted action to continue";
512
- readonly filterPlaceholder: "Filter topics…";
513
- readonly noResults: "No matching topics";
514
- };
515
- readonly journeyTimeline: {
516
- readonly label: "Journey";
517
- readonly status: {
518
- readonly complete: "Completed";
519
- readonly active: "In progress";
520
- readonly pending: "Pending";
521
- readonly attention: "Needs attention";
522
- readonly blocked: "Blocked";
523
- readonly skipped: "Skipped";
524
- };
525
- };
526
- readonly datepicker: {
527
- readonly placeholder: "Select a date...";
528
- readonly rangePlaceholder: "Select a date range...";
529
- readonly openCalendar: "Open calendar";
530
- readonly invalidDate: "Invalid date";
531
- readonly outOfRange: "Date is outside the allowed range";
532
- readonly invalidRange: "Invalid date range";
533
- };
534
- readonly fileUpload: {
535
- readonly exists: "File already added";
536
- readonly invalidType: "File type {{type}} is not allowed";
537
- readonly tooLarge: "File exceeds {{size}} limit";
538
- readonly tooMany: "Maximum {{count}} files allowed";
539
- readonly tooSmall: "File must be at least {{size}}";
540
- };
541
- readonly sankey: {
542
- readonly summary: "Sankey diagram: {{nodes}} nodes, {{links}} links — {{flows}}";
543
- readonly source: "Source";
544
- readonly target: "Target";
545
- readonly value: "Value";
546
- };
547
- readonly slider: {
548
- readonly rangeStatus: {
549
- readonly insideRecommended: "In recommended range";
550
- readonly insideValid: "In valid range";
551
- readonly insideValidOnly: "Outside recommended range, but valid";
552
- readonly outsideValid: "Outside valid range";
553
- };
554
- };
555
- readonly themeSwitcher: {
556
- readonly lightMode: "Light mode";
557
- readonly darkMode: "Dark mode";
558
- readonly systemTheme: "System theme";
559
- };
560
- readonly chart: {
561
- readonly category: "Category";
562
- readonly series: "Series {{index}}";
563
- readonly segment: "Segment";
564
- readonly value: "Value";
565
- readonly share: "Share";
566
- };
567
- readonly stepper: {
568
- readonly optional: "Optional";
569
- };
570
- }>;
382
+ export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<import("@urbicon-ui/i18n").Translations>;
571
383
  /**
572
384
  * Context-scoped translation hook for the blocks package. Call during component
573
385
  * initialisation, then use the returned `t` (conventionally aliased `bt`):
@@ -580,195 +392,7 @@ export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<{
580
392
  * Resolves against the nearest `<I18nProvider>`'s locale (or the base locale
581
393
  * when none is mounted) and re-renders reactively on locale change.
582
394
  */
583
- export declare const useBlocksI18n: () => import("@urbicon-ui/i18n").TypedTranslationFunction<{
584
- readonly accessibility: {
585
- readonly avatar: "Avatar";
586
- readonly breadcrumb: "Breadcrumb";
587
- readonly breadcrumbExpand: "Show all breadcrumb items";
588
- readonly clearInput: "Clear input";
589
- readonly clearSearch: "Clear search";
590
- readonly clearSelection: "Clear selection";
591
- readonly closeDialog: "Close dialog";
592
- readonly closeDrawer: "Close drawer";
593
- readonly dismiss: "Dismiss";
594
- readonly fileUpload: "File upload";
595
- readonly loading: "Loading";
596
- readonly maximum: "Maximum";
597
- readonly minimum: "Minimum";
598
- readonly pagination: "Page navigation";
599
- readonly progress: "Progress";
600
- readonly slider: "Slider";
601
- readonly toggle: "Toggle";
602
- readonly toggleOptions: "Toggle options";
603
- readonly removableBadge: "Removable badge";
604
- readonly removeBadge: "Remove badge";
605
- readonly removeFile: "Remove {{name}}";
606
- readonly removeTag: "Remove {{label}}";
607
- };
608
- readonly button: {
609
- readonly close: "Close";
610
- readonly apply: "Apply";
611
- readonly cancel: "Cancel";
612
- readonly confirm: "Confirm";
613
- };
614
- readonly menu: {
615
- readonly placeholder: "Select an option...";
616
- };
617
- readonly common: {
618
- readonly loading: "Loading...";
619
- };
620
- readonly languages: {
621
- readonly de: "Deutsch";
622
- readonly en: "English";
623
- readonly es: "Español";
624
- readonly fr: "Français";
625
- readonly it: "Italiano";
626
- readonly nl: "Nederlands";
627
- };
628
- readonly localeSwitcher: {
629
- readonly ariaLabel: "Language selection";
630
- readonly placeholder: "Select language...";
631
- };
632
- readonly pagination: {
633
- readonly next: "Next";
634
- readonly first: "First";
635
- readonly last: "Last";
636
- readonly page: "Page";
637
- readonly previous: "Previous";
638
- readonly pageInfo: "{{label}} {{current}} of {{total}}";
639
- readonly rangeInfo: "{{start}}–{{end}} of {{total}}";
640
- };
641
- readonly calendar: {
642
- readonly recurring: "Recurring event";
643
- readonly previousMonth: "Previous month";
644
- readonly nextMonth: "Next month";
645
- readonly previousWeek: "Previous week";
646
- readonly nextWeek: "Next week";
647
- readonly previousDay: "Previous day";
648
- readonly nextDay: "Next day";
649
- readonly previousYear: "Previous year";
650
- readonly nextYear: "Next year";
651
- readonly today: "Today";
652
- readonly calendarGrid: "Calendar";
653
- readonly weekdays: "Weekdays";
654
- readonly weekNumber: "Week number";
655
- readonly events: "Events";
656
- readonly noEvents: "No events";
657
- readonly showMore: "{{count}} more";
658
- readonly legend: "Legend";
659
- readonly yearView: "Year overview";
660
- readonly weekView: "Week view";
661
- readonly viewMonth: "Month";
662
- readonly viewYear: "Year";
663
- readonly viewWeek: "Week";
664
- readonly viewDay: "Day";
665
- readonly multiDayLabel: "Day {{current}} of {{total}}";
666
- readonly agendaView: "Agenda view";
667
- readonly viewAgenda: "Agenda";
668
- readonly viewSwitcher: "View mode";
669
- };
670
- readonly planner: {
671
- readonly previousWeek: "Previous week";
672
- readonly nextWeek: "Next week";
673
- readonly previousMonth: "Previous month";
674
- readonly nextMonth: "Next month";
675
- readonly previousRange: "Previous range";
676
- readonly nextRange: "Next range";
677
- readonly today: "Today";
678
- readonly grid: "Planner";
679
- readonly itemCount: "{{count}} items";
680
- };
681
- readonly commandPalette: {
682
- readonly noResults: "No results found.";
683
- readonly search: "Search...";
684
- readonly hints: {
685
- readonly navigate: "Navigate";
686
- readonly select: "Select";
687
- readonly close: "Close";
688
- };
689
- };
690
- readonly compositionBar: {
691
- readonly total: "Total";
692
- readonly summary: "Composition: {{total}} ({{count}} shares) — {{parts}}";
693
- readonly share: "Share";
694
- readonly value: "Value";
695
- readonly percent: "Percent";
696
- readonly remaining: "remaining";
697
- };
698
- readonly guide: {
699
- readonly next: "Next";
700
- readonly previous: "Back";
701
- readonly skip: "Skip tour";
702
- readonly done: "Done";
703
- readonly step: "Step {{current}} of {{total}}";
704
- readonly tour: "Guided tour";
705
- readonly close: "Close";
706
- readonly openHelp: "Help";
707
- readonly backToList: "All topics";
708
- readonly info: "More information";
709
- readonly infoAbout: "More information about {{label}}";
710
- readonly dismiss: "Dismiss hint";
711
- readonly startTour: "Start the guided tour";
712
- readonly actionRequired: "Complete the highlighted action to continue";
713
- readonly filterPlaceholder: "Filter topics…";
714
- readonly noResults: "No matching topics";
715
- };
716
- readonly journeyTimeline: {
717
- readonly label: "Journey";
718
- readonly status: {
719
- readonly complete: "Completed";
720
- readonly active: "In progress";
721
- readonly pending: "Pending";
722
- readonly attention: "Needs attention";
723
- readonly blocked: "Blocked";
724
- readonly skipped: "Skipped";
725
- };
726
- };
727
- readonly datepicker: {
728
- readonly placeholder: "Select a date...";
729
- readonly rangePlaceholder: "Select a date range...";
730
- readonly openCalendar: "Open calendar";
731
- readonly invalidDate: "Invalid date";
732
- readonly outOfRange: "Date is outside the allowed range";
733
- readonly invalidRange: "Invalid date range";
734
- };
735
- readonly fileUpload: {
736
- readonly exists: "File already added";
737
- readonly invalidType: "File type {{type}} is not allowed";
738
- readonly tooLarge: "File exceeds {{size}} limit";
739
- readonly tooMany: "Maximum {{count}} files allowed";
740
- readonly tooSmall: "File must be at least {{size}}";
741
- };
742
- readonly sankey: {
743
- readonly summary: "Sankey diagram: {{nodes}} nodes, {{links}} links — {{flows}}";
744
- readonly source: "Source";
745
- readonly target: "Target";
746
- readonly value: "Value";
747
- };
748
- readonly slider: {
749
- readonly rangeStatus: {
750
- readonly insideRecommended: "In recommended range";
751
- readonly insideValid: "In valid range";
752
- readonly insideValidOnly: "Outside recommended range, but valid";
753
- readonly outsideValid: "Outside valid range";
754
- };
755
- };
756
- readonly themeSwitcher: {
757
- readonly lightMode: "Light mode";
758
- readonly darkMode: "Dark mode";
759
- readonly systemTheme: "System theme";
760
- };
761
- readonly chart: {
762
- readonly category: "Category";
763
- readonly series: "Series {{index}}";
764
- readonly segment: "Segment";
765
- readonly value: "Value";
766
- readonly share: "Share";
767
- };
768
- readonly stepper: {
769
- readonly optional: "Optional";
770
- };
771
- }>;
395
+ export declare const useBlocksI18n: any;
772
396
  export declare const hasBlocksTranslation: (key: string) => boolean, getBlocksLocales: () => import("@urbicon-ui/i18n").Locale[];
773
397
  export type BlocksTranslationKey = keyof typeof enTranslations;
774
398
  export { blocksTranslations };
@@ -1,53 +1,53 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
2
  export declare const alertVariants: ((props?: {
3
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
3
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
4
4
  variant?: "inline" | "filled" | "soft" | undefined;
5
5
  size?: "sm" | "md" | "lg" | undefined;
6
6
  } | undefined) => {
7
7
  base: (props?: ({
8
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
8
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
9
9
  variant?: "inline" | "filled" | "soft" | undefined;
10
10
  size?: "sm" | "md" | "lg" | undefined;
11
11
  } & {
12
12
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
13
13
  }) | undefined) => string;
14
14
  icon: (props?: ({
15
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
15
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
16
16
  variant?: "inline" | "filled" | "soft" | undefined;
17
17
  size?: "sm" | "md" | "lg" | undefined;
18
18
  } & {
19
19
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
20
20
  }) | undefined) => string;
21
21
  content: (props?: ({
22
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
22
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
23
23
  variant?: "inline" | "filled" | "soft" | undefined;
24
24
  size?: "sm" | "md" | "lg" | undefined;
25
25
  } & {
26
26
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
27
27
  }) | undefined) => string;
28
28
  title: (props?: ({
29
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
29
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
30
30
  variant?: "inline" | "filled" | "soft" | undefined;
31
31
  size?: "sm" | "md" | "lg" | undefined;
32
32
  } & {
33
33
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
34
34
  }) | undefined) => string;
35
35
  description: (props?: ({
36
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
36
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
37
37
  variant?: "inline" | "filled" | "soft" | undefined;
38
38
  size?: "sm" | "md" | "lg" | undefined;
39
39
  } & {
40
40
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
41
41
  }) | undefined) => string;
42
42
  actions: (props?: ({
43
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
43
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
44
44
  variant?: "inline" | "filled" | "soft" | undefined;
45
45
  size?: "sm" | "md" | "lg" | undefined;
46
46
  } & {
47
47
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
48
48
  }) | undefined) => string;
49
49
  dismissButton: (props?: ({
50
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
50
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
51
51
  variant?: "inline" | "filled" | "soft" | undefined;
52
52
  size?: "sm" | "md" | "lg" | undefined;
53
53
  } & {
@@ -120,6 +120,7 @@
120
120
  onclick={handleClick}
121
121
  role={ariaProps.role}
122
122
  aria-checked={ariaProps['aria-checked']}
123
+ data-value={ariaProps['data-value']}
123
124
  aria-pressed={ariaProps.role ? undefined : effectiveActive || effectivePressed || undefined}
124
125
  aria-disabled={effectiveDisabled}
125
126
  aria-busy={loading}
@@ -66,15 +66,26 @@
66
66
  }
67
67
  });
68
68
 
69
- // Child Button values in registration (= document) order. Buttons register
70
- // during their render pass (before any effect runs), so this is fully
71
- // populated in DOM order by the time roving reads it it lets the radiogroup
72
- // map the reactive selection back to a radio's position without the shared
73
- // <Button> exposing its value in the DOM.
74
- const buttonOrder: string[] = [];
69
+ // Bumped (deferred to a microtask — registration runs during the child's
70
+ // render, where a synchronous $state write would be state_unsafe_mutation)
71
+ // whenever a Button registers, so the roving effect below re-runs for
72
+ // buttons mounted *after* the group's initial render. Without it a
73
+ // runtime-added radio kept its native tabbability until the next selection
74
+ // change a second tab stop in the radiogroup.
75
+ let registryVersion = $state(0);
76
+ let registryBumpQueued = false;
77
+
78
+ function noteRegistration() {
79
+ if (registryBumpQueued) return;
80
+ registryBumpQueued = true;
81
+ queueMicrotask(() => {
82
+ registryBumpQueued = false;
83
+ registryVersion++;
84
+ });
85
+ }
75
86
 
76
87
  function registerButton(buttonValue: string | undefined) {
77
- if (buttonValue && !buttonOrder.includes(buttonValue)) buttonOrder.push(buttonValue);
88
+ noteRegistration();
78
89
  return {
79
90
  get isSelected() {
80
91
  return buttonValue ? selectedValues.has(buttonValue) : false;
@@ -100,17 +111,18 @@
100
111
  onSelectionChange?.(value, Array.from(selectedValues));
101
112
  },
102
113
  getButtonProps() {
103
- // A value-less Button is an action button, not a selection option, so it
104
- // gets no radio/checkbox role. This also keeps `rovingRadios()` (which
105
- // collects `[role="radio"]`) index-aligned with `buttonOrder` (gated on
106
- // `buttonValue`): a value-less radio would otherwise sit in the roving
107
- // array but not the order registry, drifting the two index spaces and
108
- // misplacing the tab stop / arrow-nav origin.
114
+ // A value-less Button is an action button, not a selection option, so
115
+ // it gets no radio/checkbox role. Selection options additionally carry
116
+ // their value as `data-value`, committed in the same render as the
117
+ // role the radiogroup resolves the selected radio by *matching
118
+ // value* against the queried elements, so duplicate values and
119
+ // runtime-mounted/removed buttons can never drift an index space.
109
120
  if (selection === 'none' || !buttonValue) return {};
110
121
  const checked = selectedValues.has(buttonValue);
111
122
  return {
112
123
  role: selection === 'single' ? ('radio' as const) : ('checkbox' as const),
113
- 'aria-checked': checked
124
+ 'aria-checked': checked,
125
+ 'data-value': buttonValue
114
126
  };
115
127
  }
116
128
  };
@@ -165,24 +177,29 @@
165
177
  : [];
166
178
  }
167
179
 
168
- // Position of the selected radio, read from the reactive selection (not the
169
- // DOM's aria-checked) so it is correct even on the first paint — a parent
170
- // effect can run before the child <Button>s commit their aria, so the DOM
171
- // isn't a reliable source there. `buttonOrder` is in document order, matching
172
- // the radios queried above.
173
- function selectedRadioIndex(): number {
174
- for (let i = 0; i < buttonOrder.length; i++) {
175
- if (selectedValues.has(buttonOrder[i])) return i;
176
- }
177
- return -1;
180
+ // Position of the selected radio within the queried elements: each radio
181
+ // carries its value as `data-value` (same render commit as its role), and
182
+ // the *selected* one is read from the reactive selection not the DOM's
183
+ // aria-checked, which a parent effect can observe before the child
184
+ // <Button>s commit it. Matching by value (not by a parallel registration
185
+ // index) keeps duplicates and runtime-mounted/removed buttons correct: the
186
+ // tab stop sticks to the element whose value is selected, wherever it sits.
187
+ function selectedRadioIndex(radios: readonly HTMLButtonElement[]): number {
188
+ return radios.findIndex((radio) => {
189
+ const v = radio.dataset.value;
190
+ return v !== undefined && selectedValues.has(v);
191
+ });
178
192
  }
179
193
 
180
194
  $effect(() => {
195
+ // `registryVersion` re-runs the assignment when Buttons mount after the
196
+ // group's initial render (the DOM query itself is not reactive).
197
+ void registryVersion;
181
198
  if (selection !== 'single' || !containerElement) return;
182
199
  const radios = rovingRadios();
183
200
  if (radios.length === 0) return;
184
201
 
185
- const checkedIndex = selectedRadioIndex();
202
+ const checkedIndex = selectedRadioIndex(radios);
186
203
  // Nothing selected yet → the first enabled radio holds the tab stop, so the
187
204
  // group stays reachable with Tab (standard radiogroup entry behaviour).
188
205
  const activeIndex =
@@ -194,11 +211,12 @@
194
211
  radio.tabIndex = i === activeIndex ? 0 : -1;
195
212
  });
196
213
 
197
- // Restore native tabbability when the group stops roving (selection flips away
198
- // from single, or it unmounts) — otherwise the imposed -1 would strand the
199
- // buttons out of the tab order.
214
+ // Restore native tabbability when the group stops roving (selection flips
215
+ // away from single, or it unmounts) — otherwise the imposed -1 would strand
216
+ // the buttons out of the tab order. Queried fresh at teardown time so
217
+ // radios mounted since this run are restored too.
200
218
  return () => {
201
- for (const radio of radios) radio.removeAttribute('tabindex');
219
+ for (const radio of rovingRadios()) radio.removeAttribute('tabindex');
202
220
  };
203
221
  });
204
222
 
@@ -212,7 +230,7 @@
212
230
  const radios = rovingRadios();
213
231
  if (radios.length === 0) return;
214
232
 
215
- const currentIndex = selectedRadioIndex();
233
+ const currentIndex = selectedRadioIndex(radios);
216
234
  const isDisabled = (i: number) => radios[i].disabled;
217
235
  let newIndex: number;
218
236
 
@@ -102,6 +102,12 @@ export interface ButtonGroupContext {
102
102
  getButtonProps: () => {
103
103
  role?: 'radio' | 'checkbox';
104
104
  'aria-checked'?: boolean;
105
+ /**
106
+ * The Button's selection value, exposed on the element so the group can
107
+ * resolve the selected radio by matching value instead of by position
108
+ * (robust against duplicate values and runtime add/remove).
109
+ */
110
+ 'data-value'?: string;
105
111
  };
106
112
  };
107
113
  }
@@ -28,6 +28,7 @@
28
28
  id: idProp,
29
29
  mint = 'none',
30
30
  onCheckedChange,
31
+ onchange: userOnChange,
31
32
  class: className = '',
32
33
  unstyled: unstyledProp = false,
33
34
  slotClasses: slotClassesProp = {},
@@ -103,7 +104,7 @@
103
104
  }
104
105
  });
105
106
 
106
- function handleChange(event: Event) {
107
+ function handleChange(event: Event & { currentTarget: EventTarget & HTMLInputElement }) {
107
108
  if (disabled) return;
108
109
  if (indeterminate) indeterminate = false;
109
110
  // Read the new value directly from the input — `bind:checked`
@@ -111,6 +112,14 @@
111
112
  const next = (event.target as HTMLInputElement).checked;
112
113
  checked = next;
113
114
  onCheckedChange?.(next);
115
+ // Forward the consumer's native onchange — Checkbox's own handler sits
116
+ // after `{...restProps}` on the <input>, so without this it would be
117
+ // silently swallowed (the exact failure that broke Table's selection
118
+ // wiring, d7b4dfe; Textarea's oninput forward is the precedent).
119
+ // `onCheckedChange(checked)` stays the canonical callback; the forward
120
+ // runs after the internal state write so the consumer observes the
121
+ // committed value.
122
+ userOnChange?.(event);
114
123
  }
115
124
  </script>
116
125