@urbicon-ui/blocks 6.24.0 → 6.26.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.
Files changed (73) hide show
  1. package/README.md +14 -0
  2. package/dist/components/Calendar/CalendarTimeGrid.svelte +2 -1
  3. package/dist/components/Calendar/calendar.variants.d.ts +63 -63
  4. package/dist/components/Calendar/calendar.variants.js +5 -3
  5. package/dist/components/CommandPalette/CommandPalette.svelte +32 -16
  6. package/dist/components/CommandPalette/CommandPalette.svelte.d.ts +1 -1
  7. package/dist/components/CommandPalette/commandPalette.variants.d.ts +10 -0
  8. package/dist/components/CommandPalette/commandPalette.variants.js +9 -7
  9. package/dist/components/CommandPalette/index.d.ts +19 -0
  10. package/dist/components/CompositionBar/CompositionBar.svelte +2 -2
  11. package/dist/components/CompositionBar/composition-bar.variants.js +1 -1
  12. package/dist/components/Planner/planner.variants.js +1 -1
  13. package/dist/components/Sankey/sankey.variants.js +1 -1
  14. package/dist/i18n/index.d.ts +378 -2
  15. package/dist/internal/charts/variants.js +2 -2
  16. package/dist/internal/date-grid/DateGridScaffold.svelte +9 -2
  17. package/dist/internal/date-grid/date-grid.svelte.d.ts +4 -2
  18. package/dist/internal/date-grid/date-grid.svelte.js +63 -7
  19. package/dist/primitives/Alert/alert.variants.d.ts +8 -8
  20. package/dist/primitives/Alert/alert.variants.js +1 -1
  21. package/dist/primitives/Avatar/Avatar.svelte +38 -24
  22. package/dist/primitives/Avatar/index.d.ts +1 -1
  23. package/dist/primitives/Badge/badge.variants.js +1 -1
  24. package/dist/primitives/Button/Button.svelte +12 -25
  25. package/dist/primitives/Button/button.variants.js +2 -2
  26. package/dist/primitives/ButtonGroup/ButtonGroup.svelte +5 -1
  27. package/dist/primitives/Checkbox/checkbox.variants.js +2 -2
  28. package/dist/primitives/Collapsible/Collapsible.svelte +10 -0
  29. package/dist/primitives/Collapsible/index.d.ts +13 -2
  30. package/dist/primitives/ConfirmDialog/ConfirmDialog.svelte +14 -0
  31. package/dist/primitives/ConfirmDialog/index.d.ts +13 -2
  32. package/dist/primitives/Dialog/Dialog.svelte +36 -8
  33. package/dist/primitives/Drawer/Drawer.svelte +34 -7
  34. package/dist/primitives/Input/Input.svelte +7 -1
  35. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +22 -22
  36. package/dist/primitives/JourneyTimeline/journey-timeline.variants.js +3 -3
  37. package/dist/primitives/Menu/Menu.svelte +8 -0
  38. package/dist/primitives/Menu/index.d.ts +11 -13
  39. package/dist/primitives/Popover/Popover.svelte +107 -4
  40. package/dist/primitives/Popover/index.d.ts +15 -2
  41. package/dist/primitives/Popover/index.js +1 -1
  42. package/dist/primitives/Popover/popover.variants.d.ts +25 -0
  43. package/dist/primitives/Popover/popover.variants.js +35 -0
  44. package/dist/primitives/Progress/progress.variants.js +1 -1
  45. package/dist/primitives/RadioGroup/radioGroup.variants.js +2 -2
  46. package/dist/primitives/Select/index.d.ts +8 -1
  47. package/dist/primitives/Slider/Slider.svelte +32 -3
  48. package/dist/primitives/Spinner/spinner.variants.d.ts +16 -16
  49. package/dist/primitives/Stepper/stepper.variants.d.ts +11 -11
  50. package/dist/primitives/Stepper/stepper.variants.js +2 -2
  51. package/dist/primitives/Textarea/Textarea.svelte +5 -1
  52. package/dist/primitives/Toast/toast.variants.d.ts +12 -12
  53. package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -3
  54. package/dist/primitives/Tooltip/tooltip.variants.js +14 -3
  55. package/dist/style/foundation.css +41 -7
  56. package/dist/style/interaction.css +19 -2
  57. package/dist/style/semantic.css +84 -1
  58. package/dist/style/themes/forest.css +3 -1
  59. package/dist/style/themes/neutral.css +10 -1
  60. package/dist/style/themes/ocean.css +5 -1
  61. package/dist/style/themes/sunset.css +3 -1
  62. package/dist/utils/compose-handlers.d.ts +72 -0
  63. package/dist/utils/compose-handlers.js +65 -0
  64. package/dist/utils/figma-token-export.js +6 -2
  65. package/dist/utils/guide.svelte.js +92 -29
  66. package/dist/utils/index.d.ts +1 -1
  67. package/dist/utils/index.js +1 -1
  68. package/dist/utils/overlay-tokens.d.ts +17 -0
  69. package/dist/utils/overlay-tokens.js +30 -0
  70. package/dist/utils/overlay.js +24 -2
  71. package/dist/utils/use-floating-panel.svelte.js +9 -3
  72. package/dist/utils/variants.js +5 -1
  73. package/package.json +3 -3
@@ -379,7 +379,195 @@ declare const blocksTranslations: {
379
379
  };
380
380
  };
381
381
  };
382
- export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<import("@urbicon-ui/i18n").Translations>;
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
+ }>;
383
571
  /**
384
572
  * Context-scoped translation hook for the blocks package. Call during component
385
573
  * initialisation, then use the returned `t` (conventionally aliased `bt`):
@@ -392,7 +580,195 @@ export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<import("
392
580
  * Resolves against the nearest `<I18nProvider>`'s locale (or the base locale
393
581
  * when none is mounted) and re-renders reactively on locale change.
394
582
  */
395
- export declare const useBlocksI18n: any;
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
+ }>;
396
772
  export declare const hasBlocksTranslation: (key: string) => boolean, getBlocksLocales: () => import("@urbicon-ui/i18n").Locale[];
397
773
  export type BlocksTranslationKey = keyof typeof enTranslations;
398
774
  export { blocksTranslations };
@@ -17,7 +17,7 @@ export const chartVariants = tv({
17
17
  /** Tick marks. */
18
18
  axisTick: ['stroke-border-subtle'],
19
19
  /** Tick + category labels. */
20
- axisLabel: ['fill-text-tertiary text-[10px] tabular-nums'],
20
+ axisLabel: ['fill-text-tertiary text-3xs tabular-nums'],
21
21
  /** Background gridlines. */
22
22
  grid: ['stroke-border-hairline'],
23
23
  /** Generic data mark (line path, area, point). */
@@ -29,7 +29,7 @@ export const chartVariants = tv({
29
29
  /** Donut center primary label. */
30
30
  centerLabel: ['fill-text-primary text-sm font-medium tabular-nums'],
31
31
  /** Donut center secondary label. */
32
- centerSubLabel: ['fill-text-tertiary text-[10px]'],
32
+ centerSubLabel: ['fill-text-tertiary text-3xs'],
33
33
  /** Legend container (HTML). */
34
34
  legend: ['mt-2 flex flex-wrap items-center gap-x-4 gap-y-1.5', 'text-text-secondary text-xs'],
35
35
  /** One legend entry. */
@@ -142,8 +142,15 @@
142
142
  style="position: relative; overflow: hidden;"
143
143
  onmouseleave={() => ctx.setHoveredDate(null)}
144
144
  {@attach swipeable({
145
- onSwipeLeft: () => ctx.navigate(1),
146
- onSwipeRight: () => ctx.navigate(-1),
145
+ // Direction-gated like the header arrows: a swipe at the bound should be
146
+ // inert (no navDirection flip, no clamped no-op emit), matching the
147
+ // disabled arrow button for the same direction.
148
+ onSwipeLeft: () => {
149
+ if (ctx.canGoForward) ctx.navigate(1);
150
+ },
151
+ onSwipeRight: () => {
152
+ if (ctx.canGoBack) ctx.navigate(-1);
153
+ },
147
154
  enabled: swipeEnabled && !ctx.disabled
148
155
  })}
149
156
  >
@@ -106,8 +106,10 @@ export declare class DateGridController {
106
106
  isRangeEnd(date: Date): boolean;
107
107
  isInSelectedRange(date: Date): boolean;
108
108
  isInPreviewRange(date: Date): boolean;
109
- /** Step the view by `delta` units (months / weeks / days; range shifts by its
110
- * own span in weeks). Emits the next reference date and visible range. */
109
+ /** Step the view by `delta` units (months / weeks / days; range slides its
110
+ * window by its own span in days). Every path clamps to [minDate, maxDate];
111
+ * the range window clamps span-preserving. Emits the next reference date and
112
+ * range (visible cell range, or the shifted window for `range`). */
111
113
  navigate(delta: number): void;
112
114
  /** Jump to today and focus it. Clamped to `[minDate, maxDate]`: the header Today
113
115
  * button gates itself on {@link canGoToToday} (a clamped "today" is not today, so
@@ -116,7 +116,18 @@ export class DateGridController {
116
116
  const { canGoBack, canGoForward } = clampMonth(referenceDate.getMonth(), referenceDate.getFullYear(), minDate, maxDate);
117
117
  return { canGoBack, canGoForward };
118
118
  }
119
- // week/day/range: day-granular bounds against the visible edges.
119
+ if (view === 'range') {
120
+ // Range navigation slides the *explicit* window (partial shifts allowed —
121
+ // see the span-preserving clamp in navigate()), so gate on the window
122
+ // edges, not the week-padded visible cells: the padding may spill past a
123
+ // bound while the window itself can still move.
124
+ const { start, end } = this.#orderedRange(referenceDate);
125
+ return {
126
+ canGoBack: !minDate || start.getTime() > stripTime(minDate).getTime(),
127
+ canGoForward: !maxDate || end.getTime() < stripTime(maxDate).getTime()
128
+ };
129
+ }
130
+ // week/day: day-granular bounds against the visible edges.
120
131
  const canGoBack = !minDate || stripTime(this.rangeStart) > stripTime(minDate);
121
132
  const canGoForward = !maxDate || stripTime(this.rangeEnd) < stripTime(maxDate);
122
133
  return { canGoBack, canGoForward };
@@ -232,24 +243,38 @@ export class DateGridController {
232
243
  return isInRange(date, range.start, this.hoveredDate) && !isSameDay(date, range.start);
233
244
  }
234
245
  // ─── Actions ─────────────────────────────────────────────────────────────
235
- /** Step the view by `delta` units (months / weeks / days; range shifts by its
236
- * own span in weeks). Emits the next reference date and visible range. */
246
+ /** Step the view by `delta` units (months / weeks / days; range slides its
247
+ * window by its own span in days). Every path clamps to [minDate, maxDate];
248
+ * the range window clamps span-preserving. Emits the next reference date and
249
+ * range (visible cell range, or the shifted window for `range`). */
237
250
  navigate(delta) {
238
251
  if (delta === 0)
239
252
  return;
240
253
  const { view, referenceDate, minDate, maxDate } = this.#opts;
241
- this.navDirection = delta > 0 ? 'forward' : 'backward';
242
254
  if (view === 'range') {
243
255
  // The range geometry follows the explicit rangeStart/rangeEnd inputs, not
244
256
  // referenceDate, so navigation shifts that window by its own span and emits
245
- // the shifted range — the wrapper rebinds rangeStart/rangeEnd from it.
257
+ // the shifted range — the wrapper rebinds rangeStart/rangeEnd from it. It
258
+ // deliberately bypasses #emitNavigate: that would emit the week-padded
259
+ // *visible* range, and a wrapper rebinding rangeStart/rangeEnd from it
260
+ // would silently grow a mid-week window to full weeks on every step.
261
+ // The shift is clamped span-preserving (see #clampWindowStart), so a swipe
262
+ // — not gated by canGoBack/canGoForward the way the header arrows are —
263
+ // cannot push the window past [minDate, maxDate]. A shift that cannot move
264
+ // at all (already at the bound) emits nothing: unlike week/day, a no-op
265
+ // emit here would rebind rangeStart/rangeEnd to fresh Date objects and
266
+ // fire the consumer's onNavigate with an unchanged window.
246
267
  const { start, end } = this.#orderedRange(referenceDate);
247
268
  const spanDays = daysBetween(start, end) + 1;
248
- const shiftedStart = addDays(start, delta * spanDays);
249
- const shiftedEnd = addDays(end, delta * spanDays);
269
+ const shiftedStart = this.#clampWindowStart(addDays(start, delta * spanDays), spanDays);
270
+ if (shiftedStart.getTime() === start.getTime())
271
+ return;
272
+ this.navDirection = shiftedStart.getTime() > start.getTime() ? 'forward' : 'backward';
273
+ const shiftedEnd = addDays(shiftedStart, spanDays - 1);
250
274
  this.#opts.onNavigate?.(shiftedStart, { start: shiftedStart, end: shiftedEnd });
251
275
  return;
252
276
  }
277
+ this.navDirection = delta > 0 ? 'forward' : 'backward';
253
278
  let next;
254
279
  switch (view) {
255
280
  case 'month': {
@@ -368,6 +393,37 @@ export class DateGridController {
368
393
  const re = stripTime(this.#opts.rangeEnd ?? reference);
369
394
  return rs.getTime() <= re.getTime() ? { start: rs, end: re } : { start: re, end: rs };
370
395
  }
396
+ /** Span-preserving sliding-window clamp for range navigation. A window of
397
+ * `spanDays` days starting at `idealStart` must lie inside [minDate, maxDate]
398
+ * as a whole: the earliest allowed start is minDate, the latest is
399
+ * `maxDate − (spanDays − 1)` (inclusive bounds — the window's last day is
400
+ * `start + spanDays − 1` and must not pass maxDate). Clamping only the start
401
+ * against maxDate would collapse the span; this keeps it exact, allowing
402
+ * partial shifts up to the bound. Degenerate case — the span is longer than
403
+ * the navigable interval, so no start satisfies both bounds — pins the window
404
+ * to minDate (its tail spills past maxDate; matches clampDate's minDate-wins
405
+ * ordering) and warns in DEV instead of crashing, mirroring the inverted-
406
+ * bounds warning in the constructor. */
407
+ #clampWindowStart(idealStart, spanDays) {
408
+ const { minDate, maxDate } = this.#opts;
409
+ const earliest = minDate ? stripTime(minDate) : undefined;
410
+ const latest = maxDate ? addDays(stripTime(maxDate), -(spanDays - 1)) : undefined;
411
+ if (earliest && latest && earliest.getTime() > latest.getTime()) {
412
+ if (import.meta.env?.DEV) {
413
+ console.warn(`[DateGrid] a ${spanDays}-day range window cannot fit between minDate ` +
414
+ `(${minDate?.toLocaleDateString()}) and maxDate (${maxDate?.toLocaleDateString()}) ` +
415
+ `— pinning the window to minDate; its tail spills past maxDate. ` +
416
+ `Widen the bounds or shorten the window.`);
417
+ }
418
+ return earliest;
419
+ }
420
+ let next = idealStart;
421
+ if (latest && next.getTime() > latest.getTime())
422
+ next = latest;
423
+ if (earliest && next.getTime() < earliest.getTime())
424
+ next = earliest;
425
+ return next;
426
+ }
371
427
  /** Pure cell geometry for an arbitrary reference date (no side effects). */
372
428
  #cellsFor(reference) {
373
429
  const wso = this.#opts.weekStartsOn;
@@ -1,53 +1,53 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
2
  export declare const alertVariants: ((props?: {
3
- intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
3
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
8
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
15
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
22
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
29
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
36
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
43
+ intent?: "info" | "primary" | "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?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
50
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
51
51
  variant?: "inline" | "filled" | "soft" | undefined;
52
52
  size?: "sm" | "md" | "lg" | undefined;
53
53
  } & {
@@ -154,7 +154,7 @@ export const alertVariants = tv({
154
154
  {
155
155
  intent: 'warning',
156
156
  variant: 'filled',
157
- class: { base: 'bg-warning text-text-on-surface', dismissButton: 'text-text-on-surface' }
157
+ class: { base: 'bg-warning text-text-on-warning', dismissButton: 'text-text-on-warning' }
158
158
  },
159
159
  {
160
160
  intent: 'danger',