@gerege-systems/ui 0.11.2
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/LICENSE +21 -0
- package/README.md +234 -0
- package/dist-lib/__tests__/helpers/color.d.ts +9 -0
- package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
- package/dist-lib/components/ui/Accordion.d.ts +21 -0
- package/dist-lib/components/ui/Accordion.js +71 -0
- package/dist-lib/components/ui/Accordion.js.map +1 -0
- package/dist-lib/components/ui/Alert.d.ts +51 -0
- package/dist-lib/components/ui/Alert.js +79 -0
- package/dist-lib/components/ui/Alert.js.map +1 -0
- package/dist-lib/components/ui/Avatar.d.ts +52 -0
- package/dist-lib/components/ui/Avatar.js +94 -0
- package/dist-lib/components/ui/Avatar.js.map +1 -0
- package/dist-lib/components/ui/Badge.d.ts +26 -0
- package/dist-lib/components/ui/Badge.js +76 -0
- package/dist-lib/components/ui/Badge.js.map +1 -0
- package/dist-lib/components/ui/Breadcrumbs.d.ts +33 -0
- package/dist-lib/components/ui/Breadcrumbs.js +52 -0
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
- package/dist-lib/components/ui/Button.d.ts +52 -0
- package/dist-lib/components/ui/Button.js +131 -0
- package/dist-lib/components/ui/Button.js.map +1 -0
- package/dist-lib/components/ui/Calendar.d.ts +15 -0
- package/dist-lib/components/ui/Calendar.js +139 -0
- package/dist-lib/components/ui/Calendar.js.map +1 -0
- package/dist-lib/components/ui/Card.d.ts +39 -0
- package/dist-lib/components/ui/Card.js +97 -0
- package/dist-lib/components/ui/Card.js.map +1 -0
- package/dist-lib/components/ui/Carousel.d.ts +27 -0
- package/dist-lib/components/ui/Carousel.js +174 -0
- package/dist-lib/components/ui/Carousel.js.map +1 -0
- package/dist-lib/components/ui/Chart.d.ts +87 -0
- package/dist-lib/components/ui/Chart.js +487 -0
- package/dist-lib/components/ui/Chart.js.map +1 -0
- package/dist-lib/components/ui/Checkbox.d.ts +28 -0
- package/dist-lib/components/ui/Checkbox.js +70 -0
- package/dist-lib/components/ui/Checkbox.js.map +1 -0
- package/dist-lib/components/ui/Combobox.d.ts +67 -0
- package/dist-lib/components/ui/Combobox.js +195 -0
- package/dist-lib/components/ui/Combobox.js.map +1 -0
- package/dist-lib/components/ui/CommandPalette.d.ts +112 -0
- package/dist-lib/components/ui/CommandPalette.js +179 -0
- package/dist-lib/components/ui/CommandPalette.js.map +1 -0
- package/dist-lib/components/ui/ContextMenu.d.ts +50 -0
- package/dist-lib/components/ui/ContextMenu.js +165 -0
- package/dist-lib/components/ui/ContextMenu.js.map +1 -0
- package/dist-lib/components/ui/DataGrid.d.ts +79 -0
- package/dist-lib/components/ui/DataGrid.js +99 -0
- package/dist-lib/components/ui/DataGrid.js.map +1 -0
- package/dist-lib/components/ui/DatePicker.d.ts +45 -0
- package/dist-lib/components/ui/DatePicker.js +193 -0
- package/dist-lib/components/ui/DatePicker.js.map +1 -0
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +147 -0
- package/dist-lib/components/ui/DesignSystemProvider.js +135 -0
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
- package/dist-lib/components/ui/Dialog.d.ts +77 -0
- package/dist-lib/components/ui/Dialog.js +165 -0
- package/dist-lib/components/ui/Dialog.js.map +1 -0
- package/dist-lib/components/ui/Drawer.d.ts +44 -0
- package/dist-lib/components/ui/Drawer.js +132 -0
- package/dist-lib/components/ui/Drawer.js.map +1 -0
- package/dist-lib/components/ui/DropdownMenu.d.ts +47 -0
- package/dist-lib/components/ui/DropdownMenu.js +169 -0
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
- package/dist-lib/components/ui/EmptyState.d.ts +48 -0
- package/dist-lib/components/ui/EmptyState.js +46 -0
- package/dist-lib/components/ui/EmptyState.js.map +1 -0
- package/dist-lib/components/ui/ErrorState.d.ts +51 -0
- package/dist-lib/components/ui/ErrorState.js +69 -0
- package/dist-lib/components/ui/ErrorState.js.map +1 -0
- package/dist-lib/components/ui/FileUpload.d.ts +26 -0
- package/dist-lib/components/ui/FileUpload.js +111 -0
- package/dist-lib/components/ui/FileUpload.js.map +1 -0
- package/dist-lib/components/ui/Form.d.ts +36 -0
- package/dist-lib/components/ui/Form.js +131 -0
- package/dist-lib/components/ui/Form.js.map +1 -0
- package/dist-lib/components/ui/Icon.d.ts +34 -0
- package/dist-lib/components/ui/Icon.js +32 -0
- package/dist-lib/components/ui/Icon.js.map +1 -0
- package/dist-lib/components/ui/IconButton.d.ts +34 -0
- package/dist-lib/components/ui/IconButton.js +64 -0
- package/dist-lib/components/ui/IconButton.js.map +1 -0
- package/dist-lib/components/ui/Input.d.ts +54 -0
- package/dist-lib/components/ui/Input.js +158 -0
- package/dist-lib/components/ui/Input.js.map +1 -0
- package/dist-lib/components/ui/Kbd.d.ts +16 -0
- package/dist-lib/components/ui/Kbd.js +24 -0
- package/dist-lib/components/ui/Kbd.js.map +1 -0
- package/dist-lib/components/ui/MultiSelect.d.ts +64 -0
- package/dist-lib/components/ui/MultiSelect.js +210 -0
- package/dist-lib/components/ui/MultiSelect.js.map +1 -0
- package/dist-lib/components/ui/Pagination.d.ts +55 -0
- package/dist-lib/components/ui/Pagination.js +153 -0
- package/dist-lib/components/ui/Pagination.js.map +1 -0
- package/dist-lib/components/ui/Popover.d.ts +16 -0
- package/dist-lib/components/ui/Popover.js +35 -0
- package/dist-lib/components/ui/Popover.js.map +1 -0
- package/dist-lib/components/ui/Progress.d.ts +40 -0
- package/dist-lib/components/ui/Progress.js +100 -0
- package/dist-lib/components/ui/Progress.js.map +1 -0
- package/dist-lib/components/ui/RadioGroup.d.ts +27 -0
- package/dist-lib/components/ui/RadioGroup.js +81 -0
- package/dist-lib/components/ui/RadioGroup.js.map +1 -0
- package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
- package/dist-lib/components/ui/RelativeTime.js +31 -0
- package/dist-lib/components/ui/RelativeTime.js.map +1 -0
- package/dist-lib/components/ui/ScrollArea.d.ts +21 -0
- package/dist-lib/components/ui/ScrollArea.js +60 -0
- package/dist-lib/components/ui/ScrollArea.js.map +1 -0
- package/dist-lib/components/ui/Select.d.ts +61 -0
- package/dist-lib/components/ui/Select.js +139 -0
- package/dist-lib/components/ui/Select.js.map +1 -0
- package/dist-lib/components/ui/Separator.d.ts +13 -0
- package/dist-lib/components/ui/Separator.js +26 -0
- package/dist-lib/components/ui/Separator.js.map +1 -0
- package/dist-lib/components/ui/Sheet.d.ts +55 -0
- package/dist-lib/components/ui/Sheet.js +136 -0
- package/dist-lib/components/ui/Sheet.js.map +1 -0
- package/dist-lib/components/ui/Sidebar.d.ts +91 -0
- package/dist-lib/components/ui/Sidebar.js +212 -0
- package/dist-lib/components/ui/Sidebar.js.map +1 -0
- package/dist-lib/components/ui/Skeleton.d.ts +37 -0
- package/dist-lib/components/ui/Skeleton.js +29 -0
- package/dist-lib/components/ui/Skeleton.js.map +1 -0
- package/dist-lib/components/ui/Slider.d.ts +33 -0
- package/dist-lib/components/ui/Slider.js +64 -0
- package/dist-lib/components/ui/Slider.js.map +1 -0
- package/dist-lib/components/ui/Snackbar.d.ts +21 -0
- package/dist-lib/components/ui/Snackbar.js +57 -0
- package/dist-lib/components/ui/Snackbar.js.map +1 -0
- package/dist-lib/components/ui/Spinner.d.ts +23 -0
- package/dist-lib/components/ui/Spinner.js +54 -0
- package/dist-lib/components/ui/Spinner.js.map +1 -0
- package/dist-lib/components/ui/Stepper.d.ts +40 -0
- package/dist-lib/components/ui/Stepper.js +111 -0
- package/dist-lib/components/ui/Stepper.js.map +1 -0
- package/dist-lib/components/ui/Switch.d.ts +33 -0
- package/dist-lib/components/ui/Switch.js +80 -0
- package/dist-lib/components/ui/Switch.js.map +1 -0
- package/dist-lib/components/ui/Table.d.ts +71 -0
- package/dist-lib/components/ui/Table.js +179 -0
- package/dist-lib/components/ui/Table.js.map +1 -0
- package/dist-lib/components/ui/Tabs.d.ts +32 -0
- package/dist-lib/components/ui/Tabs.js +91 -0
- package/dist-lib/components/ui/Tabs.js.map +1 -0
- package/dist-lib/components/ui/TagInput.d.ts +39 -0
- package/dist-lib/components/ui/TagInput.js +111 -0
- package/dist-lib/components/ui/TagInput.js.map +1 -0
- package/dist-lib/components/ui/Textarea.d.ts +34 -0
- package/dist-lib/components/ui/Textarea.js +88 -0
- package/dist-lib/components/ui/Textarea.js.map +1 -0
- package/dist-lib/components/ui/Timeline.d.ts +33 -0
- package/dist-lib/components/ui/Timeline.js +56 -0
- package/dist-lib/components/ui/Timeline.js.map +1 -0
- package/dist-lib/components/ui/Toast.d.ts +53 -0
- package/dist-lib/components/ui/Toast.js +158 -0
- package/dist-lib/components/ui/Toast.js.map +1 -0
- package/dist-lib/components/ui/Tooltip.d.ts +38 -0
- package/dist-lib/components/ui/Tooltip.js +60 -0
- package/dist-lib/components/ui/Tooltip.js.map +1 -0
- package/dist-lib/components/ui/TopNav.d.ts +37 -0
- package/dist-lib/components/ui/TopNav.js +69 -0
- package/dist-lib/components/ui/TopNav.js.map +1 -0
- package/dist-lib/components/ui/Tree.d.ts +51 -0
- package/dist-lib/components/ui/Tree.js +170 -0
- package/dist-lib/components/ui/Tree.js.map +1 -0
- package/dist-lib/hooks/use-debounce.d.ts +19 -0
- package/dist-lib/hooks/use-debounce.js +36 -0
- package/dist-lib/hooks/use-debounce.js.map +1 -0
- package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
- package/dist-lib/hooks/use-delayed-loading.js +20 -0
- package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
- package/dist-lib/hooks/use-field-ids.d.ts +30 -0
- package/dist-lib/hooks/use-field-ids.js +18 -0
- package/dist-lib/hooks/use-field-ids.js.map +1 -0
- package/dist-lib/hooks/use-media-query.d.ts +11 -0
- package/dist-lib/hooks/use-media-query.js +22 -0
- package/dist-lib/hooks/use-media-query.js.map +1 -0
- package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
- package/dist-lib/hooks/use-modifier-key.js +19 -0
- package/dist-lib/hooks/use-modifier-key.js.map +1 -0
- package/dist-lib/hooks/use-strings.d.ts +5 -0
- package/dist-lib/hooks/use-strings.js +12 -0
- package/dist-lib/hooks/use-strings.js.map +1 -0
- package/dist-lib/hooks/use-toast.d.ts +48 -0
- package/dist-lib/hooks/use-toast.js +67 -0
- package/dist-lib/hooks/use-toast.js.map +1 -0
- package/dist-lib/icon.d.ts +8 -0
- package/dist-lib/icon.js +7 -0
- package/dist-lib/icon.js.map +1 -0
- package/dist-lib/icons/index.d.ts +80 -0
- package/dist-lib/icons/index.js +163 -0
- package/dist-lib/icons/index.js.map +1 -0
- package/dist-lib/illustrations/index.d.ts +10 -0
- package/dist-lib/illustrations/index.js +114 -0
- package/dist-lib/illustrations/index.js.map +1 -0
- package/dist-lib/index.d.ts +68 -0
- package/dist-lib/index.js +291 -0
- package/dist-lib/index.js.map +1 -0
- package/dist-lib/lib/cva.d.ts +6 -0
- package/dist-lib/lib/format.d.ts +65 -0
- package/dist-lib/lib/format.js +95 -0
- package/dist-lib/lib/format.js.map +1 -0
- package/dist-lib/lib/return-focus.d.ts +11 -0
- package/dist-lib/lib/return-focus.js +12 -0
- package/dist-lib/lib/return-focus.js.map +1 -0
- package/dist-lib/lib/strings.d.ts +190 -0
- package/dist-lib/lib/strings.js +141 -0
- package/dist-lib/lib/strings.js.map +1 -0
- package/dist-lib/lib/strings.mn.d.ts +3 -0
- package/dist-lib/lib/strings.mn.js +115 -0
- package/dist-lib/lib/strings.mn.js.map +1 -0
- package/dist-lib/lib/utils.d.ts +11 -0
- package/dist-lib/lib/utils.js +10 -0
- package/dist-lib/lib/utils.js.map +1 -0
- package/dist-lib/styles.css +1 -0
- package/dist-lib/theme.css +840 -0
- package/package.json +158 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataGrid.js","sources":["../../../src/components/ui/DataGrid.tsx"],"sourcesContent":["'use client';\n\nimport { isValidElement, useMemo, useState, type ReactNode } from 'react';\nimport { Settings } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { formatDate } from '@/lib/format';\nimport { useStrings } from '@/hooks/use-strings';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './DropdownMenu';\nimport { IconButton } from './IconButton';\nimport { Input } from './Input';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n TableSortHeader,\n} from './Table';\nimport { Skeleton } from './Skeleton';\n\nexport interface DataGridColumn<TRow> {\n /**\n * Stable key used for visibility, sort, and React lists. Autocompletes\n * row fields; any string is allowed for computed columns (with `cell`).\n */\n key: (keyof TRow & string) | (string & {});\n /** Header label. */\n header: ReactNode;\n /**\n * Render the cell. Defaults to the value at `row[key]`: `Date` →\n * `formatDate` (yyyy-MM-dd HH:mm), primitives as-is, other objects via\n * `String()`.\n */\n cell?: (row: TRow) => ReactNode;\n /** Cell width — passed to `<col>` so the grid keeps shape during loading. */\n width?: string;\n /** Allow sorting on this column. */\n sortable?: boolean;\n /** Right-align numeric / monetary columns. */\n align?: 'left' | 'right';\n}\n\nexport interface DataGridProps<TRow extends { id: string | number }> {\n columns: DataGridColumn<TRow>[];\n rows: TRow[];\n loading?: boolean;\n /** Initial sort state. */\n sort?: { key: string; direction: 'asc' | 'desc' } | null;\n onSortChange?: (sort: { key: string; direction: 'asc' | 'desc' }) => void;\n /** Show a filter input row above the grid. */\n filter?: { value: string; onChange: (q: string) => void; placeholder?: string };\n /** Empty-state node when no rows are visible. */\n emptyState?: ReactNode;\n /**\n * Controlled column visibility: `{ [key]: boolean }` (missing = visible).\n * Omit for internal state. At least one column always stays visible.\n */\n columnVisibility?: Record<string, boolean>;\n onColumnVisibilityChange?: (next: Record<string, boolean>) => void;\n className?: string;\n}\n\n/** Default cell renderer for `row[key]` when no `cell` is given. */\nfunction renderValue(v: unknown, emptyLabel: string): ReactNode {\n if (v === null || v === undefined || v === '') {\n return (\n <span aria-label={emptyLabel} className=\"text-foreground-subtle\">\n —\n </span>\n );\n }\n if (v instanceof Date) return formatDate(v);\n if (typeof v === 'object') return isValidElement(v) ? v : String(v);\n return v as ReactNode;\n}\n\n/**\n * Composite grid built from `Table` + DropdownMenu + Input. Provides\n * column visibility toggle, an inline filter, sortable headers, and\n * loading / empty states.\n *\n * @example\n * <DataGrid\n * columns={[\n * { key: 'name', header: 'Name', sortable: true },\n * { key: 'status', header: 'Status', cell: r => <Badge tone={...}>{r.status}</Badge> },\n * { key: 'updatedAt', header: 'Updated', align: 'right' },\n * ]}\n * rows={rows}\n * filter={{ value: q, onChange: setQ }}\n * sort={sort}\n * onSortChange={setSort}\n * />\n *\n * @do Provide a meaningful empty state with a primary action when the grid\n * starts empty (no items at all, not just filtered out).\n * @dont Render thousands of rows synchronously — virtualise with `@tanstack/react-virtual`\n * and wrap with this component's headers as a shell.\n */\nexport function DataGrid<TRow extends { id: string | number }>({\n columns,\n rows,\n loading,\n sort,\n onSortChange,\n filter,\n emptyState,\n columnVisibility,\n onColumnVisibilityChange,\n className,\n}: DataGridProps<TRow>) {\n const strings = useStrings();\n const [internalVisibility, setInternalVisibility] = useState<Record<string, boolean>>({});\n const visibility = columnVisibility ?? internalVisibility;\n const isVisible = (key: string) => visibility[key] !== false;\n const visibleColumns = useMemo(\n () => columns.filter((c) => visibility[c.key] !== false),\n [columns, visibility],\n );\n const setColumnVisible = (key: string, visible: boolean) => {\n // Never let the user hide the last visible column — an empty grid has no\n // affordance to bring columns back.\n if (!visible && visibleColumns.length <= 1) return;\n const next = { ...visibility, [key]: visible };\n if (columnVisibility === undefined) setInternalVisibility(next);\n onColumnVisibilityChange?.(next);\n };\n\n const renderRows = () => {\n if (loading) {\n return Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={`skel-${i}`}>\n {visibleColumns.map((c) => (\n <TableCell key={c.key}>\n <Skeleton variant=\"text\" className=\"w-3/4\" />\n </TableCell>\n ))}\n </TableRow>\n ));\n }\n if (rows.length === 0) {\n return (\n <TableRow>\n <TableCell colSpan={Math.max(1, visibleColumns.length)} className=\"h-32 text-center\">\n {emptyState ?? <span className=\"text-foreground-subtle\">{strings.dataGrid.empty}</span>}\n </TableCell>\n </TableRow>\n );\n }\n return rows.map((row) => (\n <TableRow key={row.id}>\n {visibleColumns.map((c) => (\n <TableCell key={c.key} align={c.align ?? 'left'}>\n {c.cell\n ? c.cell(row)\n : renderValue((row as Record<string, unknown>)[c.key], strings.dataGrid.emptyCell)}\n </TableCell>\n ))}\n </TableRow>\n ));\n };\n\n return (\n <div className={cn('flex flex-col gap-3', className)}>\n <div className=\"flex items-center justify-between gap-2\">\n {filter ? (\n <Input\n type=\"search\"\n placeholder={filter.placeholder ?? strings.dataGrid.filterPlaceholder}\n value={filter.value}\n onChange={(e) => filter.onChange(e.target.value)}\n clearable\n onClear={() => filter.onChange('')}\n className=\"w-full max-w-sm\"\n hideLabel\n label={strings.dataGrid.filterRows}\n />\n ) : (\n <div />\n )}\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n aria-label={strings.dataGrid.columnVisibility}\n icon={<Settings />}\n variant=\"outline\"\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuLabel>{strings.dataGrid.columns}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {columns.map((c) => (\n <DropdownMenuCheckboxItem\n key={c.key}\n checked={isVisible(c.key)}\n disabled={isVisible(c.key) && visibleColumns.length <= 1}\n onCheckedChange={(v) => setColumnVisible(c.key, v === true)}\n >\n {c.header}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <Table containerClassName=\"rounded-lg border border-border\">\n <colgroup>\n {visibleColumns.map((c) => (\n <col key={c.key} style={c.width ? { width: c.width } : undefined} />\n ))}\n </colgroup>\n <TableHeader>\n <TableRow>\n {visibleColumns.map((c) =>\n c.sortable && onSortChange ? (\n <TableSortHeader\n key={c.key}\n align={c.align ?? 'left'}\n sortKey={c.key}\n currentSort={sort ?? null}\n onSortChange={(k, d) => onSortChange({ key: k, direction: d })}\n >\n {c.header}\n </TableSortHeader>\n ) : (\n <TableHead key={c.key} align={c.align ?? 'left'}>\n {c.header}\n </TableHead>\n ),\n )}\n </TableRow>\n </TableHeader>\n <TableBody>{renderRows()}</TableBody>\n </Table>\n </div>\n );\n}\n"],"names":["renderValue","v","emptyLabel","formatDate","isValidElement","DataGrid","columns","rows","loading","sort","onSortChange","filter","emptyState","columnVisibility","onColumnVisibilityChange","className","strings","useStrings","internalVisibility","setInternalVisibility","useState","visibility","isVisible","key","visibleColumns","useMemo","c","setColumnVisible","visible","next","renderRows","_","i","jsx","TableRow","TableCell","Skeleton","row","cn","jsxs","Input","DropdownMenu","DropdownMenuTrigger","IconButton","Settings","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuCheckboxItem","Table","TableHeader","TableSortHeader","k","d","TableHead","TableBody"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DateRange, DayPickerProps, Matcher } from 'react-day-picker';
|
|
3
|
+
type CalendarLocale = DayPickerProps['locale'];
|
|
4
|
+
interface PickerCommonProps {
|
|
5
|
+
label?: ReactNode;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Restrict to a date range. Days outside are not selectable. */
|
|
10
|
+
fromDate?: Date;
|
|
11
|
+
toDate?: Date;
|
|
12
|
+
/** Extra RDP matcher(s) for disabled days, merged with `fromDate`/`toDate`. */
|
|
13
|
+
disabledDays?: Matcher | Matcher[];
|
|
14
|
+
/** Custom display formatter for the trigger text. */
|
|
15
|
+
formatDate?: (d: Date) => string;
|
|
16
|
+
/** date-fns locale object passed through to react-day-picker. */
|
|
17
|
+
locale?: CalendarLocale;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface DatePickerProps extends PickerCommonProps {
|
|
21
|
+
value?: Date;
|
|
22
|
+
onChange: (date: Date | undefined) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Single-date picker.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* <DatePicker label="Date of birth" value={dob} onChange={setDob} />
|
|
29
|
+
*
|
|
30
|
+
* @do Use locale-aware formatting via `formatDate` / `locale`.
|
|
31
|
+
* @dont Roll a custom calendar — Radix Popover + react-day-picker handles a11y.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DatePicker: import('react').ForwardRefExoticComponent<DatePickerProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
34
|
+
export interface DateRangePickerProps extends PickerCommonProps {
|
|
35
|
+
value?: DateRange;
|
|
36
|
+
onChange: (range: DateRange | undefined) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Two-date range picker. `value` is `{from, to}`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* <DateRangePicker label="Reporting period" value={range} onChange={setRange} />
|
|
43
|
+
*/
|
|
44
|
+
export declare const DateRangePicker: import('react').ForwardRefExoticComponent<DateRangePickerProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as n, jsxs as $ } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as O, useId as R, useState as S, useMemo as F } from "react";
|
|
4
|
+
import * as f from "@radix-ui/react-popover";
|
|
5
|
+
import { Calendar as A } from "../../icons/index.js";
|
|
6
|
+
import { Calendar as D } from "./Calendar.js";
|
|
7
|
+
import { cn as C } from "../../lib/utils.js";
|
|
8
|
+
import { useStrings as w } from "../../hooks/use-strings.js";
|
|
9
|
+
function z(t, e) {
|
|
10
|
+
if (e != null && e.code)
|
|
11
|
+
return t.toLocaleDateString(e.code, { year: "numeric", month: "short", day: "numeric" });
|
|
12
|
+
const o = (r) => String(r).padStart(2, "0");
|
|
13
|
+
return `${t.getFullYear()}-${o(t.getMonth() + 1)}-${o(t.getDate())}`;
|
|
14
|
+
}
|
|
15
|
+
function v(t, e, o) {
|
|
16
|
+
return F(() => {
|
|
17
|
+
const r = [];
|
|
18
|
+
return t && r.push({ before: t }), e && r.push({ after: e }), o && r.push(...Array.isArray(o) ? o : [o]), r.length ? r : void 0;
|
|
19
|
+
}, [t, e, o]);
|
|
20
|
+
}
|
|
21
|
+
function B({
|
|
22
|
+
label: t,
|
|
23
|
+
placeholder: e,
|
|
24
|
+
hasValue: o,
|
|
25
|
+
children: r,
|
|
26
|
+
error: s,
|
|
27
|
+
fieldId: i,
|
|
28
|
+
disabled: g,
|
|
29
|
+
open: d
|
|
30
|
+
}) {
|
|
31
|
+
const a = `${i}-error`, p = `${i}-calendar`, c = o ? r : e;
|
|
32
|
+
return /* @__PURE__ */ $("div", { className: "flex flex-col gap-1.5", children: [
|
|
33
|
+
t && /* @__PURE__ */ n(
|
|
34
|
+
"label",
|
|
35
|
+
{
|
|
36
|
+
id: `${i}-label`,
|
|
37
|
+
htmlFor: i,
|
|
38
|
+
className: "text-foreground text-sm font-medium",
|
|
39
|
+
children: t
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
/* @__PURE__ */ n(f.Trigger, { asChild: !0, children: /* @__PURE__ */ $(
|
|
43
|
+
"button",
|
|
44
|
+
{
|
|
45
|
+
id: i,
|
|
46
|
+
type: "button",
|
|
47
|
+
role: "combobox",
|
|
48
|
+
"aria-expanded": d,
|
|
49
|
+
"aria-controls": p,
|
|
50
|
+
"aria-labelledby": t ? `${i}-label` : void 0,
|
|
51
|
+
"aria-label": t ? void 0 : e,
|
|
52
|
+
disabled: g,
|
|
53
|
+
"aria-invalid": !!s || void 0,
|
|
54
|
+
"aria-describedby": s ? a : void 0,
|
|
55
|
+
className: C(
|
|
56
|
+
"bg-card inline-flex h-9 w-full items-center justify-start gap-2 rounded-md border px-3 text-left text-lg md:text-sm",
|
|
57
|
+
"transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]",
|
|
58
|
+
"focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
59
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
60
|
+
s ? "border-danger focus-visible:border-danger focus-visible:ring-danger" : "border-border-input focus-visible:border-accent focus-visible:ring-ring",
|
|
61
|
+
!o && "text-foreground-subtle"
|
|
62
|
+
),
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ n(A, { className: "text-foreground-subtle size-4", "aria-hidden": !0 }),
|
|
65
|
+
/* @__PURE__ */ n("span", { className: "truncate", title: typeof c == "string" ? c : void 0, children: c })
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
) }),
|
|
69
|
+
s && /* @__PURE__ */ n("p", { id: a, className: "text-danger-text text-xs", children: s })
|
|
70
|
+
] });
|
|
71
|
+
}
|
|
72
|
+
const T = O(function({
|
|
73
|
+
value: e,
|
|
74
|
+
onChange: o,
|
|
75
|
+
label: r,
|
|
76
|
+
placeholder: s,
|
|
77
|
+
error: i,
|
|
78
|
+
disabled: g,
|
|
79
|
+
fromDate: d,
|
|
80
|
+
toDate: a,
|
|
81
|
+
disabledDays: p,
|
|
82
|
+
formatDate: c,
|
|
83
|
+
locale: h,
|
|
84
|
+
className: P
|
|
85
|
+
}, k) {
|
|
86
|
+
const N = w(), u = c ?? ((m) => z(m, h)), b = s ?? N.datePicker.pickDate, l = R(), [x, y] = S(!1), M = v(d, a, p);
|
|
87
|
+
return /* @__PURE__ */ n("div", { ref: k, className: C(P), children: /* @__PURE__ */ $(f.Root, { open: x, onOpenChange: y, children: [
|
|
88
|
+
/* @__PURE__ */ n(
|
|
89
|
+
B,
|
|
90
|
+
{
|
|
91
|
+
label: r,
|
|
92
|
+
placeholder: b,
|
|
93
|
+
hasValue: !!e,
|
|
94
|
+
error: i,
|
|
95
|
+
fieldId: l,
|
|
96
|
+
disabled: g,
|
|
97
|
+
open: x,
|
|
98
|
+
children: e ? u(e) : ""
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ n(f.Portal, { children: /* @__PURE__ */ n(
|
|
102
|
+
f.Content,
|
|
103
|
+
{
|
|
104
|
+
id: `${l}-calendar`,
|
|
105
|
+
"aria-labelledby": r ? `${l}-label` : void 0,
|
|
106
|
+
"aria-label": r ? void 0 : b,
|
|
107
|
+
align: "start",
|
|
108
|
+
sideOffset: 4,
|
|
109
|
+
className: "text-popover-foreground z-[var(--z-popover)] rounded-lg shadow-md",
|
|
110
|
+
children: /* @__PURE__ */ n(
|
|
111
|
+
D,
|
|
112
|
+
{
|
|
113
|
+
mode: "single",
|
|
114
|
+
selected: e,
|
|
115
|
+
defaultMonth: e,
|
|
116
|
+
onSelect: (m) => {
|
|
117
|
+
o(m ?? void 0), m && y(!1);
|
|
118
|
+
},
|
|
119
|
+
startMonth: d,
|
|
120
|
+
endMonth: a,
|
|
121
|
+
disabled: M,
|
|
122
|
+
locale: h
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
) })
|
|
127
|
+
] }) });
|
|
128
|
+
});
|
|
129
|
+
T.displayName = "DatePicker";
|
|
130
|
+
const L = O(
|
|
131
|
+
function({
|
|
132
|
+
value: e,
|
|
133
|
+
onChange: o,
|
|
134
|
+
label: r,
|
|
135
|
+
placeholder: s,
|
|
136
|
+
error: i,
|
|
137
|
+
disabled: g,
|
|
138
|
+
fromDate: d,
|
|
139
|
+
toDate: a,
|
|
140
|
+
disabledDays: p,
|
|
141
|
+
formatDate: c,
|
|
142
|
+
locale: h,
|
|
143
|
+
className: P
|
|
144
|
+
}, k) {
|
|
145
|
+
const N = w(), u = c ?? ((j) => z(j, h)), b = s ?? N.datePicker.pickRange, l = R(), [x, y] = S(!1), M = !!(e != null && e.from), m = v(d, a, p);
|
|
146
|
+
return /* @__PURE__ */ n("div", { ref: k, className: C(P), children: /* @__PURE__ */ $(f.Root, { open: x, onOpenChange: y, children: [
|
|
147
|
+
/* @__PURE__ */ n(
|
|
148
|
+
B,
|
|
149
|
+
{
|
|
150
|
+
label: r,
|
|
151
|
+
placeholder: b,
|
|
152
|
+
hasValue: M,
|
|
153
|
+
error: i,
|
|
154
|
+
fieldId: l,
|
|
155
|
+
disabled: g,
|
|
156
|
+
open: x,
|
|
157
|
+
children: e != null && e.from && e.to ? `${u(e.from)} – ${u(e.to)}` : e != null && e.from ? u(e.from) : ""
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
/* @__PURE__ */ n(f.Portal, { children: /* @__PURE__ */ n(
|
|
161
|
+
f.Content,
|
|
162
|
+
{
|
|
163
|
+
id: `${l}-calendar`,
|
|
164
|
+
"aria-labelledby": r ? `${l}-label` : void 0,
|
|
165
|
+
"aria-label": r ? void 0 : b,
|
|
166
|
+
align: "start",
|
|
167
|
+
sideOffset: 4,
|
|
168
|
+
className: "text-popover-foreground z-[var(--z-popover)] rounded-lg shadow-md",
|
|
169
|
+
children: /* @__PURE__ */ n(
|
|
170
|
+
D,
|
|
171
|
+
{
|
|
172
|
+
mode: "range",
|
|
173
|
+
selected: e,
|
|
174
|
+
defaultMonth: e == null ? void 0 : e.from,
|
|
175
|
+
onSelect: o,
|
|
176
|
+
numberOfMonths: 2,
|
|
177
|
+
startMonth: d,
|
|
178
|
+
endMonth: a,
|
|
179
|
+
disabled: m,
|
|
180
|
+
locale: h
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
) })
|
|
185
|
+
] }) });
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
L.displayName = "DateRangePicker";
|
|
189
|
+
export {
|
|
190
|
+
T as DatePicker,
|
|
191
|
+
L as DateRangePicker
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=DatePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/ui/DatePicker.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, useId, useMemo, useState, type ReactNode } from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport type { DateRange, DayPickerProps, Matcher } from 'react-day-picker';\nimport { Calendar as CalendarIcon } from '@/icons';\nimport { Calendar } from './Calendar';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\n/* -----------------------------------------------------------------------------\n * Two related components share the same trigger shell:\n *\n * <DatePicker value={date} onChange={setDate} /> // single date\n * <DateRangePicker value={range} onChange={setRange} /> // {from,to}\n * --------------------------------------------------------------------------- */\n\ntype CalendarLocale = DayPickerProps['locale'];\n\n/**\n * Default trigger text. With a date-fns `locale` the date is rendered in that\n * locale; without one it is the canonical `yyyy-MM-dd` — deterministic on\n * server and client, so SSR output never depends on the host's locale.\n */\nfunction defaultFormatDate(d: Date, locale?: CalendarLocale): string {\n if (locale?.code) {\n return d.toLocaleDateString(locale.code, { year: 'numeric', month: 'short', day: 'numeric' });\n }\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/** Merge the fromDate/toDate bounds with any consumer-supplied RDP matcher. */\nfunction useBounds(fromDate?: Date, toDate?: Date, disabled?: Matcher | Matcher[]) {\n return useMemo<Matcher[] | undefined>(() => {\n const list: Matcher[] = [];\n if (fromDate) list.push({ before: fromDate });\n if (toDate) list.push({ after: toDate });\n if (disabled) list.push(...(Array.isArray(disabled) ? disabled : [disabled]));\n return list.length ? list : undefined;\n }, [fromDate, toDate, disabled]);\n}\n\nfunction PickerTrigger({\n label,\n placeholder,\n hasValue,\n children,\n error,\n fieldId,\n disabled,\n open,\n}: {\n label?: ReactNode;\n placeholder: string;\n hasValue: boolean;\n children: ReactNode;\n error?: ReactNode;\n fieldId: string;\n disabled?: boolean;\n open: boolean;\n}) {\n const errorId = `${fieldId}-error`;\n const calendarId = `${fieldId}-calendar`;\n const text = hasValue ? children : placeholder;\n return (\n <div className=\"flex flex-col gap-1.5\">\n {label && (\n <label\n id={`${fieldId}-label`}\n htmlFor={fieldId}\n className=\"text-foreground text-sm font-medium\"\n >\n {label}\n </label>\n )}\n <PopoverPrimitive.Trigger asChild>\n <button\n id={fieldId}\n type=\"button\"\n // combobox-with-dialog pattern: lets the field carry aria-invalid like other inputs.\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={calendarId}\n // role=combobox takes no name from content; expose the placeholder.\n aria-labelledby={label ? `${fieldId}-label` : undefined}\n aria-label={label ? undefined : placeholder}\n disabled={disabled}\n aria-invalid={Boolean(error) || undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'bg-card inline-flex h-9 w-full items-center justify-start gap-2 rounded-md border px-3 text-left text-lg md:text-sm',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n error\n ? 'border-danger focus-visible:border-danger focus-visible:ring-danger'\n : 'border-border-input focus-visible:border-accent focus-visible:ring-ring',\n !hasValue && 'text-foreground-subtle',\n )}\n >\n <CalendarIcon className=\"text-foreground-subtle size-4\" aria-hidden />\n <span className=\"truncate\" title={typeof text === 'string' ? text : undefined}>\n {text}\n </span>\n </button>\n </PopoverPrimitive.Trigger>\n {error && (\n <p id={errorId} className=\"text-danger-text text-xs\">\n {error}\n </p>\n )}\n </div>\n );\n}\n\ninterface PickerCommonProps {\n label?: ReactNode;\n placeholder?: string;\n error?: ReactNode;\n disabled?: boolean;\n /** Restrict to a date range. Days outside are not selectable. */\n fromDate?: Date;\n toDate?: Date;\n /** Extra RDP matcher(s) for disabled days, merged with `fromDate`/`toDate`. */\n disabledDays?: Matcher | Matcher[];\n /** Custom display formatter for the trigger text. */\n formatDate?: (d: Date) => string;\n /** date-fns locale object passed through to react-day-picker. */\n locale?: CalendarLocale;\n className?: string;\n}\n\nexport interface DatePickerProps extends PickerCommonProps {\n value?: Date;\n onChange: (date: Date | undefined) => void;\n}\n\n/**\n * Single-date picker.\n *\n * @example\n * <DatePicker label=\"Date of birth\" value={dob} onChange={setDob} />\n *\n * @do Use locale-aware formatting via `formatDate` / `locale`.\n * @dont Roll a custom calendar — Radix Popover + react-day-picker handles a11y.\n */\nexport const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(function DatePicker(\n {\n value,\n onChange,\n label,\n placeholder: placeholderProp,\n error,\n disabled,\n fromDate,\n toDate,\n disabledDays,\n formatDate: formatDateProp,\n locale,\n className,\n },\n ref,\n) {\n const strings = useStrings();\n const formatDate = formatDateProp ?? ((d: Date) => defaultFormatDate(d, locale));\n const placeholder = placeholderProp ?? strings.datePicker.pickDate;\n const fieldId = useId();\n const [open, setOpen] = useState(false);\n const bounds = useBounds(fromDate, toDate, disabledDays);\n\n return (\n <div ref={ref} className={cn(className)}>\n <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n <PickerTrigger\n label={label}\n placeholder={placeholder}\n hasValue={Boolean(value)}\n error={error}\n fieldId={fieldId}\n disabled={disabled}\n open={open}\n >\n {value ? formatDate(value) : ''}\n </PickerTrigger>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n id={`${fieldId}-calendar`}\n // Radix gives the popover role=\"dialog\"; name it after the field.\n aria-labelledby={label ? `${fieldId}-label` : undefined}\n aria-label={label ? undefined : placeholder}\n align=\"start\"\n sideOffset={4}\n className=\"text-popover-foreground z-[var(--z-popover)] rounded-lg shadow-md\"\n >\n <Calendar\n mode=\"single\"\n selected={value}\n // RDP 9 ignores `selected` for the initial month; open on the value.\n defaultMonth={value}\n onSelect={(d) => {\n onChange(d ?? undefined);\n if (d) setOpen(false);\n }}\n startMonth={fromDate}\n endMonth={toDate}\n disabled={bounds}\n locale={locale}\n />\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </div>\n );\n});\nDatePicker.displayName = 'DatePicker';\n\nexport interface DateRangePickerProps extends PickerCommonProps {\n value?: DateRange;\n onChange: (range: DateRange | undefined) => void;\n}\n\n/**\n * Two-date range picker. `value` is `{from, to}`.\n *\n * @example\n * <DateRangePicker label=\"Reporting period\" value={range} onChange={setRange} />\n */\nexport const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(\n function DateRangePicker(\n {\n value,\n onChange,\n label,\n placeholder: placeholderProp,\n error,\n disabled,\n fromDate,\n toDate,\n disabledDays,\n formatDate: formatDateProp,\n locale,\n className,\n },\n ref,\n ) {\n const strings = useStrings();\n const formatDate = formatDateProp ?? ((d: Date) => defaultFormatDate(d, locale));\n const placeholder = placeholderProp ?? strings.datePicker.pickRange;\n const fieldId = useId();\n const [open, setOpen] = useState(false);\n const hasValue = Boolean(value?.from);\n const bounds = useBounds(fromDate, toDate, disabledDays);\n\n return (\n <div ref={ref} className={cn(className)}>\n <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n <PickerTrigger\n label={label}\n placeholder={placeholder}\n hasValue={hasValue}\n error={error}\n fieldId={fieldId}\n disabled={disabled}\n open={open}\n >\n {value?.from && value.to\n ? `${formatDate(value.from)} – ${formatDate(value.to)}`\n : value?.from\n ? formatDate(value.from)\n : ''}\n </PickerTrigger>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n id={`${fieldId}-calendar`}\n aria-labelledby={label ? `${fieldId}-label` : undefined}\n aria-label={label ? undefined : placeholder}\n align=\"start\"\n sideOffset={4}\n className=\"text-popover-foreground z-[var(--z-popover)] rounded-lg shadow-md\"\n >\n <Calendar\n mode=\"range\"\n selected={value}\n defaultMonth={value?.from}\n onSelect={onChange}\n numberOfMonths={2}\n startMonth={fromDate}\n endMonth={toDate}\n disabled={bounds}\n locale={locale}\n />\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </div>\n );\n },\n);\nDateRangePicker.displayName = 'DateRangePicker';\n"],"names":["defaultFormatDate","d","locale","pad","n","useBounds","fromDate","toDate","disabled","useMemo","list","PickerTrigger","label","placeholder","hasValue","children","error","fieldId","open","errorId","calendarId","text","jsxs","jsx","PopoverPrimitive","cn","CalendarIcon","DatePicker","forwardRef","value","onChange","placeholderProp","disabledDays","formatDateProp","className","ref","strings","useStrings","formatDate","useId","setOpen","useState","bounds","Calendar","DateRangePicker"],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { DeepPartial, UiStrings } from '../../lib/strings.js';
|
|
3
|
+
/**
|
|
4
|
+
* Per-brand token overrides. Keys are CSS variable names (without the `--`
|
|
5
|
+
* prefix); values are CSS values. Any unspecified token falls back to the
|
|
6
|
+
* design system default.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* <DesignSystemProvider tokens={{ accent: '#ff5555', 'radius-md': '10px' }}>
|
|
10
|
+
* <App />
|
|
11
|
+
* </DesignSystemProvider>
|
|
12
|
+
*
|
|
13
|
+
* @example Swap in one of the built-in accent presets
|
|
14
|
+
* <DesignSystemProvider tokens={brandPresets.violet}>
|
|
15
|
+
* <Card>...</Card>
|
|
16
|
+
* </DesignSystemProvider>
|
|
17
|
+
*/
|
|
18
|
+
export type BrandTokens = Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Mode-aware token overrides: `light` applies always, `dark` wins under the
|
|
21
|
+
* `.dark` class (on `<html>` or any ancestor). Use this for anything with a
|
|
22
|
+
* different value per mode — accent, subtle surfaces, ring.
|
|
23
|
+
*/
|
|
24
|
+
export interface BrandTokenPair {
|
|
25
|
+
light: BrandTokens;
|
|
26
|
+
dark?: BrandTokens;
|
|
27
|
+
}
|
|
28
|
+
export interface DesignSystemProviderProps extends HTMLAttributes<HTMLDivElement> {
|
|
29
|
+
/**
|
|
30
|
+
* Token overrides (CSS variable name → value). A flat record applies in both
|
|
31
|
+
* modes; a `{ light, dark }` pair applies `dark` under `.dark`.
|
|
32
|
+
*/
|
|
33
|
+
tokens?: BrandTokens | BrandTokenPair;
|
|
34
|
+
/**
|
|
35
|
+
* Override any of the library's built-in UI strings (aria-labels,
|
|
36
|
+
* placeholders, empty states). Partial — merged over the parent provider /
|
|
37
|
+
* English defaults. Pass `mnStrings` for Mongolian.
|
|
38
|
+
*/
|
|
39
|
+
strings?: DeepPartial<UiStrings>;
|
|
40
|
+
/** Children to scope this brand to. */
|
|
41
|
+
children: ReactNode;
|
|
42
|
+
}
|
|
43
|
+
export declare function DesignSystemProvider({ tokens, strings, className, children, style, ...props }: DesignSystemProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
/**
|
|
45
|
+
* Built-in accent presets — swap the system's single accent colour without
|
|
46
|
+
* touching anything else.
|
|
47
|
+
*
|
|
48
|
+
* These override the *semantic* tokens the utilities resolve to. theme.css
|
|
49
|
+
* maps the accent utilities with `@theme inline` (e.g. `bg-accent` →
|
|
50
|
+
* `var(--accent)`, `bg-accent-soft` → `var(--accent-subtle)`), so the override
|
|
51
|
+
* must target `--accent` / `--accent-subtle` / `--accent-subtle-foreground` /
|
|
52
|
+
* `--ring` — NOT `--color-accent` (which `inline` never emits). The hover/
|
|
53
|
+
* active steps (`--color-accent-700/800`) come from the raw palette and are
|
|
54
|
+
* mode-agnostic. Values mirror the `[data-accent]` presets in theme.css: the
|
|
55
|
+
* dark half lifts the accent to the 400 step (readable as text on neutral-950)
|
|
56
|
+
* and darkens the subtle surface. `default` is the built-in graphite-indigo.
|
|
57
|
+
*/
|
|
58
|
+
export declare const brandPresets: {
|
|
59
|
+
/** The library default — graphite indigo. */
|
|
60
|
+
default: BrandTokenPair;
|
|
61
|
+
/** Blue. */
|
|
62
|
+
blue: {
|
|
63
|
+
light: {
|
|
64
|
+
accent: string;
|
|
65
|
+
'color-accent-700': string;
|
|
66
|
+
'color-accent-800': string;
|
|
67
|
+
'accent-subtle': string;
|
|
68
|
+
'accent-subtle-foreground': string;
|
|
69
|
+
ring: string;
|
|
70
|
+
};
|
|
71
|
+
dark: {
|
|
72
|
+
accent: string;
|
|
73
|
+
'accent-subtle': string;
|
|
74
|
+
'accent-subtle-foreground': string;
|
|
75
|
+
ring: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/** Violet. */
|
|
79
|
+
violet: {
|
|
80
|
+
light: {
|
|
81
|
+
accent: string;
|
|
82
|
+
'color-accent-700': string;
|
|
83
|
+
'color-accent-800': string;
|
|
84
|
+
'accent-subtle': string;
|
|
85
|
+
'accent-subtle-foreground': string;
|
|
86
|
+
ring: string;
|
|
87
|
+
};
|
|
88
|
+
dark: {
|
|
89
|
+
accent: string;
|
|
90
|
+
'accent-subtle': string;
|
|
91
|
+
'accent-subtle-foreground': string;
|
|
92
|
+
ring: string;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
/** Emerald. */
|
|
96
|
+
emerald: {
|
|
97
|
+
light: {
|
|
98
|
+
accent: string;
|
|
99
|
+
'color-accent-700': string;
|
|
100
|
+
'color-accent-800': string;
|
|
101
|
+
'accent-subtle': string;
|
|
102
|
+
'accent-subtle-foreground': string;
|
|
103
|
+
ring: string;
|
|
104
|
+
};
|
|
105
|
+
dark: {
|
|
106
|
+
accent: string;
|
|
107
|
+
'accent-subtle': string;
|
|
108
|
+
'accent-subtle-foreground': string;
|
|
109
|
+
ring: string;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
/** Rose. */
|
|
113
|
+
rose: {
|
|
114
|
+
light: {
|
|
115
|
+
accent: string;
|
|
116
|
+
'color-accent-700': string;
|
|
117
|
+
'color-accent-800': string;
|
|
118
|
+
'accent-subtle': string;
|
|
119
|
+
'accent-subtle-foreground': string;
|
|
120
|
+
ring: string;
|
|
121
|
+
};
|
|
122
|
+
dark: {
|
|
123
|
+
accent: string;
|
|
124
|
+
'accent-subtle': string;
|
|
125
|
+
'accent-subtle-foreground': string;
|
|
126
|
+
ring: string;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
/** Amber. */
|
|
130
|
+
amber: {
|
|
131
|
+
light: {
|
|
132
|
+
accent: string;
|
|
133
|
+
'color-accent-700': string;
|
|
134
|
+
'color-accent-800': string;
|
|
135
|
+
'accent-subtle': string;
|
|
136
|
+
'accent-subtle-foreground': string;
|
|
137
|
+
ring: string;
|
|
138
|
+
};
|
|
139
|
+
dark: {
|
|
140
|
+
accent: string;
|
|
141
|
+
'accent-subtle': string;
|
|
142
|
+
'accent-subtle-foreground': string;
|
|
143
|
+
ring: string;
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
export type BrandName = keyof typeof brandPresets;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as f, jsx as h } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo as m, useId as p } from "react";
|
|
4
|
+
import { cn as $ } from "../../lib/utils.js";
|
|
5
|
+
import { useStrings as v, StringsContext as j } from "../../hooks/use-strings.js";
|
|
6
|
+
import { mergeStrings as y } from "../../lib/strings.js";
|
|
7
|
+
function S(c) {
|
|
8
|
+
return "light" in c && typeof c.light == "object";
|
|
9
|
+
}
|
|
10
|
+
function s(c) {
|
|
11
|
+
return Object.entries(c).map(([e, l]) => `${e.startsWith("--") ? e : `--${e}`}:${l}`).join(";");
|
|
12
|
+
}
|
|
13
|
+
function W({
|
|
14
|
+
tokens: c,
|
|
15
|
+
strings: e,
|
|
16
|
+
className: l,
|
|
17
|
+
children: k,
|
|
18
|
+
style: i,
|
|
19
|
+
...u
|
|
20
|
+
}) {
|
|
21
|
+
const r = v(), d = m(
|
|
22
|
+
() => y(r, e),
|
|
23
|
+
[r, e]
|
|
24
|
+
), n = p(), a = { ...i };
|
|
25
|
+
let t = null;
|
|
26
|
+
if (c)
|
|
27
|
+
if (S(c)) {
|
|
28
|
+
const o = `[data-brand-scope="${n}"]`;
|
|
29
|
+
t = `${o}{${s(c.light)}}`, c.dark && (t += `.dark ${o},.dark${o}{${s(c.dark)}}`);
|
|
30
|
+
} else
|
|
31
|
+
for (const [o, g] of Object.entries(c)) {
|
|
32
|
+
const b = o.startsWith("--") ? o : `--${o}`;
|
|
33
|
+
a[b] = g;
|
|
34
|
+
}
|
|
35
|
+
return /* @__PURE__ */ f(j.Provider, { value: d, children: [
|
|
36
|
+
t && /* @__PURE__ */ h("style", { "data-brand-scope-style": n, children: t }),
|
|
37
|
+
/* @__PURE__ */ h("div", { "data-brand-scope": n, className: $("contents", l), style: a, ...u, children: k })
|
|
38
|
+
] });
|
|
39
|
+
}
|
|
40
|
+
const C = {
|
|
41
|
+
/** The library default — graphite indigo. */
|
|
42
|
+
default: { light: {} },
|
|
43
|
+
/** Blue. */
|
|
44
|
+
blue: {
|
|
45
|
+
light: {
|
|
46
|
+
accent: "oklch(0.55 0.16 250)",
|
|
47
|
+
"color-accent-700": "oklch(0.47 0.16 250)",
|
|
48
|
+
"color-accent-800": "oklch(0.39 0.14 250)",
|
|
49
|
+
"accent-subtle": "oklch(0.96 0.03 250)",
|
|
50
|
+
"accent-subtle-foreground": "oklch(0.45 0.15 250)",
|
|
51
|
+
ring: "oklch(0.62 0.16 250)"
|
|
52
|
+
},
|
|
53
|
+
dark: {
|
|
54
|
+
accent: "oklch(0.64 0.15 250)",
|
|
55
|
+
"accent-subtle": "oklch(0.24 0.06 250)",
|
|
56
|
+
"accent-subtle-foreground": "oklch(0.82 0.1 250)",
|
|
57
|
+
ring: "oklch(0.68 0.15 250)"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
/** Violet. */
|
|
61
|
+
violet: {
|
|
62
|
+
light: {
|
|
63
|
+
accent: "oklch(0.53 0.20 295)",
|
|
64
|
+
"color-accent-700": "oklch(0.45 0.18 295)",
|
|
65
|
+
"color-accent-800": "oklch(0.38 0.16 295)",
|
|
66
|
+
"accent-subtle": "oklch(0.96 0.03 295)",
|
|
67
|
+
"accent-subtle-foreground": "oklch(0.46 0.18 295)",
|
|
68
|
+
ring: "oklch(0.60 0.20 295)"
|
|
69
|
+
},
|
|
70
|
+
dark: {
|
|
71
|
+
accent: "oklch(0.64 0.18 295)",
|
|
72
|
+
"accent-subtle": "oklch(0.25 0.07 295)",
|
|
73
|
+
"accent-subtle-foreground": "oklch(0.83 0.12 295)",
|
|
74
|
+
ring: "oklch(0.68 0.18 295)"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
/** Emerald. */
|
|
78
|
+
emerald: {
|
|
79
|
+
light: {
|
|
80
|
+
accent: "oklch(0.55 0.13 160)",
|
|
81
|
+
"color-accent-700": "oklch(0.47 0.13 160)",
|
|
82
|
+
"color-accent-800": "oklch(0.39 0.12 160)",
|
|
83
|
+
"accent-subtle": "oklch(0.96 0.03 160)",
|
|
84
|
+
"accent-subtle-foreground": "oklch(0.42 0.12 160)",
|
|
85
|
+
ring: "oklch(0.60 0.13 160)"
|
|
86
|
+
},
|
|
87
|
+
dark: {
|
|
88
|
+
accent: "oklch(0.64 0.14 160)",
|
|
89
|
+
"accent-subtle": "oklch(0.23 0.05 160)",
|
|
90
|
+
"accent-subtle-foreground": "oklch(0.8 0.12 160)",
|
|
91
|
+
ring: "oklch(0.68 0.14 160)"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
/** Rose. */
|
|
95
|
+
rose: {
|
|
96
|
+
light: {
|
|
97
|
+
accent: "oklch(0.57 0.19 12)",
|
|
98
|
+
"color-accent-700": "oklch(0.49 0.18 12)",
|
|
99
|
+
"color-accent-800": "oklch(0.41 0.16 12)",
|
|
100
|
+
"accent-subtle": "oklch(0.96 0.03 12)",
|
|
101
|
+
"accent-subtle-foreground": "oklch(0.48 0.18 12)",
|
|
102
|
+
ring: "oklch(0.62 0.19 12)"
|
|
103
|
+
},
|
|
104
|
+
dark: {
|
|
105
|
+
accent: "oklch(0.65 0.18 12)",
|
|
106
|
+
"accent-subtle": "oklch(0.25 0.07 12)",
|
|
107
|
+
"accent-subtle-foreground": "oklch(0.83 0.12 12)",
|
|
108
|
+
ring: "oklch(0.7 0.18 12)"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
/** Amber. */
|
|
112
|
+
amber: {
|
|
113
|
+
light: {
|
|
114
|
+
// 0.56 keeps white text at 4.8:1 (0.62 was 3.8:1).
|
|
115
|
+
accent: "oklch(0.56 0.14 65)",
|
|
116
|
+
"color-accent-700": "oklch(0.54 0.13 65)",
|
|
117
|
+
"color-accent-800": "oklch(0.46 0.12 65)",
|
|
118
|
+
"accent-subtle": "oklch(0.96 0.04 75)",
|
|
119
|
+
"accent-subtle-foreground": "oklch(0.48 0.12 65)",
|
|
120
|
+
ring: "oklch(0.64 0.14 65)"
|
|
121
|
+
// 3.17:1 on background-muted (0.66 was 2.93)
|
|
122
|
+
},
|
|
123
|
+
dark: {
|
|
124
|
+
accent: "oklch(0.66 0.14 65)",
|
|
125
|
+
"accent-subtle": "oklch(0.26 0.05 70)",
|
|
126
|
+
"accent-subtle-foreground": "oklch(0.85 0.12 80)",
|
|
127
|
+
ring: "oklch(0.72 0.14 65)"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
export {
|
|
132
|
+
W as DesignSystemProvider,
|
|
133
|
+
C as brandPresets
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=DesignSystemProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DesignSystemProvider.js","sources":["../../../src/components/ui/DesignSystemProvider.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useMemo, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\nimport { StringsContext, useStrings } from '@/hooks/use-strings';\nimport { mergeStrings, type DeepPartial, type UiStrings } from '@/lib/strings';\n\n/**\n * Per-brand token overrides. Keys are CSS variable names (without the `--`\n * prefix); values are CSS values. Any unspecified token falls back to the\n * design system default.\n *\n * @example\n * <DesignSystemProvider tokens={{ accent: '#ff5555', 'radius-md': '10px' }}>\n * <App />\n * </DesignSystemProvider>\n *\n * @example Swap in one of the built-in accent presets\n * <DesignSystemProvider tokens={brandPresets.violet}>\n * <Card>...</Card>\n * </DesignSystemProvider>\n */\nexport type BrandTokens = Record<string, string>;\n\n/**\n * Mode-aware token overrides: `light` applies always, `dark` wins under the\n * `.dark` class (on `<html>` or any ancestor). Use this for anything with a\n * different value per mode — accent, subtle surfaces, ring.\n */\nexport interface BrandTokenPair {\n light: BrandTokens;\n dark?: BrandTokens;\n}\n\nfunction isPair(t: BrandTokens | BrandTokenPair): t is BrandTokenPair {\n return 'light' in t && typeof t.light === 'object';\n}\n\nfunction toDeclarations(tokens: BrandTokens): string {\n return Object.entries(tokens)\n .map(([k, v]) => `${k.startsWith('--') ? k : `--${k}`}:${v}`)\n .join(';');\n}\n\nexport interface DesignSystemProviderProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Token overrides (CSS variable name → value). A flat record applies in both\n * modes; a `{ light, dark }` pair applies `dark` under `.dark`.\n */\n tokens?: BrandTokens | BrandTokenPair;\n /**\n * Override any of the library's built-in UI strings (aria-labels,\n * placeholders, empty states). Partial — merged over the parent provider /\n * English defaults. Pass `mnStrings` for Mongolian.\n */\n strings?: DeepPartial<UiStrings>;\n /** Children to scope this brand to. */\n children: ReactNode;\n}\n\nexport function DesignSystemProvider({\n tokens,\n strings,\n className,\n children,\n style,\n ...props\n}: DesignSystemProviderProps) {\n // Build the inline style from tokens — wrapping every key with `--` so\n // consumers can pass either `accent` or `color-accent` without remembering\n // the prefix dance.\n const parentStrings = useStrings();\n const resolvedStrings = useMemo(\n () => mergeStrings(parentStrings, strings),\n [parentStrings, strings],\n );\n const scopeId = useId();\n const css: CSSProperties = { ...style };\n let sheet: string | null = null;\n if (tokens) {\n if (isPair(tokens)) {\n // Mode-aware tokens cannot be inline (inline beats `.dark` rules), so\n // they go through a scoped <style>: light on the scope, dark under .dark.\n const scope = `[data-brand-scope=\"${scopeId}\"]`;\n sheet = `${scope}{${toDeclarations(tokens.light)}}`;\n if (tokens.dark) {\n sheet += `.dark ${scope},.dark${scope}{${toDeclarations(tokens.dark)}}`;\n }\n } else {\n for (const [k, v] of Object.entries(tokens)) {\n const key = k.startsWith('--') ? k : `--${k}`;\n (css as Record<string, string>)[key] = v;\n }\n }\n }\n\n return (\n <StringsContext.Provider value={resolvedStrings}>\n {sheet && <style data-brand-scope-style={scopeId}>{sheet}</style>}\n <div data-brand-scope={scopeId} className={cn('contents', className)} style={css} {...props}>\n {children}\n </div>\n </StringsContext.Provider>\n );\n}\n\n/**\n * Built-in accent presets — swap the system's single accent colour without\n * touching anything else.\n *\n * These override the *semantic* tokens the utilities resolve to. theme.css\n * maps the accent utilities with `@theme inline` (e.g. `bg-accent` →\n * `var(--accent)`, `bg-accent-soft` → `var(--accent-subtle)`), so the override\n * must target `--accent` / `--accent-subtle` / `--accent-subtle-foreground` /\n * `--ring` — NOT `--color-accent` (which `inline` never emits). The hover/\n * active steps (`--color-accent-700/800`) come from the raw palette and are\n * mode-agnostic. Values mirror the `[data-accent]` presets in theme.css: the\n * dark half lifts the accent to the 400 step (readable as text on neutral-950)\n * and darkens the subtle surface. `default` is the built-in graphite-indigo.\n */\nexport const brandPresets = {\n /** The library default — graphite indigo. */\n default: { light: {} } as BrandTokenPair,\n /** Blue. */\n blue: {\n light: {\n accent: 'oklch(0.55 0.16 250)',\n 'color-accent-700': 'oklch(0.47 0.16 250)',\n 'color-accent-800': 'oklch(0.39 0.14 250)',\n 'accent-subtle': 'oklch(0.96 0.03 250)',\n 'accent-subtle-foreground': 'oklch(0.45 0.15 250)',\n ring: 'oklch(0.62 0.16 250)',\n },\n dark: {\n accent: 'oklch(0.64 0.15 250)',\n 'accent-subtle': 'oklch(0.24 0.06 250)',\n 'accent-subtle-foreground': 'oklch(0.82 0.1 250)',\n ring: 'oklch(0.68 0.15 250)',\n },\n },\n /** Violet. */\n violet: {\n light: {\n accent: 'oklch(0.53 0.20 295)',\n 'color-accent-700': 'oklch(0.45 0.18 295)',\n 'color-accent-800': 'oklch(0.38 0.16 295)',\n 'accent-subtle': 'oklch(0.96 0.03 295)',\n 'accent-subtle-foreground': 'oklch(0.46 0.18 295)',\n ring: 'oklch(0.60 0.20 295)',\n },\n dark: {\n accent: 'oklch(0.64 0.18 295)',\n 'accent-subtle': 'oklch(0.25 0.07 295)',\n 'accent-subtle-foreground': 'oklch(0.83 0.12 295)',\n ring: 'oklch(0.68 0.18 295)',\n },\n },\n /** Emerald. */\n emerald: {\n light: {\n accent: 'oklch(0.55 0.13 160)',\n 'color-accent-700': 'oklch(0.47 0.13 160)',\n 'color-accent-800': 'oklch(0.39 0.12 160)',\n 'accent-subtle': 'oklch(0.96 0.03 160)',\n 'accent-subtle-foreground': 'oklch(0.42 0.12 160)',\n ring: 'oklch(0.60 0.13 160)',\n },\n dark: {\n accent: 'oklch(0.64 0.14 160)',\n 'accent-subtle': 'oklch(0.23 0.05 160)',\n 'accent-subtle-foreground': 'oklch(0.8 0.12 160)',\n ring: 'oklch(0.68 0.14 160)',\n },\n },\n /** Rose. */\n rose: {\n light: {\n accent: 'oklch(0.57 0.19 12)',\n 'color-accent-700': 'oklch(0.49 0.18 12)',\n 'color-accent-800': 'oklch(0.41 0.16 12)',\n 'accent-subtle': 'oklch(0.96 0.03 12)',\n 'accent-subtle-foreground': 'oklch(0.48 0.18 12)',\n ring: 'oklch(0.62 0.19 12)',\n },\n dark: {\n accent: 'oklch(0.65 0.18 12)',\n 'accent-subtle': 'oklch(0.25 0.07 12)',\n 'accent-subtle-foreground': 'oklch(0.83 0.12 12)',\n ring: 'oklch(0.7 0.18 12)',\n },\n },\n /** Amber. */\n amber: {\n light: {\n // 0.56 keeps white text at 4.8:1 (0.62 was 3.8:1).\n accent: 'oklch(0.56 0.14 65)',\n 'color-accent-700': 'oklch(0.54 0.13 65)',\n 'color-accent-800': 'oklch(0.46 0.12 65)',\n 'accent-subtle': 'oklch(0.96 0.04 75)',\n 'accent-subtle-foreground': 'oklch(0.48 0.12 65)',\n ring: 'oklch(0.64 0.14 65)', // 3.17:1 on background-muted (0.66 was 2.93)\n },\n dark: {\n accent: 'oklch(0.66 0.14 65)',\n 'accent-subtle': 'oklch(0.26 0.05 70)',\n 'accent-subtle-foreground': 'oklch(0.85 0.12 80)',\n ring: 'oklch(0.72 0.14 65)',\n },\n },\n} satisfies Record<string, BrandTokenPair>;\n\nexport type BrandName = keyof typeof brandPresets;\n"],"names":["isPair","t","toDeclarations","tokens","k","v","DesignSystemProvider","strings","className","children","style","props","parentStrings","useStrings","resolvedStrings","useMemo","mergeStrings","scopeId","useId","css","sheet","scope","key","jsxs","StringsContext","jsx","cn","brandPresets"],"mappings":"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"}
|