@assure-one/design-system 1.47.1 → 1.48.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.
package/dist/index.js CHANGED
@@ -1,24 +1,24 @@
1
1
  "use client";
2
- import { Skeleton, ToggleGroup, ToggleGroupItem, useOverflowTooltip, badgeVariants, DataTableBody, DataTableRow, DataTableCell, Badge, Pagination, TableBody, TableRow, TableCell, Table, TableHeader, TableHead } from './chunk-FIRPQWYR.js';
3
- export { Badge, DataTable, DataTableBody, DataTableCell, DataTableCellDue, DataTableCellId, DataTableCellMono, DataTableCellName, DataTableCheckbox, DataTableHead, DataTableHeader, DataTablePagination, DataTableResultsCount, DataTableRow, DataTableSearch, DataTableSpacer, DataTableToolbar, DataTableView, EmptyValue, PROGRESS_LEGACY_TO_INTENT, Pagination, ProgressBar, ProgressRing, SERVICE_TONES, STATUS_DOT_TONE_TO_INTENT, SegmentedProgress, Skeleton, SkeletonCircle, SkeletonText, StatusDot, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableGroupRow, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem, badgeVariants, compareSortValues, nextSortDirection, progressBarVariants, progressRingVariants, resolveBadgeAppearance, serviceToneLabel, serviceToneStyle, statusDotVariants, useDataTable } from './chunk-FIRPQWYR.js';
4
- import { Collapsible, CollapsibleTrigger, CollapsibleContent, PlainLink, Combobox, Sidebar, SidebarFooter, SidebarUser, useSidebarState, SidebarSection, SidebarLinkGroup, SidebarLink, SidebarLinkLabel, SidebarLinkBadge, CmdkCompatContext, Sheet, SheetContent, SheetTitle } from './chunk-6CXI3KRG.js';
5
- export { BrandScope, BrandScopeProvider, Chip, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, DEFAULT_COMBOBOX_MESSAGES, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarBrand, SidebarBrandText, SidebarDrawer, SidebarFooter, SidebarLink, SidebarLinkAction, SidebarLinkBadge, SidebarLinkGroup, SidebarLinkLabel, SidebarPinButton, SidebarProvider, SidebarSection, SidebarTrigger, SidebarUser, assetPath, brandScope, chipVariants, getAssetBasePath, resolveChipStyle, setAssetBasePath, sidebarLinkBadgeVariants, useBrandScope, useSidebarPeekLock, useSidebarState } from './chunk-6CXI3KRG.js';
6
- import { withPortalContainer, Backdrop, alertDialogAnatomy, OverlaySurface, OverlayTitle, OverlayDescription, Button, slot, useDsMessages, useLinkComponent, surfaceAnatomy, stateAttributes, cardAnatomy, ModalPortal, kbdAnatomy, FieldContext, fieldAnatomy, useFieldContext, MENU_SUB_TRIGGER_CHEVRON, menuSubTriggerClasses, MENU_INSET, contextMenuAnatomy, presence, menuItemClasses, MENU_ITEM_DANGER, MENU_INDICATOR_WELL, MENU_CHECK_GLYPH, menuIndicatorItemClasses, MENU_RADIO_GLYPH, menuLabelClasses, menuSeparatorClasses, useFieldControl, useMergedRefs, joinIds, Popover, PopoverTrigger, PopoverContent, FormBridge, CONTROL_SIZE_CLASSES, dialogAnatomy, DismissButton, DEFAULT_OVERLAY_MESSAGES, DISMISS_GLYPH_CLASSES, OverlayBody, headingAnatomy, deprecate, floatingSurfaceClasses, FLOATING_PADDING, FLOATING_WIDTH, labelAnatomy, warnLegacyButtonProps, resolveButtonIcons, buttonVariants, INERT_CLASSES, DISABLED_LOOK_CLASSES, inertHandlers, INERT_ATTRIBUTES, linkButtonAnatomy, useControllableState, controlChrome, mergeRefs, numberInputAnatomy, EN_MESSAGES, phoneFieldAnatomy, Input, useFieldsetContext, useFormReset2, radioGroupAnatomy, microInteractions, CONTROL_LABEL_TEXT, searchInputAnatomy, IconButton, Tooltip, TooltipTrigger, TooltipContent, codeAnatomy, textAnatomy, Slot, stackAnatomy, inlineAnatomy, statusBadgeAnatomy, useFieldsetState, FieldsetContext, fieldsetAnatomy, useFieldsetLegend, shellAnatomy, Avatar, dataTableAnatomy, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, useId as useId$1, partId, errorOnce, isLabelable, Spinner, Select, Checkbox, FIELD_PARTS, SelectRoot, SelectTrigger, SelectValue, SelectContent, SelectItem, OverlayHeader, OverlayFooter, commandAnatomy, menuShortcutClasses, useFormReset, sideDrawerAnatomy, Portal, toastAnatomy, useExitPresence, TooltipProvider, TOOLTIP_DEFAULT_DELAY_MS, DesignSystemContext, PortalContainerContext, ModalRoot, ModalOverlay, ModalContent, ModalTitle, ModalDescription } from './chunk-IIBDSTOG.js';
7
- export { Avatar, Button, Checkbox, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileChip, FileUpload, IconButton, Input, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPortal, PopoverTrigger, Pressable, SPINNER_TONE_TO_INTENT, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Spinner, Textarea, ThemeScope, Tooltip, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger, buttonVariants, iconButtonVariants, inputVariants, resolveButtonStyle, spinnerVariants, textareaVariants, useDsMessages, useFieldControl } from './chunk-IIBDSTOG.js';
8
- import { cn, ChevronDownIcon, EyeIcon, DownloadIcon, ChevronRightIcon, CheckIcon, CircleIcon, CopyIcon, CalendarIcon, XIcon, AlertTriangleIcon, ArrowRightIcon, ArrowLeftIcon, FileTextIcon, ZoomOutIcon, ZoomInIcon, lucideIcon, SquareExclamationPointIcon, TrendingUpIcon, TrendingDownIcon, SearchIcon, TableIcon, StarIcon, RowsIcon, Rows4Icon, ListChecksIcon, CheckCircleSolidIcon, HelpCircleIcon, UserIcon, SendIcon, SettingsIcon, XCircleIcon, CheckCircle2Icon, InfoIcon, ChevronLeftIcon, FolderOpenIcon, FolderClosedIcon, MinusIcon, Icon, ChartNoAxesColumnIncreasingIcon, AlertCircleIcon, PlusIcon, CircleDashedIcon, LoaderIcon, InfoCircleSolidIcon, AlertTriangleSolidIcon, XCircleSolidIcon, RotateCcwIcon, CommandIcon } from './chunk-ARCWF2S3.js';
2
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent, PlainLink, Combobox, Sidebar, SidebarFooter, SidebarUser, Chip, useSidebarState, SidebarSection, SidebarLinkGroup, SidebarLink, SidebarLinkLabel, SidebarLinkBadge, CmdkCompatContext, Sheet, SheetContent, SheetTitle } from './chunk-FO2WSYUA.js';
3
+ export { BrandScope, BrandScopeProvider, Chip, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, DEFAULT_COMBOBOX_MESSAGES, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarBrand, SidebarBrandText, SidebarDrawer, SidebarFooter, SidebarLink, SidebarLinkAction, SidebarLinkBadge, SidebarLinkGroup, SidebarLinkLabel, SidebarPinButton, SidebarProvider, SidebarSection, SidebarTrigger, SidebarUser, assetPath, brandScope, chipVariants, getAssetBasePath, resolveChipStyle, setAssetBasePath, sidebarLinkBadgeVariants, useBrandScope, useSidebarPeekLock, useSidebarState } from './chunk-FO2WSYUA.js';
4
+ import { Skeleton, ToggleGroup, ToggleGroupItem, useOverflowTooltip, badgeVariants, DataTableBody, DataTableRow, DataTableCell, Badge, VisuallyHidden, ProgressBar, Pagination, TableBody, TableRow, TableCell, Table, TableHeader, TableHead } from './chunk-YRFLK7ER.js';
5
+ export { Badge, DATA_TABLE_NO_GROUPING, DataTable, DataTableBody, DataTableCell, DataTableCellDue, DataTableCellId, DataTableCellMono, DataTableCellName, DataTableCellSecondary, DataTableCheckbox, DataTableHead, DataTableHeader, DataTableHeaderMenu, DataTableLoadMore, DataTablePagination, DataTableResultsCount, DataTableRow, DataTableSearch, DataTableSpacer, DataTableSpacerRow, DataTableToolbar, DataTableView, EmptyValue, PROGRESS_LEGACY_TO_INTENT, Pagination, ProgressBar, ProgressRing, SERVICE_TONES, STATUS_DOT_TONE_TO_INTENT, SegmentedProgress, Skeleton, SkeletonCircle, SkeletonText, StatusDot, Switch, Table, TableBody, TableCaption, TableCell, TableCellSecondary, TableFooter, TableGroupRow, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem, VisuallyHidden, badgeVariants, compareSortValues, nextSortDirection, progressBarVariants, progressRingVariants, resolveBadgeAppearance, serviceToneLabel, serviceToneStyle, statusDotVariants, useDataTable, useDataTableGrouping, useDataTableVirtualRows, useScrollRestoration } from './chunk-YRFLK7ER.js';
6
+ import { withPortalContainer, Backdrop, alertDialogAnatomy, OverlaySurface, OverlayTitle, OverlayDescription, Button, slot, useDsMessages, useLinkComponent, surfaceAnatomy, stateAttributes, cardAnatomy, ModalPortal, kbdAnatomy, FieldContext, fieldAnatomy, useFieldContext, MENU_SUB_TRIGGER_CHEVRON, menuSubTriggerClasses, MENU_INSET, contextMenuAnatomy, presence, menuItemClasses, MENU_ITEM_DANGER, MENU_INDICATOR_WELL, MENU_CHECK_GLYPH, menuIndicatorItemClasses, MENU_RADIO_GLYPH, menuLabelClasses, menuSeparatorClasses, useFieldControl, useMergedRefs, joinIds, Popover, PopoverTrigger, PopoverContent, FormBridge, CONTROL_SIZE_CLASSES, dialogAnatomy, DismissButton, DEFAULT_OVERLAY_MESSAGES, DISMISS_GLYPH_CLASSES, OverlayBody, headingAnatomy, deprecate, floatingSurfaceClasses, FLOATING_PADDING, FLOATING_WIDTH, labelAnatomy, warnLegacyButtonProps, resolveButtonIcons, buttonVariants, INERT_CLASSES, DISABLED_LOOK_CLASSES, inertHandlers, INERT_ATTRIBUTES, linkButtonAnatomy, useControllableState, controlChrome, mergeRefs, numberInputAnatomy, EN_MESSAGES, phoneFieldAnatomy, Input, useFieldsetContext, useFormReset2, radioGroupAnatomy, microInteractions, CONTROL_LABEL_TEXT, searchInputAnatomy, IconButton, Tooltip, TooltipTrigger, TooltipContent, codeAnatomy, textAnatomy, Slot, stackAnatomy, inlineAnatomy, statusBadgeAnatomy, useFieldsetState, FieldsetContext, fieldsetAnatomy, useFieldsetLegend, shellAnatomy, Avatar, useId as useId$1, partId, statStripAnatomy, Pressable, Spinner, DropdownMenuItem, filterChipsAnatomy, listPageAnatomy, dataTableAnatomy, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, errorOnce, isLabelable, Select, Checkbox, FIELD_PARTS, SelectRoot, SelectTrigger, SelectValue, SelectContent, SelectItem, OverlayHeader, OverlayFooter, commandAnatomy, menuShortcutClasses, useFormReset, sideDrawerAnatomy, Portal, toastAnatomy, useExitPresence, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, TooltipProvider, TOOLTIP_DEFAULT_DELAY_MS, DesignSystemContext, PortalContainerContext, ModalRoot, ModalOverlay, ModalContent, ModalTitle, ModalDescription } from './chunk-HV24JSRN.js';
7
+ export { Avatar, Button, Checkbox, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileChip, FileUpload, IconButton, Input, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPortal, PopoverTrigger, Pressable, SPINNER_TONE_TO_INTENT, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Spinner, Textarea, ThemeScope, Tooltip, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger, buttonVariants, iconButtonVariants, inputVariants, resolveButtonStyle, spinnerVariants, textareaVariants, useDsMessages, useFieldControl } from './chunk-HV24JSRN.js';
8
+ import { cn, ChevronDownIcon, EyeIcon, DownloadIcon, ChevronRightIcon, CheckIcon, CircleIcon, CopyIcon, CalendarIcon, XIcon, AlertTriangleIcon, ArrowRightIcon, ArrowLeftIcon, FileTextIcon, ZoomOutIcon, ZoomInIcon, lucideIcon, SquareExclamationPointIcon, TrendingUpIcon, TrendingDownIcon, SearchIcon, TableIcon, StarIcon, RowsIcon, Rows4Icon, ListChecksIcon, CheckCircleSolidIcon, HelpCircleIcon, UserIcon, SendIcon, SettingsIcon, ChevronUpIcon, XCircleIcon, CheckCircle2Icon, InfoIcon, ChevronLeftIcon, FolderOpenIcon, FolderClosedIcon, MinusIcon, Icon, ChartNoAxesColumnIncreasingIcon, MoreHorizontalIcon, AlertCircleIcon, PlusIcon, CircleDashedIcon, LoaderIcon, InfoCircleSolidIcon, AlertTriangleSolidIcon, XCircleSolidIcon, RotateCcwIcon, CommandIcon, GripVerticalIcon, LockIcon, ArrowUpIcon, ArrowDownIcon } from './chunk-ARCWF2S3.js';
9
9
  export { AlertCircleIcon, AlertTriangleIcon, AlertTriangleSolidIcon, AlignCenterIcon, AlignLeftIcon, AlignRightIcon, AppWindowIcon, ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowRightSmallIcon, ArrowUpIcon, ArrowUpRightIcon, AtSignIcon, BanIcon, BarChartIcon, BellIcon, BellRingIcon, BoldIcon, BookTextIcon, BrainIcon, BriefcaseIcon, Building2Icon, BuildingIcon, CalculatorIcon, CalendarIcon, CameraIcon, ChartNoAxesColumnIncreasingIcon, ChatBubbleIcon, CheckCheckIcon, CheckCircle2Icon, CheckCircleSolidIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsDownUpIcon, ChevronsLeftIcon, ChevronsRightIcon, ChevronsUpDownIcon, CircleDashedIcon, CircleDotIcon, CircleIcon, ClipboardCheckIcon, ClockAlertIcon, ClockIcon, CloseIcon, CommandIcon, CopyIcon, CornerDownLeftIcon, CreditCardIcon, DecimalsArrowLeftIcon, DecimalsArrowRightIcon, DocumentIcon, DollarSignIcon, DownloadIcon, ExternalLinkIcon, ExtractIcon, EyeIcon, EyeOffIcon, FileChartColumnIcon, FileIcon, FilePlusIcon, FileReturnIcon, FileTextIcon, FilterIcon, FlagIcon, FolderClosedIcon, FolderDownIcon, FolderOpenIcon, FolderPlusIcon, FolderUpIcon, GlobeIcon, GoogleBrandIcon, GraduationCapIcon, Grid2x2Icon, GripHorizontalIcon, GripVerticalIcon, HelpCircleIcon, HighlighterIcon, HistoryIcon, HomeIcon, InboxIcon, InfoCircleSolidIcon, InfoIcon, ItalicIcon, KanbanIcon, KeyIcon, LandmarkIcon, LayersIcon, LayoutDashboardIcon, LayoutGridIcon, LayoutListIcon, LightningIcon, Link2Icon, LinkIcon, ListChecksIcon, ListIcon, ListOrderedIcon, LoaderIcon, LockIcon, LockKeyholeIcon, LockOpenIcon, LogOutIcon, MailIcon, MapPinIcon, MaximizeIcon, MenuIcon, MessageCircleIcon, MessageCircleMoreIcon, MessageCircleWarningIcon, MicIcon, MicrosoftBrandIcon, MinimizeIcon, MinusIcon, MonitorIcon, MoonIcon, MoreHorizontalIcon, MoreVerticalIcon, MousePointer2Icon, MoveIcon, PaletteIcon, PanelLeftCloseIcon, PanelLeftIcon, PanelRightCloseIcon, PanelRightIcon, PaperclipIcon, PauseIcon, PenSignIcon, PenToolIcon, PencilIcon, PercentIcon, PhoneIcon, PinIcon, PinOffIcon, PlayIcon, PlusIcon, ReceiptIcon, RefreshCwIcon, RepeatIcon, ReplyIcon, RotateCcwIcon, Rows4Icon, RowsIcon, SaveIcon, SearchCheckIcon, SearchIcon, SendIcon, SettingsIcon, ShieldIcon, SigmaIcon, SlashIcon, SmartphoneIcon, SparkleIcon, SparklesIcon, SquareExclamationPointIcon, SquareFunctionIcon, SquareIcon, SquarePlayIcon, SquareSquareIcon, StampIcon, StarIcon, StopIcon, StrikethroughIcon, StripeBrandIcon, SunIcon, TableIcon, TagIcon, TeamIcon, ThumbsDownIcon, ThumbsUpIcon, TimerIcon, Trash2Icon, TrashIcon, TrashSolidIcon, TrendingDownIcon, TrendingUpIcon, TypeIcon, UnderlineIcon, Undo2Icon, UploadIcon, UserCircleIcon, UserIcon, UsersIcon, VideoIcon, Volume2Icon, VolumeXIcon, WorkflowIcon, XCircleIcon, XCircleSolidIcon, XIcon, ZoomInIcon, ZoomOutIcon, cn, createIcon, iconCursor, iconToSvg } from './chunk-ARCWF2S3.js';
10
10
  export { systemTokens } from './chunk-CPOGSNXS.js';
11
11
  import './chunk-EUHGZA6Q.js';
12
12
  export { colors, radii, reference, shadows, spacing, surfaces, typography } from './chunk-YRHRVOLV.js';
13
13
  import * as React30 from 'react';
14
- import { forwardRef, useId, useRef, useState, useLayoutEffect, createContext, useEffect, useCallback, useMemo, useImperativeHandle, Children, isValidElement, cloneElement, useSyncExternalStore, useContext, createElement } from 'react';
14
+ import { forwardRef, useId, useRef, useState, useLayoutEffect, createContext, useEffect, useCallback, useMemo, useImperativeHandle, Children, isValidElement, cloneElement, createElement, useSyncExternalStore, useContext } from 'react';
15
15
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
16
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
17
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
18
18
  import { cva } from 'class-variance-authority';
19
19
  import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio';
20
20
  import { DayPicker } from 'react-day-picker';
21
- import { format, parse, isValid, startOfDay, startOfMonth, startOfYear, subYears, endOfMonth, endOfYear, addYears } from 'date-fns';
21
+ import { format, parse, isValid, startOfDay, startOfMonth, startOfYear, subYears, endOfMonth, endOfYear, addYears, subQuarters, endOfQuarter, startOfQuarter, subMonths, addDays, parseISO } from 'date-fns';
22
22
  import { Command } from 'cmdk';
23
23
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
24
24
  import * as DialogPrimitive from '@radix-ui/react-dialog';
@@ -31,7 +31,6 @@ import * as SeparatorPrimitive from '@radix-ui/react-separator';
31
31
  import * as SliderPrimitive from '@radix-ui/react-slider';
32
32
  import { useFormStatus } from 'react-dom';
33
33
  import { Branch } from '@radix-ui/react-dismissable-layer';
34
- import * as VisuallyHiddenPrimitive from '@radix-ui/react-visually-hidden';
35
34
 
36
35
  var Accordion = React30.forwardRef(function Accordion2({ className, ...props }, ref) {
37
36
  return /* @__PURE__ */ jsx(
@@ -416,9 +415,9 @@ function fileTypeFromName(fileName) {
416
415
  const ext = dot >= 0 && dot < fileName.length - 1 ? fileName.slice(dot + 1) : "";
417
416
  return normalizeFileType(ext);
418
417
  }
419
- var FileTypeBadge = forwardRef(function FileTypeBadge2({ format: format2, type, fileName, tone = "auto", size, className, "aria-label": ariaLabel, ...props }, ref) {
418
+ var FileTypeBadge = forwardRef(function FileTypeBadge2({ format: format3, type, fileName, tone = "auto", size, className, "aria-label": ariaLabel, ...props }, ref) {
420
419
  const messages = useDsMessages("fileTypeBadge");
421
- const raw = format2 ?? type ?? (fileName != null ? fileTypeFromName(fileName) : void 0) ?? "FILE";
420
+ const raw = format3 ?? type ?? (fileName != null ? fileTypeFromName(fileName) : void 0) ?? "FILE";
422
421
  const code = normalizeFileType(raw);
423
422
  const resolvedTone = tone !== "auto" ? tone : TYPE_TO_TONE[code] ?? "neutral";
424
423
  return /* @__PURE__ */ jsx(
@@ -1059,7 +1058,7 @@ function Calendar({
1059
1058
  }) {
1060
1059
  const isRange = mode === "range";
1061
1060
  const single = !isRange ? selected : void 0;
1062
- const range = isRange ? selected : void 0;
1061
+ const range2 = isRange ? selected : void 0;
1063
1062
  const locale = useLocale(typeof localeProp === "string" ? localeProp : void 0);
1064
1063
  const localeObject = typeof localeProp === "object" ? localeProp : void 0;
1065
1064
  const localeProps = React30.useMemo(
@@ -1091,8 +1090,8 @@ function Calendar({
1091
1090
  if (minDate) return { before: minDate };
1092
1091
  return { after: maxDate };
1093
1092
  }, [minDate, maxDate]);
1094
- const rangeFrom = range?.from;
1095
- const rangeTo = range?.to;
1093
+ const rangeFrom = range2?.from;
1094
+ const rangeTo = range2?.to;
1096
1095
  const navRange = React30.useMemo(
1097
1096
  () => captionLayout === "label" ? void 0 : resolveCalendarNavRange({
1098
1097
  minDate,
@@ -2700,8 +2699,8 @@ var DateRangePicker = forwardRef(
2700
2699
  [onChange]
2701
2700
  );
2702
2701
  const handleCalendarSelect = useCallback(
2703
- (range) => {
2704
- if (!range && draftRange?.from && draftRange.to && isSameDate(draftRange.from, draftRange.to)) {
2702
+ (range2) => {
2703
+ if (!range2 && draftRange?.from && draftRange.to && isSameDate(draftRange.from, draftRange.to)) {
2705
2704
  const day = dateToIso(draftRange.from);
2706
2705
  const next = { from: day, to: day };
2707
2706
  if (!isControlled) setInternalValue(next);
@@ -2710,9 +2709,9 @@ var DateRangePicker = forwardRef(
2710
2709
  setOpen(false);
2711
2710
  return;
2712
2711
  }
2713
- setDraftRange(range);
2714
- const fromIso = range?.from ? dateToIso(range.from) : "";
2715
- const toIso = range?.to ? dateToIso(range.to) : "";
2712
+ setDraftRange(range2);
2713
+ const fromIso = range2?.from ? dateToIso(range2.from) : "";
2714
+ const toIso = range2?.to ? dateToIso(range2.to) : "";
2716
2715
  if (fromIso && toIso && fromIso !== toIso) {
2717
2716
  const next = { from: fromIso, to: toIso };
2718
2717
  if (!isControlled) setInternalValue(next);
@@ -4402,7 +4401,7 @@ var NumberInput = forwardRef(function NumberInput2({
4402
4401
  "aria-labelledby": ariaLabelledBy,
4403
4402
  "aria-describedby": ariaDescribedBy
4404
4403
  });
4405
- const format2 = useCallback(
4404
+ const format3 = useCallback(
4406
4405
  (next) => formatNumber(next, locale, formatOptions),
4407
4406
  [locale, formatOptions]
4408
4407
  );
@@ -4410,12 +4409,12 @@ var NumberInput = forwardRef(function NumberInput2({
4410
4409
  (text2) => parseNumber(text2, locale, formatOptions),
4411
4410
  [locale, formatOptions]
4412
4411
  );
4413
- const [text, setText] = useState(() => format2(value));
4412
+ const [text, setText] = useState(() => format3(value));
4414
4413
  useEffect(() => {
4415
4414
  const typed = parse2(text);
4416
4415
  const typedValue = typed == null || Number.isNaN(typed) ? null : typed;
4417
- if (typedValue !== value) setText(format2(value));
4418
- }, [value, format2, parse2]);
4416
+ if (typedValue !== value) setText(format3(value));
4417
+ }, [value, format3, parse2]);
4419
4418
  const commit = useCallback(
4420
4419
  (next) => {
4421
4420
  if (next == null || Number.isNaN(next)) {
@@ -4425,9 +4424,9 @@ var NumberInput = forwardRef(function NumberInput2({
4425
4424
  }
4426
4425
  const canonical = roundToFormat(clampNumber(next, min, max), locale, formatOptions);
4427
4426
  setValue(canonical);
4428
- setText(format2(canonical));
4427
+ setText(format3(canonical));
4429
4428
  },
4430
- [min, max, locale, formatOptions, format2, setValue]
4429
+ [min, max, locale, formatOptions, format3, setValue]
4431
4430
  );
4432
4431
  const stepBy = (direction, steps) => {
4433
4432
  if (wiring.disabled || wiring.readOnly) return;
@@ -4535,7 +4534,7 @@ var NumberInput = forwardRef(function NumberInput2({
4535
4534
  disabled: wiring.disabled,
4536
4535
  onReset: () => {
4537
4536
  setValue(defaultValue);
4538
- setText(format2(defaultValue));
4537
+ setText(format3(defaultValue));
4539
4538
  }
4540
4539
  }
4541
4540
  )
@@ -7935,12 +7934,6 @@ var Code = forwardRef(function Code2({ variant = "inline", className, children,
7935
7934
  );
7936
7935
  });
7937
7936
  Code.displayName = "Code";
7938
- var VisuallyHidden = forwardRef(
7939
- function VisuallyHidden2({ children, ...props }, ref) {
7940
- return /* @__PURE__ */ jsx(VisuallyHiddenPrimitive.Root, { ref, ...props, children });
7941
- }
7942
- );
7943
- VisuallyHidden.displayName = "VisuallyHidden";
7944
7937
  var TextOverflowTooltip = forwardRef(
7945
7938
  function TextOverflowTooltip2({ className, children, onPointerEnter, onFocus, ...props }, ref) {
7946
7939
  const overflow = useOverflowTooltip();
@@ -8279,9 +8272,28 @@ function MultiSelect(props) {
8279
8272
  return /* @__PURE__ */ jsx(Combobox, { ...props, multiple: true });
8280
8273
  }
8281
8274
  MultiSelect.displayName = "MultiSelect";
8275
+ var canMatchMedia = () => typeof window !== "undefined" && typeof window.matchMedia === "function";
8276
+ function useMediaQuery(query, { serverValue = false } = {}) {
8277
+ const subscribe3 = useCallback(
8278
+ (onStoreChange) => {
8279
+ if (!canMatchMedia()) return () => {
8280
+ };
8281
+ const list = window.matchMedia(query);
8282
+ list.addEventListener("change", onStoreChange);
8283
+ return () => list.removeEventListener("change", onStoreChange);
8284
+ },
8285
+ [query]
8286
+ );
8287
+ const getSnapshot = () => canMatchMedia() ? window.matchMedia(query).matches : serverValue;
8288
+ const getServerSnapshot2 = () => serverValue;
8289
+ return useSyncExternalStore(subscribe3, getSnapshot, getServerSnapshot2);
8290
+ }
8282
8291
 
8283
8292
  // src/foundation/use-reduced-motion.ts
8284
8293
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
8294
+ function useReducedMotion(options) {
8295
+ return useMediaQuery(REDUCED_MOTION_QUERY, options);
8296
+ }
8285
8297
  var statusLabel = {
8286
8298
  "not-started": "Not started",
8287
8299
  "in-progress": "In progress",
@@ -8904,6 +8916,7 @@ function AppSidebarRow({
8904
8916
  ),
8905
8917
  href: item.href,
8906
8918
  active,
8919
+ activeDescendant: hasActiveDescendant(item, activeId),
8907
8920
  defaultOpen: active || hasActiveDescendant(item, activeId),
8908
8921
  children: item.children.map((child) => /* @__PURE__ */ jsx(AppSidebarRow, { item: child, activeId, showTitle, RouterLink }, child.id))
8909
8922
  }
@@ -9231,10 +9244,10 @@ function Sparkline({ data, className }) {
9231
9244
  const h = 28;
9232
9245
  const min = Math.min(...data);
9233
9246
  const max = Math.max(...data);
9234
- const range = max - min || 1;
9247
+ const range2 = max - min || 1;
9235
9248
  const coords = data.map((d, i) => {
9236
9249
  const x = i / (data.length - 1) * w;
9237
- const y = h - (d - min) / range * (h - 2) - 1;
9250
+ const y = h - (d - min) / range2 * (h - 2) - 1;
9238
9251
  return [x, y];
9239
9252
  });
9240
9253
  const line = coords.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(" ");
@@ -10176,13 +10189,484 @@ var FilterChip = forwardRef(function FilterChip2({ active = false, size, count,
10176
10189
  );
10177
10190
  });
10178
10191
  FilterChip.displayName = "FilterChip";
10179
- var BulkActionBarContext = React30.createContext({ tone: "surface" });
10192
+ var StatStripContext = React30.createContext({
10193
+ loading: false,
10194
+ countUp: false,
10195
+ highlightChanges: false
10196
+ });
10197
+ var storedFlags = /* @__PURE__ */ new Map();
10198
+ var storageListeners = /* @__PURE__ */ new Set();
10199
+ function readStoredFlag(key) {
10200
+ if (!storedFlags.has(key)) {
10201
+ let value = null;
10202
+ try {
10203
+ const raw = window.localStorage.getItem(key);
10204
+ value = raw === "1" ? true : raw === "0" ? false : null;
10205
+ } catch {
10206
+ }
10207
+ storedFlags.set(key, value);
10208
+ }
10209
+ return storedFlags.get(key) ?? null;
10210
+ }
10211
+ function writeStoredFlag(key, value) {
10212
+ storedFlags.set(key, value);
10213
+ try {
10214
+ window.localStorage.setItem(key, value ? "1" : "0");
10215
+ } catch {
10216
+ }
10217
+ for (const listener of storageListeners) listener();
10218
+ }
10219
+ function subscribeStoredFlags(listener) {
10220
+ storageListeners.add(listener);
10221
+ const onStorage = (event) => {
10222
+ if (event.key === null) storedFlags.clear();
10223
+ else storedFlags.delete(event.key);
10224
+ listener();
10225
+ };
10226
+ window.addEventListener("storage", onStorage);
10227
+ return () => {
10228
+ storageListeners.delete(listener);
10229
+ window.removeEventListener("storage", onStorage);
10230
+ };
10231
+ }
10232
+ var serverFlag = () => null;
10233
+ function useStoredFlag(key) {
10234
+ const getSnapshot = React30.useCallback(() => key ? readStoredFlag(key) : null, [key]);
10235
+ return React30.useSyncExternalStore(subscribeStoredFlags, getSnapshot, serverFlag);
10236
+ }
10237
+ function readDuration(element, property, fallback) {
10238
+ const raw = getComputedStyle(element).getPropertyValue(property).trim();
10239
+ if (raw.endsWith("ms")) return Number.parseFloat(raw) || fallback;
10240
+ if (raw.endsWith("s")) return (Number.parseFloat(raw) || fallback / 1e3) * 1e3;
10241
+ return fallback;
10242
+ }
10243
+ function useCountUp(target, enabled) {
10244
+ const reduced = useReducedMotion();
10245
+ const [shown, setShown] = React30.useState(target);
10246
+ const shownRef = React30.useRef(target);
10247
+ React30.useEffect(() => {
10248
+ if (!enabled || reduced || !Number.isFinite(target)) {
10249
+ shownRef.current = target;
10250
+ setShown(target);
10251
+ return;
10252
+ }
10253
+ const from = shownRef.current;
10254
+ if (from === target) return;
10255
+ const duration = readDuration(document.documentElement, "--motion-duration-deliberate", 400);
10256
+ const start = performance.now();
10257
+ let frame = 0;
10258
+ const tick = (now) => {
10259
+ const t = Math.min(1, (now - start) / duration);
10260
+ const eased = 1 - Math.pow(1 - t, 4);
10261
+ const next = from + (target - from) * eased;
10262
+ shownRef.current = next;
10263
+ setShown(next);
10264
+ if (t < 1) frame = requestAnimationFrame(tick);
10265
+ };
10266
+ frame = requestAnimationFrame(tick);
10267
+ return () => cancelAnimationFrame(frame);
10268
+ }, [target, enabled, reduced]);
10269
+ return enabled ? shown : target;
10270
+ }
10271
+ var GLOW_MS = 1200;
10272
+ function useChangeGlow(value, enabled) {
10273
+ const previous = React30.useRef(value);
10274
+ const [glowing, setGlowing] = React30.useState(false);
10275
+ React30.useEffect(() => {
10276
+ const changed = !Object.is(previous.current, value);
10277
+ previous.current = value;
10278
+ if (!enabled || !changed) {
10279
+ setGlowing(false);
10280
+ return;
10281
+ }
10282
+ setGlowing(true);
10283
+ const timer = setTimeout(() => setGlowing(false), GLOW_MS);
10284
+ return () => clearTimeout(timer);
10285
+ }, [value, enabled]);
10286
+ return enabled && glowing;
10287
+ }
10288
+ var WIDE_COLUMNS_VAR = "--ds-stat-strip-columns";
10289
+ var StatStrip = React30.forwardRef(function StatStrip2({
10290
+ children,
10291
+ label,
10292
+ columns,
10293
+ collapsible = false,
10294
+ collapsed: collapsedProp,
10295
+ defaultCollapsed = false,
10296
+ onCollapsedChange,
10297
+ storageKey,
10298
+ loading = false,
10299
+ countUp = false,
10300
+ highlightChanges = false,
10301
+ className,
10302
+ style,
10303
+ id: idProp,
10304
+ ...props
10305
+ }, forwardedRef) {
10306
+ const messages = useDsMessages("statStrip");
10307
+ const id = useId$1(idProp, "stat-strip");
10308
+ const contentId = partId(id, "content");
10309
+ const stored = useStoredFlag(storageKey);
10310
+ const [uncontrolled, setUncontrolled] = React30.useState(defaultCollapsed);
10311
+ const collapsed = collapsible ? collapsedProp ?? stored ?? uncontrolled : false;
10312
+ const rootRef = React30.useRef(null);
10313
+ const fromHeight = React30.useRef(null);
10314
+ React30.useLayoutEffect(() => {
10315
+ const element = rootRef.current;
10316
+ const from = fromHeight.current;
10317
+ fromHeight.current = null;
10318
+ if (!element || from === null || typeof element.animate !== "function") return;
10319
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
10320
+ const to = element.getBoundingClientRect().height;
10321
+ if (Math.abs(to - from) < 1) return;
10322
+ const style2 = getComputedStyle(element);
10323
+ element.animate([{ height: `${from}px` }, { height: `${to}px` }], {
10324
+ duration: readDuration(element, "--motion-duration-spatial", 250),
10325
+ easing: style2.getPropertyValue("--motion-ease-move").trim() || "cubic-bezier(0.4, 0, 0.2, 1)"
10326
+ });
10327
+ }, [collapsed]);
10328
+ const toggle = () => {
10329
+ const next = !collapsed;
10330
+ fromHeight.current = rootRef.current?.getBoundingClientRect().height ?? null;
10331
+ setUncontrolled(next);
10332
+ if (storageKey) writeStoredFlag(storageKey, next);
10333
+ onCollapsedChange?.(next);
10334
+ };
10335
+ const setRefs = React30.useCallback(
10336
+ (node) => {
10337
+ rootRef.current = node;
10338
+ if (typeof forwardedRef === "function") forwardedRef(node);
10339
+ else if (forwardedRef) forwardedRef.current = node;
10340
+ },
10341
+ [forwardedRef]
10342
+ );
10343
+ const context = React30.useMemo(
10344
+ () => ({ loading, countUp, highlightChanges }),
10345
+ [loading, countUp, highlightChanges]
10346
+ );
10347
+ const toggleLabel = collapsed ? messages.expand : messages.collapse;
10348
+ const [content, setContent] = React30.useState(null);
10349
+ const [scrollable, setScrollable] = React30.useState(false);
10350
+ React30.useEffect(() => {
10351
+ if (!content || !collapsed) {
10352
+ setScrollable(false);
10353
+ return;
10354
+ }
10355
+ const measure = () => setScrollable(
10356
+ content.scrollWidth > content.clientWidth + 1 && !content.querySelector("button:not([disabled])")
10357
+ );
10358
+ measure();
10359
+ if (typeof ResizeObserver === "undefined") return;
10360
+ const observer = new ResizeObserver(measure);
10361
+ observer.observe(content);
10362
+ return () => observer.disconnect();
10363
+ }, [content, collapsed]);
10364
+ return /* @__PURE__ */ jsx(StatStripContext, { value: context, children: /* @__PURE__ */ jsxs(
10365
+ "div",
10366
+ {
10367
+ ref: setRefs,
10368
+ id,
10369
+ role: "group",
10370
+ "aria-label": label ?? messages.region,
10371
+ "aria-busy": loading || void 0,
10372
+ ...slot(statStripAnatomy, "root"),
10373
+ ...stateAttributes({ state: collapsed ? "collapsed" : "expanded", loading }),
10374
+ "data-collapsible": collapsible ? "" : void 0,
10375
+ className: cn(
10376
+ "ds:group/stat-strip ds:@container/stat-strip ds:relative ds:overflow-hidden",
10377
+ "ds:rounded-card ds:border-rule ds:bg-surface ds:shadow-quiet ds:border",
10378
+ className
10379
+ ),
10380
+ style: columns === void 0 ? style : { [WIDE_COLUMNS_VAR]: Math.max(1, Math.round(columns)), ...style },
10381
+ ...props,
10382
+ children: [
10383
+ /* @__PURE__ */ createElement(
10384
+ "div",
10385
+ {
10386
+ ref: setContent,
10387
+ id: contentId,
10388
+ ...slot(statStripAnatomy, "content"),
10389
+ ...scrollable ? { tabIndex: 0, role: "region", "aria-label": label ?? messages.region } : {},
10390
+ className: cn(
10391
+ // Expanded: a grid whose hairlines are each cell's inset right and
10392
+ // bottom edge. The grid overhangs the card by 1px on both axes, so
10393
+ // the last column's and last row's edges fall under the root's
10394
+ // `overflow-hidden` instead of doubling the border. Unlike
10395
+ // `gap-px` over a rule-coloured fill, a short last row leaves
10396
+ // surface — not a grey block — in the empty cells.
10397
+ "ds:-me-px ds:-mb-px ds:grid ds:grid-cols-2 ds:@lg/stat-strip:grid-cols-3",
10398
+ columns === void 0 ? "ds:@4xl/stat-strip:auto-cols-fr ds:@4xl/stat-strip:grid-flow-col ds:@4xl/stat-strip:grid-cols-none" : "ds:@4xl/stat-strip:grid-cols-[repeat(var(--ds-stat-strip-columns),minmax(0,1fr))]",
10399
+ "ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:duration-[var(--motion-duration-overlay)] ds:ease-[var(--motion-ease-enter)]",
10400
+ // Collapsed: one line of inline metrics that scrolls sideways,
10401
+ // leaving room on the right for the chevron.
10402
+ "ds:group-data-[state=collapsed]/stat-strip:flex ds:group-data-[state=collapsed]/stat-strip:items-center",
10403
+ "ds:group-data-[state=collapsed]/stat-strip:overflow-x-auto ds:group-data-[state=collapsed]/stat-strip:py-1",
10404
+ "ds:group-data-[state=collapsed]/stat-strip:m-0 ds:group-data-[state=collapsed]/stat-strip:ps-2 ds:group-data-[state=collapsed]/stat-strip:[scrollbar-width:none]",
10405
+ collapsible && "ds:group-data-[state=collapsed]/stat-strip:pe-10",
10406
+ "ds:focus-visible:outline-none ds:focus-visible:[box-shadow:inset_0_0_0_2px_var(--color-brand-ring)]"
10407
+ ),
10408
+ key: collapsed ? "collapsed" : "expanded"
10409
+ },
10410
+ children
10411
+ ),
10412
+ collapsible && /* @__PURE__ */ jsx(
10413
+ "div",
10414
+ {
10415
+ ...slot(statStripAnatomy, "toggle"),
10416
+ className: cn(
10417
+ "ds:absolute ds:end-1.5 ds:top-1.5",
10418
+ // Collapsed: a full-height surface pad with a short fade on its
10419
+ // leading edge, so metrics scrolling under it dissolve instead
10420
+ // of colliding with the chevron.
10421
+ "ds:group-data-[state=collapsed]/stat-strip:inset-y-0 ds:group-data-[state=collapsed]/stat-strip:end-0",
10422
+ "ds:group-data-[state=collapsed]/stat-strip:flex ds:group-data-[state=collapsed]/stat-strip:items-center",
10423
+ "ds:group-data-[state=collapsed]/stat-strip:bg-surface ds:group-data-[state=collapsed]/stat-strip:pe-1.5",
10424
+ "ds:group-data-[state=collapsed]/stat-strip:before:absolute ds:group-data-[state=collapsed]/stat-strip:before:inset-y-0",
10425
+ "ds:group-data-[state=collapsed]/stat-strip:before:end-full ds:group-data-[state=collapsed]/stat-strip:before:w-6",
10426
+ "ds:group-data-[state=collapsed]/stat-strip:before:bg-linear-to-l ds:group-data-[state=collapsed]/stat-strip:before:from-surface ds:group-data-[state=collapsed]/stat-strip:before:to-transparent"
10427
+ ),
10428
+ children: /* @__PURE__ */ jsx(
10429
+ IconButton,
10430
+ {
10431
+ variant: "ghost",
10432
+ size: "xs",
10433
+ label: toggleLabel,
10434
+ "aria-expanded": !collapsed,
10435
+ "aria-controls": contentId,
10436
+ onClick: toggle,
10437
+ icon: collapsed ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronUpIcon, {})
10438
+ }
10439
+ )
10440
+ }
10441
+ )
10442
+ ]
10443
+ }
10444
+ ) });
10445
+ });
10446
+ StatStrip.displayName = "StatStrip";
10447
+ var SELECTED_FILL = {
10448
+ neutral: "ds:data-[pressed]:bg-bg-2",
10449
+ brand: "ds:data-[pressed]:bg-brand-bg",
10450
+ info: "ds:data-[pressed]:bg-info/10",
10451
+ success: "ds:data-[pressed]:bg-success-bg",
10452
+ warning: "ds:data-[pressed]:bg-warning-bg",
10453
+ danger: "ds:data-[pressed]:bg-danger-bg"
10454
+ };
10455
+ var SELECTED_RULE = {
10456
+ neutral: "ds:bg-fg-3",
10457
+ brand: "ds:bg-brand",
10458
+ info: "ds:bg-info",
10459
+ success: "ds:bg-success-fg",
10460
+ warning: "ds:bg-warning-fg",
10461
+ danger: "ds:bg-danger-fg"
10462
+ };
10463
+ var CAPTION_INK = {
10464
+ neutral: "ds:text-fg-3",
10465
+ brand: "ds:text-brand",
10466
+ info: "ds:text-info",
10467
+ success: "ds:text-success-fg",
10468
+ warning: "ds:text-warning-fg",
10469
+ danger: "ds:text-danger-fg"
10470
+ };
10471
+ var defaultFormat3 = (value) => Math.round(value).toLocaleString();
10472
+ var CELL_CLASSES = cn(
10473
+ "ds:relative ds:flex ds:min-w-0 ds:flex-col ds:justify-start ds:px-5 ds:py-3 ds:text-start",
10474
+ "ds:bg-surface ds:transition-colors ds:duration-[var(--motion-duration-deliberate)] ds:ease-[var(--motion-ease-standard)] ds:motion-reduce:transition-none",
10475
+ // The hairlines: the inset right and bottom edge of every cell (inset, so a
10476
+ // neighbour's background cannot paint over them; see the content's
10477
+ // overhang for the card edge).
10478
+ "ds:[box-shadow:inset_-1px_0_0_var(--color-rule),inset_0_-1px_0_var(--color-rule)]",
10479
+ // Collapsed: an inline `label value` pair with a short divider before it.
10480
+ "ds:group-data-[state=collapsed]/stat-strip:shrink-0 ds:group-data-[state=collapsed]/stat-strip:flex-row",
10481
+ "ds:group-data-[state=collapsed]/stat-strip:items-baseline ds:group-data-[state=collapsed]/stat-strip:gap-1.5",
10482
+ "ds:group-data-[state=collapsed]/stat-strip:rounded-btn ds:group-data-[state=collapsed]/stat-strip:px-2.5 ds:group-data-[state=collapsed]/stat-strip:py-1",
10483
+ "ds:group-data-[state=collapsed]/stat-strip:shadow-none",
10484
+ "ds:group-data-[state=collapsed]/stat-strip:not-first:before:absolute ds:group-data-[state=collapsed]/stat-strip:not-first:before:start-0",
10485
+ "ds:group-data-[state=collapsed]/stat-strip:not-first:before:top-1/2 ds:group-data-[state=collapsed]/stat-strip:not-first:before:h-4",
10486
+ "ds:group-data-[state=collapsed]/stat-strip:not-first:before:w-px ds:group-data-[state=collapsed]/stat-strip:not-first:before:-translate-y-1/2",
10487
+ "ds:group-data-[state=collapsed]/stat-strip:not-first:before:bg-rule"
10488
+ );
10489
+ var PRESSABLE_CELL_CLASSES = cn(
10490
+ "ds:cursor-pointer ds:hover:bg-surface-hover",
10491
+ // An inset ring: a cell on the card edge would have an outer ring clipped
10492
+ // by the root's overflow.
10493
+ "ds:focus-visible:[box-shadow:inset_0_0_0_2px_var(--color-brand-ring)]",
10494
+ "ds:data-[disabled]:opacity-45"
10495
+ );
10496
+ var StatStripItem = React30.forwardRef(
10497
+ function StatStripItem2({
10498
+ label,
10499
+ value,
10500
+ format: format3 = defaultFormat3,
10501
+ caption,
10502
+ progress,
10503
+ progressLabel,
10504
+ intent,
10505
+ tintCaption = false,
10506
+ pressed,
10507
+ onPressedChange,
10508
+ disabled,
10509
+ loading: loadingProp,
10510
+ countUp: countUpProp,
10511
+ highlightChanges: highlightProp,
10512
+ className,
10513
+ onClick,
10514
+ ...props
10515
+ }, ref) {
10516
+ const strip = React30.use(StatStripContext);
10517
+ const loading = loadingProp ?? strip.loading;
10518
+ const numeric = typeof value === "number";
10519
+ const animate = numeric && (countUpProp ?? strip.countUp);
10520
+ const shown = useCountUp(numeric ? value : 0, animate);
10521
+ const glowing = useChangeGlow(value, highlightProp ?? strip.highlightChanges);
10522
+ const toggles = pressed !== void 0 || onPressedChange !== void 0;
10523
+ const resolvedIntent = intent ?? "neutral";
10524
+ const finalText = numeric ? format3(value) : null;
10525
+ const valueNode = numeric ? animate ? /* @__PURE__ */ jsxs(Fragment, { children: [
10526
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: format3(shown) }),
10527
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: finalText })
10528
+ ] }) : finalText : value;
10529
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
10530
+ toggles && /* @__PURE__ */ jsx(
10531
+ "span",
10532
+ {
10533
+ "aria-hidden": "true",
10534
+ className: cn(
10535
+ // Inside the cell rather than on its border, so the grid's
10536
+ // hairlines stay unbroken while the filter is on.
10537
+ "ds:absolute ds:inset-x-0 ds:top-0 ds:h-0.5 ds:opacity-0",
10538
+ "ds:transition-opacity ds:duration-[var(--motion-duration-overlay)] ds:motion-reduce:transition-none",
10539
+ "ds:group-data-[pressed]/stat-strip-item:opacity-100",
10540
+ "ds:group-data-[state=collapsed]/stat-strip:hidden",
10541
+ SELECTED_RULE[resolvedIntent]
10542
+ )
10543
+ }
10544
+ ),
10545
+ /* @__PURE__ */ jsx(
10546
+ "span",
10547
+ {
10548
+ ...slot(statStripAnatomy, "label"),
10549
+ className: "ds:text-helper ds:text-fg-3 ds:truncate ds:font-medium ds:group-data-[state=collapsed]/stat-strip:font-normal",
10550
+ children: label
10551
+ }
10552
+ ),
10553
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: ": " }),
10554
+ /* @__PURE__ */ jsxs("span", { className: "ds:mt-1.5 ds:flex ds:min-w-0 ds:items-center ds:gap-3 ds:group-data-[state=collapsed]/stat-strip:mt-0", children: [
10555
+ loading ? /* @__PURE__ */ jsx(Skeleton, { className: "ds:h-7 ds:w-16 ds:group-data-[state=collapsed]/stat-strip:h-4 ds:group-data-[state=collapsed]/stat-strip:w-8" }) : /* @__PURE__ */ jsx(
10556
+ "span",
10557
+ {
10558
+ ...slot(statStripAnatomy, "value"),
10559
+ className: cn(
10560
+ "ds:text-title-page ds:text-fg ds:shrink-0 ds:truncate ds:leading-none ds:font-semibold ds:tabular-nums",
10561
+ "ds:group-data-[state=collapsed]/stat-strip:text-body ds:group-data-[state=collapsed]/stat-strip:leading-normal"
10562
+ ),
10563
+ children: valueNode
10564
+ }
10565
+ ),
10566
+ progress !== void 0 && !loading && /* @__PURE__ */ jsx(
10567
+ ProgressBar,
10568
+ {
10569
+ value: Math.max(0, Math.min(100, progress)),
10570
+ max: 100,
10571
+ size: "xs",
10572
+ intent: intent ?? "brand",
10573
+ "aria-label": progressLabel ?? (typeof label === "string" ? label : void 0),
10574
+ className: "ds:min-w-0 ds:flex-1 ds:group-data-[state=collapsed]/stat-strip:hidden"
10575
+ }
10576
+ )
10577
+ ] }),
10578
+ caption !== void 0 && caption !== null && /* @__PURE__ */ jsx(
10579
+ "span",
10580
+ {
10581
+ ...slot(statStripAnatomy, "caption"),
10582
+ className: cn(
10583
+ "ds:text-helper ds:mt-2 ds:block ds:truncate ds:group-data-[state=collapsed]/stat-strip:hidden",
10584
+ tintCaption ? CAPTION_INK[resolvedIntent] : "ds:text-fg-3"
10585
+ ),
10586
+ children: loading ? /* @__PURE__ */ jsx(Skeleton, { className: "ds:h-3 ds:w-24" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
10587
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: ", " }),
10588
+ caption
10589
+ ] })
10590
+ }
10591
+ )
10592
+ ] });
10593
+ const shared = {
10594
+ ...slot(statStripAnatomy, "item"),
10595
+ "data-glow": glowing ? "" : void 0,
10596
+ "data-intent": intent
10597
+ };
10598
+ const glowClass = "ds:data-[glow]:bg-brand-bg";
10599
+ if (toggles) {
10600
+ return /* @__PURE__ */ jsx(
10601
+ Pressable,
10602
+ {
10603
+ ref,
10604
+ pressed: pressed ?? false,
10605
+ disabled,
10606
+ onClick: (event) => {
10607
+ onClick?.(event);
10608
+ if (!event.defaultPrevented) onPressedChange?.(!(pressed ?? false));
10609
+ },
10610
+ className: cn(
10611
+ "ds:group/stat-strip-item",
10612
+ CELL_CLASSES,
10613
+ PRESSABLE_CELL_CLASSES,
10614
+ glowClass,
10615
+ SELECTED_FILL[resolvedIntent],
10616
+ className
10617
+ ),
10618
+ ...props,
10619
+ ...shared,
10620
+ children: body
10621
+ }
10622
+ );
10623
+ }
10624
+ return /* @__PURE__ */ jsx(
10625
+ "div",
10626
+ {
10627
+ ref,
10628
+ className: cn(CELL_CLASSES, glowClass, className),
10629
+ ...onClick ? { onClick } : {},
10630
+ ...props,
10631
+ ...shared,
10632
+ children: body
10633
+ }
10634
+ );
10635
+ }
10636
+ );
10637
+ StatStripItem.displayName = "StatStripItem";
10638
+ var BulkActionBarContext = React30.createContext({
10639
+ tone: "surface",
10640
+ unavailable: false
10641
+ });
10642
+ function useBulkLayer(openProp, defaultOpen, onOpenChange, disabled) {
10643
+ const { unavailable } = React30.use(BulkActionBarContext);
10644
+ const blocked = unavailable || disabled;
10645
+ const [uncontrolled, setUncontrolled] = React30.useState(defaultOpen);
10646
+ const open = openProp ?? uncontrolled;
10647
+ const setOpen = React30.useCallback(
10648
+ (next) => {
10649
+ if (next && blocked) return;
10650
+ setUncontrolled(next);
10651
+ onOpenChange?.(next);
10652
+ },
10653
+ [blocked, onOpenChange]
10654
+ );
10655
+ React30.useEffect(() => {
10656
+ if (blocked && open) setOpen(false);
10657
+ }, [blocked, open, setOpen]);
10658
+ return { open: open && !blocked, setOpen };
10659
+ }
10180
10660
  function actionClasses(tone, destructive) {
10181
10661
  const base = cn(
10182
- "ds:inline-flex ds:h-8 ds:items-center ds:gap-1.5 ds:rounded-btn ds:px-3 ds:text-[13px] ds:font-medium",
10662
+ "ds:inline-flex ds:h-8 ds:shrink-0 ds:items-center ds:gap-1.5 ds:rounded-btn ds:px-3 ds:text-[13px] ds:font-medium ds:whitespace-nowrap",
10183
10663
  "ds:transition-colors ds:duration-[var(--duration-fast)] ds:ease-[var(--ease-out-quart)]",
10184
10664
  "ds:focus-visible:outline-none ds:focus-visible:[box-shadow:var(--shadow-focus-ring)]",
10185
- "ds:disabled:pointer-events-none ds:disabled:opacity-45",
10665
+ "ds:disabled:pointer-events-none ds:disabled:opacity-45 ds:data-[loading]:opacity-100",
10666
+ // While loading, the spinner takes the leading icon's place so the row
10667
+ // does not shift.
10668
+ "ds:data-[loading]:[&>svg:first-of-type]:hidden",
10669
+ "ds:aria-disabled:opacity-45",
10186
10670
  "ds:motion-reduce:transition-none",
10187
10671
  "ds:[&_svg]:size-4 ds:[&_svg]:shrink-0"
10188
10672
  );
@@ -10199,7 +10683,7 @@ function actionClasses(tone, destructive) {
10199
10683
  destructive ? "ds:text-control-destructive-fg ds:hover:bg-control-destructive-hover-bg" : "ds:text-control-fg ds:hover:bg-control-hover-bg ds:hover:text-control-hover-fg"
10200
10684
  );
10201
10685
  }
10202
- var BulkActionBarAction = React30.forwardRef(function BulkActionBarAction2({ asChild = false, destructive, className, type, ...props }, ref) {
10686
+ var BulkActionBarAction = React30.forwardRef(function BulkActionBarAction2({ asChild = false, destructive, loading, className, type, children, ...props }, ref) {
10203
10687
  const { tone } = React30.use(BulkActionBarContext);
10204
10688
  const Comp = asChild ? Slot : "button";
10205
10689
  return /* @__PURE__ */ jsx(
@@ -10207,8 +10691,14 @@ var BulkActionBarAction = React30.forwardRef(function BulkActionBarAction2({ asC
10207
10691
  {
10208
10692
  ref,
10209
10693
  type: asChild ? void 0 : type ?? "button",
10694
+ "aria-busy": loading || void 0,
10695
+ "data-loading": loading ? "" : void 0,
10210
10696
  className: cn(actionClasses(tone, destructive), className),
10211
- ...props
10697
+ ...props,
10698
+ children: loading && !asChild ? /* @__PURE__ */ jsxs(Fragment, { children: [
10699
+ /* @__PURE__ */ jsx(Spinner, { size: "xs", "aria-hidden": "true" }),
10700
+ children
10701
+ ] }) : children
10212
10702
  }
10213
10703
  );
10214
10704
  });
@@ -10228,13 +10718,116 @@ function BulkActionBarSeparator({ className }) {
10228
10718
  );
10229
10719
  }
10230
10720
  var BAR_ENTER = "ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:motion-safe:slide-in-from-bottom-2 ds:duration-[var(--motion-duration-overlay)] ds:ease-[var(--motion-ease-enter)]";
10721
+ var PRESENCE_BASE = "ds:transition-[opacity,translate] ds:motion-reduce:transition-none";
10722
+ var PRESENCE_SHOWN = "ds:translate-y-0 ds:opacity-100 ds:duration-[var(--motion-duration-overlay)] ds:ease-[var(--motion-ease-enter)]";
10723
+ var PRESENCE_HIDDEN = "ds:pointer-events-none ds:translate-y-2 ds:opacity-0 ds:duration-[var(--motion-duration-overlay-exit)] ds:ease-[var(--motion-ease-exit)]";
10724
+ var SURFACE_CHROME = "ds:border-rule ds:bg-surface ds:text-fg ds:border";
10725
+ var FLOATING_SURFACE_CHROME = "ds:border-rule ds:bg-surface-floating ds:text-fg ds:border ds:shadow-pop";
10726
+ var INVERSE_CHROME = "ds:bg-selection-floating-bg ds:text-selection-floating-fg ds:shadow-pop";
10727
+ var ESCAPE_OWNERS = "[role='dialog'],[role='alertdialog'],[role='menu'],[role='listbox'],[data-radix-popper-content-wrapper]";
10728
+ function useEscapeToClear(active, clear) {
10729
+ const latest = React30.useRef(clear);
10730
+ React30.useEffect(() => {
10731
+ latest.current = clear;
10732
+ });
10733
+ React30.useEffect(() => {
10734
+ if (!active) return;
10735
+ const onKeyDown = (event) => {
10736
+ if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
10737
+ if (document.querySelector(ESCAPE_OWNERS)) return;
10738
+ latest.current();
10739
+ };
10740
+ document.addEventListener("keydown", onKeyDown);
10741
+ return () => document.removeEventListener("keydown", onKeyDown);
10742
+ }, [active]);
10743
+ }
10744
+ var ITEM_SELECTOR = "button, a[href], input, select, textarea, [role='button'], [role='link']";
10745
+ function toolbarItems(toolbar) {
10746
+ return Array.from(toolbar.querySelectorAll(ITEM_SELECTOR)).filter(
10747
+ (element) => element.closest("[role='toolbar']") === toolbar && !element.parentElement?.closest(ITEM_SELECTOR)
10748
+ );
10749
+ }
10750
+ var isFocusable = (element) => !element.disabled && !element.closest("fieldset:disabled");
10751
+ function useRovingToolbar(toolbar) {
10752
+ const active = React30.useRef(null);
10753
+ React30.useEffect(() => {
10754
+ if (!toolbar) return;
10755
+ const sync = () => {
10756
+ const items = toolbarItems(toolbar);
10757
+ const focusable = items.filter(isFocusable);
10758
+ if (!active.current || !focusable.includes(active.current)) active.current = focusable[0] ?? null;
10759
+ for (const item of items) {
10760
+ const next = item === active.current ? "0" : "-1";
10761
+ if (item.getAttribute("tabindex") !== next) item.setAttribute("tabindex", next);
10762
+ }
10763
+ };
10764
+ const onFocusIn = (event) => {
10765
+ const item = toolbarItems(toolbar).find((candidate) => candidate.contains(event.target));
10766
+ if (item && item !== active.current) {
10767
+ active.current = item;
10768
+ sync();
10769
+ }
10770
+ };
10771
+ const onKeyDown = (event) => {
10772
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;
10773
+ const target = event.target;
10774
+ if (target.matches("input, textarea, select, [contenteditable='true']")) return;
10775
+ const items = toolbarItems(toolbar).filter(isFocusable);
10776
+ const index = items.findIndex((item2) => item2.contains(target));
10777
+ if (index === -1) return;
10778
+ const rtl = getComputedStyle(toolbar).direction === "rtl";
10779
+ let next;
10780
+ switch (event.key) {
10781
+ case "ArrowRight":
10782
+ next = rtl ? index - 1 : index + 1;
10783
+ break;
10784
+ case "ArrowLeft":
10785
+ next = rtl ? index + 1 : index - 1;
10786
+ break;
10787
+ case "Home":
10788
+ next = 0;
10789
+ break;
10790
+ case "End":
10791
+ next = items.length - 1;
10792
+ break;
10793
+ default:
10794
+ return;
10795
+ }
10796
+ event.preventDefault();
10797
+ const item = items[(next + items.length) % items.length];
10798
+ if (!item) return;
10799
+ active.current = item;
10800
+ sync();
10801
+ item.focus();
10802
+ item.scrollIntoView?.({ block: "nearest", inline: "nearest" });
10803
+ };
10804
+ sync();
10805
+ const observer = new MutationObserver(sync);
10806
+ observer.observe(toolbar, {
10807
+ subtree: true,
10808
+ childList: true,
10809
+ attributes: true,
10810
+ attributeFilter: ["disabled"]
10811
+ });
10812
+ toolbar.addEventListener("focusin", onFocusIn);
10813
+ toolbar.addEventListener("keydown", onKeyDown);
10814
+ return () => {
10815
+ observer.disconnect();
10816
+ toolbar.removeEventListener("focusin", onFocusIn);
10817
+ toolbar.removeEventListener("keydown", onKeyDown);
10818
+ };
10819
+ }, [toolbar]);
10820
+ }
10231
10821
  function BulkActionBarClear({
10232
- variant,
10822
+ clearStyle,
10823
+ tone,
10233
10824
  clearLabel,
10234
10825
  clearDisabled,
10826
+ escapeHint,
10827
+ escapeKey,
10235
10828
  onClear
10236
10829
  }) {
10237
- if (variant === "page-floating") {
10830
+ if (clearStyle === "icon") {
10238
10831
  return /* @__PURE__ */ jsx(
10239
10832
  "button",
10240
10833
  {
@@ -10242,9 +10835,9 @@ function BulkActionBarClear({
10242
10835
  onClick: onClear,
10243
10836
  disabled: clearDisabled,
10244
10837
  className: cn(
10245
- "ds:text-selection-floating-muted-fg ds:ml-1 ds:inline-flex ds:size-7 ds:items-center ds:justify-center ds:rounded-icon",
10838
+ "ds:ml-1 ds:inline-flex ds:size-7 ds:shrink-0 ds:items-center ds:justify-center ds:rounded-icon",
10246
10839
  "ds:transition-colors ds:duration-[var(--duration-fast)] ds:ease-[var(--ease-out-quart)]",
10247
- "ds:hover:bg-selection-floating-hover-bg ds:hover:text-selection-floating-fg",
10840
+ tone === "inverse" ? "ds:text-selection-floating-muted-fg ds:hover:bg-selection-floating-hover-bg ds:hover:text-selection-floating-fg" : "ds:text-control-muted-fg ds:hover:bg-control-hover-bg ds:hover:text-control-hover-fg",
10248
10841
  "ds:focus-visible:outline-none ds:focus-visible:[box-shadow:var(--shadow-focus-ring)]",
10249
10842
  "ds:disabled:pointer-events-none ds:disabled:opacity-45 ds:motion-reduce:transition-none"
10250
10843
  ),
@@ -10253,68 +10846,304 @@ function BulkActionBarClear({
10253
10846
  }
10254
10847
  );
10255
10848
  }
10256
- return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, disabled: clearDisabled, children: clearLabel });
10849
+ if (clearStyle === "labelled") {
10850
+ return /* @__PURE__ */ jsxs(
10851
+ BulkActionBarAction,
10852
+ {
10853
+ onClick: onClear,
10854
+ disabled: clearDisabled,
10855
+ "aria-keyshortcuts": escapeHint ? "Escape" : void 0,
10856
+ className: tone === "inverse" ? void 0 : "ds:text-control-muted-fg",
10857
+ children: [
10858
+ /* @__PURE__ */ jsx(XIcon, { "aria-hidden": "true" }),
10859
+ clearLabel,
10860
+ escapeHint && /* @__PURE__ */ jsx(
10861
+ Kbd,
10862
+ {
10863
+ variant: "flat",
10864
+ "aria-hidden": "true",
10865
+ className: tone === "inverse" ? "ds:border-selection-floating-rule ds:text-selection-floating-muted-fg ds:bg-transparent" : void 0,
10866
+ children: escapeKey
10867
+ }
10868
+ )
10869
+ ]
10870
+ }
10871
+ );
10872
+ }
10873
+ return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, disabled: clearDisabled, className: "ds:shrink-0", children: clearLabel });
10257
10874
  }
10258
10875
  function BulkActionBar({
10259
10876
  variant = "table-inline",
10260
10877
  selectedCount,
10261
10878
  label,
10262
10879
  children,
10880
+ secondaryActions,
10263
10881
  onClear,
10264
10882
  clearLabel: clearLabelProp,
10265
10883
  clearDisabled,
10266
10884
  footer,
10885
+ scope,
10886
+ toolbarLabel,
10887
+ escapeToClear = true,
10888
+ keepMounted: keepMountedProp,
10889
+ tone: toneProp,
10890
+ overflow: overflowProp,
10891
+ clearStyle: clearStyleProp,
10892
+ busy = false,
10893
+ returnFocusRef,
10267
10894
  className,
10268
10895
  ...props
10269
10896
  }) {
10270
10897
  const messages = useDsMessages("bulkActionBar");
10271
- if (selectedCount === 0) return null;
10272
- const clearLabel = clearLabelProp ?? messages.clearSelection;
10273
- const labelText = label ?? `${selectedCount} selected`;
10274
- const tone = variant === "page-floating" ? "inverse" : "surface";
10275
- const content = /* @__PURE__ */ jsxs(BulkActionBarContext, { value: { tone }, children: [
10276
- /* @__PURE__ */ jsx("span", { className: "ds:text-sm ds:font-medium ds:tabular-nums ds:whitespace-nowrap", children: labelText }),
10898
+ const floating = variant === "floating";
10899
+ const responsive = Boolean(secondaryActions?.length);
10900
+ const keepMounted = keepMountedProp ?? floating;
10901
+ const tone = toneProp ?? (variant === "page-floating" ? "inverse" : "surface");
10902
+ const overflow = overflowProp ?? (floating || responsive ? "scroll" : "wrap");
10903
+ const clearStyle = clearStyleProp ?? (floating ? "labelled" : variant === "page-floating" ? "icon" : "text");
10904
+ const shown = selectedCount > 0;
10905
+ const [lastCount, setLastCount] = React30.useState(selectedCount);
10906
+ if (shown && selectedCount !== lastCount) setLastCount(selectedCount);
10907
+ const count = shown ? selectedCount : lastCount;
10908
+ const [toolbar, setToolbar] = React30.useState(null);
10909
+ useRovingToolbar(toolbar);
10910
+ const secondaryRef = React30.useRef(null);
10911
+ const measurementRef = React30.useRef(null);
10912
+ const [secondaryCollapsed, setSecondaryCollapsed] = React30.useState(false);
10913
+ const [secondaryMenuOpen, setSecondaryMenuOpen] = React30.useState(false);
10914
+ const transferSecondaryFocus = React30.useRef(false);
10915
+ React30.useLayoutEffect(() => {
10916
+ if (!toolbar || !responsive) return;
10917
+ const container = toolbar.parentElement;
10918
+ const measure = () => {
10919
+ const group = secondaryRef.current;
10920
+ const measurement2 = measurementRef.current;
10921
+ if (!container || !group || !measurement2) return;
10922
+ const style = getComputedStyle(toolbar);
10923
+ const border = parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth);
10924
+ const spacer = toolbar.querySelector("[data-bulk-spacer]");
10925
+ const spare = spacer ? Math.max(0, spacer.getBoundingClientRect().width - parseFloat(getComputedStyle(spacer).minWidth)) : 0;
10926
+ const inlineWidth = toolbar.scrollWidth + border - spare - group.getBoundingClientRect().width + measurement2.getBoundingClientRect().width;
10927
+ const collapse = inlineWidth > container.clientWidth + 1;
10928
+ if (!collapse && secondaryMenuOpen) return;
10929
+ if (collapse !== secondaryCollapsed) {
10930
+ transferSecondaryFocus.current = group.contains(document.activeElement);
10931
+ setSecondaryCollapsed(collapse);
10932
+ }
10933
+ };
10934
+ measure();
10935
+ if (typeof ResizeObserver === "undefined") return;
10936
+ const observer = new ResizeObserver(measure);
10937
+ observer.observe(toolbar);
10938
+ if (container) observer.observe(container);
10939
+ if (measurementRef.current) observer.observe(measurementRef.current);
10940
+ return () => observer.disconnect();
10941
+ }, [toolbar, responsive, secondaryActions, secondaryCollapsed, secondaryMenuOpen, tone]);
10942
+ React30.useLayoutEffect(() => {
10943
+ if (transferSecondaryFocus.current) {
10944
+ (secondaryRef.current?.querySelector("button:not(:disabled)") ?? toolbar?.querySelector("button:not(:disabled), a[href]"))?.focus();
10945
+ transferSecondaryFocus.current = false;
10946
+ }
10947
+ }, [secondaryCollapsed, toolbar]);
10948
+ const returnFocus = React30.useCallback(() => {
10949
+ returnFocusRef?.current?.focus();
10950
+ }, [returnFocusRef]);
10951
+ const clear = React30.useCallback(() => {
10952
+ onClear();
10953
+ returnFocus();
10954
+ }, [onClear, returnFocus]);
10955
+ useEscapeToClear(shown && escapeToClear && !clearDisabled && !busy, clear);
10956
+ const wasShown = React30.useRef(shown);
10957
+ const focusInside = React30.useRef(false);
10958
+ React30.useEffect(() => {
10959
+ if (wasShown.current && !shown && (focusInside.current || toolbar?.contains(document.activeElement))) {
10960
+ returnFocus();
10961
+ }
10962
+ wasShown.current = shown;
10963
+ }, [shown, returnFocus, toolbar]);
10964
+ if (!shown && !keepMounted) return null;
10965
+ const clearLabel = clearLabelProp ?? (clearStyle === "labelled" ? messages.clear : messages.clearSelection);
10966
+ const labelText = label ?? messages.selected(count);
10967
+ const announcement = shown ? messages.selected(selectedCount) : lastCount > 0 ? messages.cleared : "";
10968
+ const presence2 = keepMounted ? cn(PRESENCE_BASE, shown ? PRESENCE_SHOWN : PRESENCE_HIDDEN) : void 0;
10969
+ const enter = keepMounted ? void 0 : BAR_ENTER;
10970
+ const hiddenProps = keepMounted && !shown ? { inert: true, "aria-hidden": true } : {};
10971
+ const footerNode = keepMounted && !shown ? null : footer;
10972
+ const content = /* @__PURE__ */ jsxs(BulkActionBarContext, { value: { tone, unavailable: busy || !shown }, children: [
10973
+ /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-sm ds:font-medium ds:tabular-nums ds:whitespace-nowrap", children: labelText }),
10974
+ scope !== void 0 && scope !== null && /* @__PURE__ */ jsx(
10975
+ "fieldset",
10976
+ {
10977
+ disabled: busy || void 0,
10978
+ className: "ds:m-0 ds:flex ds:shrink-0 ds:items-center ds:gap-1 ds:border-0 ds:p-0 ds:text-sm ds:whitespace-nowrap",
10979
+ children: scope
10980
+ }
10981
+ ),
10277
10982
  /* @__PURE__ */ jsx(BulkActionBarSeparator, {}),
10278
- /* @__PURE__ */ jsx("div", { className: "ds:flex ds:flex-wrap ds:items-center ds:gap-1", children }),
10279
- /* @__PURE__ */ jsx("div", { className: "ds:min-w-2 ds:flex-1" }),
10983
+ /* @__PURE__ */ jsxs(
10984
+ "fieldset",
10985
+ {
10986
+ disabled: busy || void 0,
10987
+ className: cn(
10988
+ "ds:m-0 ds:flex ds:items-center ds:gap-1 ds:border-0 ds:p-0",
10989
+ overflow === "wrap" ? "ds:flex-wrap" : "ds:flex-nowrap"
10990
+ ),
10991
+ children: [
10992
+ children,
10993
+ responsive && /* @__PURE__ */ jsx("span", { ref: secondaryRef, className: "ds:inline-flex ds:shrink-0 ds:items-center ds:gap-1", children: secondaryCollapsed ? /* @__PURE__ */ jsx(BulkActionBarMore, { open: secondaryMenuOpen, onOpenChange: setSecondaryMenuOpen, children: secondaryActions?.map((action) => /* @__PURE__ */ jsxs(
10994
+ BulkActionBarMenuItem,
10995
+ {
10996
+ disabled: Boolean(action.disabled),
10997
+ destructive: Boolean(action.destructive),
10998
+ onSelect: action.onSelect,
10999
+ children: [
11000
+ action.icon,
11001
+ action.label
11002
+ ]
11003
+ },
11004
+ action.key
11005
+ )) }) : secondaryActions?.map((action) => /* @__PURE__ */ jsxs(
11006
+ BulkActionBarAction,
11007
+ {
11008
+ disabled: action.disabled,
11009
+ destructive: action.destructive,
11010
+ onClick: action.onSelect,
11011
+ children: [
11012
+ action.icon,
11013
+ action.label
11014
+ ]
11015
+ },
11016
+ action.key
11017
+ )) })
11018
+ ]
11019
+ }
11020
+ ),
11021
+ /* @__PURE__ */ jsx("div", { "data-bulk-spacer": "", className: "ds:min-w-2 ds:flex-1" }),
11022
+ clearStyle === "labelled" && /* @__PURE__ */ jsx(BulkActionBarSeparator, {}),
10280
11023
  /* @__PURE__ */ jsx(
10281
11024
  BulkActionBarClear,
10282
11025
  {
10283
- variant,
11026
+ clearStyle,
11027
+ tone,
10284
11028
  clearLabel,
10285
- clearDisabled,
10286
- onClear
11029
+ clearDisabled: clearDisabled || busy,
11030
+ escapeHint: escapeToClear,
11031
+ escapeKey: messages.escapeKey,
11032
+ onClear: clear
10287
11033
  }
10288
11034
  )
10289
11035
  ] });
11036
+ const measurement = responsive ? /* @__PURE__ */ jsx(
11037
+ "div",
11038
+ {
11039
+ ref: measurementRef,
11040
+ "aria-hidden": "true",
11041
+ className: "ds:pointer-events-none ds:invisible ds:absolute ds:flex ds:w-max ds:items-center ds:gap-1",
11042
+ children: secondaryActions?.map((action) => /* @__PURE__ */ jsxs("span", { className: actionClasses(tone, action.destructive), children: [
11043
+ action.icon,
11044
+ action.label
11045
+ ] }, action.key))
11046
+ }
11047
+ ) : null;
11048
+ const live = /* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "polite", "aria-atomic": "true", children: announcement });
11049
+ const toolbarProps = {
11050
+ ref: setToolbar,
11051
+ ...hiddenProps,
11052
+ role: "toolbar",
11053
+ "aria-label": toolbarLabel ?? messages.toolbar,
11054
+ "aria-orientation": "horizontal",
11055
+ "aria-busy": busy || void 0,
11056
+ "data-tone": tone,
11057
+ "data-overflow": overflow,
11058
+ // Native fieldsets do not disable anchors or custom pressables.
11059
+ onClickCapture: (event) => {
11060
+ if (busy) {
11061
+ event.preventDefault();
11062
+ event.stopPropagation();
11063
+ }
11064
+ },
11065
+ onPointerDownCapture: (event) => {
11066
+ if (busy) {
11067
+ event.preventDefault();
11068
+ event.stopPropagation();
11069
+ }
11070
+ },
11071
+ onKeyDownCapture: (event) => {
11072
+ if (busy && (event.key === "Enter" || event.key === " ")) {
11073
+ event.preventDefault();
11074
+ event.stopPropagation();
11075
+ }
11076
+ },
11077
+ onFocusCapture: () => {
11078
+ focusInside.current = true;
11079
+ },
11080
+ onBlurCapture: (event) => {
11081
+ focusInside.current = event.currentTarget.contains(event.relatedTarget);
11082
+ }
11083
+ };
11084
+ const scrollClasses = overflow === "scroll" ? "ds:flex-nowrap ds:overflow-x-auto ds:whitespace-nowrap ds:[scrollbar-width:thin]" : void 0;
10290
11085
  if (variant === "sidebar-anchored") {
10291
11086
  return /* @__PURE__ */ jsxs(
10292
11087
  "div",
10293
11088
  {
10294
11089
  className: cn(
10295
11090
  "ds:fixed ds:right-6 ds:bottom-6 ds:left-[calc(var(--shell-sidebar-w)+1.5rem)] ds:z-[var(--z-sticky)]",
11091
+ presence2,
10296
11092
  className
10297
11093
  ),
10298
11094
  ...props,
10299
11095
  children: [
10300
- /* @__PURE__ */ jsx("div", { className: cn("ds:border-rule ds:bg-surface ds:text-fg ds:flex ds:items-center ds:gap-3 ds:rounded-card ds:border ds:px-6 ds:py-3 ds:shadow-card", BAR_ENTER), children: content }),
10301
- footer
11096
+ live,
11097
+ measurement,
11098
+ /* @__PURE__ */ jsx(
11099
+ "div",
11100
+ {
11101
+ ...toolbarProps,
11102
+ className: cn(
11103
+ tone === "inverse" ? INVERSE_CHROME : cn(SURFACE_CHROME, "ds:shadow-card"),
11104
+ "ds:flex ds:items-center ds:gap-3 ds:rounded-card ds:px-6 ds:py-3",
11105
+ scrollClasses,
11106
+ enter
11107
+ ),
11108
+ children: content
11109
+ }
11110
+ ),
11111
+ footerNode
10302
11112
  ]
10303
11113
  }
10304
11114
  );
10305
11115
  }
10306
- if (variant === "page-floating") {
11116
+ if (variant === "page-floating" || floating) {
10307
11117
  return /* @__PURE__ */ jsxs(
10308
11118
  "div",
10309
11119
  {
10310
11120
  className: cn(
10311
11121
  "ds:fixed ds:bottom-4 ds:left-1/2 ds:z-[var(--z-sticky)] ds:-translate-x-1/2",
11122
+ floating && "ds:max-w-[calc(100vw-2rem)]",
11123
+ responsive && "ds:pointer-events-none ds:flex ds:w-screen ds:max-w-[calc(100vw-2rem)] ds:flex-col ds:items-center",
11124
+ presence2,
10312
11125
  className
10313
11126
  ),
10314
11127
  ...props,
10315
11128
  children: [
10316
- /* @__PURE__ */ jsx("div", { className: cn("ds:bg-selection-floating-bg ds:text-selection-floating-fg ds:flex ds:items-center ds:gap-2 ds:rounded-card ds:px-3.5 ds:py-1.5 ds:shadow-pop", BAR_ENTER), children: content }),
10317
- footer
11129
+ live,
11130
+ measurement,
11131
+ /* @__PURE__ */ jsx(
11132
+ "div",
11133
+ {
11134
+ ...toolbarProps,
11135
+ className: cn(
11136
+ tone === "inverse" ? INVERSE_CHROME : FLOATING_SURFACE_CHROME,
11137
+ "ds:flex ds:items-center ds:gap-2 ds:rounded-card ds:px-3.5 ds:py-1.5",
11138
+ floating && "ds:max-w-full",
11139
+ responsive && "ds:pointer-events-auto ds:w-max ds:max-w-full",
11140
+ scrollClasses,
11141
+ enter
11142
+ ),
11143
+ children: content
11144
+ }
11145
+ ),
11146
+ responsive && footerNode ? /* @__PURE__ */ jsx("div", { className: "ds:pointer-events-auto", children: footerNode }) : footerNode
10318
11147
  ]
10319
11148
  }
10320
11149
  );
@@ -10322,92 +11151,1628 @@ function BulkActionBar({
10322
11151
  return /* @__PURE__ */ jsxs(
10323
11152
  "div",
10324
11153
  {
10325
- className: cn("ds:sticky ds:bottom-4 ds:z-[var(--z-sticky)] ds:mt-3 ds:flex ds:justify-center", className),
11154
+ className: cn("ds:sticky ds:bottom-4 ds:z-[var(--z-sticky)] ds:mt-3 ds:flex ds:justify-center", presence2, className),
10326
11155
  ...props,
10327
11156
  children: [
10328
- /* @__PURE__ */ jsx("div", { className: cn("ds:border-rule ds:bg-surface ds:flex ds:w-fit ds:max-w-full ds:flex-wrap ds:items-center ds:gap-x-3 ds:gap-y-1.5 ds:overflow-x-auto ds:rounded-card ds:border ds:px-4 ds:py-2 ds:shadow-card", BAR_ENTER), children: content }),
10329
- footer
10330
- ]
10331
- }
10332
- );
11157
+ live,
11158
+ measurement,
11159
+ /* @__PURE__ */ jsx(
11160
+ "div",
11161
+ {
11162
+ ...toolbarProps,
11163
+ className: cn(
11164
+ tone === "inverse" ? INVERSE_CHROME : cn(SURFACE_CHROME, "ds:shadow-card"),
11165
+ "ds:flex ds:w-fit ds:max-w-full ds:items-center ds:gap-x-3 ds:gap-y-1.5 ds:overflow-x-auto ds:rounded-card ds:px-4 ds:py-2",
11166
+ overflow === "wrap" ? "ds:flex-wrap" : scrollClasses,
11167
+ enter
11168
+ ),
11169
+ children: content
11170
+ }
11171
+ ),
11172
+ footerNode
11173
+ ]
11174
+ }
11175
+ );
11176
+ }
11177
+ var SubDepthContext = React30.createContext(0);
11178
+ function BulkActionBarMenu({
11179
+ label,
11180
+ icon,
11181
+ children,
11182
+ chevron = true,
11183
+ destructive,
11184
+ disabled,
11185
+ "aria-label": ariaLabel,
11186
+ open,
11187
+ defaultOpen,
11188
+ onOpenChange,
11189
+ align = "start",
11190
+ className,
11191
+ contentClassName
11192
+ }) {
11193
+ const layer = useBulkLayer(open, defaultOpen ?? false, onOpenChange, disabled);
11194
+ return /* @__PURE__ */ jsxs(DropdownMenu, { open: layer.open, onOpenChange: layer.setOpen, children: [
11195
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(BulkActionBarAction, { destructive, "aria-label": ariaLabel, className, children: [
11196
+ icon,
11197
+ label,
11198
+ chevron && /* @__PURE__ */ jsx(ChevronDownIcon, { "aria-hidden": "true", className: "ds:-me-1 ds:opacity-60" })
11199
+ ] }) }),
11200
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align, side: "top", className: contentClassName, children: /* @__PURE__ */ jsx(SubDepthContext, { value: 0, children }) })
11201
+ ] });
11202
+ }
11203
+ function BulkActionBarMore({ label, "aria-label": ariaLabel, ...props }) {
11204
+ const messages = useDsMessages("bulkActionBar");
11205
+ const iconOnly = label === null;
11206
+ return /* @__PURE__ */ jsx(
11207
+ BulkActionBarMenu,
11208
+ {
11209
+ ...props,
11210
+ label: iconOnly ? null : label ?? messages.more,
11211
+ "aria-label": ariaLabel ?? (iconOnly ? messages.more : void 0),
11212
+ icon: /* @__PURE__ */ jsx(MoreHorizontalIcon, { "aria-hidden": "true" }),
11213
+ chevron: false,
11214
+ align: props.align ?? "end"
11215
+ }
11216
+ );
11217
+ }
11218
+ function Eligibility({ eligible, total }) {
11219
+ const messages = useDsMessages("bulkActionBar");
11220
+ if (eligible === void 0 || total === void 0 || eligible === total) return null;
11221
+ return /* @__PURE__ */ jsx("span", { className: "ds:text-fg-3 ds:ms-auto ds:ps-4 ds:text-xs ds:tabular-nums", children: messages.eligible(eligible, total) });
11222
+ }
11223
+ function Reason({ children }) {
11224
+ return /* @__PURE__ */ jsx("span", { className: "ds:text-fg-3 ds:block ds:text-xs ds:font-normal", children });
11225
+ }
11226
+ function sharedBulkValue(values) {
11227
+ if (values.length === 0) return void 0;
11228
+ const first = values[0];
11229
+ return values.every((value) => Object.is(value, first)) ? first : void 0;
11230
+ }
11231
+ function BulkActionBarMenuRadioGroup({
11232
+ options,
11233
+ selectedValues,
11234
+ onValueChange,
11235
+ total
11236
+ }) {
11237
+ const shared = sharedBulkValue(selectedValues);
11238
+ const denominator = total ?? selectedValues.length;
11239
+ return /* @__PURE__ */ jsx(DropdownMenuRadioGroup, { value: shared ?? "", onValueChange, children: options.map((option) => {
11240
+ const disabled = option.disabledReason !== void 0 || option.eligible === 0;
11241
+ return /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: option.value, disabled, className: "ds:items-start", children: /* @__PURE__ */ jsxs("span", { className: "ds:flex ds:min-w-0 ds:flex-1 ds:flex-col", children: [
11242
+ /* @__PURE__ */ jsxs("span", { className: "ds:flex ds:items-center", children: [
11243
+ option.label,
11244
+ /* @__PURE__ */ jsx(Eligibility, { eligible: option.eligible, total: denominator })
11245
+ ] }),
11246
+ option.disabledReason !== void 0 && /* @__PURE__ */ jsx(Reason, { children: option.disabledReason })
11247
+ ] }) }, option.value);
11248
+ }) });
11249
+ }
11250
+ var BulkActionBarMenuItem = React30.forwardRef(
11251
+ function BulkActionBarMenuItem2({ children, eligible, total, disabledReason, disabled, className, ...props }, ref) {
11252
+ return /* @__PURE__ */ jsx(
11253
+ DropdownMenuItem,
11254
+ {
11255
+ ref,
11256
+ disabled: disabled || disabledReason !== void 0 || eligible === 0,
11257
+ className: cn("ds:items-start", className),
11258
+ ...props,
11259
+ children: /* @__PURE__ */ jsxs("span", { className: "ds:flex ds:min-w-0 ds:flex-1 ds:flex-col", children: [
11260
+ /* @__PURE__ */ jsxs("span", { className: "ds:flex ds:items-center ds:gap-2", children: [
11261
+ children,
11262
+ /* @__PURE__ */ jsx(Eligibility, { eligible, total })
11263
+ ] }),
11264
+ disabledReason !== void 0 && /* @__PURE__ */ jsx(Reason, { children: disabledReason })
11265
+ ] })
11266
+ }
11267
+ );
11268
+ }
11269
+ );
11270
+ BulkActionBarMenuItem.displayName = "BulkActionBarMenuItem";
11271
+ function BulkActionBarMenuSub({ label, children, disabled }) {
11272
+ const depth = React30.use(SubDepthContext);
11273
+ if (depth > 0) {
11274
+ errorOnce(
11275
+ "C-BULK-SUB",
11276
+ "nested",
11277
+ "BulkActionBarMenuSub: a bulk menu has at most one Sub level. Move the deeper choices into a BulkActionBarPicker."
11278
+ );
11279
+ }
11280
+ return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
11281
+ /* @__PURE__ */ jsx(DropdownMenuSubTrigger, { disabled: disabled ?? false, children: label }),
11282
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { children: /* @__PURE__ */ jsx(SubDepthContext, { value: depth + 1, children }) })
11283
+ ] });
11284
+ }
11285
+ function BulkActionBarPicker({
11286
+ label,
11287
+ icon,
11288
+ children,
11289
+ chevron = true,
11290
+ disabled,
11291
+ "aria-label": ariaLabel,
11292
+ open: openProp,
11293
+ defaultOpen = false,
11294
+ onOpenChange,
11295
+ align = "start",
11296
+ width = "md",
11297
+ padding = "sm",
11298
+ className,
11299
+ contentClassName
11300
+ }) {
11301
+ const { open, setOpen } = useBulkLayer(openProp, defaultOpen, onOpenChange, disabled);
11302
+ const close = React30.useCallback(() => setOpen(false), [setOpen]);
11303
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
11304
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(BulkActionBarAction, { "aria-label": ariaLabel, className, children: [
11305
+ icon,
11306
+ label,
11307
+ chevron && /* @__PURE__ */ jsx(ChevronDownIcon, { "aria-hidden": "true", className: "ds:-me-1 ds:opacity-60" })
11308
+ ] }) }),
11309
+ /* @__PURE__ */ jsx(PopoverContent, { align, side: "top", width, padding, className: contentClassName, children: typeof children === "function" ? children({ close }) : children })
11310
+ ] });
10333
11311
  }
10334
11312
  var PrimaryAction = forwardRef(
10335
11313
  function PrimaryAction2({ arrow, iconRight, children, className, ...props }, ref) {
10336
11314
  return /* @__PURE__ */ jsx(
10337
11315
  Button,
10338
11316
  {
10339
- ref,
11317
+ ref,
11318
+ size: "sm",
11319
+ iconRight: arrow ? /* @__PURE__ */ jsx(ArrowRightIcon, { className: "ds:size-4" }) : iconRight,
11320
+ className: cn(className),
11321
+ ...props,
11322
+ children
11323
+ }
11324
+ );
11325
+ }
11326
+ );
11327
+ PrimaryAction.displayName = "PrimaryAction";
11328
+ var SecondaryAction = forwardRef(
11329
+ function SecondaryAction2({ className, children, ...props }, ref) {
11330
+ return /* @__PURE__ */ jsx(Button, { ref, variant: "ghost", size: "sm", className: cn(className), ...props, children });
11331
+ }
11332
+ );
11333
+ SecondaryAction.displayName = "SecondaryAction";
11334
+ var DangerAction = forwardRef(function DangerAction2({ onConfirm, className, children, ...props }, ref) {
11335
+ return /* @__PURE__ */ jsx(
11336
+ Button,
11337
+ {
11338
+ ref,
11339
+ variant: "destructive",
11340
+ size: "sm",
11341
+ onClick: onConfirm,
11342
+ className: cn(className),
11343
+ ...props,
11344
+ children
11345
+ }
11346
+ );
11347
+ });
11348
+ DangerAction.displayName = "DangerAction";
11349
+ var IconAction = forwardRef(function IconAction2({ className, children, ...props }, ref) {
11350
+ return /* @__PURE__ */ jsx(Button, { ref, variant: "ghost", size: "icon", className: cn(className), ...props, children });
11351
+ });
11352
+ IconAction.displayName = "IconAction";
11353
+ var Numeric = forwardRef(function Numeric2({ className, children, ...props }, ref) {
11354
+ return /* @__PURE__ */ jsx("span", { ref, className: cn("ds:tabular ds:font-mono", className), ...props, children });
11355
+ });
11356
+ Numeric.displayName = "Numeric";
11357
+ var MutedSpec = forwardRef(function MutedSpec2({ className, children, ...props }, ref) {
11358
+ return /* @__PURE__ */ jsx("span", { ref, className: cn("ds:font-spec ds:text-fg-3", className), ...props, children });
11359
+ });
11360
+ MutedSpec.displayName = "MutedSpec";
11361
+ var Eyebrow = forwardRef(function Eyebrow2({ numeric, className, children, ...props }, ref) {
11362
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(numeric ? "ds:eyebrow-num" : "ds:eyebrow", className), ...props, children });
11363
+ });
11364
+ Eyebrow.displayName = "Eyebrow";
11365
+ var KbdHint = forwardRef(function KbdHint2(props, ref) {
11366
+ return /* @__PURE__ */ jsx(Kbd, { ref, variant: "flat", ...props });
11367
+ });
11368
+ KbdHint.displayName = "KbdHint";
11369
+ var DISPLAY_MENU_DUE_PRESETS = [
11370
+ "overdue",
11371
+ "dueToday",
11372
+ "dueNext7",
11373
+ "dueThisMonth",
11374
+ "noDate"
11375
+ ];
11376
+ var DISPLAY_MENU_CALENDAR_PRESETS = [
11377
+ "today",
11378
+ "last7",
11379
+ "last30",
11380
+ "thisMonth",
11381
+ "lastMonth",
11382
+ "thisQuarter",
11383
+ "lastQuarter",
11384
+ "ytd"
11385
+ ];
11386
+ var DISPLAY_MENU_DATE_PRESETS = [
11387
+ ...DISPLAY_MENU_DUE_PRESETS,
11388
+ ...DISPLAY_MENU_CALENDAR_PRESETS,
11389
+ "custom"
11390
+ ];
11391
+ var DUE_SET = new Set(DISPLAY_MENU_DUE_PRESETS);
11392
+ function isDisplayMenuDuePreset(preset) {
11393
+ return DUE_SET.has(preset);
11394
+ }
11395
+ function toDisplayMenuIsoDate(date) {
11396
+ return format(date, "yyyy-MM-dd");
11397
+ }
11398
+ function toDay(value) {
11399
+ if (value == null || value === "") return null;
11400
+ const date = typeof value === "string" ? parseISO(value) : value;
11401
+ if (!isValid(date)) return null;
11402
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
11403
+ }
11404
+ function range(from, to) {
11405
+ return { from: toDisplayMenuIsoDate(from), to: toDisplayMenuIsoDate(to) };
11406
+ }
11407
+ function resolveDisplayMenuDatePreset(preset, now) {
11408
+ const today = toDay(now) ?? now;
11409
+ switch (preset) {
11410
+ case "dueToday":
11411
+ case "today":
11412
+ return range(today, today);
11413
+ case "dueNext7":
11414
+ return range(today, addDays(today, 7));
11415
+ case "dueThisMonth":
11416
+ return range(today, endOfMonth(today));
11417
+ case "last7":
11418
+ return range(addDays(today, -6), today);
11419
+ case "last30":
11420
+ return range(addDays(today, -29), today);
11421
+ case "thisMonth":
11422
+ return range(startOfMonth(today), endOfMonth(today));
11423
+ case "lastMonth": {
11424
+ const last = subMonths(today, 1);
11425
+ return range(startOfMonth(last), endOfMonth(last));
11426
+ }
11427
+ case "thisQuarter":
11428
+ return range(startOfQuarter(today), endOfQuarter(today));
11429
+ case "lastQuarter": {
11430
+ const last = subQuarters(today, 1);
11431
+ return range(startOfQuarter(last), endOfQuarter(last));
11432
+ }
11433
+ case "ytd":
11434
+ return range(startOfYear(today), today);
11435
+ default:
11436
+ return void 0;
11437
+ }
11438
+ }
11439
+ function detectDisplayMenuDatePreset(value, now, presets = DISPLAY_MENU_DATE_PRESETS) {
11440
+ if (!value || !value.from && !value.to) return "custom";
11441
+ for (const preset of presets) {
11442
+ const resolved = resolveDisplayMenuDatePreset(preset, now);
11443
+ if (resolved && resolved.from === value.from && resolved.to === value.to) return preset;
11444
+ }
11445
+ return "custom";
11446
+ }
11447
+ function effectiveDisplayMenuDatePreset(value, now, presets) {
11448
+ if (!value) return null;
11449
+ if (value.preset !== "custom") return value.preset;
11450
+ return detectDisplayMenuDatePreset(value.range, now, presets);
11451
+ }
11452
+ function matchesDisplayMenuDate(value, filter, now) {
11453
+ if (!filter) return true;
11454
+ const day = toDay(value);
11455
+ if (filter.preset === "noDate") return day === null;
11456
+ if (day === null) return false;
11457
+ const today = toDay(now) ?? now;
11458
+ if (filter.preset === "overdue") return day.getTime() < today.getTime();
11459
+ const bounds = filter.preset === "custom" ? filter.range : resolveDisplayMenuDatePreset(filter.preset, now);
11460
+ if (!bounds) return true;
11461
+ const from = toDay(bounds.from);
11462
+ const to = toDay(bounds.to);
11463
+ if (from && day.getTime() < from.getTime()) return false;
11464
+ if (to && day.getTime() > to.getTime()) return false;
11465
+ return true;
11466
+ }
11467
+ var NUMBER_PATTERN = /^-?\d*\.?\d+$|^-?\d+\.$/;
11468
+ function parseDisplayMenuNumber(input) {
11469
+ let text = input.replace(/\s+/g, "");
11470
+ if (!text) return null;
11471
+ let negative = false;
11472
+ if (text.startsWith("(") && text.endsWith(")")) {
11473
+ negative = true;
11474
+ text = text.slice(1, -1);
11475
+ } else if (text.includes("(") || text.includes(")")) {
11476
+ return null;
11477
+ }
11478
+ if (text.startsWith("-")) {
11479
+ if (negative) return null;
11480
+ negative = true;
11481
+ text = text.slice(1);
11482
+ }
11483
+ text = text.replace(/^\$/, "");
11484
+ if (text.startsWith("-")) {
11485
+ if (negative) return null;
11486
+ negative = true;
11487
+ text = text.slice(1);
11488
+ }
11489
+ if (text.includes(",")) {
11490
+ if (!/^\d{1,3}(,\d{3})+(\.\d*)?$/.test(text)) return null;
11491
+ text = text.replace(/,/g, "");
11492
+ }
11493
+ if (!NUMBER_PATTERN.test(text)) return null;
11494
+ const value = Number(text);
11495
+ if (!Number.isFinite(value)) return null;
11496
+ return negative ? -value : value;
11497
+ }
11498
+ function matchesDisplayMenuNumber(value, filter) {
11499
+ if (!filter || filter.value === null) return true;
11500
+ if (value == null || Number.isNaN(value)) return false;
11501
+ const target = filter.value;
11502
+ switch (filter.operator) {
11503
+ case "eq":
11504
+ return value === target;
11505
+ case "ne":
11506
+ return value !== target;
11507
+ case "lt":
11508
+ return value < target;
11509
+ case "lte":
11510
+ return value <= target;
11511
+ case "gt":
11512
+ return value > target;
11513
+ case "gte":
11514
+ return value >= target;
11515
+ case "between": {
11516
+ const upper = filter.to ?? null;
11517
+ const low = upper === null ? target : Math.min(target, upper);
11518
+ const high = upper === null ? Infinity : Math.max(target, upper);
11519
+ return value >= low && value <= high;
11520
+ }
11521
+ default:
11522
+ return true;
11523
+ }
11524
+ }
11525
+ function formatDisplayMenuNumber(value, formatKind = "number", locale) {
11526
+ if (formatKind === "currency") {
11527
+ return new Intl.NumberFormat(locale ?? "en-US", {
11528
+ style: "currency",
11529
+ currency: "USD",
11530
+ maximumFractionDigits: Number.isInteger(value) ? 0 : 2
11531
+ }).format(value);
11532
+ }
11533
+ return new Intl.NumberFormat(locale ?? "en-US", { maximumFractionDigits: 6 }).format(value);
11534
+ }
11535
+ var DISPLAY_MENU_NUMBER_SYMBOLS = {
11536
+ eq: "=",
11537
+ ne: "\u2260",
11538
+ lt: "<",
11539
+ lte: "\u2264",
11540
+ gt: ">",
11541
+ gte: "\u2265",
11542
+ between: "\u2194"
11543
+ };
11544
+ function matchesDisplayMenuText(value, filter) {
11545
+ const needle = filter?.text.trim().toLowerCase() ?? "";
11546
+ if (!filter || !needle) return true;
11547
+ const haystack = (value ?? "").trim().toLowerCase();
11548
+ return filter.operator === "startsWith" ? haystack.startsWith(needle) : haystack.includes(needle);
11549
+ }
11550
+ function toList(values) {
11551
+ if (values == null || values === "") return [];
11552
+ return typeof values === "string" ? [values] : values;
11553
+ }
11554
+ function matchesDisplayMenuOperator(rowValues, selected, operator = "isAnyOf") {
11555
+ const values = toList(rowValues);
11556
+ if (operator === "isEmpty") return values.length === 0;
11557
+ if (operator === "isNotEmpty") return values.length > 0;
11558
+ if (selected.size === 0) return true;
11559
+ const hit = values.some((value) => selected.has(value));
11560
+ return operator === "isNot" || operator === "isNoneOf" ? !hit : hit;
11561
+ }
11562
+ function matchesDisplayMenuTags(rowTags, selected, match = "any") {
11563
+ if (selected.size === 0) return true;
11564
+ const tags = new Set(rowTags ?? []);
11565
+ if (match === "all") {
11566
+ for (const tag of selected) if (!tags.has(tag)) return false;
11567
+ return true;
11568
+ }
11569
+ for (const tag of selected) if (tags.has(tag)) return true;
11570
+ return false;
11571
+ }
11572
+ function visibleDisplayMenuOptions(section, query = "", pinnedKeys = []) {
11573
+ const q = query.trim().toLowerCase();
11574
+ const pinnedSet = new Set(pinnedKeys);
11575
+ const isPinned = (o) => Boolean(o.pinned) || pinnedSet.has(o.key);
11576
+ const keep = (o) => !section.hideZero || o.count !== 0 || section.selected.has(o.key) || isPinned(o);
11577
+ const pinned = pinnedKeys.map((key) => section.options.find((o) => o.key === key)).filter((o) => o !== void 0);
11578
+ for (const o of section.options) if (o.pinned && !pinnedSet.has(o.key)) pinned.push(o);
11579
+ const rest = section.options.filter(
11580
+ (o) => !isPinned(o) && keep(o) && (!q || o.label.toLowerCase().includes(q))
11581
+ );
11582
+ return [...pinned, ...rest];
11583
+ }
11584
+ function displayMenuSectionOperator(section) {
11585
+ return section.operator ?? section.operators?.[0];
11586
+ }
11587
+ function isDisplayMenuSectionActive(section) {
11588
+ const spec = section.spec;
11589
+ if (spec) {
11590
+ switch (spec.kind) {
11591
+ case "sort":
11592
+ case "columns":
11593
+ return false;
11594
+ case "date":
11595
+ case "number":
11596
+ return spec.value !== null && (spec.kind === "date" || spec.value.value !== null);
11597
+ case "text":
11598
+ return spec.value !== null && spec.value.text.trim() !== "";
11599
+ case "boolean":
11600
+ return spec.value !== null;
11601
+ }
11602
+ }
11603
+ const operator = displayMenuSectionOperator(section);
11604
+ if (operator === "isEmpty" || operator === "isNotEmpty") return true;
11605
+ if (section.mode === "multi" || section.mode === "toggle") return section.selected.size > 0;
11606
+ return false;
11607
+ }
11608
+ function displayMenuOperatorLabel(operator, messages) {
11609
+ switch (operator) {
11610
+ case "is":
11611
+ return messages.operatorIs;
11612
+ case "isNot":
11613
+ return messages.operatorIsNot;
11614
+ case "isAnyOf":
11615
+ return messages.operatorIsAnyOf;
11616
+ case "isNoneOf":
11617
+ return messages.operatorIsNoneOf;
11618
+ case "isEmpty":
11619
+ return messages.operatorIsEmpty;
11620
+ case "isNotEmpty":
11621
+ return messages.operatorIsNotEmpty;
11622
+ }
11623
+ }
11624
+ function displayMenuDatePresetLabel(preset, messages) {
11625
+ return messages[preset];
11626
+ }
11627
+ function displayMenuNumberOperatorLabel(operator, messages) {
11628
+ const key = `number${operator.charAt(0).toUpperCase()}${operator.slice(1)}`;
11629
+ return messages[key];
11630
+ }
11631
+ function displayMenuOptionLabel(section, option, messages) {
11632
+ if (section.spec?.kind === "person") {
11633
+ if (option.key === section.spec.meKey) return messages.me;
11634
+ if (option.key === section.spec.unassignedKey) return messages.unassigned;
11635
+ }
11636
+ return option.label;
11637
+ }
11638
+ function joinDisplayMenuLabels(labels, messages, max = 2) {
11639
+ if (labels.length <= max) return labels.join(", ");
11640
+ return `${labels.slice(0, max).join(", ")} ${messages.more(labels.length - max)}`;
11641
+ }
11642
+ function formatDay(iso, locale) {
11643
+ const day = toDay(iso);
11644
+ if (!day) return iso;
11645
+ return new Intl.DateTimeFormat(locale ?? "en-US", {
11646
+ month: "short",
11647
+ day: "numeric",
11648
+ year: "numeric"
11649
+ }).format(day);
11650
+ }
11651
+ function summarizeDisplayMenuSection(section, messages, options = {}) {
11652
+ const { locale } = options;
11653
+ const spec = section.spec;
11654
+ if (spec?.kind === "sort") {
11655
+ if (spec.value.length === 0) return null;
11656
+ const labels2 = spec.value.map(
11657
+ (key) => spec.fields.find((field) => field.key === key.key)?.label ?? key.key
11658
+ );
11659
+ return messages.sortSummary(labels2.join(", "));
11660
+ }
11661
+ if (!isDisplayMenuSectionActive(section)) return null;
11662
+ if (spec) {
11663
+ switch (spec.kind) {
11664
+ case "date": {
11665
+ const value = spec.value;
11666
+ if (!value) return null;
11667
+ const now = options.now ?? spec.now ?? /* @__PURE__ */ new Date();
11668
+ const preset = effectiveDisplayMenuDatePreset(value, now, spec.presets);
11669
+ if (preset && preset !== "custom") return displayMenuDatePresetLabel(preset, messages);
11670
+ const from = value.range?.from;
11671
+ const to = value.range?.to;
11672
+ if (from && to) return messages.dateBetween(formatDay(from, locale), formatDay(to, locale));
11673
+ if (from) return messages.dateOnOrAfter(formatDay(from, locale));
11674
+ if (to) return messages.dateOnOrBefore(formatDay(to, locale));
11675
+ return messages.custom;
11676
+ }
11677
+ case "number": {
11678
+ const value = spec.value;
11679
+ if (!value || value.value === null) return null;
11680
+ const fmt = (n) => formatDisplayMenuNumber(n, spec.format ?? "number", locale);
11681
+ if (value.operator === "between") {
11682
+ return value.to == null ? `${DISPLAY_MENU_NUMBER_SYMBOLS.gte} ${fmt(value.value)}` : messages.numberBetweenSummary(fmt(value.value), fmt(value.to));
11683
+ }
11684
+ return `${DISPLAY_MENU_NUMBER_SYMBOLS[value.operator]} ${fmt(value.value)}`;
11685
+ }
11686
+ case "text": {
11687
+ const value = spec.value;
11688
+ if (!value) return null;
11689
+ const text = value.text.trim();
11690
+ return value.operator === "startsWith" ? messages.textStartsWithSummary(text) : messages.textContainsSummary(text);
11691
+ }
11692
+ case "boolean":
11693
+ return spec.value ? spec.trueLabel ?? messages.booleanTrue : spec.falseLabel ?? messages.booleanFalse;
11694
+ }
11695
+ }
11696
+ if (section.mode === "toggle") return section.options[0]?.label ?? null;
11697
+ const operator = displayMenuSectionOperator(section);
11698
+ if (operator === "isEmpty" || operator === "isNotEmpty") {
11699
+ return displayMenuOperatorLabel(operator, messages);
11700
+ }
11701
+ const labels = section.options.filter((option) => section.selected.has(option.key)).map((option) => displayMenuOptionLabel(section, option, messages));
11702
+ const joined = joinDisplayMenuLabels(labels, messages);
11703
+ if (spec?.kind === "tags" && spec.tagMatch === "all" && labels.length > 1) {
11704
+ return messages.tagMatchAllSummary(joined);
11705
+ }
11706
+ if (!section.operators || !operator) return joined;
11707
+ return `${displayMenuOperatorLabel(operator, messages)} ${joined}`;
11708
+ }
11709
+ var ITEM_ROLE = {
11710
+ item: "menuitem",
11711
+ checkbox: "menuitemcheckbox",
11712
+ radio: "menuitemradio",
11713
+ list: "menu"
11714
+ };
11715
+ var DISPLAY_MENU_ROW_CLASSES = "ds:flex ds:w-full ds:items-center ds:gap-2 ds:rounded-md ds:px-2 ds:py-1.5 ds:text-left ds:text-(length:--control-font-size-sm) ds:text-fg ds:transition-colors ds:hover:bg-surface-hover/60 ds:focus-visible:bg-bg-2/60 ds:focus-visible:shadow-none ds:focus-visible:outline-none ds:motion-reduce:transition-none";
11716
+ var DisplayMenuItem = forwardRef(
11717
+ function DisplayMenuItem2({ kind, checked, trailing, indicator, bare = false, className, children, ...props }, ref) {
11718
+ const isChecked = Boolean(checked);
11719
+ return /* @__PURE__ */ jsxs(
11720
+ Pressable,
11721
+ {
11722
+ ref,
11723
+ role: ITEM_ROLE[kind],
11724
+ "aria-checked": kind === "item" ? void 0 : isChecked,
11725
+ className: cn(DISPLAY_MENU_ROW_CLASSES, className),
11726
+ ...props,
11727
+ children: [
11728
+ kind === "checkbox" && indicator !== "check" && /* @__PURE__ */ jsx(
11729
+ "span",
11730
+ {
11731
+ "aria-hidden": "true",
11732
+ className: cn(
11733
+ "ds:flex ds:size-3.5 ds:shrink-0 ds:items-center ds:justify-center ds:rounded ds:border ds:transition-colors ds:motion-reduce:transition-none",
11734
+ isChecked ? "ds:border-brand ds:bg-brand ds:text-fg-on-brand" : "ds:border-rule"
11735
+ ),
11736
+ children: isChecked && /* @__PURE__ */ jsx(CheckIcon, { size: 10 })
11737
+ }
11738
+ ),
11739
+ bare ? children : /* @__PURE__ */ jsx("span", { className: "ds:min-w-0 ds:flex-1 ds:truncate", children }),
11740
+ trailing,
11741
+ (kind === "radio" || indicator === "check") && kind !== "item" && isChecked && /* @__PURE__ */ jsx(CheckIcon, { size: 14, "aria-hidden": "true", className: "ds:shrink-0 ds:text-brand" })
11742
+ ]
11743
+ }
11744
+ );
11745
+ }
11746
+ );
11747
+ DisplayMenuItem.displayName = "DisplayMenuItem";
11748
+ var DisplayMenuList = forwardRef(
11749
+ function DisplayMenuList2({ className, ...props }, ref) {
11750
+ return /* @__PURE__ */ jsx("div", { ref, role: ITEM_ROLE.list, className: cn("ds:p-1", className), ...props });
11751
+ }
11752
+ );
11753
+ DisplayMenuList.displayName = "DisplayMenuList";
11754
+ function DisplayMenuCount({ value }) {
11755
+ return /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:ps-2 ds:text-(length:--text-caption) ds:text-fg-3 ds:tabular-nums", children: value.toLocaleString() });
11756
+ }
11757
+ function useFilterMessages() {
11758
+ return useDsMessages("displayMenuFilters");
11759
+ }
11760
+ function PanelHeading({ id, children }) {
11761
+ return /* @__PURE__ */ jsx(
11762
+ "p",
11763
+ {
11764
+ id,
11765
+ className: "ds:px-2 ds:pt-2 ds:pb-1 ds:text-(length:--text-overline) ds:font-medium ds:uppercase ds:tracking-wide ds:text-fg-4",
11766
+ children
11767
+ }
11768
+ );
11769
+ }
11770
+ function useAnnouncer() {
11771
+ const [text, setText] = useState("");
11772
+ const announce = useCallback((next) => {
11773
+ setText("");
11774
+ const raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout;
11775
+ raf(() => setText(next));
11776
+ }, []);
11777
+ const region = /* @__PURE__ */ jsx(VisuallyHidden, { role: "status", "aria-live": "polite", children: text });
11778
+ return [region, announce];
11779
+ }
11780
+ function onListArrowKeys(event) {
11781
+ const keys = ["ArrowDown", "ArrowUp", "Home", "End"];
11782
+ if (!keys.includes(event.key)) return;
11783
+ const target = event.target;
11784
+ if (target.tagName === "INPUT") return;
11785
+ const items = Array.from(
11786
+ event.currentTarget.querySelectorAll("[data-dm-nav]:not([disabled])")
11787
+ );
11788
+ if (items.length === 0) return;
11789
+ const index = items.indexOf(target);
11790
+ if (index === -1) return;
11791
+ event.preventDefault();
11792
+ event.stopPropagation();
11793
+ const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? Math.min(items.length - 1, index + 1) : Math.max(0, index - 1);
11794
+ items[next]?.focus();
11795
+ }
11796
+ function keepTypingInField(event) {
11797
+ if (event.key.length === 1 || event.key === "Backspace" || event.key === "Delete") {
11798
+ event.stopPropagation();
11799
+ }
11800
+ }
11801
+ function CheckOptionRow({
11802
+ checked,
11803
+ onToggle,
11804
+ label,
11805
+ icon,
11806
+ count,
11807
+ disabled
11808
+ }) {
11809
+ const id = useId();
11810
+ return /* @__PURE__ */ jsxs(
11811
+ "div",
11812
+ {
11813
+ className: cn(
11814
+ "ds:flex ds:items-center ds:gap-2 ds:rounded-md ds:px-2 ds:py-1.5",
11815
+ "ds:transition-colors ds:hover:bg-surface-hover/60 ds:motion-reduce:transition-none"
11816
+ ),
11817
+ children: [
11818
+ /* @__PURE__ */ jsx(
11819
+ Checkbox,
11820
+ {
11821
+ id,
11822
+ "data-dm-nav": "",
11823
+ checked,
11824
+ disabled,
11825
+ onCheckedChange: onToggle
11826
+ }
11827
+ ),
11828
+ /* @__PURE__ */ jsxs(
11829
+ Label2,
11830
+ {
11831
+ htmlFor: id,
11832
+ className: cn(
11833
+ "ds:flex ds:min-w-0 ds:flex-1 ds:cursor-pointer ds:items-center ds:gap-2 ds:font-normal",
11834
+ "ds:text-(length:--control-font-size-sm)",
11835
+ checked ? "ds:text-fg" : "ds:text-fg-2"
11836
+ ),
11837
+ children: [
11838
+ icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ds:inline-flex ds:shrink-0", children: icon }),
11839
+ /* @__PURE__ */ jsx("span", { className: "ds:truncate", children: label })
11840
+ ]
11841
+ }
11842
+ ),
11843
+ typeof count === "number" && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-(length:--text-helper) ds:tabular-nums ds:text-fg-3", children: count })
11844
+ ]
11845
+ }
11846
+ );
11847
+ }
11848
+ function PanelSearch({
11849
+ value,
11850
+ onChange,
11851
+ label,
11852
+ onArrowDown
11853
+ }) {
11854
+ const messages = useFilterMessages();
11855
+ return /* @__PURE__ */ jsx("div", { className: "ds:p-1", children: /* @__PURE__ */ jsx(
11856
+ Input,
11857
+ {
11858
+ type: "search",
11859
+ size: "sm",
11860
+ value,
11861
+ onChange: (event) => onChange(event.target.value),
11862
+ onKeyDown: (event) => {
11863
+ keepTypingInField(event);
11864
+ if (event.key === "ArrowDown" && onArrowDown) {
11865
+ event.preventDefault();
11866
+ onArrowDown();
11867
+ }
11868
+ },
11869
+ "aria-label": label,
11870
+ placeholder: messages.searchPlaceholder,
11871
+ prefixIcon: /* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": "true" })
11872
+ }
11873
+ ) });
11874
+ }
11875
+ function PanelEmpty({ children }) {
11876
+ return /* @__PURE__ */ jsx("p", { className: "ds:px-2 ds:py-3 ds:text-center ds:text-(length:--text-helper) ds:text-fg-3", children });
11877
+ }
11878
+ function ChoiceList({
11879
+ label,
11880
+ labelledBy,
11881
+ choices,
11882
+ value,
11883
+ onChange,
11884
+ inline = false,
11885
+ className
11886
+ }) {
11887
+ return /* @__PURE__ */ jsx(
11888
+ RadioGroup2,
11889
+ {
11890
+ "aria-label": labelledBy ? void 0 : label,
11891
+ "aria-labelledby": labelledBy,
11892
+ orientation: inline ? "horizontal" : "vertical",
11893
+ value: value ?? "",
11894
+ onValueChange: (next) => onChange(next),
11895
+ onKeyDown: (event) => {
11896
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") event.stopPropagation();
11897
+ },
11898
+ className: cn(
11899
+ inline ? "ds:flex ds:flex-wrap ds:gap-x-3 ds:gap-y-1.5 ds:px-2 ds:py-1.5" : "ds:flex ds:flex-col ds:gap-1.5 ds:px-2 ds:py-1",
11900
+ className
11901
+ ),
11902
+ children: choices.map((choice) => /* @__PURE__ */ jsx(RadioGroupItem, { value: choice.value, label: choice.label }, choice.value))
11903
+ }
11904
+ );
11905
+ }
11906
+ function SortPanel({ spec }) {
11907
+ const messages = useFilterMessages();
11908
+ const [region, announce] = useAnnouncer();
11909
+ const [picking, setPicking] = useState(null);
11910
+ const maxKeys = spec.maxKeys ?? 2;
11911
+ const labelOf2 = (key) => spec.fields.find((f) => f.key === key)?.label ?? key;
11912
+ const used = new Set(spec.value.map((k) => k.key));
11913
+ const update = (next) => {
11914
+ spec.onChange(next);
11915
+ announce(
11916
+ next.length === 0 ? messages.sortChanged("\u2014") : messages.sortChanged(next.map((k) => labelOf2(k.key)).join(", "))
11917
+ );
11918
+ };
11919
+ const setField = (index, key) => {
11920
+ const field = spec.fields.find((f) => f.key === key);
11921
+ const next = [...spec.value];
11922
+ const direction = field?.defaultDirection ?? "asc";
11923
+ if (index >= next.length) next.push({ key, direction });
11924
+ else next[index] = { key, direction: next[index]?.direction ?? direction };
11925
+ setPicking(null);
11926
+ update(next);
11927
+ };
11928
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:p-1", children: [
11929
+ region,
11930
+ /* @__PURE__ */ jsx("ol", { "aria-label": messages.sortBy, className: "ds:flex ds:flex-col ds:gap-1", children: spec.value.map((sortKey, index) => {
11931
+ const label = labelOf2(sortKey.key);
11932
+ const position = index === 0 ? messages.sortBy : messages.sortThenBy;
11933
+ return /* @__PURE__ */ jsxs("li", { className: "ds:flex ds:flex-col ds:gap-0.5", children: [
11934
+ /* @__PURE__ */ jsx(PanelHeading, { children: position }),
11935
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:gap-1 ds:px-1", children: [
11936
+ /* @__PURE__ */ jsx(
11937
+ Button,
11938
+ {
11939
+ variant: "outline",
11940
+ size: "sm",
11941
+ "aria-label": `${messages.sortField(position)}: ${label}`,
11942
+ "aria-expanded": picking === index,
11943
+ onClick: () => setPicking(picking === index ? null : index),
11944
+ className: "ds:h-7 ds:min-w-0 ds:flex-1 ds:justify-start ds:px-2",
11945
+ children: /* @__PURE__ */ jsx("span", { className: "ds:truncate", children: label })
11946
+ }
11947
+ ),
11948
+ /* @__PURE__ */ jsx(
11949
+ IconButton,
11950
+ {
11951
+ variant: "ghost",
11952
+ intent: "neutral",
11953
+ size: "xs",
11954
+ tooltip: false,
11955
+ label: `${messages.sortDirection(label)}: ${sortKey.direction === "asc" ? messages.ascending : messages.descending}`,
11956
+ icon: sortKey.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUpIcon, {}) : /* @__PURE__ */ jsx(ArrowDownIcon, {}),
11957
+ onClick: () => update(
11958
+ spec.value.map(
11959
+ (k, i) => i === index ? { ...k, direction: k.direction === "asc" ? "desc" : "asc" } : k
11960
+ )
11961
+ )
11962
+ }
11963
+ ),
11964
+ index > 0 && /* @__PURE__ */ jsx(
11965
+ IconButton,
11966
+ {
11967
+ variant: "ghost",
11968
+ intent: "neutral",
11969
+ size: "xs",
11970
+ tooltip: false,
11971
+ label: messages.makePrimary(label),
11972
+ icon: /* @__PURE__ */ jsx(ArrowUpIcon, {}),
11973
+ onClick: () => {
11974
+ const next = [...spec.value];
11975
+ const [moved] = next.splice(index, 1);
11976
+ if (moved) next.unshift(moved);
11977
+ update(next);
11978
+ }
11979
+ }
11980
+ ),
11981
+ /* @__PURE__ */ jsx(
11982
+ IconButton,
11983
+ {
11984
+ variant: "ghost",
11985
+ intent: "neutral",
11986
+ size: "xs",
11987
+ tooltip: false,
11988
+ label: messages.removeSort(label),
11989
+ icon: /* @__PURE__ */ jsx(XIcon, {}),
11990
+ onClick: () => update(spec.value.filter((_, i) => i !== index))
11991
+ }
11992
+ )
11993
+ ] }),
11994
+ picking === index && /* @__PURE__ */ jsx(
11995
+ FieldPicker,
11996
+ {
11997
+ fields: spec.fields.filter((f) => f.key === sortKey.key || !used.has(f.key)),
11998
+ selected: sortKey.key,
11999
+ label: messages.chooseField,
12000
+ onPick: (key) => setField(index, key)
12001
+ }
12002
+ )
12003
+ ] }, sortKey.key);
12004
+ }) }),
12005
+ spec.value.length < maxKeys && spec.fields.some((f) => !used.has(f.key)) && /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:px-1 ds:pt-1", children: [
12006
+ /* @__PURE__ */ jsxs(
12007
+ Button,
12008
+ {
12009
+ variant: "ghost",
12010
+ size: "sm",
12011
+ "aria-expanded": picking === spec.value.length,
12012
+ onClick: () => setPicking(picking === spec.value.length ? null : spec.value.length),
12013
+ className: "ds:h-7 ds:justify-start ds:gap-1.5 ds:px-2 ds:text-fg-3 ds:hover:text-fg",
12014
+ children: [
12015
+ /* @__PURE__ */ jsx(PlusIcon, { size: 13, "aria-hidden": "true" }),
12016
+ messages.addSort
12017
+ ]
12018
+ }
12019
+ ),
12020
+ picking === spec.value.length && /* @__PURE__ */ jsx(
12021
+ FieldPicker,
12022
+ {
12023
+ fields: spec.fields.filter((f) => !used.has(f.key)),
12024
+ label: messages.chooseField,
12025
+ onPick: (key) => setField(spec.value.length, key)
12026
+ }
12027
+ )
12028
+ ] })
12029
+ ] });
12030
+ }
12031
+ function FieldPicker({
12032
+ fields,
12033
+ selected,
12034
+ label,
12035
+ onPick
12036
+ }) {
12037
+ return /* @__PURE__ */ jsx("div", { className: "ds:mx-1 ds:mt-1 ds:max-h-48 ds:overflow-y-auto ds:rounded-input ds:border ds:border-rule", children: /* @__PURE__ */ jsx(
12038
+ ChoiceList,
12039
+ {
12040
+ label,
12041
+ choices: fields.map((f) => ({ value: f.key, label: f.label })),
12042
+ value: selected ?? null,
12043
+ onChange: onPick
12044
+ }
12045
+ ) });
12046
+ }
12047
+ function resolveColumnOrder(columns, order) {
12048
+ const known = new Set(columns.map((c) => c.key));
12049
+ const seen = /* @__PURE__ */ new Set();
12050
+ const out = [];
12051
+ for (const key of order) {
12052
+ if (!known.has(key) || seen.has(key)) continue;
12053
+ seen.add(key);
12054
+ out.push(key);
12055
+ }
12056
+ for (const c of columns) if (!seen.has(c.key)) out.push(c.key);
12057
+ return out;
12058
+ }
12059
+ function moveColumn(order, key, delta, isLocked) {
12060
+ const from = order.indexOf(key);
12061
+ const to = from + delta;
12062
+ if (from === -1 || isLocked(key) || to < 0 || to >= order.length) return null;
12063
+ const step = delta > 0 ? 1 : -1;
12064
+ for (let i = from + step; i !== to + step; i += step) {
12065
+ const passed = order[i];
12066
+ if (passed !== void 0 && isLocked(passed)) return null;
12067
+ }
12068
+ const next = [...order];
12069
+ next.splice(from, 1);
12070
+ next.splice(to, 0, key);
12071
+ return next;
12072
+ }
12073
+ function ColumnsPanel({ spec }) {
12074
+ const messages = useFilterMessages();
12075
+ const [region, announce] = useAnnouncer();
12076
+ const [dragging, setDragging] = useState(null);
12077
+ const rowRefs = useRef(/* @__PURE__ */ new Map());
12078
+ const order = resolveColumnOrder(spec.columns, spec.order);
12079
+ const byKey = new Map(spec.columns.map((c) => [c.key, c]));
12080
+ const isLocked = (key) => Boolean(byKey.get(key)?.locked);
12081
+ const move = (key, delta, refocus = true) => {
12082
+ const next = moveColumn(order, key, delta, isLocked);
12083
+ if (!next) return;
12084
+ spec.onOrderChange(next);
12085
+ announce(messages.columnMoved(byKey.get(key)?.label ?? key, next.indexOf(key) + 1, next.length));
12086
+ if (refocus) requestAnimationFrame(() => rowRefs.current.get(key)?.focus());
12087
+ };
12088
+ const toggleHidden = (key) => {
12089
+ const next = new Set(spec.hidden);
12090
+ const label = byKey.get(key)?.label ?? key;
12091
+ if (next.has(key)) {
12092
+ next.delete(key);
12093
+ announce(messages.columnShown(label));
12094
+ } else {
12095
+ next.add(key);
12096
+ announce(messages.columnHidden(label));
12097
+ }
12098
+ spec.onHiddenChange(next);
12099
+ };
12100
+ const onDrop = (event, target) => {
12101
+ event.preventDefault();
12102
+ if (!dragging || dragging === target) return;
12103
+ const delta = order.indexOf(target) - order.indexOf(dragging);
12104
+ move(dragging, delta, false);
12105
+ setDragging(null);
12106
+ };
12107
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col", children: [
12108
+ region,
12109
+ /* @__PURE__ */ jsx("ul", { "aria-label": messages.columnsList, className: "ds:flex ds:flex-col ds:p-1", children: order.map((key, index) => {
12110
+ const column = byKey.get(key);
12111
+ if (!column) return null;
12112
+ const locked = Boolean(column.locked);
12113
+ const hideable = !locked && column.hideable !== false;
12114
+ const canUp = moveColumn(order, key, -1, isLocked) !== null;
12115
+ const canDown = moveColumn(order, key, 1, isLocked) !== null;
12116
+ return (
12117
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- Alt+Arrow reorder and drop target on the focusable row; the checkbox and move buttons are the named controls
12118
+ /* @__PURE__ */ jsxs(
12119
+ "li",
12120
+ {
12121
+ ref: (el) => {
12122
+ if (el) rowRefs.current.set(key, el);
12123
+ else rowRefs.current.delete(key);
12124
+ },
12125
+ tabIndex: -1,
12126
+ "aria-label": `${column.label}, ${index + 1} / ${order.length}${locked ? `, ${messages.locked}` : ""}`,
12127
+ onDragStart: (event) => {
12128
+ if (!event.target.closest("[data-dm-grip]")) {
12129
+ event.preventDefault();
12130
+ return;
12131
+ }
12132
+ event.dataTransfer.effectAllowed = "move";
12133
+ setDragging(key);
12134
+ },
12135
+ onDragEnd: () => setDragging(null),
12136
+ onDragOver: (event) => {
12137
+ if (dragging && !locked) event.preventDefault();
12138
+ },
12139
+ onDrop: (event) => onDrop(event, key),
12140
+ onKeyDown: (event) => {
12141
+ if (event.altKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
12142
+ event.preventDefault();
12143
+ event.stopPropagation();
12144
+ move(key, event.key === "ArrowUp" ? -1 : 1);
12145
+ }
12146
+ },
12147
+ className: cn(
12148
+ "ds:flex ds:items-center ds:gap-1.5 ds:rounded-md ds:px-1 ds:py-1",
12149
+ "ds:transition-colors ds:hover:bg-surface-hover/60 ds:motion-reduce:transition-none",
12150
+ "ds:focus-visible:outline-none ds:focus-visible:[box-shadow:var(--shadow-focus-ring)]",
12151
+ dragging === key && "ds:opacity-50"
12152
+ ),
12153
+ children: [
12154
+ /* @__PURE__ */ jsx(
12155
+ "span",
12156
+ {
12157
+ "data-dm-grip": "",
12158
+ draggable: !locked,
12159
+ "aria-hidden": "true",
12160
+ className: cn(
12161
+ "ds:inline-flex ds:shrink-0 ds:text-fg-4",
12162
+ locked ? "ds:invisible" : "ds:cursor-grab"
12163
+ ),
12164
+ children: /* @__PURE__ */ jsx(GripVerticalIcon, { size: 14 })
12165
+ }
12166
+ ),
12167
+ /* @__PURE__ */ jsx(
12168
+ Checkbox,
12169
+ {
12170
+ checked: !spec.hidden.has(key),
12171
+ disabled: !hideable,
12172
+ "aria-label": messages.showColumn(column.label),
12173
+ onCheckedChange: () => toggleHidden(key),
12174
+ onKeyDown: (event) => {
12175
+ if (event.altKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
12176
+ event.preventDefault();
12177
+ event.stopPropagation();
12178
+ move(key, event.key === "ArrowUp" ? -1 : 1);
12179
+ }
12180
+ }
12181
+ }
12182
+ ),
12183
+ /* @__PURE__ */ jsx("span", { className: "ds:min-w-0 ds:flex-1 ds:truncate ds:text-(length:--control-font-size-sm) ds:text-fg", children: column.label }),
12184
+ locked ? /* @__PURE__ */ jsx(LockIcon, { size: 13, "aria-label": messages.locked, className: "ds:shrink-0 ds:text-fg-4" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12185
+ /* @__PURE__ */ jsx(
12186
+ IconButton,
12187
+ {
12188
+ variant: "ghost",
12189
+ intent: "neutral",
12190
+ size: "xs",
12191
+ tooltip: false,
12192
+ label: messages.moveUp(column.label),
12193
+ icon: /* @__PURE__ */ jsx(ArrowUpIcon, {}),
12194
+ disabled: !canUp,
12195
+ onClick: () => move(key, -1)
12196
+ }
12197
+ ),
12198
+ /* @__PURE__ */ jsx(
12199
+ IconButton,
12200
+ {
12201
+ variant: "ghost",
12202
+ intent: "neutral",
12203
+ size: "xs",
12204
+ tooltip: false,
12205
+ label: messages.moveDown(column.label),
12206
+ icon: /* @__PURE__ */ jsx(ArrowDownIcon, {}),
12207
+ disabled: !canDown,
12208
+ onClick: () => move(key, 1)
12209
+ }
12210
+ )
12211
+ ] })
12212
+ ]
12213
+ },
12214
+ key
12215
+ )
12216
+ );
12217
+ }) }),
12218
+ spec.onReset && /* @__PURE__ */ jsx("div", { className: "ds:flex ds:justify-end ds:border-t ds:border-rule ds:p-1", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: spec.onReset, className: "ds:h-6 ds:px-2 ds:text-fg-3", children: messages.resetColumns }) })
12219
+ ] });
12220
+ }
12221
+ var SEARCH_THRESHOLD = 8;
12222
+ function DisplayMenuOperatorPicker({ section }) {
12223
+ const messages = useFilterMessages();
12224
+ const operators = section.operators ?? [];
12225
+ const current = displayMenuSectionOperator(section) ?? null;
12226
+ if (operators.length < 2) return null;
12227
+ return /* @__PURE__ */ jsx("div", { className: "ds:shrink-0 ds:border-b ds:border-rule", children: /* @__PURE__ */ jsx(
12228
+ ChoiceList,
12229
+ {
12230
+ inline: true,
12231
+ label: messages.operator,
12232
+ choices: operators.map((op) => ({ value: op, label: displayMenuOperatorLabel(op, messages) })),
12233
+ value: current,
12234
+ onChange: (op) => section.onOperatorChange?.(op)
12235
+ }
12236
+ ) });
12237
+ }
12238
+ function DisplayMenuSpecPanel({ section, onDone }) {
12239
+ const spec = section.spec;
12240
+ if (!spec) return null;
12241
+ switch (spec.kind) {
12242
+ case "person":
12243
+ case "tags":
12244
+ return /* @__PURE__ */ jsx(ValueListPanel, { section, spec });
12245
+ case "date":
12246
+ return /* @__PURE__ */ jsx(DatePanel, { spec });
12247
+ case "number":
12248
+ return /* @__PURE__ */ jsx(NumberPanel, { spec });
12249
+ case "text":
12250
+ return /* @__PURE__ */ jsx(TextPanel, { spec, label: section.label });
12251
+ case "boolean":
12252
+ return /* @__PURE__ */ jsx(BooleanPanel, { spec, label: section.label, onDone });
12253
+ case "sort":
12254
+ return /* @__PURE__ */ jsx(SortPanel, { spec });
12255
+ case "columns":
12256
+ return /* @__PURE__ */ jsx(ColumnsPanel, { spec });
12257
+ default:
12258
+ return null;
12259
+ }
12260
+ }
12261
+ function ValueListPanel({
12262
+ section,
12263
+ spec
12264
+ }) {
12265
+ const messages = useFilterMessages();
12266
+ const [query, setQuery] = useState("");
12267
+ const listRef = useRef(null);
12268
+ const operator = displayMenuSectionOperator(section);
12269
+ const emptiness = operator === "isEmpty" || operator === "isNotEmpty";
12270
+ const pinnedKeys = spec.kind === "person" ? [spec.meKey, spec.unassignedKey].filter((k) => k !== void 0) : [];
12271
+ const options = visibleDisplayMenuOptions(section, query, pinnedKeys);
12272
+ const searchable = section.searchable ?? section.options.length > SEARCH_THRESHOLD;
12273
+ const matchId = useId();
12274
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col", children: [
12275
+ spec.kind === "tags" && /* @__PURE__ */ jsxs("div", { className: "ds:border-b ds:border-rule ds:pb-1", children: [
12276
+ /* @__PURE__ */ jsx(PanelHeading, { id: matchId, children: messages.tagMatch }),
12277
+ /* @__PURE__ */ jsx(
12278
+ ChoiceList,
12279
+ {
12280
+ inline: true,
12281
+ labelledBy: matchId,
12282
+ choices: [
12283
+ { value: "any", label: messages.tagMatchAny },
12284
+ { value: "all", label: messages.tagMatchAll }
12285
+ ],
12286
+ value: spec.tagMatch,
12287
+ onChange: spec.onTagMatchChange
12288
+ }
12289
+ )
12290
+ ] }),
12291
+ !emptiness && /* @__PURE__ */ jsxs(Fragment, { children: [
12292
+ searchable && /* @__PURE__ */ jsx(
12293
+ PanelSearch,
12294
+ {
12295
+ value: query,
12296
+ onChange: setQuery,
12297
+ label: messages.search(section.label),
12298
+ onArrowDown: () => listRef.current?.querySelector("[data-dm-nav]")?.focus()
12299
+ }
12300
+ ),
12301
+ /* @__PURE__ */ jsx("div", { ref: listRef, onKeyDown: onListArrowKeys, className: "ds:p-1", children: options.length === 0 ? /* @__PURE__ */ jsx(PanelEmpty, { children: messages.noMatches }) : options.map((option) => /* @__PURE__ */ jsx(
12302
+ CheckOptionRow,
12303
+ {
12304
+ checked: section.selected.has(option.key),
12305
+ onToggle: () => section.onSelect(option.key),
12306
+ label: displayMenuOptionLabel(section, option, messages),
12307
+ icon: option.icon,
12308
+ count: option.count
12309
+ },
12310
+ option.key
12311
+ )) }),
12312
+ section.onClear && section.selected.size > 0 && /* @__PURE__ */ jsx(PanelFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: section.onClear, className: "ds:h-6 ds:px-2 ds:text-fg-3", children: messages.clear }) })
12313
+ ] })
12314
+ ] });
12315
+ }
12316
+ function PanelFooter({ children }) {
12317
+ return /* @__PURE__ */ jsx("div", { className: "ds:flex ds:items-center ds:justify-end ds:gap-1 ds:border-t ds:border-rule ds:p-1", children });
12318
+ }
12319
+ function DatePanel({ spec }) {
12320
+ const messages = useFilterMessages();
12321
+ const now = spec.now ?? /* @__PURE__ */ new Date();
12322
+ const presets = spec.presets ?? DISPLAY_MENU_DATE_PRESETS;
12323
+ const active = effectiveDisplayMenuDatePreset(spec.value, now, presets);
12324
+ const [showCustom, setShowCustom] = useState(active === "custom");
12325
+ const dueId = useId();
12326
+ const calendarId = useId();
12327
+ const fromId = useId();
12328
+ const toId = useId();
12329
+ const due = presets.filter((p) => isDisplayMenuDuePreset(p));
12330
+ const calendar = presets.filter(
12331
+ (p) => p !== "custom" && DISPLAY_MENU_CALENDAR_PRESETS.includes(p)
12332
+ );
12333
+ const offersCustom = presets.includes("custom");
12334
+ const choose = (preset) => {
12335
+ if (preset === "custom") {
12336
+ setShowCustom(true);
12337
+ spec.onChange({ preset: "custom", range: spec.value?.range });
12338
+ return;
12339
+ }
12340
+ setShowCustom(false);
12341
+ spec.onChange({ preset, range: resolveDisplayMenuDatePreset(preset, now) });
12342
+ };
12343
+ const toChoices = (list) => list.map((p) => ({ value: p, label: displayMenuDatePresetLabel(p, messages) }));
12344
+ const range2 = spec.value?.range ?? {};
12345
+ const setRange = (next) => spec.onChange({ preset: "custom", range: { ...range2, ...next } });
12346
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:p-1", children: [
12347
+ due.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
12348
+ /* @__PURE__ */ jsx(PanelHeading, { id: dueId, children: messages.dateDue }),
12349
+ /* @__PURE__ */ jsx(ChoiceList, { labelledBy: dueId, choices: toChoices(due), value: active, onChange: choose })
12350
+ ] }),
12351
+ (calendar.length > 0 || offersCustom) && /* @__PURE__ */ jsxs(Fragment, { children: [
12352
+ /* @__PURE__ */ jsx(PanelHeading, { id: calendarId, children: messages.dateCalendar }),
12353
+ /* @__PURE__ */ jsx(
12354
+ ChoiceList,
12355
+ {
12356
+ labelledBy: calendarId,
12357
+ choices: toChoices(offersCustom ? [...calendar, "custom"] : calendar),
12358
+ value: showCustom ? "custom" : active,
12359
+ onChange: choose
12360
+ }
12361
+ )
12362
+ ] }),
12363
+ showCustom && /* @__PURE__ */ jsxs("div", { className: "ds:grid ds:grid-cols-2 ds:gap-2 ds:px-2 ds:pt-2 ds:pb-1", children: [
12364
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:gap-1", children: [
12365
+ /* @__PURE__ */ jsx(Label2, { htmlFor: fromId, className: "ds:text-(length:--text-helper) ds:text-fg-3", children: messages.dateFrom }),
12366
+ /* @__PURE__ */ jsx(
12367
+ Input,
12368
+ {
12369
+ id: fromId,
12370
+ type: "date",
12371
+ size: "sm",
12372
+ value: range2.from ?? "",
12373
+ max: range2.to,
12374
+ onKeyDown: keepTypingInField,
12375
+ onChange: (e) => setRange({ from: e.target.value || void 0 })
12376
+ }
12377
+ )
12378
+ ] }),
12379
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:gap-1", children: [
12380
+ /* @__PURE__ */ jsx(Label2, { htmlFor: toId, className: "ds:text-(length:--text-helper) ds:text-fg-3", children: messages.dateTo }),
12381
+ /* @__PURE__ */ jsx(
12382
+ Input,
12383
+ {
12384
+ id: toId,
12385
+ type: "date",
12386
+ size: "sm",
12387
+ value: range2.to ?? "",
12388
+ min: range2.from,
12389
+ onKeyDown: keepTypingInField,
12390
+ onChange: (e) => setRange({ to: e.target.value || void 0 })
12391
+ }
12392
+ )
12393
+ ] })
12394
+ ] }),
12395
+ spec.value && /* @__PURE__ */ jsx(PanelFooter, { children: /* @__PURE__ */ jsx(
12396
+ Button,
12397
+ {
12398
+ variant: "ghost",
10340
12399
  size: "sm",
10341
- iconRight: arrow ? /* @__PURE__ */ jsx(ArrowRightIcon, { className: "ds:size-4" }) : iconRight,
10342
- className: cn(className),
10343
- ...props,
10344
- children
12400
+ onClick: () => {
12401
+ setShowCustom(false);
12402
+ spec.onChange(null);
12403
+ },
12404
+ className: "ds:h-6 ds:px-2 ds:text-fg-3",
12405
+ children: messages.clear
10345
12406
  }
12407
+ ) })
12408
+ ] });
12409
+ }
12410
+ var NUMBER_OPERATORS = ["eq", "ne", "lt", "lte", "gt", "gte", "between"];
12411
+ function NumberPanel({ spec }) {
12412
+ const messages = useFilterMessages();
12413
+ const operator = spec.value?.operator ?? "gt";
12414
+ const show = (n) => n === null || n === void 0 ? "" : formatDisplayMenuNumber(n, spec.format ?? "number");
12415
+ const [draft, setDraft] = useState({ value: show(spec.value?.value), to: show(spec.value?.to) });
12416
+ const [invalid, setInvalid] = useState({ value: false, to: false });
12417
+ const valueId = useId();
12418
+ const toId = useId();
12419
+ const errorId = useId();
12420
+ const commit = (nextOperator, nextDraft = draft) => {
12421
+ const value = nextDraft.value.trim() === "" ? null : parseDisplayMenuNumber(nextDraft.value);
12422
+ const to = nextDraft.to.trim() === "" ? null : parseDisplayMenuNumber(nextDraft.to);
12423
+ const bad = {
12424
+ value: nextDraft.value.trim() !== "" && value === null,
12425
+ to: nextOperator === "between" && nextDraft.to.trim() !== "" && to === null
12426
+ };
12427
+ setInvalid(bad);
12428
+ if (bad.value || bad.to) return;
12429
+ if (value === null) {
12430
+ spec.onChange(null);
12431
+ return;
12432
+ }
12433
+ spec.onChange(
12434
+ nextOperator === "between" ? { operator: nextOperator, value, to } : { operator: nextOperator, value }
10346
12435
  );
10347
- }
10348
- );
10349
- PrimaryAction.displayName = "PrimaryAction";
10350
- var SecondaryAction = forwardRef(
10351
- function SecondaryAction2({ className, children, ...props }, ref) {
10352
- return /* @__PURE__ */ jsx(Button, { ref, variant: "ghost", size: "sm", className: cn(className), ...props, children });
10353
- }
10354
- );
10355
- SecondaryAction.displayName = "SecondaryAction";
10356
- var DangerAction = forwardRef(function DangerAction2({ onConfirm, className, children, ...props }, ref) {
10357
- return /* @__PURE__ */ jsx(
10358
- Button,
12436
+ };
12437
+ const field = (key, id, label) => /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:gap-1", children: [
12438
+ /* @__PURE__ */ jsx(Label2, { htmlFor: id, className: "ds:text-(length:--text-helper) ds:text-fg-3", children: label }),
12439
+ /* @__PURE__ */ jsx(
12440
+ Input,
12441
+ {
12442
+ id,
12443
+ size: "sm",
12444
+ inputMode: "decimal",
12445
+ value: draft[key],
12446
+ "aria-invalid": invalid[key] || void 0,
12447
+ "aria-describedby": invalid[key] ? errorId : void 0,
12448
+ onChange: (e) => setDraft((d) => ({ ...d, [key]: e.target.value })),
12449
+ onBlur: () => commit(operator),
12450
+ onKeyDown: (e) => {
12451
+ keepTypingInField(e);
12452
+ if (e.key === "Enter") {
12453
+ e.preventDefault();
12454
+ commit(operator);
12455
+ }
12456
+ }
12457
+ }
12458
+ )
12459
+ ] });
12460
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:p-1", children: [
12461
+ /* @__PURE__ */ jsx(
12462
+ ChoiceList,
12463
+ {
12464
+ inline: true,
12465
+ label: messages.operator,
12466
+ choices: NUMBER_OPERATORS.map((op) => ({ value: op, label: displayMenuNumberOperatorLabel(op, messages) })),
12467
+ value: operator,
12468
+ onChange: (op) => commit(op)
12469
+ }
12470
+ ),
12471
+ /* @__PURE__ */ jsxs("div", { className: cn("ds:grid ds:gap-2 ds:px-1 ds:pt-1 ds:pb-1", operator === "between" ? "ds:grid-cols-2" : "ds:grid-cols-1"), children: [
12472
+ field("value", valueId, operator === "between" ? messages.numberMin : messages.numberValue),
12473
+ operator === "between" && field("to", toId, messages.numberMax)
12474
+ ] }),
12475
+ (invalid.value || invalid.to) && /* @__PURE__ */ jsx("p", { id: errorId, className: "ds:px-2 ds:pb-1 ds:text-(length:--text-helper) ds:text-danger-fg", children: messages.numberInvalid }),
12476
+ spec.value && /* @__PURE__ */ jsx(PanelFooter, { children: /* @__PURE__ */ jsx(
12477
+ Button,
12478
+ {
12479
+ variant: "ghost",
12480
+ size: "sm",
12481
+ onClick: () => {
12482
+ setDraft({ value: "", to: "" });
12483
+ setInvalid({ value: false, to: false });
12484
+ spec.onChange(null);
12485
+ },
12486
+ className: "ds:h-6 ds:px-2 ds:text-fg-3",
12487
+ children: messages.clear
12488
+ }
12489
+ ) })
12490
+ ] });
12491
+ }
12492
+ function TextPanel({
12493
+ spec,
12494
+ label
12495
+ }) {
12496
+ const messages = useFilterMessages();
12497
+ const operator = spec.value?.operator ?? "contains";
12498
+ const text = spec.value?.text ?? "";
12499
+ const set = (next) => {
12500
+ const value = { operator: next.operator ?? operator, text: next.text ?? text };
12501
+ spec.onChange(value.text === "" ? next.operator ? { ...value } : null : value);
12502
+ };
12503
+ return /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:flex-col ds:p-1", children: [
12504
+ /* @__PURE__ */ jsx(
12505
+ ChoiceList,
12506
+ {
12507
+ inline: true,
12508
+ label: messages.operator,
12509
+ choices: [
12510
+ { value: "contains", label: messages.textContains },
12511
+ { value: "startsWith", label: messages.textStartsWith }
12512
+ ],
12513
+ value: operator,
12514
+ onChange: (op) => set({ operator: op })
12515
+ }
12516
+ ),
12517
+ /* @__PURE__ */ jsx("div", { className: "ds:px-1 ds:pt-1 ds:pb-1", children: /* @__PURE__ */ jsx(
12518
+ Input,
12519
+ {
12520
+ size: "sm",
12521
+ value: text,
12522
+ "aria-label": `${label}, ${messages.textValue}`,
12523
+ placeholder: spec.placeholder,
12524
+ onKeyDown: keepTypingInField,
12525
+ onChange: (e) => set({ text: e.target.value })
12526
+ }
12527
+ ) })
12528
+ ] });
12529
+ }
12530
+ function BooleanPanel({
12531
+ spec,
12532
+ label,
12533
+ onDone
12534
+ }) {
12535
+ const messages = useFilterMessages();
12536
+ const value = spec.value === null ? "any" : spec.value ? "true" : "false";
12537
+ return /* @__PURE__ */ jsx("div", { className: "ds:p-1", children: /* @__PURE__ */ jsx(
12538
+ ChoiceList,
10359
12539
  {
10360
- ref,
10361
- variant: "destructive",
10362
- size: "sm",
10363
- onClick: onConfirm,
10364
- className: cn(className),
10365
- ...props,
10366
- children
12540
+ label,
12541
+ choices: [
12542
+ { value: "any", label: messages.booleanAny },
12543
+ { value: "true", label: spec.trueLabel ?? messages.booleanTrue },
12544
+ { value: "false", label: spec.falseLabel ?? messages.booleanFalse }
12545
+ ],
12546
+ value,
12547
+ onChange: (next) => {
12548
+ spec.onChange(next === "any" ? null : next === "true");
12549
+ if (typeof window !== "undefined" && window.event instanceof MouseEvent) onDone?.();
12550
+ }
10367
12551
  }
12552
+ ) });
12553
+ }
12554
+ var MENU_ITEM_SELECTOR = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
12555
+ var FOCUSABLE_SELECTOR = 'input:not([disabled]),button:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
12556
+ var TYPEAHEAD_RESET_MS = 500;
12557
+ function menuItems(container) {
12558
+ return Array.from(container.querySelectorAll(MENU_ITEM_SELECTOR)).filter(
12559
+ (item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true" && item.getAttribute("data-disabled") === null
10368
12560
  );
10369
- });
10370
- DangerAction.displayName = "DangerAction";
10371
- var IconAction = forwardRef(function IconAction2({ className, children, ...props }, ref) {
10372
- return /* @__PURE__ */ jsx(Button, { ref, variant: "ghost", size: "icon", className: cn(className), ...props, children });
10373
- });
10374
- IconAction.displayName = "IconAction";
10375
- var Numeric = forwardRef(function Numeric2({ className, children, ...props }, ref) {
10376
- return /* @__PURE__ */ jsx("span", { ref, className: cn("ds:tabular ds:font-mono", className), ...props, children });
10377
- });
10378
- Numeric.displayName = "Numeric";
10379
- var MutedSpec = forwardRef(function MutedSpec2({ className, children, ...props }, ref) {
10380
- return /* @__PURE__ */ jsx("span", { ref, className: cn("ds:font-spec ds:text-fg-3", className), ...props, children });
10381
- });
10382
- MutedSpec.displayName = "MutedSpec";
10383
- var Eyebrow = forwardRef(function Eyebrow2({ numeric, className, children, ...props }, ref) {
10384
- return /* @__PURE__ */ jsx("div", { ref, className: cn(numeric ? "ds:eyebrow-num" : "ds:eyebrow", className), ...props, children });
10385
- });
10386
- Eyebrow.displayName = "Eyebrow";
10387
- var KbdHint = forwardRef(function KbdHint2(props, ref) {
10388
- return /* @__PURE__ */ jsx(Kbd, { ref, variant: "flat", ...props });
10389
- });
10390
- KbdHint.displayName = "KbdHint";
10391
- var SEARCH_THRESHOLD = 8;
12561
+ }
12562
+ function labelOf(item) {
12563
+ return (item.getAttribute("data-typeahead") ?? item.textContent ?? "").trim().toLowerCase();
12564
+ }
12565
+ function typeaheadMatch(items, from, query) {
12566
+ if (items.length === 0 || !query) return void 0;
12567
+ const start = query.length === 1 ? from + 1 : Math.max(from, 0);
12568
+ for (let offset = 0; offset < items.length; offset += 1) {
12569
+ const item = items[(start + offset + items.length) % items.length];
12570
+ if (item && labelOf(item).startsWith(query)) return item;
12571
+ }
12572
+ return void 0;
12573
+ }
12574
+ function useRovingMenu() {
12575
+ const buffer = useRef("");
12576
+ const timer = useRef(null);
12577
+ useEffect(
12578
+ () => () => {
12579
+ if (timer.current) clearTimeout(timer.current);
12580
+ },
12581
+ []
12582
+ );
12583
+ return useCallback((event) => {
12584
+ if (event.defaultPrevented) return;
12585
+ const target = event.target;
12586
+ if (target.closest("input, textarea, select, [contenteditable='true']")) return;
12587
+ const items = menuItems(event.currentTarget);
12588
+ if (items.length === 0) return;
12589
+ const current = target.closest(MENU_ITEM_SELECTOR);
12590
+ const index = current ? items.indexOf(current) : -1;
12591
+ let next;
12592
+ switch (event.key) {
12593
+ case "ArrowDown":
12594
+ next = items[(index + 1) % items.length];
12595
+ break;
12596
+ case "ArrowUp":
12597
+ next = items[index <= 0 ? items.length - 1 : index - 1];
12598
+ break;
12599
+ case "Home":
12600
+ next = items[0];
12601
+ break;
12602
+ case "End":
12603
+ next = items[items.length - 1];
12604
+ break;
12605
+ default: {
12606
+ const printable = event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.metaKey && !event.altKey;
12607
+ if (!printable) return;
12608
+ if (timer.current) clearTimeout(timer.current);
12609
+ buffer.current += event.key.toLowerCase();
12610
+ timer.current = setTimeout(() => {
12611
+ buffer.current = "";
12612
+ }, TYPEAHEAD_RESET_MS);
12613
+ next = typeaheadMatch(items, index, buffer.current);
12614
+ }
12615
+ }
12616
+ if (next) {
12617
+ event.preventDefault();
12618
+ next.focus();
12619
+ }
12620
+ }, []);
12621
+ }
12622
+ function DisplayMenuViewsBar({
12623
+ views,
12624
+ messages,
12625
+ onAnnounce,
12626
+ listOpen,
12627
+ onListOpenChange: setListOpen
12628
+ }) {
12629
+ const listId = useId();
12630
+ const roving = useRovingMenu();
12631
+ const active = views.items.find((v) => v.key === views.activeKey);
12632
+ const activeLabel = active?.label ?? messages.noView;
12633
+ return /* @__PURE__ */ jsxs("div", { className: "ds:border-b ds:border-rule", children: [
12634
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:gap-1 ds:px-1.5 ds:py-1", children: [
12635
+ /* @__PURE__ */ jsxs(
12636
+ Button,
12637
+ {
12638
+ variant: "ghost",
12639
+ size: "sm",
12640
+ "aria-haspopup": "menu",
12641
+ "aria-expanded": listOpen,
12642
+ "aria-controls": listOpen ? listId : void 0,
12643
+ onClick: () => setListOpen(!listOpen),
12644
+ onKeyDown: (event) => {
12645
+ if (event.key === "ArrowDown") {
12646
+ event.preventDefault();
12647
+ setListOpen(true);
12648
+ requestAnimationFrame(
12649
+ () => document.getElementById(listId)?.querySelector('[aria-checked="true"], [role="menuitemradio"]')?.focus()
12650
+ );
12651
+ }
12652
+ },
12653
+ className: "ds:h-7 ds:min-w-0 ds:flex-1 ds:justify-start ds:gap-1.5 ds:px-2",
12654
+ children: [
12655
+ /* @__PURE__ */ jsx("span", { className: "ds:text-fg-3", children: messages.views }),
12656
+ /* @__PURE__ */ jsx("span", { className: "ds:min-w-0 ds:truncate ds:text-fg", children: activeLabel }),
12657
+ views.dirty && /* @__PURE__ */ jsxs(Fragment, { children: [
12658
+ /* @__PURE__ */ jsx(
12659
+ "span",
12660
+ {
12661
+ "data-dirty": "",
12662
+ "aria-hidden": "true",
12663
+ className: "ds:size-1.5 ds:shrink-0 ds:rounded-full ds:bg-brand"
12664
+ }
12665
+ ),
12666
+ /* @__PURE__ */ jsxs(VisuallyHidden, { children: [
12667
+ ", ",
12668
+ messages.unsavedChanges
12669
+ ] })
12670
+ ] }),
12671
+ /* @__PURE__ */ jsx(
12672
+ ChevronDownIcon,
12673
+ {
12674
+ size: 13,
12675
+ "aria-hidden": "true",
12676
+ className: cn(
12677
+ "ds:ms-auto ds:shrink-0 ds:text-fg-3 ds:transition-transform ds:motion-reduce:transition-none",
12678
+ listOpen && "ds:rotate-180"
12679
+ )
12680
+ }
12681
+ )
12682
+ ]
12683
+ }
12684
+ ),
12685
+ views.onSave && /* @__PURE__ */ jsx(
12686
+ Button,
12687
+ {
12688
+ variant: "ghost",
12689
+ size: "sm",
12690
+ onClick: views.onSave,
12691
+ className: "ds:h-7 ds:shrink-0 ds:px-2 ds:text-fg-3 ds:hover:text-fg",
12692
+ children: messages.saveView
12693
+ }
12694
+ )
12695
+ ] }),
12696
+ listOpen && /* @__PURE__ */ jsx(
12697
+ DisplayMenuList,
12698
+ {
12699
+ id: listId,
12700
+ "aria-label": messages.views,
12701
+ onKeyDown: roving,
12702
+ className: "ds:max-h-48 ds:overflow-y-auto ds:pt-0",
12703
+ children: views.items.map((view) => /* @__PURE__ */ jsxs(
12704
+ DisplayMenuItem,
12705
+ {
12706
+ kind: "radio",
12707
+ checked: view.key === views.activeKey,
12708
+ onClick: () => {
12709
+ views.onSelect(view.key);
12710
+ onAnnounce(messages.announceView(view.label));
12711
+ setListOpen(false);
12712
+ },
12713
+ children: [
12714
+ view.label,
12715
+ view.description && /* @__PURE__ */ jsx("span", { className: "ds:block ds:truncate ds:text-(length:--text-caption) ds:text-fg-3", children: view.description })
12716
+ ]
12717
+ },
12718
+ view.key
12719
+ ))
12720
+ }
12721
+ )
12722
+ ] });
12723
+ }
12724
+ var SEARCH_THRESHOLD2 = 8;
10392
12725
  var HOVER_OPEN_MS = 90;
10393
12726
  var HOVER_CLOSE_MS = 220;
10394
12727
  var FLYOUT_WIDTH = 224;
12728
+ var WIDE_FLYOUT_WIDTH = 288;
12729
+ var WIDE_KINDS = /* @__PURE__ */ new Set(["date", "number", "text", "sort", "columns"]);
10395
12730
  var MIN_FLYOUT_HEIGHT = 120;
10396
12731
  var VIEWPORT_MARGIN = 24;
10397
12732
  var COLLISION_PADDING_PX = 12;
12733
+ function flyoutWidthFor(section) {
12734
+ return section?.spec && WIDE_KINDS.has(section.spec.kind) ? WIDE_FLYOUT_WIDTH : FLYOUT_WIDTH;
12735
+ }
12736
+ function usesFilterSummary(section) {
12737
+ return section.spec !== void 0 || section.operators !== void 0;
12738
+ }
10398
12739
  function DisplayMenu({
10399
12740
  sections,
10400
12741
  onReset,
10401
12742
  isAtDefault,
10402
- tooltip = "Filters, grouping, ordering, columns",
12743
+ tooltip: tooltipProp,
10403
12744
  dataTour,
10404
12745
  triggerIcon,
10405
- triggerLabel = "Display",
12746
+ triggerLabel: triggerLabelProp,
10406
12747
  className,
10407
- flyoutSide = "auto"
12748
+ flyoutSide = "auto",
12749
+ activeCount,
12750
+ match,
12751
+ onMatchChange,
12752
+ density,
12753
+ onDensityChange,
12754
+ views,
12755
+ open: openProp,
12756
+ defaultOpen = false,
12757
+ onOpenChange,
12758
+ openSection: openSectionProp,
12759
+ onOpenSectionChange,
12760
+ messages: messagesProp
10408
12761
  }) {
10409
- const [open, setOpen] = useState(false);
10410
- const [openKey, setOpenKey] = useState(null);
12762
+ const messages = useDsMessages("displayMenu", messagesProp);
12763
+ const filterMessages = useDsMessages("displayMenuFilters");
12764
+ const triggerLabel = triggerLabelProp ?? messages.trigger;
12765
+ const tooltip = tooltipProp ?? messages.tooltip;
12766
+ const [open, setOpen] = useControllableState({
12767
+ prop: openProp,
12768
+ defaultProp: defaultOpen,
12769
+ onChange: onOpenChange
12770
+ });
12771
+ const [openKey, setOpenKeyState] = useControllableState({
12772
+ prop: openSectionProp,
12773
+ defaultProp: null,
12774
+ onChange: onOpenSectionChange
12775
+ });
10411
12776
  const [flyout, setFlyout] = useState({
10412
12777
  top: 0,
10413
12778
  side: "left",
@@ -10415,6 +12780,11 @@ function DisplayMenu({
10415
12780
  });
10416
12781
  const [rootQuery, setRootQuery] = useState("");
10417
12782
  const [sectionQuery, setSectionQuery] = useState("");
12783
+ const [viewsOpen, setViewsOpen] = useState(false);
12784
+ const [announcer, announce] = useAnnouncer();
12785
+ const headingId = useId();
12786
+ const rootRoving = useRovingMenu();
12787
+ const flyoutRoving = useRovingMenu();
10418
12788
  const panelRef = useRef(null);
10419
12789
  const rowRefs = useRef(/* @__PURE__ */ new Map());
10420
12790
  const openTimer = useRef(null);
@@ -10427,30 +12797,39 @@ function DisplayMenu({
10427
12797
  closeTimer.current = null;
10428
12798
  }, []);
10429
12799
  useEffect(() => clearTimers, [clearTimers]);
10430
- const positionFor = useCallback((key) => {
10431
- const panel = panelRef.current;
10432
- const row = rowRefs.current.get(key);
10433
- if (!panel || !row) return;
10434
- const panelRect = panel.getBoundingClientRect();
10435
- const rowRect = row.getBoundingClientRect();
10436
- const maxTop = Math.max(0, window.innerHeight - panelRect.top - 24 - MIN_FLYOUT_HEIGHT);
10437
- const top = Math.min(Math.max(rowRect.top - panelRect.top - 4, 0), maxTop);
10438
- const maxHeight = Math.max(
10439
- MIN_FLYOUT_HEIGHT,
10440
- window.innerHeight - (panelRect.top + top) - VIEWPORT_MARGIN
10441
- );
10442
- const side = flyoutSide === "auto" ? panelRect.left >= FLYOUT_WIDTH + 16 ? "left" : "right" : flyoutSide;
10443
- setFlyout({ top, side, maxHeight });
10444
- }, [flyoutSide]);
12800
+ const sectionFor = useCallback(
12801
+ (key) => key === null ? void 0 : sections.find((s) => s.key === key),
12802
+ [sections]
12803
+ );
12804
+ const positionFor = useCallback(
12805
+ (key) => {
12806
+ const panel = panelRef.current;
12807
+ const row = rowRefs.current.get(key);
12808
+ if (!panel || !row) return;
12809
+ const panelRect = panel.getBoundingClientRect();
12810
+ const rowRect = row.getBoundingClientRect();
12811
+ const width = flyoutWidthFor(sectionFor(key));
12812
+ const maxTop = Math.max(0, window.innerHeight - panelRect.top - VIEWPORT_MARGIN - MIN_FLYOUT_HEIGHT);
12813
+ const top = Math.min(Math.max(rowRect.top - panelRect.top - 4, 0), maxTop);
12814
+ const maxHeight = Math.max(
12815
+ MIN_FLYOUT_HEIGHT,
12816
+ window.innerHeight - (panelRect.top + top) - VIEWPORT_MARGIN
12817
+ );
12818
+ const side = flyoutSide === "auto" ? panelRect.left >= width + 16 ? "left" : "right" : flyoutSide;
12819
+ setFlyout({ top, side, maxHeight });
12820
+ },
12821
+ [flyoutSide, sectionFor]
12822
+ );
10445
12823
  const openSection = useCallback(
10446
12824
  (key) => {
10447
12825
  clearTimers();
10448
12826
  positionFor(key);
10449
- setOpenKey(key);
12827
+ setOpenKeyState(key);
10450
12828
  setSectionQuery("");
10451
12829
  },
10452
- [clearTimers, positionFor]
12830
+ [clearTimers, positionFor, setOpenKeyState]
10453
12831
  );
12832
+ const closeSection = useCallback(() => setOpenKeyState(null), [setOpenKeyState]);
10454
12833
  const scheduleOpen = useCallback(
10455
12834
  (key) => {
10456
12835
  clearTimers();
@@ -10465,20 +12844,23 @@ function DisplayMenu({
10465
12844
  );
10466
12845
  const scheduleClose = useCallback(() => {
10467
12846
  clearTimers();
10468
- closeTimer.current = setTimeout(() => setOpenKey(null), HOVER_CLOSE_MS);
10469
- }, [clearTimers]);
12847
+ closeTimer.current = setTimeout(closeSection, HOVER_CLOSE_MS);
12848
+ }, [clearTimers, closeSection]);
10470
12849
  useEffect(() => {
10471
12850
  if (!open) {
10472
12851
  clearTimers();
10473
- setOpenKey(null);
12852
+ if (openSectionProp === void 0) setOpenKeyState(null);
10474
12853
  setRootQuery("");
10475
12854
  setSectionQuery("");
12855
+ setViewsOpen(false);
10476
12856
  }
10477
- }, [open, clearTimers]);
10478
- const activeSection = useMemo(
10479
- () => sections.find((s) => s.key === openKey) ?? null,
10480
- [sections, openKey]
10481
- );
12857
+ }, [open, clearTimers, openSectionProp, setOpenKeyState]);
12858
+ useLayoutEffect(() => {
12859
+ if (!open || openKey === null) return;
12860
+ const frame = requestAnimationFrame(() => positionFor(openKey));
12861
+ return () => cancelAnimationFrame(frame);
12862
+ }, [open, openKey, positionFor]);
12863
+ const activeSection = useMemo(() => sectionFor(openKey) ?? null, [sectionFor, openKey]);
10482
12864
  const rootRows = useMemo(() => {
10483
12865
  const q = rootQuery.trim().toLowerCase();
10484
12866
  if (!q) return sections.map((section) => ({ kind: "section", section }));
@@ -10487,7 +12869,8 @@ function DisplayMenu({
10487
12869
  if (section.label.toLowerCase().includes(q)) rows.push({ kind: "section", section });
10488
12870
  }
10489
12871
  for (const section of sections) {
10490
- if (section.content) continue;
12872
+ if (section.content || section.mode === "toggle") continue;
12873
+ if (section.spec && section.spec.kind !== "person" && section.spec.kind !== "tags") continue;
10491
12874
  for (const option of section.options) {
10492
12875
  if (option.label.toLowerCase().includes(q)) rows.push({ kind: "option", section, option });
10493
12876
  }
@@ -10496,12 +12879,192 @@ function DisplayMenu({
10496
12879
  }, [sections, rootQuery]);
10497
12880
  const sectionRows = useMemo(() => {
10498
12881
  if (!activeSection) return [];
12882
+ if (activeSection.hideZero || activeSection.options.some((o) => o.pinned)) {
12883
+ return visibleDisplayMenuOptions(activeSection, sectionQuery);
12884
+ }
10499
12885
  const q = sectionQuery.trim().toLowerCase();
10500
12886
  return q ? activeSection.options.filter((o) => o.label.toLowerCase().includes(q)) : activeSection.options;
10501
12887
  }, [activeSection, sectionQuery]);
10502
- const showFlyoutSearch = !!activeSection && !activeSection.content && (activeSection.searchable ?? activeSection.options.length > SEARCH_THRESHOLD);
12888
+ const activeOperator = activeSection ? displayMenuSectionOperator(activeSection) : void 0;
12889
+ const emptinessOperator = activeOperator === "isEmpty" || activeOperator === "isNotEmpty";
12890
+ const showFlyoutSearch = !!activeSection && !activeSection.content && !activeSection.spec && !emptinessOperator && (activeSection.searchable ?? activeSection.options.length > SEARCH_THRESHOLD2);
10503
12891
  const anySelected = sections.some((s) => s.mode === "multi" && s.selected.size > 0);
10504
- const showDot = !isAtDefault || anySelected;
12892
+ const showDot = activeCount !== void 0 ? activeCount > 0 : !isAtDefault || anySelected;
12893
+ const triggerName = activeCount !== void 0 && activeCount > 0 ? messages.activeFilters(triggerLabel, activeCount) : showDot ? messages.triggerCustomised(triggerLabel) : triggerLabel;
12894
+ const grouped = sections.some((s) => s.group !== void 0);
12895
+ const selectOption = (section, option) => {
12896
+ const wasSelected = section.selected.has(option.key);
12897
+ section.onSelect(option.key);
12898
+ const label = displayMenuOptionLabel(section, option, filterMessages);
12899
+ announce(
12900
+ wasSelected && section.mode !== "single" ? messages.announceDeselected(section.label, label) : messages.announceSelected(section.label, label)
12901
+ );
12902
+ };
12903
+ const focusFlyout = () => requestAnimationFrame(() => {
12904
+ const root = flyoutRef.current;
12905
+ if (!root) return;
12906
+ const target = root.querySelector('[role="radio"][tabindex="0"], [aria-checked="true"]') ?? root.querySelector(`input, ${MENU_ITEM_SELECTOR}, ${FOCUSABLE_SELECTOR}`);
12907
+ target?.focus();
12908
+ });
12909
+ const focusRow = (key) => rowRefs.current.get(key)?.querySelector("button")?.focus();
12910
+ const renderSectionRow = (section) => {
12911
+ if (section.mode === "toggle") {
12912
+ const option = section.options[0];
12913
+ if (!option) return null;
12914
+ const checked = section.selected.has(option.key);
12915
+ return /* @__PURE__ */ jsxs(
12916
+ DisplayMenuItem,
12917
+ {
12918
+ kind: "checkbox",
12919
+ checked,
12920
+ "data-typeahead": section.label,
12921
+ onMouseEnter: () => {
12922
+ clearTimers();
12923
+ closeSection();
12924
+ },
12925
+ onClick: () => selectOption(section, option),
12926
+ trailing: typeof option.count === "number" ? /* @__PURE__ */ jsx(DisplayMenuCount, { value: option.count }) : null,
12927
+ children: [
12928
+ section.icon && /* @__PURE__ */ jsx("span", { className: "ds:me-1.5 ds:inline-flex ds:text-fg-3", "aria-hidden": "true", children: section.icon }),
12929
+ option.label || section.label
12930
+ ]
12931
+ },
12932
+ section.key
12933
+ );
12934
+ }
12935
+ const count = section.selected.size;
12936
+ const filterSummary = usesFilterSummary(section) ? summarizeDisplayMenuSection(section, filterMessages) : null;
12937
+ const valueLabel = section.summary ?? filterSummary ?? (section.mode === "single" ? section.options.find((o) => section.selected.has(o.key))?.label ?? "" : "");
12938
+ const shownValue = valueLabel || (section.emptyLabel && !isDisplayMenuSectionActive(section) ? section.emptyLabel : "");
12939
+ const showCountBadge = section.mode === "multi" && count > 0 && !usesFilterSummary(section);
12940
+ const isOpen = section.key === openKey;
12941
+ return (
12942
+ /* Hover handlers sit on the wrapper so the cursor can cross
12943
+ * the gap to `section.trailing` without closing the flyout.
12944
+ * The wrapper is not itself a control — the button inside is,
12945
+ * and it carries the keyboard equivalent (ArrowRight / Enter /
12946
+ * Space open the same flyout). Nothing here is mouse-only. */
12947
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
12948
+ /* @__PURE__ */ jsxs(
12949
+ "div",
12950
+ {
12951
+ ref: (el) => {
12952
+ if (el) rowRefs.current.set(section.key, el);
12953
+ else rowRefs.current.delete(section.key);
12954
+ },
12955
+ className: "ds:flex ds:items-center ds:gap-0.5",
12956
+ onMouseEnter: () => scheduleOpen(section.key),
12957
+ onMouseLeave: scheduleClose,
12958
+ children: [
12959
+ /* @__PURE__ */ jsxs(
12960
+ DisplayMenuItem,
12961
+ {
12962
+ kind: "item",
12963
+ bare: true,
12964
+ "aria-haspopup": "menu",
12965
+ "aria-expanded": isOpen,
12966
+ "data-typeahead": section.label,
12967
+ onClick: () => openSection(section.key),
12968
+ onKeyDown: (e) => {
12969
+ if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
12970
+ e.preventDefault();
12971
+ openSection(section.key);
12972
+ focusFlyout();
12973
+ }
12974
+ },
12975
+ className: cn(
12976
+ "ds:min-w-0 ds:flex-1",
12977
+ isOpen && "ds:bg-bg-2/60"
12978
+ ),
12979
+ children: [
12980
+ section.icon && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true", children: section.icon }),
12981
+ /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate", children: section.label }),
12982
+ showCountBadge && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:rounded-full ds:bg-brand-bg ds:px-1.5 ds:text-(length:--text-caption) ds:font-medium ds:text-brand", children: count }),
12983
+ !showCountBadge && shownValue && /* @__PURE__ */ jsx("span", { className: "ds:max-w-24 ds:shrink-0 ds:truncate ds:text-fg-3", children: shownValue }),
12984
+ /* @__PURE__ */ jsx(ChevronRightIcon, { size: 13, className: "ds:shrink-0 ds:text-fg-3" })
12985
+ ]
12986
+ }
12987
+ ),
12988
+ section.trailing
12989
+ ]
12990
+ },
12991
+ section.key
12992
+ )
12993
+ );
12994
+ };
12995
+ const renderRootRows = () => {
12996
+ if (rootRows.length === 0) return /* @__PURE__ */ jsx(EmptyRow, { label: messages.noMatches });
12997
+ if (rootQuery.trim() || !grouped) return rootRows.map(renderRootRow);
12998
+ const order = ["filter", "arrange"];
12999
+ const buckets = order.map((group) => ({
13000
+ group,
13001
+ rows: sections.filter((s) => (s.group ?? "filter") === group)
13002
+ }));
13003
+ return buckets.filter((b) => b.rows.length > 0).map(({ group, rows }) => /* @__PURE__ */ jsxs(
13004
+ "div",
13005
+ {
13006
+ role: "group",
13007
+ "aria-labelledby": `${headingId}-${group}`,
13008
+ className: "ds:not-first:mt-1 ds:not-first:border-t ds:not-first:border-rule ds:not-first:pt-1",
13009
+ children: [
13010
+ /* @__PURE__ */ jsx(
13011
+ "p",
13012
+ {
13013
+ id: `${headingId}-${group}`,
13014
+ className: "ds:px-2 ds:pt-1 ds:pb-0.5 ds:text-(length:--text-caption) ds:font-medium ds:uppercase ds:tracking-wide ds:text-fg-4",
13015
+ children: group === "filter" ? messages.groupFilter : messages.groupArrange
13016
+ }
13017
+ ),
13018
+ rows.map(renderSectionRow)
13019
+ ]
13020
+ },
13021
+ group
13022
+ ));
13023
+ };
13024
+ const renderRootRow = (row) => {
13025
+ if (row.kind === "option") {
13026
+ const { section, option } = row;
13027
+ const isActive = section.selected.has(option.key);
13028
+ const isMulti = section.mode === "multi";
13029
+ return /* @__PURE__ */ jsxs(
13030
+ DisplayMenuItem,
13031
+ {
13032
+ kind: isMulti ? "checkbox" : "radio",
13033
+ indicator: "check",
13034
+ bare: true,
13035
+ checked: isActive,
13036
+ onMouseEnter: () => {
13037
+ clearTimers();
13038
+ closeSection();
13039
+ },
13040
+ onClick: () => selectOption(section, option),
13041
+ className: "ds:gap-1.5",
13042
+ children: [
13043
+ section.icon && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true", children: section.icon }),
13044
+ /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:truncate ds:text-fg-3", children: section.label }),
13045
+ /* @__PURE__ */ jsx(ChevronRightIcon, { size: 12, className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true" }),
13046
+ /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate ds:text-fg", children: displayMenuOptionLabel(section, option, filterMessages) })
13047
+ ]
13048
+ },
13049
+ `${section.key}:${option.key}`
13050
+ );
13051
+ }
13052
+ return renderSectionRow(row.section);
13053
+ };
13054
+ const onEscapeKeyDown = (e) => {
13055
+ if (viewsOpen) {
13056
+ e.preventDefault();
13057
+ setViewsOpen(false);
13058
+ return;
13059
+ }
13060
+ if (openKey) {
13061
+ e.preventDefault();
13062
+ const key = openKey;
13063
+ const fromFlyout = flyoutRef.current?.contains(e.target);
13064
+ closeSection();
13065
+ if (fromFlyout) focusRow(key);
13066
+ }
13067
+ };
10505
13068
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
10506
13069
  /* @__PURE__ */ jsxs(Tooltip, { delayMs: 300, children: [
10507
13070
  /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
@@ -10509,7 +13072,7 @@ function DisplayMenu({
10509
13072
  {
10510
13073
  variant: "outline",
10511
13074
  size: "sm",
10512
- "aria-label": showDot ? `${triggerLabel} (customised)` : triggerLabel,
13075
+ "aria-label": triggerName,
10513
13076
  "data-tour": dataTour,
10514
13077
  className: cn("ds:relative ds:shrink-0", className),
10515
13078
  children: [
@@ -10531,17 +13094,22 @@ function DisplayMenu({
10531
13094
  PopoverContent,
10532
13095
  {
10533
13096
  align: "end",
10534
- sideOffset: 6,
10535
- collisionPadding: COLLISION_PADDING_PX,
10536
- className: "ds:w-64 ds:overflow-visible ds:p-0",
10537
- onKeyDown: (e) => {
10538
- if (e.key === "Escape" && openKey) {
10539
- e.preventDefault();
10540
- e.stopPropagation();
10541
- setOpenKey(null);
10542
- }
10543
- },
13097
+ sideOffset: 6,
13098
+ collisionPadding: COLLISION_PADDING_PX,
13099
+ className: "ds:w-64 ds:overflow-visible ds:p-0",
13100
+ onEscapeKeyDown,
10544
13101
  children: /* @__PURE__ */ jsxs("div", { ref: panelRef, className: "ds:relative ds:isolate", children: [
13102
+ announcer,
13103
+ views && /* @__PURE__ */ jsx(
13104
+ DisplayMenuViewsBar,
13105
+ {
13106
+ views,
13107
+ messages,
13108
+ onAnnounce: announce,
13109
+ listOpen: viewsOpen,
13110
+ onListOpenChange: setViewsOpen
13111
+ }
13112
+ ),
10545
13113
  /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:gap-1.5 ds:border-b ds:border-rule ds:px-2.5 ds:py-1.5", children: [
10546
13114
  /* @__PURE__ */ jsx(SearchIcon, { size: 13, className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true" }),
10547
13115
  /* @__PURE__ */ jsx(
@@ -10550,139 +13118,82 @@ function DisplayMenu({
10550
13118
  type: "text",
10551
13119
  value: rootQuery,
10552
13120
  onChange: (e) => setRootQuery(e.target.value),
10553
- placeholder: "Filter\u2026",
10554
- "aria-label": "Filter options and values",
13121
+ onKeyDown: (e) => {
13122
+ if (e.key === "ArrowDown") {
13123
+ e.preventDefault();
13124
+ panelRef.current?.querySelector(`[data-dm-root] :is(${MENU_ITEM_SELECTOR})`)?.focus();
13125
+ }
13126
+ },
13127
+ placeholder: messages.rootSearchPlaceholder,
13128
+ "aria-label": messages.rootSearchLabel,
10555
13129
  className: "ds:h-6 ds:w-full ds:bg-transparent ds:text-[13px] ds:text-fg ds:outline-none ds:placeholder:text-fg-3"
10556
13130
  }
10557
13131
  )
10558
13132
  ] }),
13133
+ match !== void 0 && onMatchChange && sections.some((s) => (s.group ?? "filter") === "filter") && /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:justify-between ds:gap-2 ds:border-b ds:border-rule ds:px-2.5 ds:py-1", children: [
13134
+ /* @__PURE__ */ jsx("span", { className: "ds:text-(length:--text-helper) ds:text-fg-3", id: `${headingId}-match`, children: messages.matchLabel }),
13135
+ /* @__PURE__ */ jsxs(
13136
+ ToggleGroup,
13137
+ {
13138
+ type: "single",
13139
+ variant: "segmented",
13140
+ size: "xs",
13141
+ value: match,
13142
+ "aria-labelledby": `${headingId}-match`,
13143
+ onValueChange: (next) => {
13144
+ if (next === "all" || next === "any") onMatchChange(next);
13145
+ },
13146
+ children: [
13147
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "all", className: "ds:px-2 ds:text-(length:--text-helper)", children: messages.matchAll }),
13148
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "any", className: "ds:px-2 ds:text-(length:--text-helper)", children: messages.matchAny })
13149
+ ]
13150
+ }
13151
+ )
13152
+ ] }),
10559
13153
  /* @__PURE__ */ jsx(
10560
13154
  "div",
10561
13155
  {
10562
13156
  role: "menu",
10563
13157
  "aria-label": triggerLabel,
13158
+ "data-dm-root": "",
13159
+ onKeyDown: rootRoving,
10564
13160
  className: "ds:scrollbar-thin ds:max-h-[min(32rem,70vh)] ds:overflow-y-auto ds:p-1",
10565
- children: rootRows.length === 0 ? /* @__PURE__ */ jsx(EmptyRow, {}) : rootRows.map((row) => {
10566
- if (row.kind === "option") {
10567
- const { section: section2, option } = row;
10568
- const isActive = section2.selected.has(option.key);
10569
- const isMulti = section2.mode === "multi";
10570
- return /* @__PURE__ */ jsxs(
10571
- "button",
10572
- {
10573
- type: "button",
10574
- role: isMulti ? "menuitemcheckbox" : "menuitemradio",
10575
- "aria-checked": isActive,
10576
- onMouseEnter: () => {
10577
- clearTimers();
10578
- setOpenKey(null);
10579
- },
10580
- onClick: () => section2.onSelect(option.key),
10581
- className: "ds:flex ds:w-full ds:items-center ds:gap-1.5 ds:rounded-md ds:px-2 ds:py-1.5 ds:text-left ds:text-[13px] ds:transition-colors ds:hover:bg-surface-hover/60 ds:focus-visible:bg-bg-2/60 ds:focus-visible:outline-none ds:motion-reduce:transition-none",
10582
- children: [
10583
- section2.icon && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true", children: section2.icon }),
10584
- /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:truncate ds:text-fg-3", children: section2.label }),
10585
- /* @__PURE__ */ jsx(
10586
- ChevronRightIcon,
10587
- {
10588
- size: 12,
10589
- className: "ds:shrink-0 ds:text-fg-3",
10590
- "aria-hidden": "true"
10591
- }
10592
- ),
10593
- /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate ds:text-fg", children: option.label }),
10594
- isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 14, className: "ds:shrink-0 ds:text-brand" })
10595
- ]
10596
- },
10597
- `${section2.key}:${option.key}`
10598
- );
10599
- }
10600
- const { section } = row;
10601
- const count = section.selected.size;
10602
- const valueLabel = section.summary ?? (section.mode === "single" ? section.options.find((o) => section.selected.has(o.key))?.label ?? "" : "");
10603
- const isOpen = section.key === openKey;
10604
- return (
10605
- /* Hover handlers sit on the wrapper so the cursor can cross
10606
- * the gap to `section.trailing` without closing the flyout.
10607
- * The wrapper is not itself a control — the button inside is,
10608
- * and it carries the keyboard equivalent (ArrowRight / Enter /
10609
- * Space open the same flyout). Nothing here is mouse-only. */
10610
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
10611
- /* @__PURE__ */ jsxs(
10612
- "div",
10613
- {
10614
- ref: (el) => {
10615
- if (el) rowRefs.current.set(section.key, el);
10616
- else rowRefs.current.delete(section.key);
10617
- },
10618
- className: "ds:flex ds:items-center ds:gap-0.5",
10619
- onMouseEnter: () => scheduleOpen(section.key),
10620
- onMouseLeave: scheduleClose,
10621
- children: [
10622
- /* @__PURE__ */ jsxs(
10623
- "button",
10624
- {
10625
- type: "button",
10626
- role: "menuitem",
10627
- "aria-haspopup": "menu",
10628
- "aria-expanded": isOpen,
10629
- onClick: () => openSection(section.key),
10630
- onKeyDown: (e) => {
10631
- if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
10632
- e.preventDefault();
10633
- openSection(section.key);
10634
- requestAnimationFrame(
10635
- () => flyoutRef.current?.querySelector("input, [role='menuitemcheckbox'], [role='menuitemradio']")?.focus()
10636
- );
10637
- }
10638
- },
10639
- className: cn(
10640
- "ds:flex ds:min-w-0 ds:flex-1 ds:items-center ds:gap-2 ds:rounded-md ds:px-2 ds:py-1.5 ds:text-left ds:text-[13px] ds:text-fg ds:transition-colors ds:hover:bg-surface-hover/60 ds:focus-visible:bg-bg-2/60 ds:focus-visible:outline-none ds:motion-reduce:transition-none",
10641
- isOpen && "ds:bg-bg-2/60"
10642
- ),
10643
- children: [
10644
- section.icon && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true", children: section.icon }),
10645
- /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate", children: section.label }),
10646
- section.mode === "multi" && count > 0 && /* @__PURE__ */ jsx("span", { className: "ds:shrink-0 ds:rounded-full ds:bg-brand-bg ds:px-1.5 ds:text-[11px] ds:font-medium ds:text-brand", children: count }),
10647
- section.mode !== "multi" && valueLabel && /* @__PURE__ */ jsx("span", { className: "ds:max-w-24 ds:shrink-0 ds:truncate ds:text-[13px] ds:text-fg-3", children: valueLabel }),
10648
- /* @__PURE__ */ jsx(ChevronRightIcon, { size: 13, className: "ds:shrink-0 ds:text-fg-3" })
10649
- ]
10650
- }
10651
- ),
10652
- section.trailing
10653
- ]
10654
- },
10655
- section.key
10656
- )
10657
- );
10658
- })
13161
+ children: renderRootRows()
10659
13162
  }
10660
13163
  ),
10661
- /* @__PURE__ */ jsx("div", { className: "ds:border-t ds:border-rule ds:p-1", children: /* @__PURE__ */ jsxs(
10662
- Button,
10663
- {
10664
- variant: "ghost",
10665
- size: "sm",
10666
- onClick: onReset,
10667
- disabled: isAtDefault,
10668
- className: "ds:h-7 ds:w-full ds:justify-start ds:gap-1.5 ds:px-2 ds:text-xs ds:text-fg-3 ds:hover:text-fg",
10669
- children: [
10670
- /* @__PURE__ */ jsx(RotateCcwIcon, { size: 13 }),
10671
- "Reset to default"
10672
- ]
10673
- }
10674
- ) }),
10675
- activeSection && /* @__PURE__ */ jsxs(
13164
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:gap-1 ds:border-t ds:border-rule ds:p-1", children: [
13165
+ /* @__PURE__ */ jsxs(
13166
+ Button,
13167
+ {
13168
+ variant: "ghost",
13169
+ size: "sm",
13170
+ onClick: () => {
13171
+ onReset();
13172
+ announce(messages.announceReset);
13173
+ },
13174
+ disabled: isAtDefault,
13175
+ className: "ds:h-7 ds:flex-1 ds:justify-start ds:gap-1.5 ds:px-2 ds:text-xs ds:text-fg-3 ds:hover:text-fg",
13176
+ children: [
13177
+ /* @__PURE__ */ jsx(RotateCcwIcon, { size: 13 }),
13178
+ messages.reset
13179
+ ]
13180
+ }
13181
+ ),
13182
+ density !== void 0 && onDensityChange && /* @__PURE__ */ jsx(TableDensityToggle, { size: "xs", value: density, onValueChange: onDensityChange })
13183
+ ] }),
13184
+ activeSection && // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- delegated ArrowLeft/hover for the controls inside; the flyout is a surface, not a control
13185
+ /* @__PURE__ */ jsxs(
10676
13186
  "div",
10677
13187
  {
10678
13188
  ref: flyoutRef,
10679
13189
  tabIndex: -1,
10680
13190
  "data-ds-presence": "floating",
10681
13191
  "data-state": "open",
13192
+ "data-kind": activeSection.spec?.kind,
10682
13193
  style: {
10683
13194
  top: flyout.top,
10684
- width: FLYOUT_WIDTH,
10685
- maxHeight: flyout.maxHeight,
13195
+ width: flyoutWidthFor(activeSection),
13196
+ maxHeight: flyout.maxHeight || void 0,
10686
13197
  transformOrigin: flyout.side === "left" ? "right top" : "left top"
10687
13198
  },
10688
13199
  className: cn(
@@ -10696,7 +13207,17 @@ function DisplayMenu({
10696
13207
  ),
10697
13208
  onMouseEnter: clearTimers,
10698
13209
  onMouseLeave: scheduleClose,
13210
+ onKeyDown: (e) => {
13211
+ const inField = e.target.closest("input, textarea");
13212
+ if (e.key === "ArrowLeft" && !inField && !e.defaultPrevented) {
13213
+ e.preventDefault();
13214
+ const key = activeSection.key;
13215
+ closeSection();
13216
+ focusRow(key);
13217
+ }
13218
+ },
10699
13219
  children: [
13220
+ activeSection.operators && /* @__PURE__ */ jsx(DisplayMenuOperatorPicker, { section: activeSection }),
10700
13221
  showFlyoutSearch && /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:shrink-0 ds:items-center ds:gap-1.5 ds:border-b ds:border-rule ds:px-2.5 ds:py-1.5", children: [
10701
13222
  /* @__PURE__ */ jsx(SearchIcon, { size: 13, className: "ds:shrink-0 ds:text-fg-3", "aria-hidden": "true" }),
10702
13223
  /* @__PURE__ */ jsx(
@@ -10705,85 +13226,98 @@ function DisplayMenu({
10705
13226
  type: "text",
10706
13227
  value: sectionQuery,
10707
13228
  onChange: (e) => setSectionQuery(e.target.value),
10708
- placeholder: `Filter ${activeSection.label.toLowerCase()}\u2026`,
10709
- "aria-label": `Filter ${activeSection.label}`,
13229
+ placeholder: messages.sectionSearchPlaceholder(activeSection.label),
13230
+ "aria-label": messages.sectionSearchLabel(activeSection.label),
10710
13231
  className: "ds:h-6 ds:w-full ds:bg-transparent ds:text-[13px] ds:text-fg ds:outline-none ds:placeholder:text-fg-3"
10711
13232
  }
10712
13233
  )
10713
13234
  ] }),
10714
- /* @__PURE__ */ jsx(
10715
- "div",
10716
- {
10717
- role: "menu",
10718
- "aria-label": activeSection.label,
10719
- className: "ds:scrollbar-thin ds:min-h-0 ds:flex-1 ds:max-h-[min(24rem,60vh)] ds:overflow-y-auto ds:p-1",
10720
- children: activeSection.content ? (
10721
- /* Consumer-supplied controls (date range, numeric bound).
10722
- * Rendered raw — the section owns its own labelling and
10723
- * layout; the flyout only provides the surface. */
10724
- /* @__PURE__ */ jsx("div", { className: "ds:p-1.5", children: activeSection.content })
10725
- ) : sectionRows.length === 0 ? /* @__PURE__ */ jsx(
10726
- EmptyRow,
10727
- {
10728
- label: activeSection.options.length === 0 ? "Nothing to filter by" : "No matches"
10729
- }
10730
- ) : sectionRows.map((option) => {
10731
- const isActive = activeSection.selected.has(option.key);
10732
- const isMulti = activeSection.mode === "multi";
10733
- return /* @__PURE__ */ jsxs(
10734
- "button",
13235
+ activeSection.spec ? (
13236
+ /* Typed panels own inputs and radio groups — a menu may only own
13237
+ * items, so this container is a labelled group instead. */
13238
+ /* @__PURE__ */ jsx(
13239
+ "div",
13240
+ {
13241
+ role: "group",
13242
+ "aria-label": activeSection.label,
13243
+ className: "ds:scrollbar-thin ds:min-h-0 ds:flex-1 ds:overflow-y-auto",
13244
+ children: /* @__PURE__ */ jsx(DisplayMenuSpecPanel, { section: activeSection, onDone: closeSection })
13245
+ }
13246
+ )
13247
+ ) : emptinessOperator ? null : (
13248
+ /* The menu is the option list alone: the search box above and
13249
+ * the Clear action below are not menu items, so they sit
13250
+ * outside it (a menu may only own items, G10). */
13251
+ /* @__PURE__ */ jsx(
13252
+ "div",
13253
+ {
13254
+ role: "menu",
13255
+ "aria-label": activeSection.label,
13256
+ onKeyDown: flyoutRoving,
13257
+ className: "ds:scrollbar-thin ds:min-h-0 ds:flex-1 ds:max-h-[min(24rem,60vh)] ds:overflow-y-auto ds:p-1",
13258
+ children: activeSection.content ? (
13259
+ /* Consumer-supplied controls (date range, numeric bound).
13260
+ * Rendered raw — the section owns its own labelling and
13261
+ * layout; the flyout only provides the surface. */
13262
+ /* @__PURE__ */ jsx("div", { className: "ds:p-1.5", children: activeSection.content })
13263
+ ) : sectionRows.length === 0 ? /* @__PURE__ */ jsx(
13264
+ EmptyRow,
10735
13265
  {
10736
- type: "button",
10737
- role: isMulti ? "menuitemcheckbox" : "menuitemradio",
10738
- "aria-checked": isActive,
10739
- onClick: () => {
10740
- activeSection.onSelect(option.key);
10741
- if (!isMulti) setOpenKey(null);
10742
- },
10743
- onKeyDown: (e) => {
10744
- if (e.key === "ArrowLeft") {
10745
- e.preventDefault();
10746
- setOpenKey(null);
10747
- rowRefs.current.get(activeSection.key)?.querySelector("button")?.focus();
10748
- }
10749
- },
10750
- className: cn(
10751
- "ds:flex ds:w-full ds:items-center ds:gap-2 ds:rounded-md ds:px-2 ds:py-1.5 ds:text-left ds:text-[13px] ds:transition-colors ds:hover:bg-surface-hover/60 ds:focus-visible:bg-bg-2/60 ds:focus-visible:outline-none ds:motion-reduce:transition-none",
10752
- isActive ? "ds:text-fg" : "ds:text-fg-3 ds:hover:text-fg"
10753
- ),
10754
- children: [
10755
- isMulti && /* @__PURE__ */ jsx(
10756
- "span",
10757
- {
10758
- "aria-hidden": "true",
10759
- className: cn(
10760
- "ds:flex ds:size-3.5 ds:shrink-0 ds:items-center ds:justify-center ds:rounded ds:border ds:transition-colors",
10761
- isActive ? "ds:border-brand ds:bg-brand ds:text-fg-on-brand" : "ds:border-rule"
10762
- ),
10763
- children: isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 10 })
10764
- }
13266
+ label: activeSection.options.length === 0 ? messages.nothingToFilter : messages.noMatches
13267
+ }
13268
+ ) : sectionRows.map((option) => {
13269
+ const isActive = activeSection.selected.has(option.key);
13270
+ const isMulti = activeSection.mode === "multi";
13271
+ return /* @__PURE__ */ jsxs(
13272
+ "button",
13273
+ {
13274
+ type: "button",
13275
+ role: isMulti ? "menuitemcheckbox" : "menuitemradio",
13276
+ "aria-checked": isActive,
13277
+ "data-typeahead": option.label,
13278
+ onClick: () => {
13279
+ selectOption(activeSection, option);
13280
+ if (!isMulti) closeSection();
13281
+ },
13282
+ className: cn(
13283
+ "ds:flex ds:w-full ds:items-center ds:gap-2 ds:rounded-md ds:px-2 ds:py-1.5 ds:text-left ds:text-[13px] ds:transition-colors ds:hover:bg-surface-hover/60 ds:focus-visible:bg-bg-2/60 ds:focus-visible:outline-none ds:motion-reduce:transition-none",
13284
+ isActive ? "ds:text-fg" : "ds:text-fg-3 ds:hover:text-fg"
10765
13285
  ),
10766
- /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate", children: option.label }),
10767
- !isMulti && isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 14, className: "ds:shrink-0 ds:text-brand" })
10768
- ]
10769
- },
10770
- option.key
10771
- );
10772
- })
10773
- }
13286
+ children: [
13287
+ isMulti && /* @__PURE__ */ jsx(
13288
+ "span",
13289
+ {
13290
+ "aria-hidden": "true",
13291
+ className: cn(
13292
+ "ds:flex ds:size-3.5 ds:shrink-0 ds:items-center ds:justify-center ds:rounded ds:border ds:transition-colors",
13293
+ isActive ? "ds:border-brand ds:bg-brand ds:text-fg-on-brand" : "ds:border-rule"
13294
+ ),
13295
+ children: isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 10 })
13296
+ }
13297
+ ),
13298
+ option.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ds:inline-flex ds:shrink-0", children: option.icon }),
13299
+ /* @__PURE__ */ jsx("span", { className: "ds:flex-1 ds:truncate", children: option.label }),
13300
+ typeof option.count === "number" && /* @__PURE__ */ jsx(DisplayMenuCount, { value: option.count }),
13301
+ !isMulti && isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 14, className: "ds:shrink-0 ds:text-brand" })
13302
+ ]
13303
+ },
13304
+ option.key
13305
+ );
13306
+ })
13307
+ }
13308
+ )
10774
13309
  ),
10775
- activeSection.mode === "multi" && activeSection.selected.size > 0 && activeSection.onClear && /* @__PURE__ */ jsx("div", { className: "ds:shrink-0 ds:border-t ds:border-rule ds:p-1", children: /* @__PURE__ */ jsxs(
13310
+ !activeSection.spec && activeSection.mode === "multi" && activeSection.selected.size > 0 && activeSection.onClear && /* @__PURE__ */ jsx("div", { className: "ds:shrink-0 ds:border-t ds:border-rule ds:p-1", children: /* @__PURE__ */ jsx(
10776
13311
  Button,
10777
13312
  {
10778
13313
  variant: "ghost",
10779
13314
  size: "sm",
10780
- onClick: activeSection.onClear,
13315
+ onClick: () => {
13316
+ activeSection.onClear?.();
13317
+ announce(messages.announceCleared(activeSection.label));
13318
+ },
10781
13319
  className: "ds:h-6 ds:w-full ds:justify-start ds:px-2 ds:text-[11px] ds:text-fg-3 ds:hover:text-fg",
10782
- children: [
10783
- "Clear ",
10784
- activeSection.selected.size,
10785
- " selected"
10786
- ]
13320
+ children: messages.clearSelected(activeSection.selected.size)
10787
13321
  }
10788
13322
  ) })
10789
13323
  ]
@@ -10794,9 +13328,220 @@ function DisplayMenu({
10794
13328
  )
10795
13329
  ] });
10796
13330
  }
10797
- function EmptyRow({ label = "No matches" }) {
13331
+ function EmptyRow({ label }) {
10798
13332
  return /* @__PURE__ */ jsx("p", { className: "ds:px-2 ds:py-3 ds:text-center ds:text-[11px] ds:text-fg-3", children: label });
10799
13333
  }
13334
+ function facetSummary(facet) {
13335
+ return facet.summary ?? (facet.values ?? []).map((v) => v.label).join(", ");
13336
+ }
13337
+ var FilterChips = forwardRef(function FilterChips2({
13338
+ facets,
13339
+ onEdit,
13340
+ onRemove,
13341
+ onRemoveValue,
13342
+ onClearAll,
13343
+ maxVisible = 3,
13344
+ size = "sm",
13345
+ trailing,
13346
+ messages: messagesProp,
13347
+ className,
13348
+ ...props
13349
+ }, ref) {
13350
+ const messages = useDsMessages("filterChips", messagesProp);
13351
+ const [panelOpen, setPanelOpen] = useState(false);
13352
+ if (facets.length === 0) return null;
13353
+ const limit = Math.max(1, maxVisible);
13354
+ const collapse = facets.length > limit + 1;
13355
+ const visible = collapse ? facets.slice(0, limit) : facets;
13356
+ const hidden = collapse ? facets.slice(limit) : [];
13357
+ return /* @__PURE__ */ jsxs(
13358
+ "div",
13359
+ {
13360
+ ref,
13361
+ role: "group",
13362
+ "aria-label": messages.region,
13363
+ ...slot(filterChipsAnatomy, "root"),
13364
+ className: cn("ds:flex ds:min-w-0 ds:flex-wrap ds:items-center ds:gap-1.5", className),
13365
+ ...props,
13366
+ children: [
13367
+ visible.map((facet) => {
13368
+ const text = messages.facet(facet.label, facetSummary(facet));
13369
+ return /* @__PURE__ */ jsx(
13370
+ Chip,
13371
+ {
13372
+ size,
13373
+ "data-facet": facet.key,
13374
+ ...slot(filterChipsAnatomy, "chip"),
13375
+ ...onEdit ? { onClick: () => onEdit(facet.key) } : {},
13376
+ onRemove: () => onRemove(facet.key),
13377
+ removeLabel: messages.remove(facet.label),
13378
+ className: "ds:max-w-72 ds:[&_[data-slot=chip-label]]:truncate",
13379
+ title: text,
13380
+ children: text
13381
+ },
13382
+ facet.key
13383
+ );
13384
+ }),
13385
+ hidden.length > 0 && /* @__PURE__ */ jsxs(Popover, { open: panelOpen, onOpenChange: setPanelOpen, children: [
13386
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
13387
+ Chip,
13388
+ {
13389
+ size,
13390
+ ...slot(filterChipsAnatomy, "overflow"),
13391
+ "aria-label": messages.moreLabel(hidden.length),
13392
+ "aria-haspopup": "dialog",
13393
+ "aria-expanded": panelOpen,
13394
+ onClick: () => setPanelOpen((open) => !open),
13395
+ children: messages.more(hidden.length)
13396
+ }
13397
+ ) }),
13398
+ /* @__PURE__ */ jsxs(
13399
+ PopoverContent,
13400
+ {
13401
+ align: "start",
13402
+ sideOffset: 6,
13403
+ padding: "none",
13404
+ "aria-label": messages.panelTitle(facets.length),
13405
+ ...slot(filterChipsAnatomy, "overflow-panel"),
13406
+ children: [
13407
+ /* @__PURE__ */ jsxs("div", { className: "ds:flex ds:items-center ds:justify-between ds:gap-2 ds:border-b ds:border-rule ds:px-3 ds:py-2", children: [
13408
+ /* @__PURE__ */ jsx("p", { className: "ds:text-xs ds:font-semibold ds:text-fg", children: messages.panelTitle(facets.length) }),
13409
+ onClearAll && /* @__PURE__ */ jsx(
13410
+ Button,
13411
+ {
13412
+ variant: "ghost",
13413
+ size: "sm",
13414
+ onClick: () => {
13415
+ onClearAll();
13416
+ setPanelOpen(false);
13417
+ },
13418
+ children: messages.clearFilters
13419
+ }
13420
+ )
13421
+ ] }),
13422
+ /* @__PURE__ */ jsx("ul", { className: "ds:scrollbar-thin ds:max-h-72 ds:space-y-3 ds:overflow-y-auto ds:p-3", children: facets.map((facet) => /* @__PURE__ */ jsxs("li", { ...slot(filterChipsAnatomy, "overflow-facet"), children: [
13423
+ /* @__PURE__ */ jsx("p", { className: "ds:mb-1 ds:text-xs ds:font-medium ds:text-fg-3", children: facet.label }),
13424
+ /* @__PURE__ */ jsx("ul", { className: "ds:space-y-0.5", children: onRemoveValue && facet.values && facet.values.length > 0 ? facet.values.map((value) => /* @__PURE__ */ jsx(
13425
+ ValueRow,
13426
+ {
13427
+ label: value.label,
13428
+ removeLabel: messages.removeValue(facet.label, value.label),
13429
+ onRemove: () => onRemoveValue(facet.key, value.key)
13430
+ },
13431
+ value.key
13432
+ )) : /* @__PURE__ */ jsx(
13433
+ ValueRow,
13434
+ {
13435
+ label: facetSummary(facet),
13436
+ removeLabel: messages.remove(facet.label),
13437
+ onRemove: () => onRemove(facet.key)
13438
+ }
13439
+ ) })
13440
+ ] }, facet.key)) })
13441
+ ]
13442
+ }
13443
+ )
13444
+ ] }),
13445
+ trailing,
13446
+ onClearAll && /* @__PURE__ */ jsx(
13447
+ Button,
13448
+ {
13449
+ variant: "ghost",
13450
+ size: "sm",
13451
+ onClick: onClearAll,
13452
+ ...slot(filterChipsAnatomy, "clear"),
13453
+ className: "ds:text-fg-3 ds:hover:text-fg",
13454
+ children: messages.clearAll
13455
+ }
13456
+ )
13457
+ ]
13458
+ }
13459
+ );
13460
+ });
13461
+ FilterChips.displayName = "FilterChips";
13462
+ function ValueRow({
13463
+ label,
13464
+ removeLabel,
13465
+ onRemove
13466
+ }) {
13467
+ return /* @__PURE__ */ jsxs(
13468
+ "li",
13469
+ {
13470
+ ...slot(filterChipsAnatomy, "overflow-value"),
13471
+ className: "ds:flex ds:items-center ds:justify-between ds:gap-2 ds:rounded-input ds:py-0.5 ds:ps-1 ds:pe-0.5 ds:transition-colors ds:duration-[var(--duration-fast)] ds:hover:bg-surface-hover ds:motion-reduce:transition-none",
13472
+ children: [
13473
+ /* @__PURE__ */ jsx("span", { className: "ds:min-w-0 ds:truncate ds:text-sm ds:text-fg", children: label }),
13474
+ /* @__PURE__ */ jsx(
13475
+ IconButton,
13476
+ {
13477
+ variant: "ghost",
13478
+ intent: "neutral",
13479
+ size: "xs",
13480
+ label: removeLabel,
13481
+ tooltip: false,
13482
+ icon: /* @__PURE__ */ jsx(XIcon, {}),
13483
+ onClick: onRemove
13484
+ }
13485
+ )
13486
+ ]
13487
+ }
13488
+ );
13489
+ }
13490
+ function clearSection(section) {
13491
+ const spec = section.spec;
13492
+ if (spec && "onChange" in spec && spec.kind !== "sort") {
13493
+ spec.onChange(null);
13494
+ return;
13495
+ }
13496
+ if (section.operator === "isEmpty" || section.operator === "isNotEmpty") {
13497
+ const fallback = section.operators?.find((op) => op !== "isEmpty" && op !== "isNotEmpty");
13498
+ if (fallback) section.onOperatorChange?.(fallback);
13499
+ }
13500
+ if (section.onClear) {
13501
+ section.onClear();
13502
+ return;
13503
+ }
13504
+ for (const key of section.selected) section.onSelect(key);
13505
+ }
13506
+ function useDisplayMenuFilterChips(sections, options) {
13507
+ const filterMessages = useDsMessages("displayMenuFilters");
13508
+ const group = options?.group;
13509
+ const now = options?.now;
13510
+ return useMemo(() => {
13511
+ const grouped = sections.some((s) => s.group !== void 0);
13512
+ const eligible = sections.filter((section) => {
13513
+ if (section.spec?.kind === "sort" || section.spec?.kind === "columns") return false;
13514
+ if (group === "all" || !group && !grouped) return true;
13515
+ return section.group === (group ?? "filter");
13516
+ });
13517
+ const active = eligible.filter((section) => isDisplayMenuSectionActive(section));
13518
+ const byKey = new Map(active.map((s) => [s.key, s]));
13519
+ const facets = active.map((section) => {
13520
+ const valueList = !section.spec || section.spec.kind === "person" || section.spec.kind === "tags";
13521
+ const values = valueList && section.operator !== "isEmpty" && section.operator !== "isNotEmpty" ? section.options.filter((o) => section.selected.has(o.key)).map((o) => ({ key: o.key, label: o.label })) : void 0;
13522
+ return {
13523
+ key: section.key,
13524
+ label: section.label,
13525
+ summary: summarizeDisplayMenuSection(section, filterMessages, now ? { now } : void 0) ?? void 0,
13526
+ values
13527
+ };
13528
+ });
13529
+ return {
13530
+ facets,
13531
+ onRemove: (key) => {
13532
+ const section = byKey.get(key);
13533
+ if (section) clearSection(section);
13534
+ },
13535
+ onRemoveValue: (key, valueKey) => {
13536
+ const section = byKey.get(key);
13537
+ if (section?.selected.has(valueKey)) section.onSelect(valueKey);
13538
+ },
13539
+ onClearAll: () => {
13540
+ for (const section of active) clearSection(section);
13541
+ }
13542
+ };
13543
+ }, [sections, group, now, filterMessages]);
13544
+ }
10800
13545
  var StickyStackContext = React30.createContext(null);
10801
13546
  function StickyStack({
10802
13547
  layers,
@@ -10965,6 +13710,148 @@ var StickyStackSectionHeader = React30.forwardRef(function StickyStackSectionHea
10965
13710
  return /* @__PURE__ */ jsx(StickyStackSticky, { ref, "data-sticky-stack-section-header": "", ...props });
10966
13711
  });
10967
13712
  StickyStackSectionHeader.displayName = "StickyStackSectionHeader";
13713
+ var ListPage = forwardRef(function ListPage2({ stats, toolbar, bulkBar, children, className, ...props }, ref) {
13714
+ return /* @__PURE__ */ jsxs(
13715
+ "div",
13716
+ {
13717
+ ref,
13718
+ ...slot(listPageAnatomy, "root"),
13719
+ className: cn(
13720
+ "ds:flex ds:h-full ds:min-h-0 ds:flex-col ds:gap-3 ds:overflow-hidden",
13721
+ className
13722
+ ),
13723
+ ...props,
13724
+ children: [
13725
+ stats != null && /* @__PURE__ */ jsx("div", { ...slot(listPageAnatomy, "stats"), className: "ds:shrink-0", children: stats }),
13726
+ toolbar,
13727
+ children,
13728
+ bulkBar != null && /* @__PURE__ */ jsx("div", { ...slot(listPageAnatomy, "bulk-bar"), className: "ds:shrink-0", children: bulkBar })
13729
+ ]
13730
+ }
13731
+ );
13732
+ });
13733
+ ListPage.displayName = "ListPage";
13734
+ var ListPageToolbar = forwardRef(
13735
+ function ListPageToolbar2({ search, chips, count, actions, children, className, ...props }, ref) {
13736
+ return /* @__PURE__ */ jsxs(
13737
+ "div",
13738
+ {
13739
+ ref,
13740
+ ...slot(listPageAnatomy, "toolbar"),
13741
+ className: cn("ds:flex ds:shrink-0 ds:flex-wrap ds:items-center ds:gap-2", className),
13742
+ ...props,
13743
+ children: [
13744
+ search != null && /* @__PURE__ */ jsx("div", { ...slot(listPageAnatomy, "search"), className: "ds:flex ds:min-w-0 ds:shrink-0", children: search }),
13745
+ chips != null && /* @__PURE__ */ jsx("div", { ...slot(listPageAnatomy, "chips"), className: "ds:flex ds:min-w-0 ds:items-center", children: chips }),
13746
+ children,
13747
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ds:flex-1" }),
13748
+ count != null && /* @__PURE__ */ jsx("div", { ...slot(listPageAnatomy, "count"), className: "ds:flex ds:shrink-0 ds:items-center", children: count }),
13749
+ actions != null && /* @__PURE__ */ jsx(
13750
+ "div",
13751
+ {
13752
+ ...slot(listPageAnatomy, "actions"),
13753
+ className: "ds:flex ds:shrink-0 ds:items-center ds:gap-2",
13754
+ children: actions
13755
+ }
13756
+ )
13757
+ ]
13758
+ }
13759
+ );
13760
+ }
13761
+ );
13762
+ ListPageToolbar.displayName = "ListPageToolbar";
13763
+ var ListPageResultCount = forwardRef(
13764
+ function ListPageResultCount2({ total, start, end, loaded, locale, messages: messagesProp, className, ...props }, ref) {
13765
+ const messages = useDsMessages("listPage", messagesProp);
13766
+ const format3 = (n) => n.toLocaleString(locale);
13767
+ const text = loaded !== void 0 ? messages.loaded(format3(loaded), format3(total)) : start !== void 0 && end !== void 0 && total > 0 ? messages.range(format3(start), format3(end), format3(total)) : messages.total(format3(total));
13768
+ return /* @__PURE__ */ jsx(
13769
+ "span",
13770
+ {
13771
+ ref,
13772
+ role: "status",
13773
+ "aria-live": "polite",
13774
+ ...slot(listPageAnatomy, "result-count"),
13775
+ className: cn(
13776
+ "ds:text-xs ds:whitespace-nowrap ds:text-fg-3 ds:tabular-nums",
13777
+ className
13778
+ ),
13779
+ ...props,
13780
+ children: text
13781
+ }
13782
+ );
13783
+ }
13784
+ );
13785
+ ListPageResultCount.displayName = "ListPageResultCount";
13786
+ var SCROLL_CHILD = cn(
13787
+ "ds:[&>[data-slot=table-wrapper]]:min-h-0 ds:[&>[data-slot=table-wrapper]]:flex-initial ds:[&>[data-slot=table-wrapper]]:overflow-auto",
13788
+ "ds:[&>[data-slot=table-wrapper]]:rounded-none ds:[&>[data-slot=table-wrapper]]:border-0 ds:[&>[data-slot=table-wrapper]]:shadow-none",
13789
+ "ds:[&>[data-slot=data-table]]:min-h-0 ds:[&>[data-slot=data-table]]:flex-initial ds:[&>[data-slot=data-table]]:overflow-auto",
13790
+ "ds:[&>[data-slot=data-table]]:rounded-none ds:[&>[data-slot=data-table]]:border-0 ds:[&>[data-slot=data-table]]:shadow-none"
13791
+ );
13792
+ var ListPageTable = forwardRef(function ListPageTable2({ footer, className, children, ...props }, ref) {
13793
+ return /* @__PURE__ */ jsxs(
13794
+ "div",
13795
+ {
13796
+ ref,
13797
+ ...slot(listPageAnatomy, "table"),
13798
+ className: cn(
13799
+ "ds:flex ds:min-h-0 ds:min-w-0 ds:flex-initial ds:flex-col ds:overflow-hidden",
13800
+ "ds:rounded-surface-sm ds:border ds:border-rule-strong ds:bg-surface ds:shadow-quiet",
13801
+ SCROLL_CHILD,
13802
+ className
13803
+ ),
13804
+ ...props,
13805
+ children: [
13806
+ children,
13807
+ footer != null && /* @__PURE__ */ jsx(
13808
+ "div",
13809
+ {
13810
+ ...slot(listPageAnatomy, "footer"),
13811
+ className: "ds:shrink-0 ds:border-t ds:border-rule ds:bg-surface ds:px-(--table-cell-padding-x) ds:py-3 ds:empty:hidden ds:[&>[data-slot=pagination]]:mt-0",
13812
+ children: footer
13813
+ }
13814
+ )
13815
+ ]
13816
+ }
13817
+ );
13818
+ });
13819
+ ListPageTable.displayName = "ListPageTable";
13820
+ var ListPageScroll = forwardRef(
13821
+ function ListPageScroll2({ messages: messagesProp, className, ...props }, ref) {
13822
+ const messages = useDsMessages("listPage", messagesProp);
13823
+ const localRef = useRef(null);
13824
+ const mergedRef = useMergedRefs(ref, localRef);
13825
+ const [overflowing, setOverflowing] = useState(false);
13826
+ useEffect(() => {
13827
+ const element = localRef.current;
13828
+ if (!element || typeof ResizeObserver === "undefined") return;
13829
+ const measure = () => setOverflowing(
13830
+ element.scrollHeight > element.clientHeight + 1 || element.scrollWidth > element.clientWidth + 1
13831
+ );
13832
+ measure();
13833
+ const observer = new ResizeObserver(measure);
13834
+ observer.observe(element);
13835
+ for (const child of Array.from(element.children)) observer.observe(child);
13836
+ return () => observer.disconnect();
13837
+ }, []);
13838
+ return /* @__PURE__ */ jsx(
13839
+ "div",
13840
+ {
13841
+ ref: mergedRef,
13842
+ ...slot(listPageAnatomy, "scroll"),
13843
+ ...overflowing ? { role: "region", "aria-label": props["aria-label"] ?? messages.tableRegion, tabIndex: 0 } : {},
13844
+ className: cn(
13845
+ "ds:min-h-0 ds:flex-initial ds:overflow-auto",
13846
+ overflowing && "ds:focus-visible:outline-none ds:focus-visible:[box-shadow:var(--shadow-focus-ring)]",
13847
+ className
13848
+ ),
13849
+ ...props
13850
+ }
13851
+ );
13852
+ }
13853
+ );
13854
+ ListPageScroll.displayName = "ListPageScroll";
10968
13855
 
10969
13856
  // src/foundation/status-map.ts
10970
13857
  function defineStatusMap(map) {
@@ -11698,6 +14585,6 @@ function useTableDensity(options) {
11698
14585
  return { density, setDensity };
11699
14586
  }
11700
14587
 
11701
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityEventItem, ActivityItem, ActivityList, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppHeader, AppHeaderActions, AppHeaderBreadcrumb, AppHeaderSearch, AppHeaderTitle, AppSidebar, AppSidebarNav, AreaChart, AspectRatio, AttachmentChip, AvatarGroup, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, BulkActionBar, BulkActionBarAction, BulkActionBarSeparator, COUNTRY_CODES, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Code, CommandPalette, ConfirmActionButton, ConfirmDialog, ConfirmHost, Content6 as Content, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CountryFlag2 as CountryFlag, CountrySelect, DATE_DISPLAY_PLACEHOLDER, DEFAULT_PHONE_FIELD_MESSAGES, DangerAction, DataItem, DataTablePattern, DatePicker, DateRangePicker, DesignSystemProvider, DetailGrid, DetailMain, DetailSpine, DetailSpineHeader, DetailSpineSection, DetailSpineStats, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DismissibleChip, DisplayMenu, DonutChart, EmptyState, ErrorState, Eyebrow, Field, FieldControl, FieldDescription, FieldError, Fieldset, FieldsetLegend, FileTypeBadge, FilterChip, FolderTree, FormError, FormSection, FormSuccess, Heading, HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger, ICON_TILE_TONE_TO_INTENT, IconAction, IconTile, InfoHint, Inline, Kanban, KanbanCard, KanbanColumn, Kbd, KbdHint, KeyboardShortcutsDialog, KpiCard, Label2 as Label, LinkAction, LinkButton, LoadingRows, Main, MasterDetailLayout, MessageBubble, MessageBubbleAction, MessageBubbleTombstone, MessageComposer, MetadataGrid, MultiFilterPill, MultiSelect, MultiSelectField, MutedSpec, NotificationFilter, NotificationItem, NotificationList, NotificationPanel, NotificationPanelFooter, NotificationPanelHeader, NumberInput, Numeric, OTPInput, PageHeader, PageHeaderSep, PageHeaderSpec, PdfPreview, PeoplePicker, PhoneCountryInput, PhoneField, PhoneInput, PrimaryAction, PriorityIcon, QuestionnairePanel, RadioGroup2 as RadioGroup, RadioGroupItem, RankedBars, ResponsiveDialog, RouteTransition, ScrollArea, ScrollBar, SearchInput, SearchSelect, SecondaryAction, SectionCard, SegmentedControl, SelectableKpiCard, Separator2 as Separator, Shell, SideDrawer, Slider, SpreadsheetPreview, Stack, StackedBarChart, StarRating, StatusBadge, StatusIcon, Stepper, StickyActionBar, StickyStack, StickyStackLayer, StickyStackSectionHeader, StickyStackSticky, SubmitButton, SuiteProgress, Surface, TableDensityToggle, TableEmpty, TableError, TableLoading, Text, ToastProvider, Toolbar, VisuallyHidden, YEAR_DISPLAY_PLACEHOLDER, alertVariants, applyMask, applyYearMask, attachmentChipVariants, cardVariants, dateToIso, defineStatusMap, displayToIso, fileTypeBadgeVariants, fileTypeFromName, filterChipVariants, getFlagEmoji, resolveIconTileVariants as iconTileVariants, isoToDate, isoToDisplay, isoToYear, labelVariants, parsePhoneForEditing, searchInputVariants, starRatingVariants, suiteProgressFillVariants, useConfirm, usePrompt, useTableDensity, useToast, yearToIso };
14588
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityEventItem, ActivityItem, ActivityList, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppHeader, AppHeaderActions, AppHeaderBreadcrumb, AppHeaderSearch, AppHeaderTitle, AppSidebar, AppSidebarNav, AreaChart, AspectRatio, AttachmentChip, AvatarGroup, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, BulkActionBar, BulkActionBarAction, BulkActionBarMenu, BulkActionBarMenuItem, BulkActionBarMenuRadioGroup, BulkActionBarMenuSub, BulkActionBarMore, BulkActionBarPicker, BulkActionBarSeparator, COUNTRY_CODES, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Code, CommandPalette, ConfirmActionButton, ConfirmDialog, ConfirmHost, Content6 as Content, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CountryFlag2 as CountryFlag, CountrySelect, DATE_DISPLAY_PLACEHOLDER, DEFAULT_PHONE_FIELD_MESSAGES, DangerAction, DataItem, DataTablePattern, DatePicker, DateRangePicker, DesignSystemProvider, DetailGrid, DetailMain, DetailSpine, DetailSpineHeader, DetailSpineSection, DetailSpineStats, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DismissibleChip, DisplayMenu, DonutChart, EmptyState, ErrorState, Eyebrow, Field, FieldControl, FieldDescription, FieldError, Fieldset, FieldsetLegend, FileTypeBadge, FilterChip, FilterChips, FolderTree, FormError, FormSection, FormSuccess, Heading, HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger, ICON_TILE_TONE_TO_INTENT, IconAction, IconTile, InfoHint, Inline, Kanban, KanbanCard, KanbanColumn, Kbd, KbdHint, KeyboardShortcutsDialog, KpiCard, Label2 as Label, LinkAction, LinkButton, ListPage, ListPageResultCount, ListPageScroll, ListPageTable, ListPageToolbar, LoadingRows, Main, MasterDetailLayout, MessageBubble, MessageBubbleAction, MessageBubbleTombstone, MessageComposer, MetadataGrid, MultiFilterPill, MultiSelect, MultiSelectField, MutedSpec, NotificationFilter, NotificationItem, NotificationList, NotificationPanel, NotificationPanelFooter, NotificationPanelHeader, NumberInput, Numeric, OTPInput, PageHeader, PageHeaderSep, PageHeaderSpec, PdfPreview, PeoplePicker, PhoneCountryInput, PhoneField, PhoneInput, PrimaryAction, PriorityIcon, QuestionnairePanel, RadioGroup2 as RadioGroup, RadioGroupItem, RankedBars, ResponsiveDialog, RouteTransition, ScrollArea, ScrollBar, SearchInput, SearchSelect, SecondaryAction, SectionCard, SegmentedControl, SelectableKpiCard, Separator2 as Separator, Shell, SideDrawer, Slider, SpreadsheetPreview, Stack, StackedBarChart, StarRating, StatStrip, StatStripItem, StatusBadge, StatusIcon, Stepper, StickyActionBar, StickyStack, StickyStackLayer, StickyStackSectionHeader, StickyStackSticky, SubmitButton, SuiteProgress, Surface, TableDensityToggle, TableEmpty, TableError, TableLoading, Text, ToastProvider, Toolbar, YEAR_DISPLAY_PLACEHOLDER, alertVariants, applyMask, applyYearMask, attachmentChipVariants, cardVariants, dateToIso, defineStatusMap, detectDisplayMenuDatePreset, displayToIso, fileTypeBadgeVariants, fileTypeFromName, filterChipVariants, getFlagEmoji, resolveIconTileVariants as iconTileVariants, isDisplayMenuSectionActive, isoToDate, isoToDisplay, isoToYear, labelVariants, matchesDisplayMenuDate, matchesDisplayMenuNumber, matchesDisplayMenuOperator, matchesDisplayMenuTags, matchesDisplayMenuText, parseDisplayMenuNumber, parsePhoneForEditing, resolveDisplayMenuDatePreset, searchInputVariants, sharedBulkValue, starRatingVariants, suiteProgressFillVariants, summarizeDisplayMenuSection, useConfirm, useDisplayMenuFilterChips, usePrompt, useTableDensity, useToast, yearToIso };
11702
14589
  //# sourceMappingURL=index.js.map
11703
14590
  //# sourceMappingURL=index.js.map