@campfire-interactive/ui 0.11.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 +207 -1
- package/dist/index.js +586 -55
- package/package.json +56 -56
package/dist/index.d.ts
CHANGED
|
@@ -1716,6 +1716,212 @@ declare function timelineLayout({ items, lanes, start, end, today, calendar, min
|
|
|
1716
1716
|
*/
|
|
1717
1717
|
declare function Timeline({ items, lanes, start, end, today, todayLabel, calendar, beforeLabel, afterLabel, emptyLabel, className, ...rest }: TimelineProps): react.JSX.Element;
|
|
1718
1718
|
|
|
1719
|
+
type KanbanTone = 'neutral' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
1720
|
+
interface KanbanColumn {
|
|
1721
|
+
id: string;
|
|
1722
|
+
label: ReactNode;
|
|
1723
|
+
/** The dot beside the label. */
|
|
1724
|
+
tone?: KanbanTone;
|
|
1725
|
+
/**
|
|
1726
|
+
* The count beside the label. Defaults to the cards in the column; pass one
|
|
1727
|
+
* when the column holds fewer than it counts (done cards hidden by a filter).
|
|
1728
|
+
*/
|
|
1729
|
+
count?: number;
|
|
1730
|
+
/**
|
|
1731
|
+
* A line under the cards, e.g. "4 done hidden". A function gets the lane's
|
|
1732
|
+
* id (`'__default'` without lanes), for a figure that differs lane to lane.
|
|
1733
|
+
*/
|
|
1734
|
+
note?: ReactNode | ((lane: string) => ReactNode);
|
|
1735
|
+
}
|
|
1736
|
+
interface KanbanLane {
|
|
1737
|
+
id: string;
|
|
1738
|
+
/** Names the swimlane; shown above its row of cells. */
|
|
1739
|
+
label: ReactNode;
|
|
1740
|
+
/** A line after the label, e.g. "3 open". */
|
|
1741
|
+
detail?: ReactNode;
|
|
1742
|
+
/** Any CSS color for the lane's left edge, e.g. a program's color. */
|
|
1743
|
+
color?: string;
|
|
1744
|
+
}
|
|
1745
|
+
/** What the board needs of a card. The rest of the caller's object is passed back untouched. */
|
|
1746
|
+
interface KanbanItem {
|
|
1747
|
+
id: string;
|
|
1748
|
+
/** The column it sits in. An id not in `columns` puts it in the first. */
|
|
1749
|
+
column: string;
|
|
1750
|
+
/** The swimlane it sits in; omitted, or not in `lanes`, the first. */
|
|
1751
|
+
lane?: string;
|
|
1752
|
+
}
|
|
1753
|
+
interface KanbanBoardProps<T extends KanbanItem> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
1754
|
+
columns: KanbanColumn[];
|
|
1755
|
+
/** In the order each cell lists them; the board does not sort. */
|
|
1756
|
+
items: T[];
|
|
1757
|
+
/** Swimlanes, top to bottom. Omitted, one unlabeled lane holds everything. */
|
|
1758
|
+
lanes?: KanbanLane[];
|
|
1759
|
+
/** The card's content. The board draws the card around it, so keep buttons and links out of it. */
|
|
1760
|
+
renderCard: (item: T) => ReactNode;
|
|
1761
|
+
/** Plain text for the card, read in announcements ("Moved {item} to {column}"). */
|
|
1762
|
+
itemLabel: (item: T) => string;
|
|
1763
|
+
/** Plain text for a column in announcements. Defaults to its label when that is a string, else its id. */
|
|
1764
|
+
columnLabel?: (column: KanbanColumn) => string;
|
|
1765
|
+
/**
|
|
1766
|
+
* A card was dropped on another column in its own lane, or moved with
|
|
1767
|
+
* Alt+Left / Alt+Right. Omitted, the board is read-only: nothing drags.
|
|
1768
|
+
* The board does not move the card itself; the caller updates `items`.
|
|
1769
|
+
*/
|
|
1770
|
+
onMove?: (item: T, column: string) => void;
|
|
1771
|
+
/** Click or Enter / Space on a card. */
|
|
1772
|
+
onOpen?: (item: T) => void;
|
|
1773
|
+
/** Shown in an empty cell. */
|
|
1774
|
+
emptyLabel?: ReactNode;
|
|
1775
|
+
/** The announcement after a move. */
|
|
1776
|
+
movedLabel?: (item: string, column: string) => string;
|
|
1777
|
+
/** How narrow a column may get before the board scrolls sideways. */
|
|
1778
|
+
minColumnWidth?: string;
|
|
1779
|
+
}
|
|
1780
|
+
interface KanbanLayout<T extends KanbanItem> {
|
|
1781
|
+
lanes: {
|
|
1782
|
+
lane: KanbanLane;
|
|
1783
|
+
cells: {
|
|
1784
|
+
column: KanbanColumn;
|
|
1785
|
+
items: T[];
|
|
1786
|
+
}[];
|
|
1787
|
+
}[];
|
|
1788
|
+
/** Cards per column across every lane, before any `count` override. */
|
|
1789
|
+
counts: Record<string, number>;
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Puts each card in its lane and column. Pure, so the rules are testable: a
|
|
1793
|
+
* card naming a column or lane the board does not have goes to the first,
|
|
1794
|
+
* rather than disappearing from a board that claims to show everything.
|
|
1795
|
+
*/
|
|
1796
|
+
declare function kanbanLayout<T extends KanbanItem>(columns: KanbanColumn[], items: T[], lanes?: KanbanLane[]): KanbanLayout<T>;
|
|
1797
|
+
/** The column `delta` steps from `from`, or null past either end. */
|
|
1798
|
+
declare function kanbanStep(columns: KanbanColumn[], from: string, delta: number): string | null;
|
|
1799
|
+
/**
|
|
1800
|
+
* Cards in columns, optionally in swimlanes: tasks by status, deals by stage.
|
|
1801
|
+
*
|
|
1802
|
+
* Cards drag between columns within their own lane (a lane is what the card
|
|
1803
|
+
* belongs to, which a drop on the board is not the place to change). Every
|
|
1804
|
+
* drag has a keyboard twin: Alt+Left / Alt+Right moves the focused card one
|
|
1805
|
+
* column, focus stays on it, and the move is announced. The board never moves
|
|
1806
|
+
* a card on its own; `onMove` tells the caller, and the caller's `items` say
|
|
1807
|
+
* where it is.
|
|
1808
|
+
*/
|
|
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
|
+
|
|
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
|
+
|
|
1719
1925
|
interface SortState<K extends string> {
|
|
1720
1926
|
key: K;
|
|
1721
1927
|
direction: SortDirection;
|
|
@@ -2085,4 +2291,4 @@ declare function formatNumber(value: number | null | undefined, decimals?: numbe
|
|
|
2085
2291
|
*/
|
|
2086
2292
|
declare function formatPercent(value: number | null | undefined, decimals?: number): string;
|
|
2087
2293
|
|
|
2088
|
-
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, 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, 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
|
);
|
|
@@ -2489,10 +2489,531 @@ function Timeline({
|
|
|
2489
2489
|
] }) });
|
|
2490
2490
|
}
|
|
2491
2491
|
|
|
2492
|
+
// src/KanbanBoard.tsx
|
|
2493
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState6 } from "react";
|
|
2494
|
+
|
|
2495
|
+
// src/KanbanBoard.css
|
|
2496
|
+
styleInject("@layer components {\n :where(.cfi-ui-kanban) {\n min-width: 0;\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-kanban__scroll {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-ui-space-sm);\n overflow-x: auto;\n padding-bottom: var(--cfi-ui-space-xs);\n }\n .cfi-ui-kanban__grid {\n display: grid;\n grid-template-columns: repeat(var(--cfi-ui-kanban-columns, 4), minmax(var(--cfi-ui-kanban-min, 13rem), 1fr));\n gap: var(--cfi-ui-space-sm);\n }\n .cfi-ui-kanban__column-head {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n padding: 0 var(--cfi-ui-space-xs);\n font-weight: var(--cfi-ui-weight-semibold);\n }\n .cfi-ui-kanban__dot {\n flex: none;\n width: 0.625rem;\n height: 0.625rem;\n border-radius: var(--cfi-ui-radius-full);\n background: var(--cfi-ui-content-subtle);\n }\n .cfi-ui-kanban__column-head[data-tone=accent] .cfi-ui-kanban__dot {\n background: var(--cfi-ui-accent);\n }\n .cfi-ui-kanban__column-head[data-tone=success] .cfi-ui-kanban__dot {\n background: var(--cfi-ui-success);\n }\n .cfi-ui-kanban__column-head[data-tone=warning] .cfi-ui-kanban__dot {\n background: var(--cfi-ui-warning);\n }\n .cfi-ui-kanban__column-head[data-tone=error] .cfi-ui-kanban__dot {\n background: var(--cfi-ui-error);\n }\n .cfi-ui-kanban__column-head[data-tone=info] .cfi-ui-kanban__dot {\n background: var(--cfi-ui-info);\n }\n .cfi-ui-kanban__column-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .cfi-ui-kanban__count {\n padding: 0 var(--cfi-ui-space-xs);\n border-radius: var(--cfi-ui-radius-sm);\n background: var(--cfi-ui-surface-muted);\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n font-variant-numeric: tabular-nums;\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-kanban__lane[data-labeled] {\n padding: var(--cfi-ui-space-sm);\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: inset 4px 0 0 var(--cfi-ui-kanban-lane-color, var(--cfi-ui-border-strong));\n }\n .cfi-ui-kanban__lane-head {\n display: flex;\n align-items: baseline;\n flex-wrap: wrap;\n gap: var(--cfi-ui-space-xs) var(--cfi-ui-space-sm);\n margin-bottom: var(--cfi-ui-space-sm);\n padding-left: var(--cfi-ui-space-xs);\n }\n .cfi-ui-kanban__lane-label {\n font-weight: var(--cfi-ui-weight-semibold);\n }\n .cfi-ui-kanban__lane-detail {\n font-size: var(--cfi-ui-text-xs);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-kanban__cell {\n min-height: 6rem;\n padding: var(--cfi-ui-space-xs);\n border-radius: var(--cfi-ui-radius-lg);\n background: var(--cfi-ui-surface-sunken);\n transition: background var(--cfi-ui-transition-fast), box-shadow var(--cfi-ui-transition-fast);\n }\n .cfi-ui-kanban__cell[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-kanban__cards {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-ui-space-xs);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n .cfi-ui-kanban__card {\n padding: var(--cfi-ui-space-sm);\n border: 1px solid var(--cfi-ui-border);\n border-radius: var(--cfi-ui-radius-lg);\n background: var(--cfi-ui-surface);\n box-shadow: var(--cfi-ui-shadow-xs);\n transition:\n border-color var(--cfi-ui-transition-fast),\n box-shadow var(--cfi-ui-transition-fast),\n opacity var(--cfi-ui-transition-fast);\n }\n .cfi-ui-kanban__card[role=button] {\n cursor: pointer;\n }\n .cfi-ui-kanban__card[data-movable] {\n cursor: grab;\n }\n .cfi-ui-kanban__card[data-movable]:active {\n cursor: grabbing;\n }\n .cfi-ui-kanban__card[role=button]:hover,\n .cfi-ui-kanban__card[data-movable]:hover {\n border-color: var(--cfi-ui-border-strong);\n box-shadow: var(--cfi-ui-shadow-md);\n }\n .cfi-ui-kanban__card: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-kanban__card[data-dragging] {\n opacity: 0.4;\n }\n .cfi-ui-kanban__note,\n .cfi-ui-kanban__empty {\n margin: 0;\n padding: var(--cfi-ui-space-xs);\n font-size: var(--cfi-ui-text-xs);\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-kanban__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-kanban__cell,\n .cfi-ui-kanban__card {\n transition: none;\n }\n }\n}\n");
|
|
2497
|
+
|
|
2498
|
+
// src/KanbanBoard.tsx
|
|
2499
|
+
import { jsx as jsx29, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2500
|
+
var DEFAULT_LANE2 = { id: "__default", label: null };
|
|
2501
|
+
function kanbanLayout(columns, items, lanes) {
|
|
2502
|
+
const laneList = lanes && lanes.length > 0 ? lanes : [DEFAULT_LANE2];
|
|
2503
|
+
const columnIds = new Set(columns.map((c) => c.id));
|
|
2504
|
+
const laneIds = new Set(laneList.map((l) => l.id));
|
|
2505
|
+
const counts = Object.fromEntries(columns.map((c) => [c.id, 0]));
|
|
2506
|
+
const cells = /* @__PURE__ */ new Map();
|
|
2507
|
+
for (const item of items) {
|
|
2508
|
+
const column = columnIds.has(item.column) ? item.column : columns[0]?.id;
|
|
2509
|
+
if (column === void 0) continue;
|
|
2510
|
+
const lane = item.lane !== void 0 && laneIds.has(item.lane) ? item.lane : laneList[0].id;
|
|
2511
|
+
const key = `${lane}\0${column}`;
|
|
2512
|
+
const list = cells.get(key);
|
|
2513
|
+
if (list) list.push(item);
|
|
2514
|
+
else cells.set(key, [item]);
|
|
2515
|
+
counts[column] += 1;
|
|
2516
|
+
}
|
|
2517
|
+
return {
|
|
2518
|
+
lanes: laneList.map((lane) => ({
|
|
2519
|
+
lane,
|
|
2520
|
+
cells: columns.map((column) => ({ column, items: cells.get(`${lane.id}\0${column.id}`) ?? [] }))
|
|
2521
|
+
})),
|
|
2522
|
+
counts
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
function kanbanStep(columns, from, delta) {
|
|
2526
|
+
const i = columns.findIndex((c) => c.id === from);
|
|
2527
|
+
const next = columns[(i < 0 ? 0 : i) + delta];
|
|
2528
|
+
return next ? next.id : null;
|
|
2529
|
+
}
|
|
2530
|
+
function KanbanBoard({
|
|
2531
|
+
columns,
|
|
2532
|
+
items,
|
|
2533
|
+
lanes,
|
|
2534
|
+
renderCard,
|
|
2535
|
+
itemLabel,
|
|
2536
|
+
columnLabel = (c) => typeof c.label === "string" ? c.label : c.id,
|
|
2537
|
+
onMove,
|
|
2538
|
+
onOpen,
|
|
2539
|
+
emptyLabel = "Nothing here",
|
|
2540
|
+
movedLabel = (item, column) => `Moved ${item} to ${column}`,
|
|
2541
|
+
minColumnWidth = "13rem",
|
|
2542
|
+
className,
|
|
2543
|
+
style,
|
|
2544
|
+
...rest
|
|
2545
|
+
}) {
|
|
2546
|
+
const layout = kanbanLayout(columns, items, lanes);
|
|
2547
|
+
const [drag, setDrag] = useState6(null);
|
|
2548
|
+
const [over, setOver] = useState6(null);
|
|
2549
|
+
const [announcement, setAnnouncement] = useState6("");
|
|
2550
|
+
const rootRef = useRef4(null);
|
|
2551
|
+
const refocus = useRef4(null);
|
|
2552
|
+
useEffect4(() => {
|
|
2553
|
+
const id = refocus.current;
|
|
2554
|
+
if (!id || !rootRef.current) return;
|
|
2555
|
+
const card = rootRef.current.querySelector(`[data-kanban-card="${CSS.escape(id)}"]`);
|
|
2556
|
+
if (card) {
|
|
2557
|
+
card.focus();
|
|
2558
|
+
refocus.current = null;
|
|
2559
|
+
}
|
|
2560
|
+
});
|
|
2561
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
2562
|
+
const columnById = new Map(columns.map((c) => [c.id, c]));
|
|
2563
|
+
const move = (item, column, viaKeyboard) => {
|
|
2564
|
+
if (!onMove || column === item.column) return;
|
|
2565
|
+
if (viaKeyboard) refocus.current = item.id;
|
|
2566
|
+
onMove(item, column);
|
|
2567
|
+
const target = columnById.get(column);
|
|
2568
|
+
setAnnouncement(movedLabel(itemLabel(item), target ? columnLabel(target) : column));
|
|
2569
|
+
};
|
|
2570
|
+
const endDrag = () => {
|
|
2571
|
+
setDrag(null);
|
|
2572
|
+
setOver(null);
|
|
2573
|
+
};
|
|
2574
|
+
return /* @__PURE__ */ jsxs29(
|
|
2575
|
+
"div",
|
|
2576
|
+
{
|
|
2577
|
+
ref: rootRef,
|
|
2578
|
+
className: cn("cfi-ui", "cfi-ui-kanban", className),
|
|
2579
|
+
style: { "--cfi-ui-kanban-columns": columns.length, "--cfi-ui-kanban-min": minColumnWidth, ...style },
|
|
2580
|
+
...rest,
|
|
2581
|
+
children: [
|
|
2582
|
+
/* @__PURE__ */ jsxs29("div", { className: "cfi-ui-kanban__scroll", children: [
|
|
2583
|
+
/* @__PURE__ */ jsx29("div", { className: "cfi-ui-kanban__grid cfi-ui-kanban__head", children: columns.map((c) => /* @__PURE__ */ jsxs29("div", { className: "cfi-ui-kanban__column-head", "data-tone": c.tone ?? "neutral", children: [
|
|
2584
|
+
/* @__PURE__ */ jsx29("span", { className: "cfi-ui-kanban__dot", "aria-hidden": "true" }),
|
|
2585
|
+
/* @__PURE__ */ jsx29("span", { className: "cfi-ui-kanban__column-label", children: c.label }),
|
|
2586
|
+
/* @__PURE__ */ jsx29("span", { className: "cfi-ui-kanban__count", children: c.count ?? layout.counts[c.id] })
|
|
2587
|
+
] }, c.id)) }),
|
|
2588
|
+
layout.lanes.map(({ lane, cells }) => /* @__PURE__ */ jsxs29(
|
|
2589
|
+
"section",
|
|
2590
|
+
{
|
|
2591
|
+
className: "cfi-ui-kanban__lane",
|
|
2592
|
+
"data-labeled": lane.label ? true : void 0,
|
|
2593
|
+
style: lane.color ? { "--cfi-ui-kanban-lane-color": lane.color } : void 0,
|
|
2594
|
+
children: [
|
|
2595
|
+
lane.label ? /* @__PURE__ */ jsxs29("div", { className: "cfi-ui-kanban__lane-head", children: [
|
|
2596
|
+
/* @__PURE__ */ jsx29("span", { className: "cfi-ui-kanban__lane-label", children: lane.label }),
|
|
2597
|
+
lane.detail ? /* @__PURE__ */ jsx29("span", { className: "cfi-ui-kanban__lane-detail", children: lane.detail }) : null
|
|
2598
|
+
] }) : null,
|
|
2599
|
+
/* @__PURE__ */ jsx29("div", { className: "cfi-ui-kanban__grid", children: cells.map(({ column, items: cellItems }) => {
|
|
2600
|
+
const zone = `${lane.id}\0${column.id}`;
|
|
2601
|
+
const accepts = drag !== null && drag.lane === lane.id && drag.column !== column.id;
|
|
2602
|
+
const note = typeof column.note === "function" ? column.note(lane.id) : column.note;
|
|
2603
|
+
return /* @__PURE__ */ jsxs29(
|
|
2604
|
+
"div",
|
|
2605
|
+
{
|
|
2606
|
+
className: "cfi-ui-kanban__cell",
|
|
2607
|
+
"data-over": over === zone || void 0,
|
|
2608
|
+
onDragOver: (e) => {
|
|
2609
|
+
if (!accepts) return;
|
|
2610
|
+
e.preventDefault();
|
|
2611
|
+
e.dataTransfer.dropEffect = "move";
|
|
2612
|
+
if (over !== zone) setOver(zone);
|
|
2613
|
+
},
|
|
2614
|
+
onDragLeave: (e) => {
|
|
2615
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setOver((o) => o === zone ? null : o);
|
|
2616
|
+
},
|
|
2617
|
+
onDrop: (e) => {
|
|
2618
|
+
e.preventDefault();
|
|
2619
|
+
const item = drag ? byId.get(drag.id) : void 0;
|
|
2620
|
+
if (item && accepts) move(item, column.id, false);
|
|
2621
|
+
endDrag();
|
|
2622
|
+
},
|
|
2623
|
+
children: [
|
|
2624
|
+
/* @__PURE__ */ jsx29("ul", { className: "cfi-ui-kanban__cards", "aria-label": columnLabel(column), children: cellItems.map((item) => /* @__PURE__ */ jsx29("li", { children: /* @__PURE__ */ jsx29(
|
|
2625
|
+
"div",
|
|
2626
|
+
{
|
|
2627
|
+
className: "cfi-ui-kanban__card",
|
|
2628
|
+
"data-kanban-card": item.id,
|
|
2629
|
+
"data-dragging": drag?.id === item.id || void 0,
|
|
2630
|
+
"data-movable": onMove ? true : void 0,
|
|
2631
|
+
role: onOpen ? "button" : void 0,
|
|
2632
|
+
tabIndex: onOpen || onMove ? 0 : void 0,
|
|
2633
|
+
"aria-keyshortcuts": onMove ? "Alt+ArrowLeft Alt+ArrowRight" : void 0,
|
|
2634
|
+
draggable: onMove ? true : void 0,
|
|
2635
|
+
onDragStart: (e) => {
|
|
2636
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2637
|
+
e.dataTransfer.setData("text/plain", item.id);
|
|
2638
|
+
setTimeout(() => setDrag({ id: item.id, lane: lane.id, column: column.id }), 0);
|
|
2639
|
+
},
|
|
2640
|
+
onDragEnd: endDrag,
|
|
2641
|
+
onClick: onOpen ? () => onOpen(item) : void 0,
|
|
2642
|
+
onKeyDown: (e) => {
|
|
2643
|
+
if (e.target !== e.currentTarget) return;
|
|
2644
|
+
if (onMove && e.altKey && (e.key === "ArrowLeft" || e.key === "ArrowRight")) {
|
|
2645
|
+
e.preventDefault();
|
|
2646
|
+
const to = kanbanStep(columns, column.id, e.key === "ArrowLeft" ? -1 : 1);
|
|
2647
|
+
if (to) move(item, to, true);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (onOpen && (e.key === "Enter" || e.key === " ")) {
|
|
2651
|
+
e.preventDefault();
|
|
2652
|
+
onOpen(item);
|
|
2653
|
+
}
|
|
2654
|
+
},
|
|
2655
|
+
children: renderCard(item)
|
|
2656
|
+
}
|
|
2657
|
+
) }, item.id)) }),
|
|
2658
|
+
note ? /* @__PURE__ */ jsx29("p", { className: "cfi-ui-kanban__note", children: note }) : null,
|
|
2659
|
+
cellItems.length === 0 && !note ? /* @__PURE__ */ jsx29("p", { className: "cfi-ui-kanban__empty", children: emptyLabel }) : null
|
|
2660
|
+
]
|
|
2661
|
+
},
|
|
2662
|
+
column.id
|
|
2663
|
+
);
|
|
2664
|
+
}) })
|
|
2665
|
+
]
|
|
2666
|
+
},
|
|
2667
|
+
lane.id
|
|
2668
|
+
))
|
|
2669
|
+
] }),
|
|
2670
|
+
/* @__PURE__ */ jsx29("div", { className: "cfi-ui-kanban__live", role: "status", "aria-live": "polite", children: announcement })
|
|
2671
|
+
]
|
|
2672
|
+
}
|
|
2673
|
+
);
|
|
2674
|
+
}
|
|
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
|
+
|
|
2492
3013
|
// src/useSortState.ts
|
|
2493
|
-
import { useCallback as useCallback3, useMemo as useMemo5, useState as
|
|
3014
|
+
import { useCallback as useCallback3, useMemo as useMemo5, useState as useState8 } from "react";
|
|
2494
3015
|
function useSortState(initialKey, initialDirection = "asc") {
|
|
2495
|
-
const [sort, setSort] =
|
|
3016
|
+
const [sort, setSort] = useState8({ key: initialKey, direction: initialDirection });
|
|
2496
3017
|
const toggle = useCallback3((key) => {
|
|
2497
3018
|
setSort(
|
|
2498
3019
|
(current) => current.key === key ? { key, direction: current.direction === "asc" ? "desc" : "asc" } : { key, direction: "asc" }
|
|
@@ -2529,21 +3050,21 @@ function useSortState(initialKey, initialDirection = "asc") {
|
|
|
2529
3050
|
}
|
|
2530
3051
|
|
|
2531
3052
|
// src/SearchInput.tsx
|
|
2532
|
-
import { forwardRef as forwardRef18, useEffect as
|
|
3053
|
+
import { forwardRef as forwardRef18, useEffect as useEffect6, useRef as useRef6, useState as useState9 } from "react";
|
|
2533
3054
|
import { Search as Search2, X as X6 } from "lucide-react";
|
|
2534
3055
|
|
|
2535
3056
|
// src/SearchInput.css
|
|
2536
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");
|
|
2537
3058
|
|
|
2538
3059
|
// src/SearchInput.tsx
|
|
2539
|
-
import { jsx as
|
|
3060
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2540
3061
|
var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay = 250, placeholder = "Search\u2026", clearLabel = "Clear search", ...rest }, ref) {
|
|
2541
|
-
const [local, setLocal] =
|
|
2542
|
-
const timer =
|
|
2543
|
-
const latest =
|
|
3062
|
+
const [local, setLocal] = useState9(value);
|
|
3063
|
+
const timer = useRef6(void 0);
|
|
3064
|
+
const latest = useRef6(onChange);
|
|
2544
3065
|
latest.current = onChange;
|
|
2545
|
-
|
|
2546
|
-
|
|
3066
|
+
useEffect6(() => setLocal(value), [value]);
|
|
3067
|
+
useEffect6(() => () => clearTimeout(timer.current), []);
|
|
2547
3068
|
const push = (next) => {
|
|
2548
3069
|
setLocal(next);
|
|
2549
3070
|
clearTimeout(timer.current);
|
|
@@ -2555,7 +3076,7 @@ var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay =
|
|
|
2555
3076
|
setLocal("");
|
|
2556
3077
|
latest.current("");
|
|
2557
3078
|
};
|
|
2558
|
-
return /* @__PURE__ */
|
|
3079
|
+
return /* @__PURE__ */ jsx31(
|
|
2559
3080
|
Input,
|
|
2560
3081
|
{
|
|
2561
3082
|
...rest,
|
|
@@ -2564,20 +3085,20 @@ var SearchInput = forwardRef18(function SearchInput2({ value, onChange, delay =
|
|
|
2564
3085
|
value: local,
|
|
2565
3086
|
placeholder,
|
|
2566
3087
|
onChange: (e) => push(e.target.value),
|
|
2567
|
-
prefix: /* @__PURE__ */
|
|
2568
|
-
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
|
|
2569
3090
|
}
|
|
2570
3091
|
);
|
|
2571
3092
|
});
|
|
2572
3093
|
|
|
2573
3094
|
// src/Pagination.tsx
|
|
2574
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
3095
|
+
import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
2575
3096
|
|
|
2576
3097
|
// src/Pagination.css
|
|
2577
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");
|
|
2578
3099
|
|
|
2579
3100
|
// src/Pagination.tsx
|
|
2580
|
-
import { jsx as
|
|
3101
|
+
import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2581
3102
|
function Pagination({
|
|
2582
3103
|
page,
|
|
2583
3104
|
pageSize,
|
|
@@ -2593,12 +3114,12 @@ function Pagination({
|
|
|
2593
3114
|
const from = (current - 1) * pageSize + 1;
|
|
2594
3115
|
const to = Math.min(current * pageSize, totalItems);
|
|
2595
3116
|
const range = labels?.range?.(from, to, totalItems) ?? `${from}\u2013${to} of ${totalItems.toLocaleString()}`;
|
|
2596
|
-
return /* @__PURE__ */
|
|
3117
|
+
return /* @__PURE__ */ jsxs31("nav", { className: cn("cfi-ui", "cfi-ui-pagination", className), "aria-label": "Pagination", children: [
|
|
2597
3118
|
showRange && // aria-live so a screen-reader user hears where they landed; paging
|
|
2598
3119
|
// otherwise moves focus nowhere and announces nothing.
|
|
2599
|
-
/* @__PURE__ */
|
|
2600
|
-
/* @__PURE__ */
|
|
2601
|
-
/* @__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(
|
|
2602
3123
|
"button",
|
|
2603
3124
|
{
|
|
2604
3125
|
type: "button",
|
|
@@ -2606,15 +3127,15 @@ function Pagination({
|
|
|
2606
3127
|
onClick: () => onPageChange(current - 1),
|
|
2607
3128
|
disabled: current <= 1,
|
|
2608
3129
|
"aria-label": labels?.previous ?? "Previous page",
|
|
2609
|
-
children: /* @__PURE__ */
|
|
3130
|
+
children: /* @__PURE__ */ jsx32(ChevronLeft2, { "aria-hidden": "true" })
|
|
2610
3131
|
}
|
|
2611
3132
|
),
|
|
2612
|
-
/* @__PURE__ */
|
|
3133
|
+
/* @__PURE__ */ jsxs31("span", { className: "cfi-ui-pagination__page", children: [
|
|
2613
3134
|
current,
|
|
2614
3135
|
" / ",
|
|
2615
3136
|
pageCount
|
|
2616
3137
|
] }),
|
|
2617
|
-
/* @__PURE__ */
|
|
3138
|
+
/* @__PURE__ */ jsx32(
|
|
2618
3139
|
"button",
|
|
2619
3140
|
{
|
|
2620
3141
|
type: "button",
|
|
@@ -2622,7 +3143,7 @@ function Pagination({
|
|
|
2622
3143
|
onClick: () => onPageChange(current + 1),
|
|
2623
3144
|
disabled: current >= pageCount,
|
|
2624
3145
|
"aria-label": labels?.next ?? "Next page",
|
|
2625
|
-
children: /* @__PURE__ */
|
|
3146
|
+
children: /* @__PURE__ */ jsx32(ChevronRight2, { "aria-hidden": "true" })
|
|
2626
3147
|
}
|
|
2627
3148
|
)
|
|
2628
3149
|
] })
|
|
@@ -2633,23 +3154,23 @@ function Pagination({
|
|
|
2633
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");
|
|
2634
3155
|
|
|
2635
3156
|
// src/ListToolbar.tsx
|
|
2636
|
-
import { jsx as
|
|
3157
|
+
import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2637
3158
|
function ListToolbar({ search, filters, actions, className }) {
|
|
2638
|
-
return /* @__PURE__ */
|
|
2639
|
-
search && /* @__PURE__ */
|
|
2640
|
-
filters && /* @__PURE__ */
|
|
2641
|
-
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 })
|
|
2642
3163
|
] });
|
|
2643
3164
|
}
|
|
2644
3165
|
|
|
2645
3166
|
// src/PageHeader.tsx
|
|
2646
|
-
import { ArrowLeft, ChevronRight as
|
|
3167
|
+
import { ArrowLeft, ChevronRight as ChevronRight3 } from "lucide-react";
|
|
2647
3168
|
|
|
2648
3169
|
// src/PageHeader.css
|
|
2649
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");
|
|
2650
3171
|
|
|
2651
3172
|
// src/PageHeader.tsx
|
|
2652
|
-
import { jsx as
|
|
3173
|
+
import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2653
3174
|
function PageHeader({
|
|
2654
3175
|
title,
|
|
2655
3176
|
description,
|
|
@@ -2663,32 +3184,32 @@ function PageHeader({
|
|
|
2663
3184
|
className
|
|
2664
3185
|
}) {
|
|
2665
3186
|
const Heading = `h${level}`;
|
|
2666
|
-
return /* @__PURE__ */
|
|
3187
|
+
return /* @__PURE__ */ jsxs33(
|
|
2667
3188
|
"div",
|
|
2668
3189
|
{
|
|
2669
3190
|
className: cn("cfi-ui", "cfi-ui-page-header", className),
|
|
2670
3191
|
"data-bordered": bordered ? "" : void 0,
|
|
2671
3192
|
children: [
|
|
2672
|
-
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) => {
|
|
2673
3194
|
const last = i === breadcrumbs.length - 1;
|
|
2674
|
-
const label = crumb.render ? crumb.render(crumb.label) : crumb.href ? /* @__PURE__ */
|
|
2675
|
-
return /* @__PURE__ */
|
|
2676
|
-
i > 0 && /* @__PURE__ */
|
|
2677
|
-
/* @__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 })
|
|
2678
3199
|
] }, i);
|
|
2679
3200
|
}) }),
|
|
2680
|
-
onBack && /* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
3201
|
+
onBack && /* @__PURE__ */ jsxs33("button", { type: "button", className: "cfi-ui-page-header__back", onClick: onBack, children: [
|
|
3202
|
+
/* @__PURE__ */ jsx34(ArrowLeft, { "aria-hidden": "true" }),
|
|
2682
3203
|
backLabel
|
|
2683
3204
|
] }),
|
|
2684
|
-
/* @__PURE__ */
|
|
2685
|
-
/* @__PURE__ */
|
|
2686
|
-
/* @__PURE__ */
|
|
2687
|
-
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 })
|
|
2688
3209
|
] }),
|
|
2689
|
-
actions && /* @__PURE__ */
|
|
3210
|
+
actions && /* @__PURE__ */ jsx34("div", { className: "cfi-ui-page-header__actions", children: actions })
|
|
2690
3211
|
] }),
|
|
2691
|
-
children && /* @__PURE__ */
|
|
3212
|
+
children && /* @__PURE__ */ jsx34("div", { className: "cfi-ui-page-header__meta", children })
|
|
2692
3213
|
]
|
|
2693
3214
|
}
|
|
2694
3215
|
);
|
|
@@ -2701,7 +3222,7 @@ import { X as X7 } from "lucide-react";
|
|
|
2701
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");
|
|
2702
3223
|
|
|
2703
3224
|
// src/SelectionBar.tsx
|
|
2704
|
-
import { jsx as
|
|
3225
|
+
import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2705
3226
|
function SelectionBar({
|
|
2706
3227
|
count,
|
|
2707
3228
|
children,
|
|
@@ -2713,7 +3234,7 @@ function SelectionBar({
|
|
|
2713
3234
|
className
|
|
2714
3235
|
}) {
|
|
2715
3236
|
if (count === 0) return null;
|
|
2716
|
-
return /* @__PURE__ */
|
|
3237
|
+
return /* @__PURE__ */ jsxs34(
|
|
2717
3238
|
"div",
|
|
2718
3239
|
{
|
|
2719
3240
|
role: "status",
|
|
@@ -2722,9 +3243,9 @@ function SelectionBar({
|
|
|
2722
3243
|
"data-busy": busy ? "" : void 0,
|
|
2723
3244
|
className: cn("cfi-ui", "cfi-ui-selection-bar", className),
|
|
2724
3245
|
children: [
|
|
2725
|
-
/* @__PURE__ */
|
|
2726
|
-
children && /* @__PURE__ */
|
|
2727
|
-
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(
|
|
2728
3249
|
"button",
|
|
2729
3250
|
{
|
|
2730
3251
|
type: "button",
|
|
@@ -2732,7 +3253,7 @@ function SelectionBar({
|
|
|
2732
3253
|
onClick: onClear,
|
|
2733
3254
|
disabled: busy,
|
|
2734
3255
|
children: [
|
|
2735
|
-
/* @__PURE__ */
|
|
3256
|
+
/* @__PURE__ */ jsx35(X7, { "aria-hidden": "true" }),
|
|
2736
3257
|
clearLabel
|
|
2737
3258
|
]
|
|
2738
3259
|
}
|
|
@@ -2743,14 +3264,14 @@ function SelectionBar({
|
|
|
2743
3264
|
}
|
|
2744
3265
|
|
|
2745
3266
|
// src/usePagedRows.ts
|
|
2746
|
-
import { useEffect as
|
|
3267
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useState as useState10 } from "react";
|
|
2747
3268
|
function usePagedRows(all, initialPageSize = 25) {
|
|
2748
|
-
const [page, setPage] =
|
|
2749
|
-
const [pageSize, setSize] =
|
|
3269
|
+
const [page, setPage] = useState10(1);
|
|
3270
|
+
const [pageSize, setSize] = useState10(initialPageSize);
|
|
2750
3271
|
const totalItems = all.length;
|
|
2751
3272
|
const pageCount = Math.max(1, Math.ceil(totalItems / Math.max(1, pageSize)));
|
|
2752
3273
|
const clamped = Math.min(Math.max(1, page), pageCount);
|
|
2753
|
-
|
|
3274
|
+
useEffect7(() => {
|
|
2754
3275
|
if (clamped !== page) setPage(clamped);
|
|
2755
3276
|
}, [clamped, page]);
|
|
2756
3277
|
const rows = useMemo6(
|
|
@@ -2777,6 +3298,8 @@ export {
|
|
|
2777
3298
|
BootScreen,
|
|
2778
3299
|
Button,
|
|
2779
3300
|
ButtonGroup,
|
|
3301
|
+
CalendarGrid,
|
|
3302
|
+
CalendarLegendItem,
|
|
2780
3303
|
Card,
|
|
2781
3304
|
CardBody,
|
|
2782
3305
|
CardDescription,
|
|
@@ -2811,6 +3334,7 @@ export {
|
|
|
2811
3334
|
FilterBar,
|
|
2812
3335
|
IconButton,
|
|
2813
3336
|
Input,
|
|
3337
|
+
KanbanBoard,
|
|
2814
3338
|
Label3 as Label,
|
|
2815
3339
|
ListToolbar,
|
|
2816
3340
|
LoadingState,
|
|
@@ -2895,6 +3419,11 @@ export {
|
|
|
2895
3419
|
TooltipProvider,
|
|
2896
3420
|
TooltipRoot,
|
|
2897
3421
|
TooltipTrigger,
|
|
3422
|
+
calendarAddDays,
|
|
3423
|
+
calendarBuckets,
|
|
3424
|
+
calendarDayKey,
|
|
3425
|
+
calendarDays,
|
|
3426
|
+
calendarShift,
|
|
2898
3427
|
cn,
|
|
2899
3428
|
formatCalendarDate,
|
|
2900
3429
|
formatDate,
|
|
@@ -2902,6 +3431,8 @@ export {
|
|
|
2902
3431
|
formatMoney,
|
|
2903
3432
|
formatNumber,
|
|
2904
3433
|
formatPercent,
|
|
3434
|
+
kanbanLayout,
|
|
3435
|
+
kanbanStep,
|
|
2905
3436
|
timelineLayout,
|
|
2906
3437
|
useDebouncedValue,
|
|
2907
3438
|
useFieldControl,
|
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@campfire-interactive/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared UI primitives for the Campfire Suite — Radix behavior, plain CSS styled entirely from the design tokens, no Tailwind required",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist"
|
|
16
|
-
],
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "tsup",
|
|
19
|
-
"prepublishOnly": "npm run build",
|
|
20
|
-
"dev": "tsup --watch",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"typecheck": "tsc --noEmit"
|
|
23
|
-
},
|
|
24
|
-
"peerDependencies": {
|
|
25
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
26
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"@campfire-interactive/design-tokens": "*",
|
|
30
|
-
"@radix-ui/react-accordion": "^1.2.12",
|
|
31
|
-
"@radix-ui/react-checkbox": "^1.3.3",
|
|
32
|
-
"@radix-ui/react-dialog": "^1.1.15",
|
|
33
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
34
|
-
"@radix-ui/react-popover": "^1.1.15",
|
|
35
|
-
"@radix-ui/react-select": "^2.2.6",
|
|
36
|
-
"@radix-ui/react-slider": "^1.3.6",
|
|
37
|
-
"@radix-ui/react-slot": "^1.2.3",
|
|
38
|
-
"@radix-ui/react-switch": "^1.2.6",
|
|
39
|
-
"@radix-ui/react-tabs": "^1.1.13",
|
|
40
|
-
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
41
|
-
"@radix-ui/react-tooltip": "^1.2.8",
|
|
42
|
-
"lucide-react": "^0.487.0"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/react": "^19.0.0",
|
|
46
|
-
"@types/react-dom": "^19.0.0",
|
|
47
|
-
"react": "^19.0.0",
|
|
48
|
-
"react-dom": "^19.0.0",
|
|
49
|
-
"tsup": "^8.0.0",
|
|
50
|
-
"typescript": "^5.3.3"
|
|
51
|
-
},
|
|
52
|
-
"publishConfig": {
|
|
53
|
-
"registry": "https://registry.npmjs.org",
|
|
54
|
-
"access": "public"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@campfire-interactive/ui",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Shared UI primitives for the Campfire Suite — Radix behavior, plain CSS styled entirely from the design tokens, no Tailwind required",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
26
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@campfire-interactive/design-tokens": "*",
|
|
30
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
31
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
32
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
33
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
34
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
35
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
36
|
+
"@radix-ui/react-slider": "^1.3.6",
|
|
37
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
38
|
+
"@radix-ui/react-switch": "^1.2.6",
|
|
39
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
40
|
+
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
41
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
42
|
+
"lucide-react": "^0.487.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/react": "^19.0.0",
|
|
46
|
+
"@types/react-dom": "^19.0.0",
|
|
47
|
+
"react": "^19.0.0",
|
|
48
|
+
"react-dom": "^19.0.0",
|
|
49
|
+
"tsup": "^8.0.0",
|
|
50
|
+
"typescript": "^5.3.3"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"registry": "https://registry.npmjs.org",
|
|
54
|
+
"access": "public"
|
|
55
|
+
}
|
|
56
|
+
}
|