@campfire-interactive/ui 0.12.0 → 0.13.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.d.ts +115 -1
- package/dist/index.js +399 -55
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1808,6 +1808,120 @@ declare function kanbanStep(columns: KanbanColumn[], from: string, delta: number
|
|
|
1808
1808
|
*/
|
|
1809
1809
|
declare function KanbanBoard<T extends KanbanItem>({ columns, items, lanes, renderCard, itemLabel, columnLabel, onMove, onOpen, emptyLabel, movedLabel, minColumnWidth, className, style, ...rest }: KanbanBoardProps<T>): react.JSX.Element;
|
|
1810
1810
|
|
|
1811
|
+
type CalendarView = 'month' | 'week';
|
|
1812
|
+
/** What the grid needs of an item. The rest of the caller's object is passed back untouched. */
|
|
1813
|
+
interface CalendarItem {
|
|
1814
|
+
id: string;
|
|
1815
|
+
/**
|
|
1816
|
+
* A `YYYY-MM-DD` day, an ISO instant, or a Date, read by its UTC day (a
|
|
1817
|
+
* Prisma `@db.Date` arrives as `2028-01-01T00:00:00.000Z`). An item without
|
|
1818
|
+
* a date that parses is left off the grid and counted in `undatedLabel`.
|
|
1819
|
+
*/
|
|
1820
|
+
date: Date | string | null | undefined;
|
|
1821
|
+
/** Any CSS color for the chip, e.g. a program's color. Defaults to the accent. White text sits on it. */
|
|
1822
|
+
color?: string;
|
|
1823
|
+
/** Drawn dashed in its color and never moved: a milestone beside the tasks. */
|
|
1824
|
+
readOnly?: boolean;
|
|
1825
|
+
/** Ringed in the error color: overdue. */
|
|
1826
|
+
flagged?: boolean;
|
|
1827
|
+
/** Struck through and faded. */
|
|
1828
|
+
done?: boolean;
|
|
1829
|
+
}
|
|
1830
|
+
interface CalendarGridLabels {
|
|
1831
|
+
today: string;
|
|
1832
|
+
previous: (view: CalendarView) => string;
|
|
1833
|
+
next: (view: CalendarView) => string;
|
|
1834
|
+
month: string;
|
|
1835
|
+
week: string;
|
|
1836
|
+
view: string;
|
|
1837
|
+
/** The add button on a day. */
|
|
1838
|
+
add: (day: string) => string;
|
|
1839
|
+
more: (count: number) => string;
|
|
1840
|
+
less: string;
|
|
1841
|
+
moved: (item: string, day: string) => string;
|
|
1842
|
+
}
|
|
1843
|
+
interface CalendarGridProps<T extends CalendarItem> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
1844
|
+
/** In the order each day lists them; the grid does not sort. */
|
|
1845
|
+
items: T[];
|
|
1846
|
+
/** The chip's content. The grid draws the chip around it, so keep buttons and links out of it. */
|
|
1847
|
+
renderItem: (item: T) => ReactNode;
|
|
1848
|
+
/** Plain text for the item, read as its name and in announcements. */
|
|
1849
|
+
itemLabel: (item: T) => string;
|
|
1850
|
+
/** Controlled view; leave out and the grid keeps its own, starting at `defaultView`. */
|
|
1851
|
+
view?: CalendarView;
|
|
1852
|
+
defaultView?: CalendarView;
|
|
1853
|
+
onViewChange?: (view: CalendarView) => void;
|
|
1854
|
+
/**
|
|
1855
|
+
* Any day in the month or week shown, `YYYY-MM-DD`. Controlled the same
|
|
1856
|
+
* way as `view`; uncontrolled it starts at today.
|
|
1857
|
+
*/
|
|
1858
|
+
anchor?: string;
|
|
1859
|
+
defaultAnchor?: string;
|
|
1860
|
+
onAnchorChange?: (anchor: string) => void;
|
|
1861
|
+
/** Which day is today. Defaults to the viewer's local day. */
|
|
1862
|
+
today?: Date | string;
|
|
1863
|
+
/** 1 (Monday, the default) or 0 (Sunday). */
|
|
1864
|
+
weekStartsOn?: 0 | 1;
|
|
1865
|
+
/**
|
|
1866
|
+
* An item was dropped on another day, or moved with Alt+Arrow keys (Left /
|
|
1867
|
+
* Right a day, Up / Down a week). Omitted, nothing drags. The grid does not
|
|
1868
|
+
* move the item itself; the caller updates `items`.
|
|
1869
|
+
*/
|
|
1870
|
+
onMove?: (item: T, day: string) => void;
|
|
1871
|
+
/** Click or Enter / Space on an item. */
|
|
1872
|
+
onOpen?: (item: T) => void;
|
|
1873
|
+
/** The add button on a day, or a click on a day's empty space. Omitted, there is no add. */
|
|
1874
|
+
onAddDay?: (day: string) => void;
|
|
1875
|
+
/** How many items a month cell lists before "+N more". Week cells list every item. */
|
|
1876
|
+
maxPerDay?: number;
|
|
1877
|
+
/** Shown under the grid: what the colors mean. */
|
|
1878
|
+
legend?: ReactNode;
|
|
1879
|
+
/** A line in the footer when some items have no date, e.g. "3 tasks have no due date". */
|
|
1880
|
+
undatedLabel?: (count: number) => ReactNode;
|
|
1881
|
+
/** More controls at the end of the header, beside the view switch. */
|
|
1882
|
+
toolbar?: ReactNode;
|
|
1883
|
+
/** Month and weekday names and the date read out for a day. */
|
|
1884
|
+
locale?: string;
|
|
1885
|
+
labels?: Partial<CalendarGridLabels>;
|
|
1886
|
+
}
|
|
1887
|
+
/** An item's day as `YYYY-MM-DD` (by UTC day), or null when it has none. */
|
|
1888
|
+
declare function calendarDayKey(value: Date | string | null | undefined): string | null;
|
|
1889
|
+
/** `day` moved `n` days. */
|
|
1890
|
+
declare function calendarAddDays(day: string, n: number): string;
|
|
1891
|
+
/**
|
|
1892
|
+
* The days a view shows: six weeks from the week holding the 1st for a month
|
|
1893
|
+
* (always 42, so the grid does not jump in height between months), the
|
|
1894
|
+
* anchor's week for a week. `inRange` is false for the days of the months
|
|
1895
|
+
* either side.
|
|
1896
|
+
*/
|
|
1897
|
+
declare function calendarDays(view: CalendarView, anchor: string, weekStartsOn?: 0 | 1): {
|
|
1898
|
+
day: string;
|
|
1899
|
+
inRange: boolean;
|
|
1900
|
+
}[];
|
|
1901
|
+
/** The anchor `delta` months or weeks on. A month lands on its 1st, so a 31st never skips a short month. */
|
|
1902
|
+
declare function calendarShift(view: CalendarView, anchor: string, delta: number): string;
|
|
1903
|
+
/** Items by day, in the order given, and how many had no day. */
|
|
1904
|
+
declare function calendarBuckets<T extends CalendarItem>(items: T[]): {
|
|
1905
|
+
byDay: Map<string, T[]>;
|
|
1906
|
+
undated: number;
|
|
1907
|
+
};
|
|
1908
|
+
/**
|
|
1909
|
+
* A month or week of days with items on them: tasks by due date, beside
|
|
1910
|
+
* read-only milestones.
|
|
1911
|
+
*
|
|
1912
|
+
* Days are calendar days, read by UTC day as `Timeline` reads them, so a due
|
|
1913
|
+
* date is the same square in Detroit and in Silao. Every drag has a keyboard
|
|
1914
|
+
* twin: Alt+Left / Alt+Right moves the focused item a day, Alt+Up / Alt+Down
|
|
1915
|
+
* a week, focus stays on it (the grid follows it into the next month), and
|
|
1916
|
+
* the move is announced. The grid never moves an item on its own; `onMove`
|
|
1917
|
+
* tells the caller, and the caller's `items` say where it is.
|
|
1918
|
+
*/
|
|
1919
|
+
declare function CalendarGrid<T extends CalendarItem>({ items, renderItem, itemLabel, view: viewProp, defaultView, onViewChange, anchor: anchorProp, defaultAnchor, onAnchorChange, today: todayProp, weekStartsOn, onMove, onOpen, onAddDay, maxPerDay, legend, undatedLabel, toolbar, locale, labels: labelsProp, className, ...rest }: CalendarGridProps<T>): react.JSX.Element;
|
|
1920
|
+
/** A swatch for a legend under the grid, drawn the way the grid draws an item. */
|
|
1921
|
+
declare function CalendarLegendItem({ color, readOnly, flagged, children, }: Pick<CalendarItem, 'color' | 'readOnly' | 'flagged'> & {
|
|
1922
|
+
children: ReactNode;
|
|
1923
|
+
}): react.JSX.Element;
|
|
1924
|
+
|
|
1811
1925
|
interface SortState<K extends string> {
|
|
1812
1926
|
key: K;
|
|
1813
1927
|
direction: SortDirection;
|
|
@@ -2177,4 +2291,4 @@ declare function formatNumber(value: number | null | undefined, decimals?: numbe
|
|
|
2177
2291
|
*/
|
|
2178
2292
|
declare function formatPercent(value: number | null | undefined, decimals?: number): string;
|
|
2179
2293
|
|
|
2180
|
-
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionRoot, type AccordionSection, AccordionTrigger, type AccordionTriggerProps, Alert, type AlertProps, type AlertTone, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeProps, type BadgeTone, BootScreen, type BootScreenProps, type Breadcrumb, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, CardDescription, CardFooter, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ClassValue, Combobox, type ComboboxOption, type ComboboxProps, ComingSoon, type ComingSoonProps, ConfirmDialog, type ConfirmDialogProps, CountBadge, type CountBadgeProps, CreatableSelect, type CreatableSelectProps, DataList, DataListItem, type DataListItemProps, type DataListProps, Dialog, DialogBody, DialogClose, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, DialogPortal, DialogRoot, type DialogSize, DialogTitle, DialogTrigger, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, ExternalLink, type ExternalLinkProps, Field, type FieldProps, Fieldset, type FieldsetProps, FileButton, type FileButtonProps, FilterBar, type FilterBarProps, type FilterSelect, type FilterToggle, type FilterValue, IconButton, type IconButtonProps, Input, type InputProps, type InputSize, KanbanBoard, type KanbanBoardProps, type KanbanColumn, type KanbanItem, type KanbanLane, type KanbanLayout, type KanbanTone, Label, type LabelProps, ListToolbar, type ListToolbarProps, LoadingState, type LoadingStateProps, Menu, type MenuAction, MenuCheckboxItem, MenuContent, MenuGroup, MenuItem, type MenuItemProps, MenuLabel, type MenuProps, MenuRadioGroup, MenuRadioItem, MenuRoot, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Modal, type ModalProps, type MoneyOptions, PageHeader, type PageHeaderProps, type PagedRows, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverRoot, PopoverTrigger, Progress, type ProgressProps, RequiredMark, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOption, type SelectProps, SelectRoot, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, SelectionBar, type SelectionBarProps, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, SideModal, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortDirection, type SortState, SplitButton, type SplitButtonProps, StatGrid, type StatGridProps, StatTile, type StatTileProps, type StatTone, Switch, type SwitchProps, type TabDefinition, TabSet, type TabSetProps, Table, TableBody, TableCaption, TableCell, type TableCellProps, TableEmpty, TableFoot, TableFooter, TableHead, type TableHeadProps, TableHeader, type TableProps, TableRow, type TableRowProps, TableScroller, Tabs, TabsContent, TabsList, type TabsListProps, TabsRoot, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, Timeline, type TimelineItem, type TimelineLane, type TimelineLayout, type TimelineLayoutItem, type TimelineProps, type TimelineTone, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, ToggleGroup, type ToggleGroupProps, type ToggleOption, Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, TooltipRoot, TooltipTrigger, type UseSortStateResult, cn, formatCalendarDate, formatDate, formatDateTime, formatMoney, formatNumber, formatPercent, kanbanLayout, kanbanStep, timelineLayout, useDebouncedValue, useFieldControl, usePagedRows, usePersistentState, useSortState, useToast };
|
|
2294
|
+
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionRoot, type AccordionSection, AccordionTrigger, type AccordionTriggerProps, Alert, type AlertProps, type AlertTone, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeProps, type BadgeTone, BootScreen, type BootScreenProps, type Breadcrumb, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, CalendarGrid, type CalendarGridLabels, type CalendarGridProps, type CalendarItem, CalendarLegendItem, type CalendarView, Card, CardBody, CardDescription, CardFooter, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ClassValue, Combobox, type ComboboxOption, type ComboboxProps, ComingSoon, type ComingSoonProps, ConfirmDialog, type ConfirmDialogProps, CountBadge, type CountBadgeProps, CreatableSelect, type CreatableSelectProps, DataList, DataListItem, type DataListItemProps, type DataListProps, Dialog, DialogBody, DialogClose, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, DialogPortal, DialogRoot, type DialogSize, DialogTitle, DialogTrigger, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, ExternalLink, type ExternalLinkProps, Field, type FieldProps, Fieldset, type FieldsetProps, FileButton, type FileButtonProps, FilterBar, type FilterBarProps, type FilterSelect, type FilterToggle, type FilterValue, IconButton, type IconButtonProps, Input, type InputProps, type InputSize, KanbanBoard, type KanbanBoardProps, type KanbanColumn, type KanbanItem, type KanbanLane, type KanbanLayout, type KanbanTone, Label, type LabelProps, ListToolbar, type ListToolbarProps, LoadingState, type LoadingStateProps, Menu, type MenuAction, MenuCheckboxItem, MenuContent, MenuGroup, MenuItem, type MenuItemProps, MenuLabel, type MenuProps, MenuRadioGroup, MenuRadioItem, MenuRoot, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Modal, type ModalProps, type MoneyOptions, PageHeader, type PageHeaderProps, type PagedRows, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverRoot, PopoverTrigger, Progress, type ProgressProps, RequiredMark, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOption, type SelectProps, SelectRoot, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, SelectionBar, type SelectionBarProps, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, SideModal, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortDirection, type SortState, SplitButton, type SplitButtonProps, StatGrid, type StatGridProps, StatTile, type StatTileProps, type StatTone, Switch, type SwitchProps, type TabDefinition, TabSet, type TabSetProps, Table, TableBody, TableCaption, TableCell, type TableCellProps, TableEmpty, TableFoot, TableFooter, TableHead, type TableHeadProps, TableHeader, type TableProps, TableRow, type TableRowProps, TableScroller, Tabs, TabsContent, TabsList, type TabsListProps, TabsRoot, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, Timeline, type TimelineItem, type TimelineLane, type TimelineLayout, type TimelineLayoutItem, type TimelineProps, type TimelineTone, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, ToggleGroup, type ToggleGroupProps, type ToggleOption, Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, TooltipRoot, TooltipTrigger, type UseSortStateResult, calendarAddDays, calendarBuckets, calendarDayKey, calendarDays, calendarShift, cn, formatCalendarDate, formatDate, formatDateTime, formatMoney, formatNumber, formatPercent, kanbanLayout, kanbanStep, timelineLayout, useDebouncedValue, useFieldControl, usePagedRows, usePersistentState, useSortState, useToast };
|
package/dist/index.js
CHANGED
|
@@ -777,13 +777,13 @@ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
|
777
777
|
var Slider = forwardRef10(
|
|
778
778
|
function Slider2({ showValue = false, formatValue, className, ...rest }, ref) {
|
|
779
779
|
const values = rest.value ?? rest.defaultValue ?? [rest.min ?? 0];
|
|
780
|
-
const
|
|
780
|
+
const format2 = formatValue ?? ((v) => String(v));
|
|
781
781
|
return /* @__PURE__ */ jsxs12("div", { className: cn("cfi-ui", "cfi-ui-slider-wrap", className), children: [
|
|
782
782
|
/* @__PURE__ */ jsxs12(RadixSlider.Root, { ref, className: "cfi-ui-slider", ...rest, children: [
|
|
783
783
|
/* @__PURE__ */ jsx12(RadixSlider.Track, { className: "cfi-ui-slider__track", children: /* @__PURE__ */ jsx12(RadixSlider.Range, { className: "cfi-ui-slider__range" }) }),
|
|
784
784
|
values.map((_, i) => /* @__PURE__ */ jsx12(RadixSlider.Thumb, { className: "cfi-ui-slider__thumb" }, i))
|
|
785
785
|
] }),
|
|
786
|
-
showValue && /* @__PURE__ */ jsx12("output", { className: "cfi-ui-slider__value", children: values.map(
|
|
786
|
+
showValue && /* @__PURE__ */ jsx12("output", { className: "cfi-ui-slider__value", children: values.map(format2).join(" \u2013 ") })
|
|
787
787
|
] });
|
|
788
788
|
}
|
|
789
789
|
);
|
|
@@ -2673,10 +2673,347 @@ function KanbanBoard({
|
|
|
2673
2673
|
);
|
|
2674
2674
|
}
|
|
2675
2675
|
|
|
2676
|
+
// src/CalendarGrid.tsx
|
|
2677
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState7 } from "react";
|
|
2678
|
+
import { ChevronLeft, ChevronRight, Plus as Plus2 } from "lucide-react";
|
|
2679
|
+
|
|
2680
|
+
// src/CalendarGrid.css
|
|
2681
|
+
styleInject("@layer components {\n :where(.cfi-ui-calendar) {\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--cfi-ui-border);\n border-radius: var(--cfi-ui-radius-xl);\n background: var(--cfi-ui-surface);\n box-shadow: var(--cfi-ui-shadow-xs);\n font-family: var(--cfi-ui-font);\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-calendar__header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n padding: var(--cfi-ui-space-sm) var(--cfi-ui-space-md);\n border-bottom: 1px solid var(--cfi-ui-border);\n }\n .cfi-ui-calendar__title {\n margin: 0 0 0 var(--cfi-ui-space-xs);\n font-size: var(--cfi-ui-text-lg);\n font-weight: var(--cfi-ui-weight-semibold);\n line-height: var(--cfi-ui-leading-tight);\n }\n .cfi-ui-calendar__tools {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n margin-left: auto;\n }\n .cfi-ui-calendar__scroll {\n overflow-x: auto;\n }\n .cfi-ui-calendar__weekdays,\n .cfi-ui-calendar__grid {\n display: grid;\n grid-template-columns: repeat(7, minmax(7rem, 1fr));\n }\n .cfi-ui-calendar__weekdays {\n border-bottom: 1px solid var(--cfi-ui-border);\n background: var(--cfi-ui-surface-sunken);\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-semibold);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-calendar__weekdays > div {\n padding: var(--cfi-ui-space-2xs) var(--cfi-ui-space-sm);\n }\n .cfi-ui-calendar__day {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-ui-space-3xs);\n min-width: 0;\n min-height: 7rem;\n padding: var(--cfi-ui-space-2xs);\n border-right: 1px solid var(--cfi-ui-border);\n border-bottom: 1px solid var(--cfi-ui-border);\n transition: background var(--cfi-ui-transition-fast), box-shadow var(--cfi-ui-transition-fast);\n }\n .cfi-ui-calendar__day:nth-child(7n) {\n border-right: 0;\n }\n .cfi-ui-calendar[data-view=month] .cfi-ui-calendar__day:nth-last-child(-n+7),\n .cfi-ui-calendar[data-view=week] .cfi-ui-calendar__day {\n border-bottom: 0;\n }\n .cfi-ui-calendar[data-view=week] .cfi-ui-calendar__day {\n min-height: 20rem;\n }\n .cfi-ui-calendar__day[data-outside] {\n background: var(--cfi-ui-surface-sunken);\n }\n .cfi-ui-calendar__day[data-addable] {\n cursor: pointer;\n }\n .cfi-ui-calendar__day[data-addable]:hover {\n background: var(--cfi-ui-surface-hover);\n }\n .cfi-ui-calendar__day[data-over] {\n background: var(--cfi-ui-accent-soft);\n box-shadow: inset 0 0 0 2px var(--cfi-ui-accent);\n }\n .cfi-ui-calendar__day-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n .cfi-ui-calendar__date {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.5rem;\n height: 1.5rem;\n border-radius: var(--cfi-ui-radius-full);\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n font-variant-numeric: tabular-nums;\n }\n .cfi-ui-calendar__day[data-outside] .cfi-ui-calendar__date {\n color: var(--cfi-ui-content-subtle);\n }\n .cfi-ui-calendar__day[data-today] .cfi-ui-calendar__date {\n background: var(--cfi-ui-accent);\n color: var(--cfi-ui-accent-fg);\n font-weight: var(--cfi-ui-weight-semibold);\n }\n .cfi-ui-calendar__add {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: 0;\n border-radius: var(--cfi-ui-radius-sm);\n background: none;\n color: var(--cfi-ui-accent-text);\n cursor: pointer;\n opacity: 0;\n }\n .cfi-ui-calendar__add svg {\n width: 0.875rem;\n height: 0.875rem;\n }\n .cfi-ui-calendar__day:hover .cfi-ui-calendar__add,\n .cfi-ui-calendar__add:focus-visible {\n opacity: 1;\n }\n .cfi-ui-calendar__add:hover {\n background: var(--cfi-ui-accent-soft);\n }\n .cfi-ui-calendar__add:focus-visible,\n .cfi-ui-calendar__more:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-calendar__items {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-ui-space-3xs);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n .cfi-ui-calendar__item {\n overflow: hidden;\n padding: 0.125rem var(--cfi-ui-space-2xs);\n border: 1px solid transparent;\n border-radius: var(--cfi-ui-radius-sm);\n background: var(--cfi-ui-calendar-item, var(--cfi-ui-accent));\n color: var(--cfi-ui-content-inverse);\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n line-height: var(--cfi-ui-leading-snug);\n text-overflow: ellipsis;\n white-space: nowrap;\n box-shadow: var(--cfi-ui-shadow-xs);\n transition: filter var(--cfi-ui-transition-fast), opacity var(--cfi-ui-transition-fast);\n }\n .cfi-ui-calendar__item[role=button] {\n cursor: pointer;\n }\n .cfi-ui-calendar__item[data-movable] {\n cursor: grab;\n }\n .cfi-ui-calendar__item[data-movable]:active {\n cursor: grabbing;\n }\n .cfi-ui-calendar__item[role=button]:hover {\n filter: brightness(1.1);\n }\n .cfi-ui-calendar__item:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-calendar__item[data-read-only],\n .cfi-ui-calendar__swatch[data-read-only] {\n border: 1px dashed var(--cfi-ui-calendar-item, var(--cfi-ui-accent));\n background: var(--cfi-ui-surface);\n color: var(--cfi-ui-calendar-item, var(--cfi-ui-accent-text));\n box-shadow: none;\n }\n .cfi-ui-calendar__item[data-read-only][role=button]:hover {\n filter: none;\n background: var(--cfi-ui-surface-hover);\n }\n .cfi-ui-calendar__item[data-flagged],\n .cfi-ui-calendar__swatch[data-flagged] {\n box-shadow: 0 0 0 1px var(--cfi-ui-surface), 0 0 0 3px var(--cfi-ui-error);\n }\n .cfi-ui-calendar__item[data-done] {\n opacity: 0.6;\n text-decoration: line-through;\n }\n .cfi-ui-calendar__item[data-dragging] {\n opacity: 0.4;\n }\n .cfi-ui-calendar__more {\n align-self: flex-start;\n padding: 0 var(--cfi-ui-space-2xs);\n border: 0;\n border-radius: var(--cfi-ui-radius-sm);\n background: none;\n font: inherit;\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n color: var(--cfi-ui-content-muted);\n cursor: pointer;\n }\n .cfi-ui-calendar__more:hover {\n background: var(--cfi-ui-surface-hover);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-calendar__footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--cfi-ui-space-2xs) var(--cfi-ui-space-md);\n padding: var(--cfi-ui-space-sm) var(--cfi-ui-space-md);\n border-top: 1px solid var(--cfi-ui-border);\n font-size: var(--cfi-ui-text-xs);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-calendar__undated {\n margin-left: auto;\n }\n .cfi-ui-calendar__legend-item {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-2xs);\n }\n .cfi-ui-calendar__swatch {\n flex: none;\n width: 1.25rem;\n height: 0.75rem;\n border: 1px solid transparent;\n border-radius: var(--cfi-ui-radius-sm);\n background: var(--cfi-ui-calendar-item, var(--cfi-ui-accent));\n }\n .cfi-ui-calendar__live {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n @media (prefers-reduced-motion: reduce) {\n .cfi-ui-calendar__day,\n .cfi-ui-calendar__item {\n transition: none;\n }\n }\n}\n");
|
|
2682
|
+
|
|
2683
|
+
// src/CalendarGrid.tsx
|
|
2684
|
+
import { jsx as jsx30, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2685
|
+
var DEFAULT_LABELS = {
|
|
2686
|
+
today: "Today",
|
|
2687
|
+
previous: (view) => `Previous ${view}`,
|
|
2688
|
+
next: (view) => `Next ${view}`,
|
|
2689
|
+
month: "Month",
|
|
2690
|
+
week: "Week",
|
|
2691
|
+
view: "Calendar view",
|
|
2692
|
+
add: (day) => `Add on ${day}`,
|
|
2693
|
+
more: (count) => `+${count} more`,
|
|
2694
|
+
less: "Show fewer",
|
|
2695
|
+
moved: (item, day) => `Moved ${item} to ${day}`
|
|
2696
|
+
};
|
|
2697
|
+
var DAY2 = 864e5;
|
|
2698
|
+
function ymd2(t) {
|
|
2699
|
+
return new Date(t).toISOString().slice(0, 10);
|
|
2700
|
+
}
|
|
2701
|
+
function calendarDayKey(value) {
|
|
2702
|
+
const t = timelineTime(value, true);
|
|
2703
|
+
return t === null ? null : ymd2(t);
|
|
2704
|
+
}
|
|
2705
|
+
function calendarAddDays(day, n) {
|
|
2706
|
+
return ymd2(timelineTime(day, true) + n * DAY2);
|
|
2707
|
+
}
|
|
2708
|
+
function calendarDays(view, anchor, weekStartsOn = 1) {
|
|
2709
|
+
const a = new Date(timelineTime(anchor, true));
|
|
2710
|
+
const from = view === "month" ? Date.UTC(a.getUTCFullYear(), a.getUTCMonth(), 1) : a.getTime();
|
|
2711
|
+
const back = (new Date(from).getUTCDay() - weekStartsOn + 7) % 7;
|
|
2712
|
+
const start = from - back * DAY2;
|
|
2713
|
+
const month = a.getUTCMonth();
|
|
2714
|
+
return Array.from({ length: view === "month" ? 42 : 7 }, (_, i) => {
|
|
2715
|
+
const t = start + i * DAY2;
|
|
2716
|
+
return { day: ymd2(t), inRange: view === "week" || new Date(t).getUTCMonth() === month };
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
function calendarShift(view, anchor, delta) {
|
|
2720
|
+
if (view === "week") return calendarAddDays(anchor, delta * 7);
|
|
2721
|
+
const a = new Date(timelineTime(anchor, true));
|
|
2722
|
+
return ymd2(Date.UTC(a.getUTCFullYear(), a.getUTCMonth() + delta, 1));
|
|
2723
|
+
}
|
|
2724
|
+
function calendarBuckets(items) {
|
|
2725
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
2726
|
+
let undated = 0;
|
|
2727
|
+
for (const item of items) {
|
|
2728
|
+
const day = calendarDayKey(item.date);
|
|
2729
|
+
if (day === null) {
|
|
2730
|
+
undated += 1;
|
|
2731
|
+
continue;
|
|
2732
|
+
}
|
|
2733
|
+
const list = byDay.get(day);
|
|
2734
|
+
if (list) list.push(item);
|
|
2735
|
+
else byDay.set(day, [item]);
|
|
2736
|
+
}
|
|
2737
|
+
return { byDay, undated };
|
|
2738
|
+
}
|
|
2739
|
+
function localToday() {
|
|
2740
|
+
const now = /* @__PURE__ */ new Date();
|
|
2741
|
+
return ymd2(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
|
|
2742
|
+
}
|
|
2743
|
+
function format(day, locale, options) {
|
|
2744
|
+
return new Date(timelineTime(day, true)).toLocaleDateString(locale, { ...options, timeZone: "UTC" });
|
|
2745
|
+
}
|
|
2746
|
+
function CalendarGrid({
|
|
2747
|
+
items,
|
|
2748
|
+
renderItem,
|
|
2749
|
+
itemLabel,
|
|
2750
|
+
view: viewProp,
|
|
2751
|
+
defaultView = "month",
|
|
2752
|
+
onViewChange,
|
|
2753
|
+
anchor: anchorProp,
|
|
2754
|
+
defaultAnchor,
|
|
2755
|
+
onAnchorChange,
|
|
2756
|
+
today: todayProp,
|
|
2757
|
+
weekStartsOn = 1,
|
|
2758
|
+
onMove,
|
|
2759
|
+
onOpen,
|
|
2760
|
+
onAddDay,
|
|
2761
|
+
maxPerDay = 3,
|
|
2762
|
+
legend,
|
|
2763
|
+
undatedLabel,
|
|
2764
|
+
toolbar,
|
|
2765
|
+
locale = "en-US",
|
|
2766
|
+
labels: labelsProp,
|
|
2767
|
+
className,
|
|
2768
|
+
...rest
|
|
2769
|
+
}) {
|
|
2770
|
+
const labels = { ...DEFAULT_LABELS, ...labelsProp };
|
|
2771
|
+
const today = todayProp !== void 0 && calendarDayKey(todayProp) || localToday();
|
|
2772
|
+
const [ownView, setOwnView] = useState7(defaultView);
|
|
2773
|
+
const [ownAnchor, setOwnAnchor] = useState7(() => defaultAnchor && calendarDayKey(defaultAnchor) || today);
|
|
2774
|
+
const view = viewProp ?? ownView;
|
|
2775
|
+
const anchor = anchorProp && calendarDayKey(anchorProp) || ownAnchor;
|
|
2776
|
+
const setView = (next) => {
|
|
2777
|
+
if (viewProp === void 0) setOwnView(next);
|
|
2778
|
+
onViewChange?.(next);
|
|
2779
|
+
};
|
|
2780
|
+
const setAnchor = (next) => {
|
|
2781
|
+
if (anchorProp === void 0) setOwnAnchor(next);
|
|
2782
|
+
onAnchorChange?.(next);
|
|
2783
|
+
};
|
|
2784
|
+
const [drag, setDrag] = useState7(null);
|
|
2785
|
+
const [over, setOver] = useState7(null);
|
|
2786
|
+
const [expanded, setExpanded] = useState7(/* @__PURE__ */ new Set());
|
|
2787
|
+
const [announcement, setAnnouncement] = useState7("");
|
|
2788
|
+
const rootRef = useRef5(null);
|
|
2789
|
+
const refocus = useRef5(null);
|
|
2790
|
+
useEffect5(() => {
|
|
2791
|
+
const id = refocus.current;
|
|
2792
|
+
if (!id || !rootRef.current) return;
|
|
2793
|
+
const chip = rootRef.current.querySelector(`[data-calendar-item="${CSS.escape(id)}"]`);
|
|
2794
|
+
if (chip) {
|
|
2795
|
+
chip.focus();
|
|
2796
|
+
refocus.current = null;
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
const days = calendarDays(view, anchor, weekStartsOn);
|
|
2800
|
+
const { byDay, undated } = calendarBuckets(items);
|
|
2801
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
2802
|
+
const shown = new Set(days.map((d) => d.day));
|
|
2803
|
+
const showsToday = shown.has(today) && (view === "week" || days.find((d) => d.day === today)?.inRange);
|
|
2804
|
+
const longDay = (day) => format(day, locale, { weekday: "long", month: "long", day: "numeric", year: "numeric" });
|
|
2805
|
+
const title = view === "month" ? format(anchor, locale, { month: "long", year: "numeric" }) : `${format(days[0].day, locale, { month: "short", day: "numeric" })} \u2013 ${format(days[6].day, locale, {
|
|
2806
|
+
month: "short",
|
|
2807
|
+
day: "numeric",
|
|
2808
|
+
year: "numeric"
|
|
2809
|
+
})}`;
|
|
2810
|
+
const move = (item, day, viaKeyboard) => {
|
|
2811
|
+
if (!onMove || item.readOnly || calendarDayKey(item.date) === day) return;
|
|
2812
|
+
if (viaKeyboard) {
|
|
2813
|
+
refocus.current = item.id;
|
|
2814
|
+
if (!shown.has(day) || view === "month" && !days.find((d) => d.day === day)?.inRange) setAnchor(day);
|
|
2815
|
+
}
|
|
2816
|
+
onMove(item, day);
|
|
2817
|
+
setAnnouncement(labels.moved(itemLabel(item), longDay(day)));
|
|
2818
|
+
};
|
|
2819
|
+
const endDrag = () => {
|
|
2820
|
+
setDrag(null);
|
|
2821
|
+
setOver(null);
|
|
2822
|
+
};
|
|
2823
|
+
return /* @__PURE__ */ jsxs30("div", { ref: rootRef, className: cn("cfi-ui", "cfi-ui-calendar", className), "data-view": view, ...rest, children: [
|
|
2824
|
+
/* @__PURE__ */ jsxs30("div", { className: "cfi-ui-calendar__header", children: [
|
|
2825
|
+
/* @__PURE__ */ jsx30(Button, { size: "sm", variant: "secondary", onClick: () => setAnchor(today), disabled: showsToday || void 0, children: labels.today }),
|
|
2826
|
+
/* @__PURE__ */ jsx30(
|
|
2827
|
+
IconButton,
|
|
2828
|
+
{
|
|
2829
|
+
size: "sm",
|
|
2830
|
+
variant: "ghost",
|
|
2831
|
+
icon: /* @__PURE__ */ jsx30(ChevronLeft, { "aria-hidden": "true" }),
|
|
2832
|
+
label: labels.previous(view),
|
|
2833
|
+
onClick: () => setAnchor(calendarShift(view, anchor, -1))
|
|
2834
|
+
}
|
|
2835
|
+
),
|
|
2836
|
+
/* @__PURE__ */ jsx30(
|
|
2837
|
+
IconButton,
|
|
2838
|
+
{
|
|
2839
|
+
size: "sm",
|
|
2840
|
+
variant: "ghost",
|
|
2841
|
+
icon: /* @__PURE__ */ jsx30(ChevronRight, { "aria-hidden": "true" }),
|
|
2842
|
+
label: labels.next(view),
|
|
2843
|
+
onClick: () => setAnchor(calendarShift(view, anchor, 1))
|
|
2844
|
+
}
|
|
2845
|
+
),
|
|
2846
|
+
/* @__PURE__ */ jsx30("h2", { className: "cfi-ui-calendar__title", "aria-live": "polite", children: title }),
|
|
2847
|
+
/* @__PURE__ */ jsxs30("div", { className: "cfi-ui-calendar__tools", children: [
|
|
2848
|
+
toolbar,
|
|
2849
|
+
/* @__PURE__ */ jsx30(
|
|
2850
|
+
ToggleGroup,
|
|
2851
|
+
{
|
|
2852
|
+
size: "sm",
|
|
2853
|
+
"aria-label": labels.view,
|
|
2854
|
+
value: view,
|
|
2855
|
+
onValueChange: setView,
|
|
2856
|
+
options: [
|
|
2857
|
+
{ value: "month", label: labels.month },
|
|
2858
|
+
{ value: "week", label: labels.week }
|
|
2859
|
+
]
|
|
2860
|
+
}
|
|
2861
|
+
)
|
|
2862
|
+
] })
|
|
2863
|
+
] }),
|
|
2864
|
+
/* @__PURE__ */ jsxs30("div", { className: "cfi-ui-calendar__scroll", children: [
|
|
2865
|
+
/* @__PURE__ */ jsx30("div", { className: "cfi-ui-calendar__weekdays", "aria-hidden": "true", children: days.slice(0, 7).map(({ day }) => /* @__PURE__ */ jsx30("div", { children: format(day, locale, { weekday: "short" }) }, day)) }),
|
|
2866
|
+
/* @__PURE__ */ jsx30("div", { className: "cfi-ui-calendar__grid", children: days.map(({ day, inRange }) => {
|
|
2867
|
+
const dayItems = byDay.get(day) ?? [];
|
|
2868
|
+
const capped = view === "month" && !expanded.has(day) && dayItems.length > maxPerDay + 1;
|
|
2869
|
+
const visible = capped ? dayItems.slice(0, maxPerDay) : dayItems;
|
|
2870
|
+
const accepts = drag !== null && calendarDayKey(byId.get(drag)?.date) !== day;
|
|
2871
|
+
const name = longDay(day);
|
|
2872
|
+
return /* @__PURE__ */ jsxs30(
|
|
2873
|
+
"div",
|
|
2874
|
+
{
|
|
2875
|
+
className: "cfi-ui-calendar__day",
|
|
2876
|
+
"data-outside": !inRange || void 0,
|
|
2877
|
+
"data-today": day === today || void 0,
|
|
2878
|
+
"data-over": over === day || void 0,
|
|
2879
|
+
"data-addable": onAddDay ? true : void 0,
|
|
2880
|
+
onClick: (e) => {
|
|
2881
|
+
if (onAddDay && (e.target === e.currentTarget || e.target.dataset.calendarEmpty)) onAddDay(day);
|
|
2882
|
+
},
|
|
2883
|
+
onDragOver: (e) => {
|
|
2884
|
+
if (!accepts) return;
|
|
2885
|
+
e.preventDefault();
|
|
2886
|
+
e.dataTransfer.dropEffect = "move";
|
|
2887
|
+
if (over !== day) setOver(day);
|
|
2888
|
+
},
|
|
2889
|
+
onDragLeave: (e) => {
|
|
2890
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setOver((o) => o === day ? null : o);
|
|
2891
|
+
},
|
|
2892
|
+
onDrop: (e) => {
|
|
2893
|
+
e.preventDefault();
|
|
2894
|
+
const item = drag ? byId.get(drag) : void 0;
|
|
2895
|
+
if (item && accepts) move(item, day, false);
|
|
2896
|
+
endDrag();
|
|
2897
|
+
},
|
|
2898
|
+
children: [
|
|
2899
|
+
/* @__PURE__ */ jsxs30("div", { className: "cfi-ui-calendar__day-head", "data-calendar-empty": "1", children: [
|
|
2900
|
+
/* @__PURE__ */ jsx30("span", { className: "cfi-ui-calendar__date", "data-calendar-empty": "1", "aria-current": day === today ? "date" : void 0, children: Number(day.slice(8)) }),
|
|
2901
|
+
onAddDay ? /* @__PURE__ */ jsx30(
|
|
2902
|
+
"button",
|
|
2903
|
+
{
|
|
2904
|
+
type: "button",
|
|
2905
|
+
className: "cfi-ui-calendar__add",
|
|
2906
|
+
"aria-label": labels.add(name),
|
|
2907
|
+
title: labels.add(name),
|
|
2908
|
+
onClick: (e) => {
|
|
2909
|
+
e.stopPropagation();
|
|
2910
|
+
onAddDay(day);
|
|
2911
|
+
},
|
|
2912
|
+
children: /* @__PURE__ */ jsx30(Plus2, { "aria-hidden": "true" })
|
|
2913
|
+
}
|
|
2914
|
+
) : null
|
|
2915
|
+
] }),
|
|
2916
|
+
/* @__PURE__ */ jsx30("ul", { className: "cfi-ui-calendar__items", "aria-label": name, "data-calendar-empty": "1", children: visible.map((item) => {
|
|
2917
|
+
const movable = onMove && !item.readOnly;
|
|
2918
|
+
return /* @__PURE__ */ jsx30("li", { children: /* @__PURE__ */ jsx30(
|
|
2919
|
+
"div",
|
|
2920
|
+
{
|
|
2921
|
+
className: "cfi-ui-calendar__item",
|
|
2922
|
+
"data-calendar-item": item.id,
|
|
2923
|
+
"data-read-only": item.readOnly || void 0,
|
|
2924
|
+
"data-flagged": item.flagged || void 0,
|
|
2925
|
+
"data-done": item.done || void 0,
|
|
2926
|
+
"data-dragging": drag === item.id || void 0,
|
|
2927
|
+
"data-movable": movable || void 0,
|
|
2928
|
+
style: item.color ? { "--cfi-ui-calendar-item": item.color } : void 0,
|
|
2929
|
+
title: itemLabel(item),
|
|
2930
|
+
role: onOpen ? "button" : void 0,
|
|
2931
|
+
tabIndex: onOpen || movable ? 0 : void 0,
|
|
2932
|
+
"aria-keyshortcuts": movable ? "Alt+ArrowLeft Alt+ArrowRight Alt+ArrowUp Alt+ArrowDown" : void 0,
|
|
2933
|
+
draggable: movable || void 0,
|
|
2934
|
+
onDragStart: (e) => {
|
|
2935
|
+
e.stopPropagation();
|
|
2936
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2937
|
+
e.dataTransfer.setData("text/plain", item.id);
|
|
2938
|
+
setTimeout(() => setDrag(item.id), 0);
|
|
2939
|
+
},
|
|
2940
|
+
onDragEnd: endDrag,
|
|
2941
|
+
onClick: (e) => {
|
|
2942
|
+
e.stopPropagation();
|
|
2943
|
+
onOpen?.(item);
|
|
2944
|
+
},
|
|
2945
|
+
onKeyDown: (e) => {
|
|
2946
|
+
if (e.target !== e.currentTarget) return;
|
|
2947
|
+
const step = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[e.key];
|
|
2948
|
+
if (movable && e.altKey && step !== void 0) {
|
|
2949
|
+
e.preventDefault();
|
|
2950
|
+
move(item, calendarAddDays(day, step), true);
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
if (onOpen && (e.key === "Enter" || e.key === " ")) {
|
|
2954
|
+
e.preventDefault();
|
|
2955
|
+
onOpen(item);
|
|
2956
|
+
}
|
|
2957
|
+
},
|
|
2958
|
+
children: renderItem(item)
|
|
2959
|
+
}
|
|
2960
|
+
) }, item.id);
|
|
2961
|
+
}) }),
|
|
2962
|
+
capped || expanded.has(day) ? /* @__PURE__ */ jsx30(
|
|
2963
|
+
"button",
|
|
2964
|
+
{
|
|
2965
|
+
type: "button",
|
|
2966
|
+
className: "cfi-ui-calendar__more",
|
|
2967
|
+
onClick: (e) => {
|
|
2968
|
+
e.stopPropagation();
|
|
2969
|
+
setExpanded((all) => {
|
|
2970
|
+
const next = new Set(all);
|
|
2971
|
+
if (next.has(day)) next.delete(day);
|
|
2972
|
+
else next.add(day);
|
|
2973
|
+
return next;
|
|
2974
|
+
});
|
|
2975
|
+
},
|
|
2976
|
+
children: capped ? labels.more(dayItems.length - visible.length) : labels.less
|
|
2977
|
+
}
|
|
2978
|
+
) : null
|
|
2979
|
+
]
|
|
2980
|
+
},
|
|
2981
|
+
day
|
|
2982
|
+
);
|
|
2983
|
+
}) })
|
|
2984
|
+
] }),
|
|
2985
|
+
legend || undated > 0 && undatedLabel ? /* @__PURE__ */ jsxs30("div", { className: "cfi-ui-calendar__footer", children: [
|
|
2986
|
+
legend,
|
|
2987
|
+
undated > 0 && undatedLabel ? /* @__PURE__ */ jsx30("span", { className: "cfi-ui-calendar__undated", children: undatedLabel(undated) }) : null
|
|
2988
|
+
] }) : null,
|
|
2989
|
+
/* @__PURE__ */ jsx30("div", { className: "cfi-ui-calendar__live", role: "status", "aria-live": "polite", children: announcement })
|
|
2990
|
+
] });
|
|
2991
|
+
}
|
|
2992
|
+
function CalendarLegendItem({
|
|
2993
|
+
color,
|
|
2994
|
+
readOnly,
|
|
2995
|
+
flagged,
|
|
2996
|
+
children
|
|
2997
|
+
}) {
|
|
2998
|
+
return /* @__PURE__ */ jsxs30("span", { className: "cfi-ui-calendar__legend-item", children: [
|
|
2999
|
+
/* @__PURE__ */ jsx30(
|
|
3000
|
+
"span",
|
|
3001
|
+
{
|
|
3002
|
+
className: "cfi-ui-calendar__swatch",
|
|
3003
|
+
"data-read-only": readOnly || void 0,
|
|
3004
|
+
"data-flagged": flagged || void 0,
|
|
3005
|
+
style: color ? { "--cfi-ui-calendar-item": color } : void 0,
|
|
3006
|
+
"aria-hidden": "true"
|
|
3007
|
+
}
|
|
3008
|
+
),
|
|
3009
|
+
children
|
|
3010
|
+
] });
|
|
3011
|
+
}
|
|
3012
|
+
|
|
2676
3013
|
// src/useSortState.ts
|
|
2677
|
-
import { useCallback as useCallback3, useMemo as useMemo5, useState as
|
|
3014
|
+
import { useCallback as useCallback3, useMemo as useMemo5, useState as useState8 } from "react";
|
|
2678
3015
|
function useSortState(initialKey, initialDirection = "asc") {
|
|
2679
|
-
const [sort, setSort] =
|
|
3016
|
+
const [sort, setSort] = useState8({ key: initialKey, direction: initialDirection });
|
|
2680
3017
|
const toggle = useCallback3((key) => {
|
|
2681
3018
|
setSort(
|
|
2682
3019
|
(current) => current.key === key ? { key, direction: current.direction === "asc" ? "desc" : "asc" } : { key, direction: "asc" }
|
|
@@ -2713,21 +3050,21 @@ function useSortState(initialKey, initialDirection = "asc") {
|
|
|
2713
3050
|
}
|
|
2714
3051
|
|
|
2715
3052
|
// src/SearchInput.tsx
|
|
2716
|
-
import { forwardRef as forwardRef18, useEffect as
|
|
3053
|
+
import { forwardRef as forwardRef18, useEffect as useEffect6, useRef as useRef6, useState as useState9 } from "react";
|
|
2717
3054
|
import { Search as Search2, X as X6 } from "lucide-react";
|
|
2718
3055
|
|
|
2719
3056
|
// src/SearchInput.css
|
|
2720
3057
|
styleInject("@layer components {\n .cfi-ui-search__clear {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n background: none;\n border: none;\n color: var(--cfi-ui-content-muted);\n cursor: pointer;\n border-radius: var(--cfi-ui-radius-sm);\n }\n .cfi-ui-search__clear > svg {\n width: 0.875rem;\n height: 0.875rem;\n }\n .cfi-ui-search__clear:hover {\n color: var(--cfi-ui-content);\n }\n .cfi-ui-search__clear:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: 1px;\n }\n}\n");
|
|
2721
3058
|
|
|
2722
3059
|
// src/SearchInput.tsx
|
|
2723
|
-
import { jsx as
|
|
3060
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2724
3061
|
var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay = 250, placeholder = "Search\u2026", clearLabel = "Clear search", ...rest }, ref) {
|
|
2725
|
-
const [local, setLocal] =
|
|
2726
|
-
const timer =
|
|
2727
|
-
const latest =
|
|
3062
|
+
const [local, setLocal] = useState9(value);
|
|
3063
|
+
const timer = useRef6(void 0);
|
|
3064
|
+
const latest = useRef6(onChange);
|
|
2728
3065
|
latest.current = onChange;
|
|
2729
|
-
|
|
2730
|
-
|
|
3066
|
+
useEffect6(() => setLocal(value), [value]);
|
|
3067
|
+
useEffect6(() => () => clearTimeout(timer.current), []);
|
|
2731
3068
|
const push = (next) => {
|
|
2732
3069
|
setLocal(next);
|
|
2733
3070
|
clearTimeout(timer.current);
|
|
@@ -2739,7 +3076,7 @@ var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay =
|
|
|
2739
3076
|
setLocal("");
|
|
2740
3077
|
latest.current("");
|
|
2741
3078
|
};
|
|
2742
|
-
return /* @__PURE__ */
|
|
3079
|
+
return /* @__PURE__ */ jsx31(
|
|
2743
3080
|
Input,
|
|
2744
3081
|
{
|
|
2745
3082
|
...rest,
|
|
@@ -2748,20 +3085,20 @@ var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay =
|
|
|
2748
3085
|
value: local,
|
|
2749
3086
|
placeholder,
|
|
2750
3087
|
onChange: (e) => push(e.target.value),
|
|
2751
|
-
prefix: /* @__PURE__ */
|
|
2752
|
-
suffix: local ? /* @__PURE__ */
|
|
3088
|
+
prefix: /* @__PURE__ */ jsx31(Search2, { "aria-hidden": "true" }),
|
|
3089
|
+
suffix: local ? /* @__PURE__ */ jsx31("button", { type: "button", className: "cfi-ui-search__clear", onClick: clear, "aria-label": clearLabel, children: /* @__PURE__ */ jsx31(X6, { "aria-hidden": "true" }) }) : void 0
|
|
2753
3090
|
}
|
|
2754
3091
|
);
|
|
2755
3092
|
});
|
|
2756
3093
|
|
|
2757
3094
|
// src/Pagination.tsx
|
|
2758
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
3095
|
+
import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
2759
3096
|
|
|
2760
3097
|
// src/Pagination.css
|
|
2761
3098
|
styleInject("@layer components {\n :where(.cfi-ui-pagination) {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--cfi-ui-space-md);\n padding-block: var(--cfi-ui-space-sm);\n font-family: var(--cfi-ui-font);\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-pagination__controls {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n margin-left: auto;\n }\n .cfi-ui-pagination__page {\n font-variant-numeric: tabular-nums;\n color: var(--cfi-ui-content);\n }\n .cfi-ui-pagination__range {\n font-variant-numeric: tabular-nums;\n }\n .cfi-ui-pagination__btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--cfi-ui-control-height-sm);\n height: var(--cfi-ui-control-height-sm);\n padding: 0;\n background: var(--cfi-ui-surface);\n color: var(--cfi-ui-content);\n border: 1px solid var(--cfi-ui-border-strong);\n border-radius: var(--cfi-ui-radius-md);\n cursor: pointer;\n }\n .cfi-ui-pagination__btn > svg {\n width: 1rem;\n height: 1rem;\n }\n .cfi-ui-pagination__btn:hover:not(:disabled) {\n background: var(--cfi-ui-surface-hover);\n }\n .cfi-ui-pagination__btn:disabled {\n color: var(--cfi-ui-content-disabled);\n cursor: default;\n }\n .cfi-ui-pagination__btn:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n}\n");
|
|
2762
3099
|
|
|
2763
3100
|
// src/Pagination.tsx
|
|
2764
|
-
import { jsx as
|
|
3101
|
+
import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2765
3102
|
function Pagination({
|
|
2766
3103
|
page,
|
|
2767
3104
|
pageSize,
|
|
@@ -2777,12 +3114,12 @@ function Pagination({
|
|
|
2777
3114
|
const from = (current - 1) * pageSize + 1;
|
|
2778
3115
|
const to = Math.min(current * pageSize, totalItems);
|
|
2779
3116
|
const range = labels?.range?.(from, to, totalItems) ?? `${from}\u2013${to} of ${totalItems.toLocaleString()}`;
|
|
2780
|
-
return /* @__PURE__ */
|
|
3117
|
+
return /* @__PURE__ */ jsxs31("nav", { className: cn("cfi-ui", "cfi-ui-pagination", className), "aria-label": "Pagination", children: [
|
|
2781
3118
|
showRange && // aria-live so a screen-reader user hears where they landed; paging
|
|
2782
3119
|
// otherwise moves focus nowhere and announces nothing.
|
|
2783
|
-
/* @__PURE__ */
|
|
2784
|
-
/* @__PURE__ */
|
|
2785
|
-
/* @__PURE__ */
|
|
3120
|
+
/* @__PURE__ */ jsx32("span", { className: "cfi-ui-pagination__range", "aria-live": "polite", children: range }),
|
|
3121
|
+
/* @__PURE__ */ jsxs31("div", { className: "cfi-ui-pagination__controls", children: [
|
|
3122
|
+
/* @__PURE__ */ jsx32(
|
|
2786
3123
|
"button",
|
|
2787
3124
|
{
|
|
2788
3125
|
type: "button",
|
|
@@ -2790,15 +3127,15 @@ function Pagination({
|
|
|
2790
3127
|
onClick: () => onPageChange(current - 1),
|
|
2791
3128
|
disabled: current <= 1,
|
|
2792
3129
|
"aria-label": labels?.previous ?? "Previous page",
|
|
2793
|
-
children: /* @__PURE__ */
|
|
3130
|
+
children: /* @__PURE__ */ jsx32(ChevronLeft2, { "aria-hidden": "true" })
|
|
2794
3131
|
}
|
|
2795
3132
|
),
|
|
2796
|
-
/* @__PURE__ */
|
|
3133
|
+
/* @__PURE__ */ jsxs31("span", { className: "cfi-ui-pagination__page", children: [
|
|
2797
3134
|
current,
|
|
2798
3135
|
" / ",
|
|
2799
3136
|
pageCount
|
|
2800
3137
|
] }),
|
|
2801
|
-
/* @__PURE__ */
|
|
3138
|
+
/* @__PURE__ */ jsx32(
|
|
2802
3139
|
"button",
|
|
2803
3140
|
{
|
|
2804
3141
|
type: "button",
|
|
@@ -2806,7 +3143,7 @@ function Pagination({
|
|
|
2806
3143
|
onClick: () => onPageChange(current + 1),
|
|
2807
3144
|
disabled: current >= pageCount,
|
|
2808
3145
|
"aria-label": labels?.next ?? "Next page",
|
|
2809
|
-
children: /* @__PURE__ */
|
|
3146
|
+
children: /* @__PURE__ */ jsx32(ChevronRight2, { "aria-hidden": "true" })
|
|
2810
3147
|
}
|
|
2811
3148
|
)
|
|
2812
3149
|
] })
|
|
@@ -2817,23 +3154,23 @@ function Pagination({
|
|
|
2817
3154
|
styleInject("@layer components {\n :where(.cfi-ui-list-toolbar) {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--cfi-ui-space-md);\n min-width: 0;\n }\n .cfi-ui-list-toolbar__search {\n flex: 1 1 14rem;\n min-width: 0;\n max-width: 22rem;\n }\n .cfi-ui-list-toolbar__filters {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n min-width: 0;\n flex: 0 1 auto;\n }\n .cfi-ui-list-toolbar__actions {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n margin-inline-start: auto;\n }\n @media (max-width: 40rem) {\n .cfi-ui-list-toolbar__search,\n .cfi-ui-list-toolbar__filters,\n .cfi-ui-list-toolbar__actions {\n flex: 1 1 100%;\n max-width: none;\n margin-inline-start: 0;\n }\n }\n}\n");
|
|
2818
3155
|
|
|
2819
3156
|
// src/ListToolbar.tsx
|
|
2820
|
-
import { jsx as
|
|
3157
|
+
import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2821
3158
|
function ListToolbar({ search, filters, actions, className }) {
|
|
2822
|
-
return /* @__PURE__ */
|
|
2823
|
-
search && /* @__PURE__ */
|
|
2824
|
-
filters && /* @__PURE__ */
|
|
2825
|
-
actions && /* @__PURE__ */
|
|
3159
|
+
return /* @__PURE__ */ jsxs32("div", { className: cn("cfi-ui", "cfi-ui-list-toolbar", className), children: [
|
|
3160
|
+
search && /* @__PURE__ */ jsx33("div", { className: "cfi-ui-list-toolbar__search", children: search }),
|
|
3161
|
+
filters && /* @__PURE__ */ jsx33("div", { className: "cfi-ui-list-toolbar__filters", children: filters }),
|
|
3162
|
+
actions && /* @__PURE__ */ jsx33("div", { className: "cfi-ui-list-toolbar__actions", children: actions })
|
|
2826
3163
|
] });
|
|
2827
3164
|
}
|
|
2828
3165
|
|
|
2829
3166
|
// src/PageHeader.tsx
|
|
2830
|
-
import { ArrowLeft, ChevronRight as
|
|
3167
|
+
import { ArrowLeft, ChevronRight as ChevronRight3 } from "lucide-react";
|
|
2831
3168
|
|
|
2832
3169
|
// src/PageHeader.css
|
|
2833
3170
|
styleInject("@layer components {\n :where(.cfi-ui-page-header) {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-ui-space-xs);\n font-family: var(--cfi-ui-font);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-page-header[data-bordered] {\n padding-bottom: var(--cfi-ui-space-md);\n border-bottom: 1px solid var(--cfi-ui-border);\n }\n .cfi-ui-page-header__crumbs {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--cfi-ui-space-3xs);\n font-size: var(--cfi-ui-text-xs);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-page-header__crumb {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-3xs);\n }\n .cfi-ui-page-header__crumb-sep {\n width: 0.75rem;\n height: 0.75rem;\n flex: none;\n color: var(--cfi-ui-content-subtle);\n }\n .cfi-ui-page-header__crumb a {\n color: inherit;\n text-decoration: none;\n border-radius: var(--cfi-ui-radius-sm);\n transition: color var(--cfi-ui-transition-fast);\n }\n .cfi-ui-page-header__crumb a:hover {\n color: var(--cfi-ui-content);\n }\n .cfi-ui-page-header__crumb a:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-page-header__crumb[data-current] {\n color: var(--cfi-ui-content);\n }\n .cfi-ui-page-header__back {\n display: inline-flex;\n align-items: center;\n align-self: flex-start;\n gap: var(--cfi-ui-space-2xs);\n margin-left: -0.5rem;\n padding: 0.25rem 0.5rem;\n border: 0;\n border-radius: var(--cfi-ui-radius-md);\n background: none;\n font: inherit;\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content-muted);\n cursor: pointer;\n transition: color var(--cfi-ui-transition-fast), background var(--cfi-ui-transition-fast);\n }\n .cfi-ui-page-header__back:hover {\n background: var(--cfi-ui-surface-hover);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-page-header__back:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-page-header__back > svg {\n width: 1rem;\n height: 1rem;\n flex: none;\n }\n .cfi-ui-page-header__row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--cfi-ui-space-md);\n }\n .cfi-ui-page-header__titles {\n min-width: 0;\n }\n .cfi-ui-page-header__title,\n h1.cfi-ui-page-header__title,\n h2.cfi-ui-page-header__title,\n h3.cfi-ui-page-header__title {\n margin: 0;\n font-size: var(--cfi-ui-text-xl);\n font-weight: var(--cfi-ui-weight-semibold);\n line-height: var(--cfi-ui-leading-tight);\n letter-spacing: -0.01em;\n color: var(--cfi-ui-content);\n }\n .cfi-ui-page-header__description {\n margin: 0.25rem 0 0;\n font-size: var(--cfi-ui-text-sm);\n line-height: var(--cfi-ui-leading-normal);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-page-header__actions {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n flex: none;\n }\n .cfi-ui-page-header__meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n margin-top: var(--cfi-ui-space-3xs);\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content-muted);\n }\n @media (max-width: 640px) {\n .cfi-ui-page-header__row {\n flex-direction: column;\n align-items: stretch;\n gap: var(--cfi-ui-space-sm);\n }\n .cfi-ui-page-header__actions {\n flex-wrap: wrap;\n }\n }\n}\n");
|
|
2834
3171
|
|
|
2835
3172
|
// src/PageHeader.tsx
|
|
2836
|
-
import { jsx as
|
|
3173
|
+
import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2837
3174
|
function PageHeader({
|
|
2838
3175
|
title,
|
|
2839
3176
|
description,
|
|
@@ -2847,32 +3184,32 @@ function PageHeader({
|
|
|
2847
3184
|
className
|
|
2848
3185
|
}) {
|
|
2849
3186
|
const Heading = `h${level}`;
|
|
2850
|
-
return /* @__PURE__ */
|
|
3187
|
+
return /* @__PURE__ */ jsxs33(
|
|
2851
3188
|
"div",
|
|
2852
3189
|
{
|
|
2853
3190
|
className: cn("cfi-ui", "cfi-ui-page-header", className),
|
|
2854
3191
|
"data-bordered": bordered ? "" : void 0,
|
|
2855
3192
|
children: [
|
|
2856
|
-
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */
|
|
3193
|
+
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx34("nav", { className: "cfi-ui-page-header__crumbs", "aria-label": "Breadcrumb", children: breadcrumbs.map((crumb, i) => {
|
|
2857
3194
|
const last = i === breadcrumbs.length - 1;
|
|
2858
|
-
const label = crumb.render ? crumb.render(crumb.label) : crumb.href ? /* @__PURE__ */
|
|
2859
|
-
return /* @__PURE__ */
|
|
2860
|
-
i > 0 && /* @__PURE__ */
|
|
2861
|
-
/* @__PURE__ */
|
|
3195
|
+
const label = crumb.render ? crumb.render(crumb.label) : crumb.href ? /* @__PURE__ */ jsx34("a", { href: crumb.href, children: crumb.label }) : crumb.label;
|
|
3196
|
+
return /* @__PURE__ */ jsxs33("span", { className: "cfi-ui-page-header__crumb", "data-current": last ? "" : void 0, children: [
|
|
3197
|
+
i > 0 && /* @__PURE__ */ jsx34(ChevronRight3, { className: "cfi-ui-page-header__crumb-sep", "aria-hidden": "true" }),
|
|
3198
|
+
/* @__PURE__ */ jsx34("span", { "aria-current": last ? "page" : void 0, children: label })
|
|
2862
3199
|
] }, i);
|
|
2863
3200
|
}) }),
|
|
2864
|
-
onBack && /* @__PURE__ */
|
|
2865
|
-
/* @__PURE__ */
|
|
3201
|
+
onBack && /* @__PURE__ */ jsxs33("button", { type: "button", className: "cfi-ui-page-header__back", onClick: onBack, children: [
|
|
3202
|
+
/* @__PURE__ */ jsx34(ArrowLeft, { "aria-hidden": "true" }),
|
|
2866
3203
|
backLabel
|
|
2867
3204
|
] }),
|
|
2868
|
-
/* @__PURE__ */
|
|
2869
|
-
/* @__PURE__ */
|
|
2870
|
-
/* @__PURE__ */
|
|
2871
|
-
description && /* @__PURE__ */
|
|
3205
|
+
/* @__PURE__ */ jsxs33("div", { className: "cfi-ui-page-header__row", children: [
|
|
3206
|
+
/* @__PURE__ */ jsxs33("div", { className: "cfi-ui-page-header__titles", children: [
|
|
3207
|
+
/* @__PURE__ */ jsx34(Heading, { className: "cfi-ui-page-header__title", children: title }),
|
|
3208
|
+
description && /* @__PURE__ */ jsx34("p", { className: "cfi-ui-page-header__description", children: description })
|
|
2872
3209
|
] }),
|
|
2873
|
-
actions && /* @__PURE__ */
|
|
3210
|
+
actions && /* @__PURE__ */ jsx34("div", { className: "cfi-ui-page-header__actions", children: actions })
|
|
2874
3211
|
] }),
|
|
2875
|
-
children && /* @__PURE__ */
|
|
3212
|
+
children && /* @__PURE__ */ jsx34("div", { className: "cfi-ui-page-header__meta", children })
|
|
2876
3213
|
]
|
|
2877
3214
|
}
|
|
2878
3215
|
);
|
|
@@ -2885,7 +3222,7 @@ import { X as X7 } from "lucide-react";
|
|
|
2885
3222
|
styleInject("@layer components {\n :where(.cfi-ui-selection-bar) {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n padding: var(--cfi-ui-space-2xs) var(--cfi-ui-space-sm);\n border: 1px solid var(--cfi-ui-accent);\n border-radius: var(--cfi-ui-radius-md);\n background: var(--cfi-ui-accent-soft);\n font-family: var(--cfi-ui-font);\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content);\n min-height: 2.5rem;\n }\n .cfi-ui-selection-bar__count {\n font-weight: var(--cfi-ui-weight-medium);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n .cfi-ui-selection-bar__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--cfi-ui-space-2xs);\n }\n .cfi-ui-selection-bar__clear {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-3xs);\n margin-left: auto;\n padding: 0.25rem 0.5rem;\n border: 0;\n border-radius: var(--cfi-ui-radius-sm);\n background: none;\n font: inherit;\n font-size: var(--cfi-ui-text-sm);\n color: var(--cfi-ui-content-muted);\n cursor: pointer;\n transition: color var(--cfi-ui-transition-fast), background var(--cfi-ui-transition-fast);\n }\n .cfi-ui-selection-bar__clear:hover:not(:disabled) {\n background: var(--cfi-ui-surface-hover);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-selection-bar__clear:focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-selection-bar__clear:disabled {\n color: var(--cfi-ui-content-disabled);\n cursor: not-allowed;\n }\n .cfi-ui-selection-bar__clear > svg {\n width: 0.875rem;\n height: 0.875rem;\n flex: none;\n }\n .cfi-ui-selection-bar[data-busy] {\n opacity: 0.6;\n pointer-events: none;\n }\n .cfi-ui-selection-bar[data-variant=floating] {\n position: fixed;\n left: 50%;\n bottom: var(--cfi-ui-space-lg);\n transform: translateX(-50%);\n z-index: var(--cfi-ui-z-dropdown);\n width: max-content;\n max-width: calc(100vw - 2 * var(--cfi-ui-space-lg));\n padding: var(--cfi-ui-space-xs) var(--cfi-ui-space-md);\n border-radius: var(--cfi-ui-radius-lg);\n background: var(--cfi-ui-surface-raised);\n box-shadow: var(--cfi-ui-shadow-lg);\n }\n @media (max-width: 640px) {\n .cfi-ui-selection-bar {\n flex-wrap: wrap;\n }\n .cfi-ui-selection-bar[data-variant=floating] {\n left: var(--cfi-ui-space-sm);\n right: var(--cfi-ui-space-sm);\n width: auto;\n max-width: none;\n transform: none;\n }\n }\n}\n");
|
|
2886
3223
|
|
|
2887
3224
|
// src/SelectionBar.tsx
|
|
2888
|
-
import { jsx as
|
|
3225
|
+
import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2889
3226
|
function SelectionBar({
|
|
2890
3227
|
count,
|
|
2891
3228
|
children,
|
|
@@ -2897,7 +3234,7 @@ function SelectionBar({
|
|
|
2897
3234
|
className
|
|
2898
3235
|
}) {
|
|
2899
3236
|
if (count === 0) return null;
|
|
2900
|
-
return /* @__PURE__ */
|
|
3237
|
+
return /* @__PURE__ */ jsxs34(
|
|
2901
3238
|
"div",
|
|
2902
3239
|
{
|
|
2903
3240
|
role: "status",
|
|
@@ -2906,9 +3243,9 @@ function SelectionBar({
|
|
|
2906
3243
|
"data-busy": busy ? "" : void 0,
|
|
2907
3244
|
className: cn("cfi-ui", "cfi-ui-selection-bar", className),
|
|
2908
3245
|
children: [
|
|
2909
|
-
/* @__PURE__ */
|
|
2910
|
-
children && /* @__PURE__ */
|
|
2911
|
-
onClear && /* @__PURE__ */
|
|
3246
|
+
/* @__PURE__ */ jsx35("span", { className: "cfi-ui-selection-bar__count", children: label(count) }),
|
|
3247
|
+
children && /* @__PURE__ */ jsx35("div", { className: "cfi-ui-selection-bar__actions", children }),
|
|
3248
|
+
onClear && /* @__PURE__ */ jsxs34(
|
|
2912
3249
|
"button",
|
|
2913
3250
|
{
|
|
2914
3251
|
type: "button",
|
|
@@ -2916,7 +3253,7 @@ function SelectionBar({
|
|
|
2916
3253
|
onClick: onClear,
|
|
2917
3254
|
disabled: busy,
|
|
2918
3255
|
children: [
|
|
2919
|
-
/* @__PURE__ */
|
|
3256
|
+
/* @__PURE__ */ jsx35(X7, { "aria-hidden": "true" }),
|
|
2920
3257
|
clearLabel
|
|
2921
3258
|
]
|
|
2922
3259
|
}
|
|
@@ -2927,14 +3264,14 @@ function SelectionBar({
|
|
|
2927
3264
|
}
|
|
2928
3265
|
|
|
2929
3266
|
// src/usePagedRows.ts
|
|
2930
|
-
import { useEffect as
|
|
3267
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useState as useState10 } from "react";
|
|
2931
3268
|
function usePagedRows(all, initialPageSize = 25) {
|
|
2932
|
-
const [page, setPage] =
|
|
2933
|
-
const [pageSize, setSize] =
|
|
3269
|
+
const [page, setPage] = useState10(1);
|
|
3270
|
+
const [pageSize, setSize] = useState10(initialPageSize);
|
|
2934
3271
|
const totalItems = all.length;
|
|
2935
3272
|
const pageCount = Math.max(1, Math.ceil(totalItems / Math.max(1, pageSize)));
|
|
2936
3273
|
const clamped = Math.min(Math.max(1, page), pageCount);
|
|
2937
|
-
|
|
3274
|
+
useEffect7(() => {
|
|
2938
3275
|
if (clamped !== page) setPage(clamped);
|
|
2939
3276
|
}, [clamped, page]);
|
|
2940
3277
|
const rows = useMemo6(
|
|
@@ -2961,6 +3298,8 @@ export {
|
|
|
2961
3298
|
BootScreen,
|
|
2962
3299
|
Button,
|
|
2963
3300
|
ButtonGroup,
|
|
3301
|
+
CalendarGrid,
|
|
3302
|
+
CalendarLegendItem,
|
|
2964
3303
|
Card,
|
|
2965
3304
|
CardBody,
|
|
2966
3305
|
CardDescription,
|
|
@@ -3080,6 +3419,11 @@ export {
|
|
|
3080
3419
|
TooltipProvider,
|
|
3081
3420
|
TooltipRoot,
|
|
3082
3421
|
TooltipTrigger,
|
|
3422
|
+
calendarAddDays,
|
|
3423
|
+
calendarBuckets,
|
|
3424
|
+
calendarDayKey,
|
|
3425
|
+
calendarDays,
|
|
3426
|
+
calendarShift,
|
|
3083
3427
|
cn,
|
|
3084
3428
|
formatCalendarDate,
|
|
3085
3429
|
formatDate,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campfire-interactive/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Shared UI primitives for the Campfire Suite — Radix behavior, plain CSS styled entirely from the design tokens, no Tailwind required",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|