@exsto/ds 0.0.1-rc.15 → 0.0.1-rc.17

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 (43) hide show
  1. package/README.md +4 -0
  2. package/dist/index.js +2 -1
  3. package/dist/source-hash.txt +1 -1
  4. package/dist/styles.css +96 -0
  5. package/dist/ui/src/components/edyn/date-picker/date-picker.d.ts +1 -0
  6. package/dist/ui/src/components/edyn/date-picker/date-picker.js +23 -3
  7. package/dist/ui/src/components/edyn/date-picker/date-picker.js.map +1 -1
  8. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js +13 -2
  9. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js.map +1 -1
  10. package/dist/ui/src/components/edyn/multi-select/multi-select.js +8 -2
  11. package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -1
  12. package/dist/ui/src/components/edyn/presence/index.d.ts +1 -0
  13. package/dist/ui/src/components/edyn/presence/presence.d.ts +15 -0
  14. package/dist/ui/src/components/edyn/presence/presence.js +58 -0
  15. package/dist/ui/src/components/edyn/presence/presence.js.map +1 -0
  16. package/dist/ui/src/components/edyn/presence/presence.test.d.ts +8 -0
  17. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js +2 -0
  18. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js.map +1 -1
  19. package/dist/ui/src/components/ui/calendar/calendar.behavior.test.d.ts +9 -0
  20. package/dist/ui/src/components/ui/calendar/calendar.d.ts +1 -1
  21. package/dist/ui/src/components/ui/calendar/calendar.js +33 -2
  22. package/dist/ui/src/components/ui/calendar/calendar.js.map +1 -1
  23. package/dist/ui/src/components/ui/context-menu/context-menu.js +2 -0
  24. package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -1
  25. package/dist/ui/src/components/ui/dialog/dialog.js +2 -0
  26. package/dist/ui/src/components/ui/dialog/dialog.js.map +1 -1
  27. package/dist/ui/src/components/ui/drawer/drawer.js +2 -0
  28. package/dist/ui/src/components/ui/drawer/drawer.js.map +1 -1
  29. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +2 -0
  30. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  31. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js +2 -0
  32. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js.map +1 -1
  33. package/dist/ui/src/components/ui/popover/popover.js +2 -0
  34. package/dist/ui/src/components/ui/popover/popover.js.map +1 -1
  35. package/dist/ui/src/components/ui/select/select.js +2 -0
  36. package/dist/ui/src/components/ui/select/select.js.map +1 -1
  37. package/dist/ui/src/components/ui/sheet/sheet.js +2 -0
  38. package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -1
  39. package/dist/ui/src/index.d.ts +1 -0
  40. package/dist/ui/src/lib/focus-guards.d.ts +2 -0
  41. package/dist/ui/src/lib/focus-guards.js +52 -0
  42. package/dist/ui/src/lib/focus-guards.js.map +1 -0
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -152,6 +152,10 @@ const missing = state.kind === 'empty'; // 'refused' is already said by the pick
152
152
 
153
153
  Refused text is reported each time it changes, also when the field held no day before, and emptying the field afterwards reports `empty`. A parent's `aria-invalid` only draws the invalid border; the picker's own message is added to `aria-describedby` once, after the parent's. `DateRangePicker`, for a range, is in the main entry.
154
154
 
155
+ ## Open popups and keyboard audits
156
+
157
+ While a menu, dialog, sheet, popover, select or drawer is open, Base UI puts two empty `<span tabindex="0" data-base-ui-focus-guard>` around it. They are the sentinels that hand focus back into the popup when Tab runs past either end, so they are focusable on purpose, and they always were. From `0.0.1-rc.16` the design system also takes their `aria-hidden` off while the popup is open, because a focusable element that is hidden from assistive technology fails axe's `aria-hidden-focus` (WCAG 4.1.2). A check that walks the Tab order should skip `[data-base-ui-focus-guard]`, as it skips any other sentinel: they hold no text and name nothing.
158
+
155
159
  ## Status
156
160
 
157
161
  Release candidates (`0.0.1-rc.N`). The API is not stable yet; treat minor versions
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import { useMergedRefs } from "./ui/src/hooks/use-merged-refs.js";
14
14
  import { ScrollArea, ScrollBar } from "./ui/src/components/ui/scroll-area/scroll-area.js";
15
15
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/src/components/ui/tooltip/tooltip.js";
16
16
  import { DEFAULT_MULTI_SELECT_LABELS, MultiSelect, VIRTUALIZE_FROM, fullSelection, resolveChosen, summarize } from "./ui/src/components/edyn/multi-select/multi-select.js";
17
+ import { DEFAULT_PRESENCE_LABELS, PRESENCE_STATES, Presence } from "./ui/src/components/edyn/presence/presence.js";
17
18
  import { PROGRESS_LINE_COLOR, ProgressLine } from "./ui/src/components/edyn/progress-line/progress-line.js";
18
19
  import { RICH_TEXT_ATTRIBUTES, RICH_TEXT_ELEMENTS, RICH_TEXT_LINK_PROTOCOLS, isAllowedRichTextHref } from "./ui/src/components/edyn/rich-text-content/allow-list.js";
19
20
  import { RICH_TEXT_BODY_CLASS, RICH_TEXT_MENTION_CLASS, RichTextContent, richTextToNodes } from "./ui/src/components/edyn/rich-text-content/rich-text-content.js";
@@ -73,4 +74,4 @@ import { Tabs, TabsContent, TabsList, TabsTrigger, tabsListVariants } from "./ui
73
74
  import { Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, Toaster, createToastManager, toast, useToastManager } from "./ui/src/components/ui/toast/toast.js";
74
75
  import { Toggle, toggleVariants } from "./ui/src/components/ui/toggle/toggle.js";
75
76
  import { ToggleGroup, ToggleGroupItem } from "./ui/src/components/ui/toggle-group/toggle-group.js";
76
- export { AVATAR_TONES, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertAction, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnimatedNumber, AspectRatio, Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger, AudioPlayer, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Bubble, BubbleContent, BubbleGroup, BubbleReactions, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_MULTI_SELECT_LABELS, DateRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectionProvider, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, Field, FieldDescription, FieldError, FieldLabel, HoverCard, HoverCardContent, HoverCardTrigger, Indicator, InitialsAvatar, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, Marker, MarkerContent, MarkerIcon, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MultiSelect, NativeSelect, NativeSelectOptGroup, NativeSelectOption, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuPositioner, NavigationMenuTrigger, NumberText, PROGRESS_DURATION, PROGRESS_EASE, PROGRESS_LINE_COLOR, Pagination, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, ProgressLine, Questionnaire, QuestionnaireActions, QuestionnaireChoice, QuestionnaireChoiceDescription, QuestionnaireChoices, QuestionnaireDescription, QuestionnaireError, QuestionnaireInput, QuestionnaireItem, QuestionnaireNext, QuestionnairePrevious, QuestionnaireProgress, QuestionnaireSkip, QuestionnaireSubmit, QuestionnaireTitle, RICH_TEXT_ATTRIBUTES, RICH_TEXT_BODY_CLASS, RICH_TEXT_ELEMENTS, RICH_TEXT_LINK_PROTOCOLS, RICH_TEXT_MENTION_CLASS, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, RichTextContent, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VIRTUALIZE_FROM, VideoPlayer, avatarTone, badgeVariants, buttonVariants, clampPage, cn, createToastManager, dateToDay, dayToDate, formatCount, formatMediaTime, fullSelection, getPaginationRange, indicatorVariants, initialsOf, isAllowedRichTextHref, markerVariants, navigationMenuTriggerStyle, progressFill, progressTrack, resolveChosen, richTextToNodes, spinnerVariants, summarize, tabsListVariants, toast, toggleVariants, useAnimatedValues, useCarousel, useDirection, useIsMobile, useMediaPlayer, useMergedRefs, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, useSidebar, useToastManager };
77
+ export { AVATAR_TONES, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertAction, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnimatedNumber, AspectRatio, Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger, AudioPlayer, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Bubble, BubbleContent, BubbleGroup, BubbleReactions, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_MULTI_SELECT_LABELS, DEFAULT_PRESENCE_LABELS, DateRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectionProvider, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, Field, FieldDescription, FieldError, FieldLabel, HoverCard, HoverCardContent, HoverCardTrigger, Indicator, InitialsAvatar, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, Marker, MarkerContent, MarkerIcon, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MultiSelect, NativeSelect, NativeSelectOptGroup, NativeSelectOption, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuPositioner, NavigationMenuTrigger, NumberText, PRESENCE_STATES, PROGRESS_DURATION, PROGRESS_EASE, PROGRESS_LINE_COLOR, Pagination, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Presence, Progress, ProgressLine, Questionnaire, QuestionnaireActions, QuestionnaireChoice, QuestionnaireChoiceDescription, QuestionnaireChoices, QuestionnaireDescription, QuestionnaireError, QuestionnaireInput, QuestionnaireItem, QuestionnaireNext, QuestionnairePrevious, QuestionnaireProgress, QuestionnaireSkip, QuestionnaireSubmit, QuestionnaireTitle, RICH_TEXT_ATTRIBUTES, RICH_TEXT_BODY_CLASS, RICH_TEXT_ELEMENTS, RICH_TEXT_LINK_PROTOCOLS, RICH_TEXT_MENTION_CLASS, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, RichTextContent, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VIRTUALIZE_FROM, VideoPlayer, avatarTone, badgeVariants, buttonVariants, clampPage, cn, createToastManager, dateToDay, dayToDate, formatCount, formatMediaTime, fullSelection, getPaginationRange, indicatorVariants, initialsOf, isAllowedRichTextHref, markerVariants, navigationMenuTriggerStyle, progressFill, progressTrack, resolveChosen, richTextToNodes, spinnerVariants, summarize, tabsListVariants, toast, toggleVariants, useAnimatedValues, useCarousel, useDirection, useIsMobile, useMediaPlayer, useMergedRefs, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility, useSidebar, useToastManager };
@@ -1 +1 @@
1
- 51bef8069ad2575c9c422f14d92fe68c65cfbd0cb8727824d68ce82165e7e6b3
1
+ 188147f3f789366cf669116233eddeed7297dad660bbaa9749fc44c833dabdd3
package/dist/styles.css CHANGED
@@ -787,6 +787,10 @@
787
787
  margin-inline-start: var(--scroll-bar-gap);
788
788
  }
789
789
 
790
+ .ms-2 {
791
+ margin-inline-start: calc(var(--spacing) * 2);
792
+ }
793
+
790
794
  .ms-auto {
791
795
  margin-inline-start: auto;
792
796
  }
@@ -1921,6 +1925,11 @@
1921
1925
  border-width: 0;
1922
1926
  }
1923
1927
 
1928
+ .border-2 {
1929
+ border-style: var(--tw-border-style);
1930
+ border-width: 2px;
1931
+ }
1932
+
1924
1933
  .border-\[1\.5px\] {
1925
1934
  border-style: var(--tw-border-style);
1926
1935
  border-width: 1.5px;
@@ -1979,6 +1988,10 @@
1979
1988
  border-color: var(--input);
1980
1989
  }
1981
1990
 
1991
+ .border-muted-foreground {
1992
+ border-color: var(--muted-foreground);
1993
+ }
1994
+
1982
1995
  .border-primary\/50 {
1983
1996
  border-color: var(--primary);
1984
1997
  }
@@ -2129,6 +2142,10 @@
2129
2142
  background-color: var(--sidebar-border);
2130
2143
  }
2131
2144
 
2145
+ .bg-success {
2146
+ background-color: var(--success);
2147
+ }
2148
+
2132
2149
  .bg-success-ring\/40 {
2133
2150
  background-color: var(--success-ring);
2134
2151
  }
@@ -2143,6 +2160,10 @@
2143
2160
  background-color: #0000;
2144
2161
  }
2145
2162
 
2163
+ .bg-warning {
2164
+ background-color: var(--warning);
2165
+ }
2166
+
2146
2167
  .bg-warning-ring\/40 {
2147
2168
  background-color: var(--warning-ring);
2148
2169
  }
@@ -2598,6 +2619,11 @@
2598
2619
  font-style: italic;
2599
2620
  }
2600
2621
 
2622
+ .ordinal {
2623
+ --tw-ordinal: ordinal;
2624
+ font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
2625
+ }
2626
+
2601
2627
  .tabular-nums {
2602
2628
  --tw-numeric-spacing: tabular-nums;
2603
2629
  font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
@@ -3623,6 +3649,11 @@
3623
3649
  inset-inline: calc(var(--spacing) * -3);
3624
3650
  }
3625
3651
 
3652
+ .after\:inset-x-0\.5:after {
3653
+ content: var(--tw-content);
3654
+ inset-inline: calc(var(--spacing) * .5);
3655
+ }
3656
+
3626
3657
  .after\:-inset-y-2:after {
3627
3658
  content: var(--tw-content);
3628
3659
  inset-block: calc(var(--spacing) * -2);
@@ -3638,11 +3669,26 @@
3638
3669
  inset-inline-start: 50%;
3639
3670
  }
3640
3671
 
3672
+ .after\:-top-px:after {
3673
+ content: var(--tw-content);
3674
+ top: -1px;
3675
+ }
3676
+
3677
+ .after\:top-1\/2:after {
3678
+ content: var(--tw-content);
3679
+ top: 50%;
3680
+ }
3681
+
3641
3682
  .after\:top-full:after {
3642
3683
  content: var(--tw-content);
3643
3684
  top: 100%;
3644
3685
  }
3645
3686
 
3687
+ .after\:-right-px:after {
3688
+ content: var(--tw-content);
3689
+ right: -1px;
3690
+ }
3691
+
3646
3692
  .after\:right-0:after {
3647
3693
  content: var(--tw-content);
3648
3694
  right: 0;
@@ -3663,6 +3709,17 @@
3663
3709
  margin-left: var(--spacing);
3664
3710
  }
3665
3711
 
3712
+ .after\:size-1\.5:after {
3713
+ content: var(--tw-content);
3714
+ width: calc(var(--spacing) * 1.5);
3715
+ height: calc(var(--spacing) * 1.5);
3716
+ }
3717
+
3718
+ .after\:h-0\.5:after {
3719
+ content: var(--tw-content);
3720
+ height: calc(var(--spacing) * .5);
3721
+ }
3722
+
3666
3723
  .after\:h-\[calc\(var\(--gap\)\+1px\)\]:after {
3667
3724
  content: var(--tw-content);
3668
3725
  height: calc(var(--gap) + 1px);
@@ -3709,6 +3766,18 @@
3709
3766
  translate: var(--tw-translate-x) var(--tw-translate-y);
3710
3767
  }
3711
3768
 
3769
+ .after\:-translate-x-1\/3:after {
3770
+ content: var(--tw-content);
3771
+ --tw-translate-x: calc(calc(1 / 3 * 100%) * -1);
3772
+ translate: var(--tw-translate-x) var(--tw-translate-y);
3773
+ }
3774
+
3775
+ .after\:-translate-y-1\/2:after {
3776
+ content: var(--tw-content);
3777
+ --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
3778
+ translate: var(--tw-translate-x) var(--tw-translate-y);
3779
+ }
3780
+
3712
3781
  .after\:rounded-full:after {
3713
3782
  content: var(--tw-content);
3714
3783
  border-radius: 3.40282e38px;
@@ -3720,11 +3789,38 @@
3720
3789
  border-width: 1px;
3721
3790
  }
3722
3791
 
3792
+ .after\:border-y-3:after {
3793
+ content: var(--tw-content);
3794
+ border-block-style: var(--tw-border-style);
3795
+ border-block-width: 3px;
3796
+ }
3797
+
3798
+ .after\:border-l-5:after {
3799
+ content: var(--tw-content);
3800
+ border-left-style: var(--tw-border-style);
3801
+ border-left-width: 5px;
3802
+ }
3803
+
3723
3804
  .after\:border-border:after {
3724
3805
  content: var(--tw-content);
3725
3806
  border-color: var(--border);
3726
3807
  }
3727
3808
 
3809
+ .after\:border-y-transparent:after {
3810
+ content: var(--tw-content);
3811
+ border-block-color: #0000;
3812
+ }
3813
+
3814
+ .after\:border-l-background:after {
3815
+ content: var(--tw-content);
3816
+ border-left-color: var(--background);
3817
+ }
3818
+
3819
+ .after\:bg-background:after {
3820
+ content: var(--tw-content);
3821
+ background-color: var(--background);
3822
+ }
3823
+
3728
3824
  .after\:bg-border:after {
3729
3825
  content: var(--tw-content);
3730
3826
  background-color: var(--border);
@@ -72,5 +72,6 @@ export interface IDatePickerProps {
72
72
  /**
73
73
  * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it
74
74
  * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.
75
+ * Escape closes the calendar and gives the focus back to whichever of the two opened it; a day picked puts it on the field.
75
76
  */
76
77
  export declare const DatePicker: ({ id, value, onValueChange, min, max, disabledDates, disabled, locale, calendarLocale, placeholder, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, labels: labelOverrides, className, }: IDatePickerProps) => import('react').JSX.Element;
@@ -30,6 +30,7 @@ var problemText = (problem, labels, locale, limits, pattern) => {
30
30
  /**
31
31
  * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it
32
32
  * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.
33
+ * Escape closes the calendar and gives the focus back to whichever of the two opened it; a day picked puts it on the field.
33
34
  */
34
35
  var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled = false, locale = "en-GB", calendarLocale, placeholder, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, labels: labelOverrides, className }) => {
35
36
  const labels = useMemo(() => ({
@@ -41,6 +42,9 @@ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled
41
42
  const errorId = useId();
42
43
  const refField = useRef(null);
43
44
  const refInput = useRef(null);
45
+ const refButton = useRef(null);
46
+ const refOpener = useRef("button");
47
+ const refPicked = useRef(false);
44
48
  const refPointer = useRef(false);
45
49
  const refKnown = useRef(current);
46
50
  const refRefused = useRef(null);
@@ -101,8 +105,16 @@ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled
101
105
  setProblem(null);
102
106
  setText(formatDay(day, locale));
103
107
  if (day !== current || refRefused.current !== null) report(day, { kind: "day" });
108
+ refPicked.current = true;
104
109
  setOpen(false);
105
110
  };
111
+ const openFrom = (opener) => {
112
+ refOpener.current = opener;
113
+ refPicked.current = false;
114
+ setOpen(true);
115
+ };
116
+ /** Where the focus goes when the calendar closes: the field after a day is chosen, else whatever opened it. */
117
+ const focusAfterClose = () => refPicked.current || refOpener.current === "field" ? refInput.current : refButton.current;
106
118
  /**
107
119
  * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a
108
120
  * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus
@@ -130,12 +142,13 @@ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled
130
142
  const handleKeyDown = (event) => {
131
143
  if (event.key === "ArrowDown" && event.altKey && !disabled) {
132
144
  event.preventDefault();
133
- setOpen(true);
145
+ openFrom("field");
134
146
  return;
135
147
  }
136
148
  if (event.key === "Escape" && open) {
137
149
  event.preventDefault();
138
150
  event.stopPropagation();
151
+ refOpener.current = "field";
139
152
  setOpen(false);
140
153
  return;
141
154
  }
@@ -202,11 +215,18 @@ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled
202
215
  onKeyDown: handleKeyDown
203
216
  }), /* @__PURE__ */ jsxs(Popover, {
204
217
  open,
205
- onOpenChange: setOpen,
218
+ onOpenChange: (next) => {
219
+ if (next) {
220
+ openFrom("button");
221
+ return;
222
+ }
223
+ setOpen(false);
224
+ },
206
225
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
207
226
  render: /* @__PURE__ */ jsx(Button, {
208
227
  variant: "ghost",
209
228
  size: "icon-sm",
229
+ ref: refButton,
210
230
  "data-slot": "date-picker-trigger",
211
231
  "aria-label": labels.choose,
212
232
  disabled,
@@ -218,7 +238,7 @@ var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled
218
238
  anchor: refField,
219
239
  align: "start",
220
240
  className: "w-auto p-0",
221
- finalFocus: refInput,
241
+ finalFocus: focusAfterClose,
222
242
  onBlur: handleBlur,
223
243
  children: /* @__PURE__ */ jsx(Calendar, {
224
244
  mode: "single",
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/date-picker/date-picker.tsx"],"sourcesContent":["import { dateToDay, dayToDate } from '@edyn/ui/components/edyn/date-range-picker';\nimport { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Input } from '@edyn/ui/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport { type ChangeEvent, type FocusEvent, type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport type { Locale } from 'react-day-picker';\nimport { datePatternOf, dayProblem, formatDay, parseDateText, type TDateProblem, type TDisabledDays } from './date-text';\n\n/** Every word the component prints, so a product can translate it. */\nexport interface IDatePickerLabels {\n /** The calendar button's accessible name. */\n choose: string;\n /** Said when the text is not a date; `pattern` is what to type, written the locale's way (`DD/MM/YYYY`). */\n format: (pattern: string) => string;\n notReal: string;\n /** `min` and `max` are written the locale's way. */\n beforeMin: (min: string) => string;\n afterMax: (max: string) => string;\n unavailable: string;\n}\n\nexport const DEFAULT_DATE_PICKER_LABELS: IDatePickerLabels = {\n choose: 'Choose date',\n format: (pattern) => `Enter a date as ${pattern}.`,\n notReal: 'That date does not exist.',\n beforeMin: (min) => `Choose ${min} or later.`,\n afterMax: (max) => `Choose ${max} or earlier.`,\n unavailable: 'That date is not available.',\n};\n\n/**\n * What the field holds when it reports, so a parent can tell three things apart that all arrive as `undefined`:\n * - `day`: a usable day, typed or picked.\n * - `empty`: the field was emptied, or nothing has been put in it. A required field is right to ask for a date.\n * - `refused`: the field holds text it will not take (not a day, not a real one, or outside `min`, `max` or\n * `disabledDates`). `text` is what was typed and `problem` the message the picker shows under the field, already\n * in the picker's `labels`. The picker is saying so itself, so the page should not add a second message.\n */\nexport type TDatePickerState = { kind: 'day' } | { kind: 'empty' } | { kind: 'refused'; text: string; problem: string };\n\nexport interface IDatePickerProps {\n /** For a Label's htmlFor; inside a Field the Field's label is already wired. */\n id?: string;\n /**\n * The chosen day as `yyyy-mm-dd`, or `undefined` for none (an empty string counts as none, so a value kept for an\n * `<input type=\"date\">` drops in). A string and not a Date, on purpose: a Date is an instant, and the instant for\n * \"5 March\" is another calendar day in another time zone, so a Date serialised for an API drifts a day. The text is\n * what `<input type=\"date\">`, JSON and a .NET `DateOnly` use, and it compares as a day.\n */\n value: string | undefined;\n /**\n * Called with a valid day, or with `undefined` when the field is emptied or its text is not a usable day. The second\n * argument says which of the two it was (see `TDatePickerState`); a caller that has no use for it can ignore it.\n * Text that is refused is reported each time it changes, also when the field held no day before, so a form that\n * tracks the state hears about it. Emptying the field after that reports `empty`.\n */\n onValueChange: (value: string | undefined, state: TDatePickerState) => void;\n /** The earliest and latest day that can be chosen, as `yyyy-mm-dd`, both included. */\n min?: string;\n max?: string;\n /** Days that cannot be chosen: a list of `yyyy-mm-dd`, or a function that says so for one (weekends, say). */\n disabledDates?: TDisabledDays;\n disabled?: boolean;\n /** How a day is written and in which order it is typed (BCP 47); en-GB reads 05/03/2026. */\n locale?: string;\n /** The calendar's own words (month and weekday names), as a date-fns locale; English by default. The first weekday follows `locale`. */\n calendarLocale?: Locale;\n /** What the empty field shows; the pattern for the locale (`DD/MM/YYYY`) by default. */\n placeholder?: string;\n /** An accessible name, when there is no visible label to point at. */\n 'aria-label'?: string;\n /** Marks the field invalid (the kit's destructive border), as a Field's invalid state does. */\n 'aria-invalid'?: boolean | 'true' | 'false';\n /** The id of the text that explains it. Added to what a Field names already, and to the picker's own error. */\n 'aria-describedby'?: string;\n labels?: Partial<IDatePickerLabels>;\n /** Layout only (a width); the look is the kit's. */\n className?: string;\n}\n\nconst problemText = (\n problem: TDateProblem,\n labels: IDatePickerLabels,\n locale: string,\n limits: { min?: string; max?: string },\n pattern: string,\n): string => {\n switch (problem) {\n case 'format':\n return labels.format(pattern);\n case 'notReal':\n return labels.notReal;\n case 'beforeMin':\n return labels.beforeMin(limits.min === undefined ? '' : formatDay(limits.min, locale));\n case 'afterMax':\n return labels.afterMax(limits.max === undefined ? '' : formatDay(limits.max, locale));\n default:\n return labels.unavailable;\n }\n};\n\n/**\n * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it\n * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.\n */\nexport const DatePicker = ({\n id,\n value,\n onValueChange,\n min,\n max,\n disabledDates,\n disabled = false,\n locale = 'en-GB',\n calendarLocale,\n placeholder,\n 'aria-label': ariaLabel,\n 'aria-invalid': ariaInvalid,\n 'aria-describedby': ariaDescribedBy,\n labels: labelOverrides,\n className,\n}: IDatePickerProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_DATE_PICKER_LABELS, ...labelOverrides }), [labelOverrides]);\n const pattern = useMemo(() => datePatternOf(locale), [locale]);\n const current = value === '' ? undefined : value;\n const errorId = useId();\n const refField = useRef<HTMLDivElement | null>(null);\n const refInput = useRef<HTMLInputElement | null>(null);\n // The mouse is down inside the picker, so a blur of the field is a click on something in it, not a Tab.\n const refPointer = useRef(false);\n // The day the field last agreed on with the page, so a change from outside is told from our own report of it.\n const refKnown = useRef<string | undefined>(current);\n // The refused text last reported (with its message), or null: told apart from a fresh refusal so it is reported once,\n // and so that emptying a field that held a refused text is reported although it held no day.\n const refRefused = useRef<string | null>(null);\n const [text, setText] = useState(current === undefined ? '' : formatDay(current, locale));\n const [problem, setProblem] = useState<TDateProblem | null>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (current !== refKnown.current) {\n refKnown.current = current;\n refRefused.current = null;\n setText(current === undefined ? '' : formatDay(current, locale));\n setProblem(null);\n }\n }, [current, locale]);\n\n const report = (day: string | undefined, state: TDatePickerState) => {\n refKnown.current = day;\n refRefused.current = state.kind === 'refused' ? `${state.text}\\u0000${state.problem}` : null;\n onValueChange(day, state);\n };\n\n /**\n * Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,\n * with the state saying whether the field is empty or holds text it refused.\n */\n const commit = (typed: string) => {\n const parsed = parseDateText(typed, locale);\n if (parsed.kind === 'empty') {\n setProblem(null);\n if (current !== undefined || refRefused.current !== null) {\n report(undefined, { kind: 'empty' });\n }\n return;\n }\n const found = parsed.kind === 'problem' ? parsed.problem : dayProblem(parsed.day, { min, max, disabledDates });\n if (found !== undefined) {\n setProblem(found);\n const message = problemText(found, labels, locale, { min, max }, pattern);\n if (current !== undefined || refRefused.current !== `${typed}\\u0000${message}`) {\n report(undefined, { kind: 'refused', text: typed, problem: message });\n }\n return;\n }\n if (parsed.kind === 'day') {\n setProblem(null);\n setText(formatDay(parsed.day, locale));\n if (parsed.day !== current || refRefused.current !== null) {\n report(parsed.day, { kind: 'day' });\n }\n }\n };\n\n const pick = (date: Date | undefined) => {\n const day = dateToDay(date);\n if (day === undefined) {\n return;\n }\n setProblem(null);\n setText(formatDay(day, locale));\n if (day !== current || refRefused.current !== null) {\n report(day, { kind: 'day' });\n }\n setOpen(false);\n };\n\n /**\n * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a\n * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus\n * going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else\n * reads whatever is typed.\n */\n const handleBlur = (event: FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget as Element | null;\n const intoCalendar = next?.closest('[data-slot=\"popover-content\"]') != null;\n const intoButton = next?.closest('[data-slot=\"date-picker-trigger\"]') != null;\n const typed = refInput.current?.value ?? '';\n if (!(intoCalendar || intoButton || (next !== null && refField.current?.contains(next) === true))) {\n commit(typed);\n return;\n }\n if (event.target !== refInput.current) {\n return;\n }\n const onTheWay = intoCalendar || (intoButton && refPointer.current);\n const parsed = parseDateText(typed, locale);\n const usable = parsed.kind === 'empty' || (parsed.kind === 'day' && dayProblem(parsed.day, { min, max, disabledDates }) === undefined);\n if (usable || !onTheWay) {\n commit(typed);\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown' && event.altKey && !disabled) {\n event.preventDefault();\n setOpen(true);\n return;\n }\n // The field has no key handler of Base UI's to close the calendar, so the key would reach the Sheet or Dialog around\n // it and close that too. While the calendar is open it is this field's key; when it is closed it is the page's.\n if (event.key === 'Escape' && open) {\n event.preventDefault();\n event.stopPropagation();\n setOpen(false);\n return;\n }\n if (event.key === 'Enter') {\n const parsed = parseDateText(event.currentTarget.value, locale);\n if (parsed.kind !== 'empty' && (parsed.kind === 'problem' || dayProblem(parsed.day, { min, max, disabledDates }) !== undefined)) {\n event.preventDefault();\n }\n commit(event.currentTarget.value);\n }\n };\n\n const selected = dayToDate(current);\n const limits = { min, max };\n const invalid = problem !== null || ariaInvalid === true || ariaInvalid === 'true';\n const describedBy = [ariaDescribedBy, problem === null ? undefined : errorId].filter(Boolean).join(' ') || undefined;\n const isOff = (date: Date): boolean => {\n const day = dateToDay(date);\n return day !== undefined && dayProblem(day, { min, max, disabledDates }) !== undefined;\n };\n const today = new Date();\n const start = dayToDate(min);\n const end = dayToDate(max);\n const month = selected ?? (start !== undefined && today < start ? start : end !== undefined && today > end ? end : today);\n\n return (\n <div data-slot=\"date-picker\" className={cn('flex flex-col gap-1.5', className)}>\n <div\n ref={refField}\n className=\"relative\"\n onPointerDownCapture={() => {\n refPointer.current = true;\n }}\n onPointerUpCapture={() => {\n refPointer.current = false;\n }}\n >\n <Input\n ref={refInput}\n id={id}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n data-slot=\"date-picker-input\"\n value={text}\n placeholder={placeholder ?? pattern}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={invalid ? true : undefined}\n aria-describedby={describedBy}\n className=\"pr-9\"\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setText(event.target.value);\n setProblem(null);\n }}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n />\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n data-slot=\"date-picker-trigger\"\n aria-label={labels.choose}\n disabled={disabled}\n className=\"absolute top-0.5 right-0.5\"\n onBlur={handleBlur}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n </PopoverTrigger>\n <PopoverContent anchor={refField} align=\"start\" className=\"w-auto p-0\" finalFocus={refInput} onBlur={handleBlur}>\n <Calendar\n mode=\"single\"\n required\n autoFocus\n locale={calendarLocale}\n weekStartsOn={firstWeekdayOf(locale)}\n defaultMonth={month}\n startMonth={start}\n endMonth={end}\n selected={selected}\n disabled={isOff}\n onSelect={pick}\n />\n </PopoverContent>\n </Popover>\n </div>\n {problem !== null && (\n <p id={errorId} data-slot=\"date-picker-error\" role=\"alert\" className=\"text-sm text-destructive\">\n {problemText(problem, labels, locale, limits, pattern)}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"date-picker.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/date-picker/date-picker.tsx"],"sourcesContent":["import { dateToDay, dayToDate } from '@edyn/ui/components/edyn/date-range-picker';\nimport { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Input } from '@edyn/ui/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport { type ChangeEvent, type FocusEvent, type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport type { Locale } from 'react-day-picker';\nimport { datePatternOf, dayProblem, formatDay, parseDateText, type TDateProblem, type TDisabledDays } from './date-text';\n\n/** Every word the component prints, so a product can translate it. */\nexport interface IDatePickerLabels {\n /** The calendar button's accessible name. */\n choose: string;\n /** Said when the text is not a date; `pattern` is what to type, written the locale's way (`DD/MM/YYYY`). */\n format: (pattern: string) => string;\n notReal: string;\n /** `min` and `max` are written the locale's way. */\n beforeMin: (min: string) => string;\n afterMax: (max: string) => string;\n unavailable: string;\n}\n\nexport const DEFAULT_DATE_PICKER_LABELS: IDatePickerLabels = {\n choose: 'Choose date',\n format: (pattern) => `Enter a date as ${pattern}.`,\n notReal: 'That date does not exist.',\n beforeMin: (min) => `Choose ${min} or later.`,\n afterMax: (max) => `Choose ${max} or earlier.`,\n unavailable: 'That date is not available.',\n};\n\n/**\n * What the field holds when it reports, so a parent can tell three things apart that all arrive as `undefined`:\n * - `day`: a usable day, typed or picked.\n * - `empty`: the field was emptied, or nothing has been put in it. A required field is right to ask for a date.\n * - `refused`: the field holds text it will not take (not a day, not a real one, or outside `min`, `max` or\n * `disabledDates`). `text` is what was typed and `problem` the message the picker shows under the field, already\n * in the picker's `labels`. The picker is saying so itself, so the page should not add a second message.\n */\nexport type TDatePickerState = { kind: 'day' } | { kind: 'empty' } | { kind: 'refused'; text: string; problem: string };\n\nexport interface IDatePickerProps {\n /** For a Label's htmlFor; inside a Field the Field's label is already wired. */\n id?: string;\n /**\n * The chosen day as `yyyy-mm-dd`, or `undefined` for none (an empty string counts as none, so a value kept for an\n * `<input type=\"date\">` drops in). A string and not a Date, on purpose: a Date is an instant, and the instant for\n * \"5 March\" is another calendar day in another time zone, so a Date serialised for an API drifts a day. The text is\n * what `<input type=\"date\">`, JSON and a .NET `DateOnly` use, and it compares as a day.\n */\n value: string | undefined;\n /**\n * Called with a valid day, or with `undefined` when the field is emptied or its text is not a usable day. The second\n * argument says which of the two it was (see `TDatePickerState`); a caller that has no use for it can ignore it.\n * Text that is refused is reported each time it changes, also when the field held no day before, so a form that\n * tracks the state hears about it. Emptying the field after that reports `empty`.\n */\n onValueChange: (value: string | undefined, state: TDatePickerState) => void;\n /** The earliest and latest day that can be chosen, as `yyyy-mm-dd`, both included. */\n min?: string;\n max?: string;\n /** Days that cannot be chosen: a list of `yyyy-mm-dd`, or a function that says so for one (weekends, say). */\n disabledDates?: TDisabledDays;\n disabled?: boolean;\n /** How a day is written and in which order it is typed (BCP 47); en-GB reads 05/03/2026. */\n locale?: string;\n /** The calendar's own words (month and weekday names), as a date-fns locale; English by default. The first weekday follows `locale`. */\n calendarLocale?: Locale;\n /** What the empty field shows; the pattern for the locale (`DD/MM/YYYY`) by default. */\n placeholder?: string;\n /** An accessible name, when there is no visible label to point at. */\n 'aria-label'?: string;\n /** Marks the field invalid (the kit's destructive border), as a Field's invalid state does. */\n 'aria-invalid'?: boolean | 'true' | 'false';\n /** The id of the text that explains it. Added to what a Field names already, and to the picker's own error. */\n 'aria-describedby'?: string;\n labels?: Partial<IDatePickerLabels>;\n /** Layout only (a width); the look is the kit's. */\n className?: string;\n}\n\nconst problemText = (\n problem: TDateProblem,\n labels: IDatePickerLabels,\n locale: string,\n limits: { min?: string; max?: string },\n pattern: string,\n): string => {\n switch (problem) {\n case 'format':\n return labels.format(pattern);\n case 'notReal':\n return labels.notReal;\n case 'beforeMin':\n return labels.beforeMin(limits.min === undefined ? '' : formatDay(limits.min, locale));\n case 'afterMax':\n return labels.afterMax(limits.max === undefined ? '' : formatDay(limits.max, locale));\n default:\n return labels.unavailable;\n }\n};\n\n/**\n * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it\n * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.\n * Escape closes the calendar and gives the focus back to whichever of the two opened it; a day picked puts it on the field.\n */\nexport const DatePicker = ({\n id,\n value,\n onValueChange,\n min,\n max,\n disabledDates,\n disabled = false,\n locale = 'en-GB',\n calendarLocale,\n placeholder,\n 'aria-label': ariaLabel,\n 'aria-invalid': ariaInvalid,\n 'aria-describedby': ariaDescribedBy,\n labels: labelOverrides,\n className,\n}: IDatePickerProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_DATE_PICKER_LABELS, ...labelOverrides }), [labelOverrides]);\n const pattern = useMemo(() => datePatternOf(locale), [locale]);\n const current = value === '' ? undefined : value;\n const errorId = useId();\n const refField = useRef<HTMLDivElement | null>(null);\n const refInput = useRef<HTMLInputElement | null>(null);\n const refButton = useRef<HTMLButtonElement | null>(null);\n // What opened the calendar, so that Escape gives the focus back to it: the button when the button did it, the field\n // when Alt+ArrowDown did. Choosing a day is different (see `pick`): the field then shows the day, so it gets the focus.\n const refOpener = useRef<'button' | 'field'>('button');\n const refPicked = useRef(false);\n // The mouse is down inside the picker, so a blur of the field is a click on something in it, not a Tab.\n const refPointer = useRef(false);\n // The day the field last agreed on with the page, so a change from outside is told from our own report of it.\n const refKnown = useRef<string | undefined>(current);\n // The refused text last reported (with its message), or null: told apart from a fresh refusal so it is reported once,\n // and so that emptying a field that held a refused text is reported although it held no day.\n const refRefused = useRef<string | null>(null);\n const [text, setText] = useState(current === undefined ? '' : formatDay(current, locale));\n const [problem, setProblem] = useState<TDateProblem | null>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (current !== refKnown.current) {\n refKnown.current = current;\n refRefused.current = null;\n setText(current === undefined ? '' : formatDay(current, locale));\n setProblem(null);\n }\n }, [current, locale]);\n\n const report = (day: string | undefined, state: TDatePickerState) => {\n refKnown.current = day;\n refRefused.current = state.kind === 'refused' ? `${state.text}\\u0000${state.problem}` : null;\n onValueChange(day, state);\n };\n\n /**\n * Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,\n * with the state saying whether the field is empty or holds text it refused.\n */\n const commit = (typed: string) => {\n const parsed = parseDateText(typed, locale);\n if (parsed.kind === 'empty') {\n setProblem(null);\n if (current !== undefined || refRefused.current !== null) {\n report(undefined, { kind: 'empty' });\n }\n return;\n }\n const found = parsed.kind === 'problem' ? parsed.problem : dayProblem(parsed.day, { min, max, disabledDates });\n if (found !== undefined) {\n setProblem(found);\n const message = problemText(found, labels, locale, { min, max }, pattern);\n if (current !== undefined || refRefused.current !== `${typed}\\u0000${message}`) {\n report(undefined, { kind: 'refused', text: typed, problem: message });\n }\n return;\n }\n if (parsed.kind === 'day') {\n setProblem(null);\n setText(formatDay(parsed.day, locale));\n if (parsed.day !== current || refRefused.current !== null) {\n report(parsed.day, { kind: 'day' });\n }\n }\n };\n\n const pick = (date: Date | undefined) => {\n const day = dateToDay(date);\n if (day === undefined) {\n return;\n }\n setProblem(null);\n setText(formatDay(day, locale));\n if (day !== current || refRefused.current !== null) {\n report(day, { kind: 'day' });\n }\n refPicked.current = true;\n setOpen(false);\n };\n\n const openFrom = (opener: 'button' | 'field') => {\n refOpener.current = opener;\n refPicked.current = false;\n setOpen(true);\n };\n\n /** Where the focus goes when the calendar closes: the field after a day is chosen, else whatever opened it. */\n const focusAfterClose = (): HTMLElement | null => (refPicked.current || refOpener.current === 'field' ? refInput.current : refButton.current);\n\n /**\n * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a\n * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus\n * going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else\n * reads whatever is typed.\n */\n const handleBlur = (event: FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget as Element | null;\n const intoCalendar = next?.closest('[data-slot=\"popover-content\"]') != null;\n const intoButton = next?.closest('[data-slot=\"date-picker-trigger\"]') != null;\n const typed = refInput.current?.value ?? '';\n if (!(intoCalendar || intoButton || (next !== null && refField.current?.contains(next) === true))) {\n commit(typed);\n return;\n }\n if (event.target !== refInput.current) {\n return;\n }\n const onTheWay = intoCalendar || (intoButton && refPointer.current);\n const parsed = parseDateText(typed, locale);\n const usable = parsed.kind === 'empty' || (parsed.kind === 'day' && dayProblem(parsed.day, { min, max, disabledDates }) === undefined);\n if (usable || !onTheWay) {\n commit(typed);\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown' && event.altKey && !disabled) {\n event.preventDefault();\n openFrom('field');\n return;\n }\n // The field has no key handler of Base UI's to close the calendar, so the key would reach the Sheet or Dialog around\n // it and close that too. While the calendar is open it is this field's key; when it is closed it is the page's.\n if (event.key === 'Escape' && open) {\n event.preventDefault();\n event.stopPropagation();\n // The focus is on the field already, whatever opened the calendar: it stays there.\n refOpener.current = 'field';\n setOpen(false);\n return;\n }\n if (event.key === 'Enter') {\n const parsed = parseDateText(event.currentTarget.value, locale);\n if (parsed.kind !== 'empty' && (parsed.kind === 'problem' || dayProblem(parsed.day, { min, max, disabledDates }) !== undefined)) {\n event.preventDefault();\n }\n commit(event.currentTarget.value);\n }\n };\n\n const selected = dayToDate(current);\n const limits = { min, max };\n const invalid = problem !== null || ariaInvalid === true || ariaInvalid === 'true';\n const describedBy = [ariaDescribedBy, problem === null ? undefined : errorId].filter(Boolean).join(' ') || undefined;\n const isOff = (date: Date): boolean => {\n const day = dateToDay(date);\n return day !== undefined && dayProblem(day, { min, max, disabledDates }) !== undefined;\n };\n const today = new Date();\n const start = dayToDate(min);\n const end = dayToDate(max);\n const month = selected ?? (start !== undefined && today < start ? start : end !== undefined && today > end ? end : today);\n\n return (\n <div data-slot=\"date-picker\" className={cn('flex flex-col gap-1.5', className)}>\n <div\n ref={refField}\n className=\"relative\"\n onPointerDownCapture={() => {\n refPointer.current = true;\n }}\n onPointerUpCapture={() => {\n refPointer.current = false;\n }}\n >\n <Input\n ref={refInput}\n id={id}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n data-slot=\"date-picker-input\"\n value={text}\n placeholder={placeholder ?? pattern}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={invalid ? true : undefined}\n aria-describedby={describedBy}\n className=\"pr-9\"\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setText(event.target.value);\n setProblem(null);\n }}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n />\n <Popover\n open={open}\n onOpenChange={(next) => {\n // Only the button opens it from here (Alt+ArrowDown in the field sets the state itself, through `openFrom`).\n if (next) {\n openFrom('button');\n return;\n }\n setOpen(false);\n }}\n >\n <PopoverTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n ref={refButton}\n data-slot=\"date-picker-trigger\"\n aria-label={labels.choose}\n disabled={disabled}\n className=\"absolute top-0.5 right-0.5\"\n onBlur={handleBlur}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n </PopoverTrigger>\n <PopoverContent anchor={refField} align=\"start\" className=\"w-auto p-0\" finalFocus={focusAfterClose} onBlur={handleBlur}>\n <Calendar\n mode=\"single\"\n required\n autoFocus\n locale={calendarLocale}\n weekStartsOn={firstWeekdayOf(locale)}\n defaultMonth={month}\n startMonth={start}\n endMonth={end}\n selected={selected}\n disabled={isOff}\n onSelect={pick}\n />\n </PopoverContent>\n </Popover>\n </div>\n {problem !== null && (\n <p id={errorId} data-slot=\"date-picker-error\" role=\"alert\" className=\"text-sm text-destructive\">\n {problemText(problem, labels, locale, limits, pattern)}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -9,6 +9,16 @@ import { IconCalendar } from "@tabler/icons-react";
9
9
  //#region ../ui/src/components/edyn/date-range-picker/date-range-picker.tsx
10
10
  var dayToDate = (value) => value === void 0 ? void 0 : /* @__PURE__ */ new Date(`${value}T12:00:00`);
11
11
  var dateToDay = (date) => date === void 0 ? void 0 : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
12
+ /**
13
+ * The trigger's accessible name when it is given an `aria-label`: the label, then what the button shows ("Created between:
14
+ * 01 Mar 2026 – 05 Mar 2026"). An aria-label replaces the text inside the button, so on its own it hides the chosen dates from
15
+ * a screen reader and breaks WCAG 2.5.3 (Label in Name: the visible text must be part of the name, so that a person who says
16
+ * what they see can operate it). A label that already contains the visible text is left as it is.
17
+ */
18
+ var nameWith = (label, shown) => {
19
+ if (label === void 0) return;
20
+ return label.toLowerCase().includes(shown.toLowerCase()) ? label : `${label}: ${shown}`;
21
+ };
12
22
  /** A range of calendar days, either end optional; two months side by side, one on a phone. */
13
23
  var DateRangePicker = ({ id, value, onValueChange, placeholder = "Select date range", locale = "en-GB", summary = "range", "aria-label": ariaLabel, className, ...props }) => {
14
24
  const isMobile = useIsMobile();
@@ -20,11 +30,12 @@ var DateRangePicker = ({ id, value, onValueChange, placeholder = "Select date ra
20
30
  year: "numeric"
21
31
  });
22
32
  const ends = [from, to].filter((date) => date !== void 0).map((date) => format.format(date));
33
+ const shown = ends.length === 0 || summary === "label" ? placeholder : ends.join(" – ");
23
34
  return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
24
35
  render: /* @__PURE__ */ jsx(Button, {
25
36
  ...props,
26
37
  id,
27
- "aria-label": ariaLabel,
38
+ "aria-label": nameWith(ariaLabel, shown),
28
39
  "data-slot": "date-range-picker-trigger",
29
40
  "data-set": ends.length > 0 ? "" : void 0,
30
41
  variant: "outline",
@@ -32,7 +43,7 @@ var DateRangePicker = ({ id, value, onValueChange, placeholder = "Select date ra
32
43
  }),
33
44
  children: [/* @__PURE__ */ jsx(IconCalendar, { "aria-hidden": "true" }), /* @__PURE__ */ jsx("span", {
34
45
  className: "truncate",
35
- children: ends.length === 0 || summary === "label" ? placeholder : ends.join(" – ")
46
+ children: shown
36
47
  })]
37
48
  }), /* @__PURE__ */ jsx(PopoverContent, {
38
49
  align: "start",
@@ -1 +1 @@
1
- {"version":3,"file":"date-range-picker.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/date-range-picker/date-range-picker.tsx"],"sourcesContent":["import { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { useIsMobile } from '@edyn/ui/hooks/use-mobile';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport type { ComponentPropsWithoutRef } from 'react';\n\n/** A range of calendar days as yyyy-mm-dd, either end optional. */\nexport type TDateRange = { from?: string; to?: string };\n\n// A calendar day is read and written at midday local time, so no time zone or daylight\n// saving change can move it onto the day before or after.\nexport const dayToDate = (value: string | undefined): Date | undefined => (value === undefined ? undefined : new Date(`${value}T12:00:00`));\n\nexport const dateToDay = (date: Date | undefined): string | undefined =>\n date === undefined ? undefined : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;\n\n/** Anything else a button takes (data-*, aria-*, title) goes to the trigger. */\nexport interface IDateRangePickerProps\n extends Omit<ComponentPropsWithoutRef<'button'>, 'value' | 'defaultValue' | 'placeholder' | 'id' | 'className' | 'children' | 'onChange' | 'type'> {\n id?: string;\n value: TDateRange;\n onValueChange: (value: TDateRange) => void;\n placeholder?: string;\n /**\n * What the trigger says once a range is set. `range` (default) writes the ends; `label` keeps the label, so a\n * range the page shows elsewhere (as a removable chip, say) is not repeated here. In both, the trigger carries\n * `data-set` while either end is set, for a look of its own.\n */\n summary?: 'range' | 'label';\n /** How the ends are written on the trigger, and which weekday the calendar starts on (BCP 47); en-GB reads 05 Mar 2026 and starts on Monday. */\n locale?: string;\n /** An accessible name for the trigger, when there is no visible label to point at. */\n 'aria-label'?: string;\n className?: string;\n}\n\n/** A range of calendar days, either end optional; two months side by side, one on a phone. */\nexport const DateRangePicker = ({\n id,\n value,\n onValueChange,\n placeholder = 'Select date range',\n locale = 'en-GB',\n summary = 'range',\n 'aria-label': ariaLabel,\n className,\n ...props\n}: IDateRangePickerProps) => {\n const isMobile = useIsMobile();\n const from = dayToDate(value.from);\n const to = dayToDate(value.to);\n const format = new Intl.DateTimeFormat(locale, { day: '2-digit', month: 'short', year: 'numeric' });\n const ends = [from, to].filter((date) => date !== undefined).map((date) => format.format(date));\n return (\n <Popover>\n <PopoverTrigger\n render={\n <Button\n {...props}\n id={id}\n aria-label={ariaLabel}\n data-slot=\"date-range-picker-trigger\"\n data-set={ends.length > 0 ? '' : undefined}\n variant=\"outline\"\n className={cn('w-full min-w-0 justify-start font-normal', ends.length === 0 && 'text-muted-foreground', className)}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n <span className=\"truncate\">{ends.length === 0 || summary === 'label' ? placeholder : ends.join(' – ')}</span>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto p-0\">\n <Calendar\n mode=\"range\"\n defaultMonth={from}\n numberOfMonths={isMobile ? 1 : 2}\n weekStartsOn={firstWeekdayOf(locale)}\n selected={{ from, to }}\n onSelect={(range) => onValueChange({ from: dateToDay(range?.from), to: dateToDay(range?.to) })}\n />\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":";;;;;;;;;AAcA,IAAa,aAAa,UAAiD,UAAU,KAAA,IAAY,KAAA,oBAAY,IAAI,KAAK,GAAG,MAAM,UAAU;AAEzI,IAAa,aAAa,SACxB,SAAS,KAAA,IAAY,KAAA,IAAY,GAAG,KAAK,YAAY,EAAE,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;;AAuBlJ,IAAa,mBAAmB,EAC9B,IACA,OACA,eACA,cAAc,qBACd,SAAS,SACT,UAAU,SACV,cAAc,WACd,WACA,GAAG,YACwB;CAC3B,MAAM,WAAW,YAAY;CAC7B,MAAM,OAAO,UAAU,MAAM,IAAI;CACjC,MAAM,KAAK,UAAU,MAAM,EAAE;CAC7B,MAAM,SAAS,IAAI,KAAK,eAAe,QAAQ;EAAE,KAAK;EAAW,OAAO;EAAS,MAAM;CAAU,CAAC;CAClG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,SAAS,SAAS,KAAA,CAAS,CAAC,CAAC,KAAK,SAAS,OAAO,OAAO,IAAI,CAAC;CAC9F,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,qBAAC,gBAAD;EACE,QACE,oBAAC,QAAD;GACE,GAAI;GACA;GACJ,cAAY;GACZ,aAAU;GACV,YAAU,KAAK,SAAS,IAAI,KAAK,KAAA;GACjC,SAAQ;GACR,WAAW,GAAG,4CAA4C,KAAK,WAAW,KAAK,yBAAyB,SAAS;EAClH,CAAA;EAVL,UAAA,CAaE,oBAAC,cAAD,EAAc,eAAY,OAAQ,CAAA,GAClC,oBAAC,QAAD;GAAM,WAAU;GAAY,UAAA,KAAK,WAAW,KAAK,YAAY,UAAU,cAAc,KAAK,KAAK,KAAK;EAAQ,CAAA,CAC9F;CAChB,CAAA,GAAA,oBAAC,gBAAD;EAAgB,OAAM;EAAQ,WAAU;EACtC,UAAA,oBAAC,UAAD;GACE,MAAK;GACL,cAAc;GACd,gBAAgB,WAAW,IAAI;GAC/B,cAAc,eAAe,MAAM;GACnC,UAAU;IAAE;IAAM;GAAG;GACrB,WAAW,UAAU,cAAc;IAAE,MAAM,UAAU,OAAO,IAAI;IAAG,IAAI,UAAU,OAAO,EAAE;GAAE,CAAC;EAC9F,CAAA;CACa,CAAA,CACT,EAAA,CAAA;AAEb"}
1
+ {"version":3,"file":"date-range-picker.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/date-range-picker/date-range-picker.tsx"],"sourcesContent":["import { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { useIsMobile } from '@edyn/ui/hooks/use-mobile';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport type { ComponentPropsWithoutRef } from 'react';\n\n/** A range of calendar days as yyyy-mm-dd, either end optional. */\nexport type TDateRange = { from?: string; to?: string };\n\n// A calendar day is read and written at midday local time, so no time zone or daylight\n// saving change can move it onto the day before or after.\nexport const dayToDate = (value: string | undefined): Date | undefined => (value === undefined ? undefined : new Date(`${value}T12:00:00`));\n\nexport const dateToDay = (date: Date | undefined): string | undefined =>\n date === undefined ? undefined : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;\n\n/** Anything else a button takes (data-*, aria-*, title) goes to the trigger. */\nexport interface IDateRangePickerProps\n extends Omit<ComponentPropsWithoutRef<'button'>, 'value' | 'defaultValue' | 'placeholder' | 'id' | 'className' | 'children' | 'onChange' | 'type'> {\n id?: string;\n value: TDateRange;\n onValueChange: (value: TDateRange) => void;\n placeholder?: string;\n /**\n * What the trigger says once a range is set. `range` (default) writes the ends; `label` keeps the label, so a\n * range the page shows elsewhere (as a removable chip, say) is not repeated here. In both, the trigger carries\n * `data-set` while either end is set, for a look of its own.\n */\n summary?: 'range' | 'label';\n /** How the ends are written on the trigger, and which weekday the calendar starts on (BCP 47); en-GB reads 05 Mar 2026 and starts on Monday. */\n locale?: string;\n /** An accessible name for the trigger, when there is no visible label to point at. */\n 'aria-label'?: string;\n className?: string;\n}\n\n/**\n * The trigger's accessible name when it is given an `aria-label`: the label, then what the button shows (\"Created between:\n * 01 Mar 2026 – 05 Mar 2026\"). An aria-label replaces the text inside the button, so on its own it hides the chosen dates from\n * a screen reader and breaks WCAG 2.5.3 (Label in Name: the visible text must be part of the name, so that a person who says\n * what they see can operate it). A label that already contains the visible text is left as it is.\n */\nconst nameWith = (label: string | undefined, shown: string): string | undefined => {\n if (label === undefined) {\n return undefined;\n }\n return label.toLowerCase().includes(shown.toLowerCase()) ? label : `${label}: ${shown}`;\n};\n\n/** A range of calendar days, either end optional; two months side by side, one on a phone. */\nexport const DateRangePicker = ({\n id,\n value,\n onValueChange,\n placeholder = 'Select date range',\n locale = 'en-GB',\n summary = 'range',\n 'aria-label': ariaLabel,\n className,\n ...props\n}: IDateRangePickerProps) => {\n const isMobile = useIsMobile();\n const from = dayToDate(value.from);\n const to = dayToDate(value.to);\n const format = new Intl.DateTimeFormat(locale, { day: '2-digit', month: 'short', year: 'numeric' });\n const ends = [from, to].filter((date) => date !== undefined).map((date) => format.format(date));\n const shown = ends.length === 0 || summary === 'label' ? placeholder : ends.join(' – ');\n return (\n <Popover>\n <PopoverTrigger\n render={\n <Button\n {...props}\n id={id}\n aria-label={nameWith(ariaLabel, shown)}\n data-slot=\"date-range-picker-trigger\"\n data-set={ends.length > 0 ? '' : undefined}\n variant=\"outline\"\n className={cn('w-full min-w-0 justify-start font-normal', ends.length === 0 && 'text-muted-foreground', className)}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n <span className=\"truncate\">{shown}</span>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto p-0\">\n <Calendar\n mode=\"range\"\n defaultMonth={from}\n numberOfMonths={isMobile ? 1 : 2}\n weekStartsOn={firstWeekdayOf(locale)}\n selected={{ from, to }}\n onSelect={(range) => onValueChange({ from: dateToDay(range?.from), to: dateToDay(range?.to) })}\n />\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":";;;;;;;;;AAcA,IAAa,aAAa,UAAiD,UAAU,KAAA,IAAY,KAAA,oBAAY,IAAI,KAAK,GAAG,MAAM,UAAU;AAEzI,IAAa,aAAa,SACxB,SAAS,KAAA,IAAY,KAAA,IAAY,GAAG,KAAK,YAAY,EAAE,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;;;;;;;AA4BlJ,IAAM,YAAY,OAA2B,UAAsC;CACjF,IAAI,UAAU,KAAA,GACZ;CAEF,OAAO,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,YAAY,CAAC,IAAI,QAAQ,GAAG,MAAM,IAAI;AAClF;;AAGA,IAAa,mBAAmB,EAC9B,IACA,OACA,eACA,cAAc,qBACd,SAAS,SACT,UAAU,SACV,cAAc,WACd,WACA,GAAG,YACwB;CAC3B,MAAM,WAAW,YAAY;CAC7B,MAAM,OAAO,UAAU,MAAM,IAAI;CACjC,MAAM,KAAK,UAAU,MAAM,EAAE;CAC7B,MAAM,SAAS,IAAI,KAAK,eAAe,QAAQ;EAAE,KAAK;EAAW,OAAO;EAAS,MAAM;CAAU,CAAC;CAClG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,SAAS,SAAS,KAAA,CAAS,CAAC,CAAC,KAAK,SAAS,OAAO,OAAO,IAAI,CAAC;CAC9F,MAAM,QAAQ,KAAK,WAAW,KAAK,YAAY,UAAU,cAAc,KAAK,KAAK,KAAK;CACtF,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,qBAAC,gBAAD;EACE,QACE,oBAAC,QAAD;GACE,GAAI;GACA;GACJ,cAAY,SAAS,WAAW,KAAK;GACrC,aAAU;GACV,YAAU,KAAK,SAAS,IAAI,KAAK,KAAA;GACjC,SAAQ;GACR,WAAW,GAAG,4CAA4C,KAAK,WAAW,KAAK,yBAAyB,SAAS;EAClH,CAAA;EAVL,UAAA,CAaE,oBAAC,cAAD,EAAc,eAAY,OAAQ,CAAA,GAClC,oBAAC,QAAD;GAAM,WAAU;GAAY,UAAA;EAAY,CAAA,CAC1B;CAChB,CAAA,GAAA,oBAAC,gBAAD;EAAgB,OAAM;EAAQ,WAAU;EACtC,UAAA,oBAAC,UAAD;GACE,MAAK;GACL,cAAc;GACd,gBAAgB,WAAW,IAAI;GAC/B,cAAc,eAAe,MAAM;GACnC,UAAU;IAAE;IAAM;GAAG;GACrB,WAAW,UAAU,cAAc;IAAE,MAAM,UAAU,OAAO,IAAI;IAAG,IAAI,UAAU,OAAO,EAAE;GAAE,CAAC;EAC9F,CAAA;CACa,CAAA,CACT,EAAA,CAAA;AAEb"}
@@ -1,4 +1,5 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
+ import { useExposedFocusGuards } from "../../../lib/focus-guards.js";
2
3
  import { ScrollArea } from "../../ui/scroll-area/scroll-area.js";
3
4
  import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip/tooltip.js";
4
5
  import { popupItemClass } from "../../../lib/popup-list.js";
@@ -147,6 +148,11 @@ var Options = ({ scroller, scrollTo, busy, highlightedValue }) => {
147
148
  })
148
149
  });
149
150
  };
151
+ /** Rendered inside the portal, so it exists only while the list is open: the focus guards around the list are not aria-hidden. */
152
+ var ExposedFocusGuards = () => {
153
+ useExposedFocusGuards();
154
+ return null;
155
+ };
150
156
  /**
151
157
  * A choice of several from a list: a trigger shaped like a Select that says what is picked
152
158
  * (the placeholder, the one picked, or a count), and a popup with a search field and checkbox
@@ -250,7 +256,7 @@ var MultiSelect = ({ placeholder, searchPlaceholder, options, value, onValueChan
250
256
  className: "size-4 shrink-0 text-muted-foreground"
251
257
  })]
252
258
  }), /* @__PURE__ */ jsx(TooltipContent, { children: fullSelection(chosen, placeholder, labels, summary) })]
253
- }), /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
259
+ }), /* @__PURE__ */ jsxs(Combobox.Portal, { children: [/* @__PURE__ */ jsx(ExposedFocusGuards, {}), /* @__PURE__ */ jsx(Combobox.Positioner, {
254
260
  sideOffset: 4,
255
261
  align: "start",
256
262
  className: "z-50",
@@ -323,7 +329,7 @@ var MultiSelect = ({ placeholder, searchPlaceholder, options, value, onValueChan
323
329
  })
324
330
  ]
325
331
  })
326
- }) })]
332
+ })] })]
327
333
  });
328
334
  };
329
335
  //#endregion