@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,139 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as t, jsxs as b } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo as x } from "react";
|
|
4
|
+
import { DayPicker as w, getDefaultClassNames as _ } from "react-day-picker";
|
|
5
|
+
import * as u from "@radix-ui/react-select";
|
|
6
|
+
import { ChevronLeft as y, ChevronRight as N, ChevronDown as g } from "../../icons/index.js";
|
|
7
|
+
import { Select as k, SelectContent as D, SelectItem as j } from "./Select.js";
|
|
8
|
+
import { cn as n } from "../../lib/utils.js";
|
|
9
|
+
import { useStrings as C } from "../../hooks/use-strings.js";
|
|
10
|
+
const f = _();
|
|
11
|
+
function S({
|
|
12
|
+
options: l = [],
|
|
13
|
+
value: r,
|
|
14
|
+
onChange: o,
|
|
15
|
+
disabled: d,
|
|
16
|
+
"aria-label": c
|
|
17
|
+
}) {
|
|
18
|
+
const i = l.find((e) => e.value === Number(r));
|
|
19
|
+
return /* @__PURE__ */ b(
|
|
20
|
+
k,
|
|
21
|
+
{
|
|
22
|
+
value: r === void 0 ? void 0 : String(r),
|
|
23
|
+
onValueChange: (e) => {
|
|
24
|
+
o == null || o({ target: { value: e } });
|
|
25
|
+
},
|
|
26
|
+
disabled: d,
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ b(
|
|
29
|
+
u.Trigger,
|
|
30
|
+
{
|
|
31
|
+
"aria-label": c,
|
|
32
|
+
className: n(
|
|
33
|
+
"border-border bg-card text-foreground inline-flex h-7 items-center gap-1 rounded-md border px-2 text-sm font-medium",
|
|
34
|
+
"hover:bg-background-muted focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
35
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
36
|
+
),
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ t(u.Value, { children: i == null ? void 0 : i.label }),
|
|
39
|
+
/* @__PURE__ */ t(u.Icon, { asChild: !0, children: /* @__PURE__ */ t(g, { className: "size-3.5 opacity-60", "aria-hidden": !0 }) })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
),
|
|
43
|
+
/* @__PURE__ */ t(D, { className: "max-h-64", children: l.map((e) => /* @__PURE__ */ t(j, { value: String(e.value), disabled: e.disabled, children: e.label }, e.value)) })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const m = n(
|
|
49
|
+
"pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-md text-foreground-muted hover:bg-background-muted",
|
|
50
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
51
|
+
"disabled:opacity-40 disabled:pointer-events-none"
|
|
52
|
+
);
|
|
53
|
+
function z({
|
|
54
|
+
className: l,
|
|
55
|
+
classNames: r,
|
|
56
|
+
captionLayout: o = "dropdown",
|
|
57
|
+
startMonth: d,
|
|
58
|
+
endMonth: c,
|
|
59
|
+
labels: i,
|
|
60
|
+
weekStartsOn: e = 1,
|
|
61
|
+
...v
|
|
62
|
+
}) {
|
|
63
|
+
const s = C(), { start: p, end: h } = x(() => {
|
|
64
|
+
const a = /* @__PURE__ */ new Date();
|
|
65
|
+
return {
|
|
66
|
+
start: d ?? new Date(a.getFullYear() - 100, 0, 1),
|
|
67
|
+
end: c ?? new Date(a.getFullYear() + 10, 11, 31)
|
|
68
|
+
};
|
|
69
|
+
}, [d, c]);
|
|
70
|
+
return /* @__PURE__ */ t(
|
|
71
|
+
w,
|
|
72
|
+
{
|
|
73
|
+
showOutsideDays: !0,
|
|
74
|
+
weekStartsOn: e,
|
|
75
|
+
captionLayout: o,
|
|
76
|
+
startMonth: p,
|
|
77
|
+
endMonth: h,
|
|
78
|
+
className: n("border-border bg-card inline-block rounded-lg border p-3", l),
|
|
79
|
+
labels: {
|
|
80
|
+
labelPrevious: () => s.calendar.previous,
|
|
81
|
+
labelNext: () => s.calendar.next,
|
|
82
|
+
labelMonthDropdown: () => s.calendar.chooseMonth,
|
|
83
|
+
labelYearDropdown: () => s.calendar.chooseYear,
|
|
84
|
+
labelNav: () => s.calendar.nav,
|
|
85
|
+
...i
|
|
86
|
+
},
|
|
87
|
+
classNames: {
|
|
88
|
+
...f,
|
|
89
|
+
root: n(f.root, "relative"),
|
|
90
|
+
months: "relative flex flex-col gap-4 sm:flex-row sm:gap-6",
|
|
91
|
+
month: "flex flex-col gap-3",
|
|
92
|
+
month_caption: "relative flex h-8 items-center justify-center px-8",
|
|
93
|
+
dropdowns: "flex items-center justify-center gap-2",
|
|
94
|
+
caption_label: "flex items-center gap-1 text-sm font-medium",
|
|
95
|
+
// The caption is positioned and painted after the nav, so the nav must
|
|
96
|
+
// sit above it (z-10) for its buttons to receive clicks. The container
|
|
97
|
+
// itself ignores pointer events so the centred dropdowns underneath
|
|
98
|
+
// stay clickable — only the buttons opt back in.
|
|
99
|
+
nav: "pointer-events-none absolute inset-x-0 top-0 z-10 flex h-8 items-center justify-between",
|
|
100
|
+
button_previous: m,
|
|
101
|
+
button_next: m,
|
|
102
|
+
month_grid: "w-full border-collapse",
|
|
103
|
+
weekdays: "grid grid-cols-7",
|
|
104
|
+
weekday: "h-8 w-9 text-center text-xs font-medium uppercase tracking-wide text-foreground-subtle",
|
|
105
|
+
week: "mt-0.5 grid grid-cols-7",
|
|
106
|
+
day: "relative h-9 w-9 p-0 text-center",
|
|
107
|
+
day_button: n(
|
|
108
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-md text-sm hover:bg-background-muted",
|
|
109
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
110
|
+
),
|
|
111
|
+
// RDP 9 puts aria-selected / the modifier classes on the <td>, so the
|
|
112
|
+
// button is styled through its cell. `[&_button:hover]` outranks the
|
|
113
|
+
// button's own `hover:` rule so selected days keep the accent on hover.
|
|
114
|
+
selected: n(
|
|
115
|
+
"[&_button]:bg-accent [&_button]:text-on-accent [&_button:hover]:bg-accent-hover",
|
|
116
|
+
"[&_button]:focus-visible:ring-offset-accent-soft"
|
|
117
|
+
),
|
|
118
|
+
today: "[&_button]:border [&_button]:border-border",
|
|
119
|
+
outside: "text-foreground-subtle",
|
|
120
|
+
disabled: "opacity-40 pointer-events-none",
|
|
121
|
+
range_start: "[&_button]:rounded-r-none",
|
|
122
|
+
range_end: "[&_button]:rounded-l-none",
|
|
123
|
+
range_middle: "[&_button]:bg-accent-soft [&_button]:text-foreground [&_button:hover]:bg-accent-soft [&_button]:rounded-none",
|
|
124
|
+
hidden: "invisible",
|
|
125
|
+
...r
|
|
126
|
+
},
|
|
127
|
+
components: {
|
|
128
|
+
Dropdown: S,
|
|
129
|
+
Chevron: ({ orientation: a }) => a === "left" ? /* @__PURE__ */ t(y, { className: "size-4" }) : a === "right" ? /* @__PURE__ */ t(N, { className: "size-4" }) : /* @__PURE__ */ t(g, { className: "size-3.5 opacity-60" })
|
|
130
|
+
},
|
|
131
|
+
...v
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
z.displayName = "Calendar";
|
|
136
|
+
export {
|
|
137
|
+
z as Calendar
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=Calendar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Calendar.js","sources":["../../../src/components/ui/Calendar.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, type ChangeEvent } from 'react';\nimport {\n DayPicker,\n getDefaultClassNames,\n type DayPickerProps,\n type DropdownProps,\n} from 'react-day-picker';\nimport * as SelectPrimitive from '@radix-ui/react-select';\nimport { ChevronDown, ChevronLeft, ChevronRight } from '@/icons';\nimport { Select, SelectContent, SelectItem } from './Select';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\n// RDP's structural class names (`rdp-*`). Its stylesheet is intentionally NOT\n// imported — every visual rule below is ours, so there is no cascade to fight\n// and no CSS side effect for tree-shaken consumers.\nconst rdp = getDefaultClassNames();\n\nexport type CalendarProps = DayPickerProps & { className?: string };\n\n/**\n * Month / year dropdown rendered with the design-system Select instead of a\n * native `<select>` — the native popup is unbounded (a century of years fills\n * the screen on macOS) and can't be styled. Radix gives us a max-height,\n * internal scrolling, and auto-scroll to the selected year.\n */\nfunction CalendarDropdown({\n options = [],\n value,\n onChange,\n disabled,\n 'aria-label': ariaLabel,\n}: DropdownProps) {\n const selected = options.find((opt) => opt.value === Number(value));\n\n return (\n <Select\n value={value === undefined ? undefined : String(value)}\n onValueChange={(v) => {\n // RDP's handler expects a native select change event — fake the shape.\n onChange?.({ target: { value: v } } as unknown as ChangeEvent<HTMLSelectElement>);\n }}\n disabled={disabled}\n >\n <SelectPrimitive.Trigger\n aria-label={ariaLabel}\n className={cn(\n 'border-border bg-card text-foreground inline-flex h-7 items-center gap-1 rounded-md border px-2 text-sm font-medium',\n 'hover:bg-background-muted focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n 'disabled:pointer-events-none disabled:opacity-50',\n )}\n >\n <SelectPrimitive.Value>{selected?.label}</SelectPrimitive.Value>\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"size-3.5 opacity-60\" aria-hidden />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n <SelectContent className=\"max-h-64\">\n {options.map((opt) => (\n <SelectItem key={opt.value} value={String(opt.value)} disabled={opt.disabled}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nconst navButton = cn(\n 'pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-md text-foreground-muted hover:bg-background-muted',\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:opacity-40 disabled:pointer-events-none',\n);\n\n/**\n * Standalone calendar surface. Used by DatePicker, but can also be embedded\n * directly in popovers, sheets, or inline forms. Wraps `react-day-picker` v9.\n *\n * Not a forwardRef: RDP v9's `DayPicker` is a plain function component and\n * exposes no root ref, so a forwarded ref would silently be dropped.\n */\nexport function Calendar({\n className,\n classNames,\n captionLayout = 'dropdown',\n startMonth,\n endMonth,\n labels,\n weekStartsOn = 1,\n ...props\n}: CalendarProps) {\n const strings = useStrings();\n // Bound the month/year dropdowns. Consumers can narrow this with\n // startMonth / endMonth; the default spans a century back to a decade ahead\n // so the year dropdown is useful for both birthdays and future scheduling.\n // Memoised — fresh Date identities every render defeat RDP's internal\n // memoisation and re-derive the navigable range on each paint.\n const { start, end } = useMemo(() => {\n const now = new Date();\n return {\n start: startMonth ?? new Date(now.getFullYear() - 100, 0, 1),\n end: endMonth ?? new Date(now.getFullYear() + 10, 11, 31),\n };\n }, [startMonth, endMonth]);\n\n return (\n <DayPicker\n showOutsideDays\n weekStartsOn={weekStartsOn}\n captionLayout={captionLayout}\n startMonth={start}\n endMonth={end}\n className={cn('border-border bg-card inline-block rounded-lg border p-3', className)}\n labels={{\n labelPrevious: () => strings.calendar.previous,\n labelNext: () => strings.calendar.next,\n labelMonthDropdown: () => strings.calendar.chooseMonth,\n labelYearDropdown: () => strings.calendar.chooseYear,\n labelNav: () => strings.calendar.nav,\n ...labels,\n }}\n classNames={{\n ...rdp,\n root: cn(rdp.root, 'relative'),\n months: 'relative flex flex-col gap-4 sm:flex-row sm:gap-6',\n month: 'flex flex-col gap-3',\n month_caption: 'relative flex h-8 items-center justify-center px-8',\n dropdowns: 'flex items-center justify-center gap-2',\n caption_label: 'flex items-center gap-1 text-sm font-medium',\n // The caption is positioned and painted after the nav, so the nav must\n // sit above it (z-10) for its buttons to receive clicks. The container\n // itself ignores pointer events so the centred dropdowns underneath\n // stay clickable — only the buttons opt back in.\n nav: 'pointer-events-none absolute inset-x-0 top-0 z-10 flex h-8 items-center justify-between',\n button_previous: navButton,\n button_next: navButton,\n month_grid: 'w-full border-collapse',\n weekdays: 'grid grid-cols-7',\n weekday:\n 'h-8 w-9 text-center text-xs font-medium uppercase tracking-wide text-foreground-subtle',\n week: 'mt-0.5 grid grid-cols-7',\n day: 'relative h-9 w-9 p-0 text-center',\n day_button: cn(\n 'inline-flex h-9 w-9 items-center justify-center rounded-md text-sm hover:bg-background-muted',\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n ),\n // RDP 9 puts aria-selected / the modifier classes on the <td>, so the\n // button is styled through its cell. `[&_button:hover]` outranks the\n // button's own `hover:` rule so selected days keep the accent on hover.\n selected: cn(\n '[&_button]:bg-accent [&_button]:text-on-accent [&_button:hover]:bg-accent-hover',\n '[&_button]:focus-visible:ring-offset-accent-soft',\n ),\n today: '[&_button]:border [&_button]:border-border',\n outside: 'text-foreground-subtle',\n disabled: 'opacity-40 pointer-events-none',\n range_start: '[&_button]:rounded-r-none',\n range_end: '[&_button]:rounded-l-none',\n range_middle:\n '[&_button]:bg-accent-soft [&_button]:text-foreground [&_button:hover]:bg-accent-soft [&_button]:rounded-none',\n hidden: 'invisible',\n ...classNames,\n }}\n components={{\n Dropdown: CalendarDropdown,\n Chevron: ({ orientation }) => {\n if (orientation === 'left') return <ChevronLeft className=\"size-4\" />;\n if (orientation === 'right') return <ChevronRight className=\"size-4\" />;\n // up/down — used for the month/year dropdown carets\n return <ChevronDown className=\"size-3.5 opacity-60\" />;\n },\n }}\n {...props}\n />\n );\n}\nCalendar.displayName = 'Calendar';\n"],"names":["rdp","getDefaultClassNames","CalendarDropdown","options","value","onChange","disabled","ariaLabel","selected","opt","jsxs","Select","v","SelectPrimitive","cn","jsx","ChevronDown","SelectContent","SelectItem","navButton","Calendar","className","classNames","captionLayout","startMonth","endMonth","labels","weekStartsOn","props","strings","useStrings","start","end","useMemo","now","DayPicker","orientation","ChevronLeft","ChevronRight"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { VariantProps } from '../../lib/cva.js';
|
|
3
|
+
declare const card: (props?: ({
|
|
4
|
+
variant?: "default" | "interactive" | null | undefined;
|
|
5
|
+
padding?: "none" | "md" | "sm" | "lg" | null | undefined;
|
|
6
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
|
+
export interface CardProps extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof card> {
|
|
8
|
+
/** Render the child element (e.g. a router `<Link>`) with Card styles. */
|
|
9
|
+
asChild?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Bounded surface used to group related content. Per the refined-minimal
|
|
13
|
+
* rules, cards on the page have a hairline border and no shadow.
|
|
14
|
+
*
|
|
15
|
+
* @example Header + content + footer
|
|
16
|
+
* <Card>
|
|
17
|
+
* <CardHeader>
|
|
18
|
+
* <CardTitle>Storage</CardTitle>
|
|
19
|
+
* <CardDescription>Usage across all projects.</CardDescription>
|
|
20
|
+
* </CardHeader>
|
|
21
|
+
* <CardContent>…</CardContent>
|
|
22
|
+
* <CardFooter><Button>Upgrade</Button></CardFooter>
|
|
23
|
+
* </Card>
|
|
24
|
+
*
|
|
25
|
+
* @example Clickable list row
|
|
26
|
+
* <Card variant="interactive" onClick={open}>…</Card>
|
|
27
|
+
*
|
|
28
|
+
* @do Use the `border` style. If a card needs to "float" (modal, dropdown),
|
|
29
|
+
* it isn't a Card — it's a Popover/Dialog.
|
|
30
|
+
* @dont Add `shadow-lg` to Cards. Shadows on inline surfaces violate the
|
|
31
|
+
* refined-minimal direction.
|
|
32
|
+
*/
|
|
33
|
+
export declare const Card: import('react').ForwardRefExoticComponent<CardProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
34
|
+
export declare const CardHeader: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import('react').RefAttributes<HTMLDivElement>>;
|
|
35
|
+
export declare const CardTitle: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLHeadingElement> & import('react').RefAttributes<HTMLHeadingElement>>;
|
|
36
|
+
export declare const CardDescription: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLParagraphElement> & import('react').RefAttributes<HTMLParagraphElement>>;
|
|
37
|
+
export declare const CardContent: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import('react').RefAttributes<HTMLDivElement>>;
|
|
38
|
+
export declare const CardFooter: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import('react').RefAttributes<HTMLDivElement>>;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as d } from "react";
|
|
4
|
+
import { Slot as C } from "@radix-ui/react-slot";
|
|
5
|
+
import { cn as i } from "../../lib/utils.js";
|
|
6
|
+
import { cva as v } from "class-variance-authority";
|
|
7
|
+
const x = v(
|
|
8
|
+
[
|
|
9
|
+
"rounded-lg border bg-card text-card-foreground",
|
|
10
|
+
"transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]"
|
|
11
|
+
],
|
|
12
|
+
{
|
|
13
|
+
variants: {
|
|
14
|
+
variant: {
|
|
15
|
+
default: "border-border",
|
|
16
|
+
interactive: [
|
|
17
|
+
"border-border hover:border-border-strong hover:bg-background-subtle cursor-pointer",
|
|
18
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
19
|
+
"focus-visible:ring-offset-background"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
padding: {
|
|
23
|
+
none: "",
|
|
24
|
+
sm: "p-4",
|
|
25
|
+
/* CANON: 16 compact/mobile, 24 desktop — never 20. */
|
|
26
|
+
md: "p-4 md:p-6",
|
|
27
|
+
lg: "p-6"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
padding: "md"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
), N = d(function({ className: t, variant: r, padding: e, onClick: s, onKeyDown: c, role: f, tabIndex: l, asChild: m, ...p }, g) {
|
|
36
|
+
const b = m ? C : "div", u = r === "interactive" && typeof s == "function";
|
|
37
|
+
return /* @__PURE__ */ o(
|
|
38
|
+
b,
|
|
39
|
+
{
|
|
40
|
+
ref: g,
|
|
41
|
+
role: f ?? (u ? "button" : void 0),
|
|
42
|
+
tabIndex: l ?? (u ? 0 : void 0),
|
|
43
|
+
onClick: s,
|
|
44
|
+
onKeyDown: u || c ? (a) => {
|
|
45
|
+
c == null || c(a), !(a.defaultPrevented || !u) && a.target === a.currentTarget && (a.key === "Enter" || a.key === " ") && (a.preventDefault(), a.currentTarget.click());
|
|
46
|
+
} : void 0,
|
|
47
|
+
className: i(x({ variant: r, padding: e }), t),
|
|
48
|
+
...p
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
N.displayName = "Card";
|
|
53
|
+
const y = d(
|
|
54
|
+
function({ className: t, ...r }, e) {
|
|
55
|
+
return /* @__PURE__ */ o("div", { ref: e, className: i("flex flex-col gap-1 pb-4", t), ...r });
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
y.displayName = "CardHeader";
|
|
59
|
+
const h = d(
|
|
60
|
+
function({ className: t, children: r, ...e }, s) {
|
|
61
|
+
return /* @__PURE__ */ o(
|
|
62
|
+
"h3",
|
|
63
|
+
{
|
|
64
|
+
ref: s,
|
|
65
|
+
className: i("text-foreground text-base leading-tight font-semibold", t),
|
|
66
|
+
...e,
|
|
67
|
+
children: r
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
h.displayName = "CardTitle";
|
|
73
|
+
const k = d(function({ className: t, ...r }, e) {
|
|
74
|
+
return /* @__PURE__ */ o("p", { ref: e, className: i("text-foreground-muted text-sm", t), ...r });
|
|
75
|
+
});
|
|
76
|
+
k.displayName = "CardDescription";
|
|
77
|
+
const T = d(
|
|
78
|
+
function({ className: t, ...r }, e) {
|
|
79
|
+
return /* @__PURE__ */ o("div", { ref: e, className: i("text-foreground text-sm", t), ...r });
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
T.displayName = "CardContent";
|
|
83
|
+
const F = d(
|
|
84
|
+
function({ className: t, ...r }, e) {
|
|
85
|
+
return /* @__PURE__ */ o("div", { ref: e, className: i("flex items-center gap-2 pt-4", t), ...r });
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
F.displayName = "CardFooter";
|
|
89
|
+
export {
|
|
90
|
+
N as Card,
|
|
91
|
+
T as CardContent,
|
|
92
|
+
k as CardDescription,
|
|
93
|
+
F as CardFooter,
|
|
94
|
+
y as CardHeader,
|
|
95
|
+
h as CardTitle
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=Card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.js","sources":["../../../src/components/ui/Card.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type KeyboardEvent } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { cn } from '@/lib/utils';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst card = cva(\n [\n 'rounded-lg border bg-card text-card-foreground',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n ],\n {\n variants: {\n variant: {\n default: 'border-border',\n interactive: [\n 'border-border hover:border-border-strong hover:bg-background-subtle cursor-pointer',\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-background',\n ],\n },\n padding: {\n none: '',\n sm: 'p-4',\n /* CANON: 16 compact/mobile, 24 desktop — never 20. */\n md: 'p-4 md:p-6',\n lg: 'p-6',\n },\n },\n defaultVariants: {\n variant: 'default',\n padding: 'md',\n },\n },\n);\n\nexport interface CardProps extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof card> {\n /** Render the child element (e.g. a router `<Link>`) with Card styles. */\n asChild?: boolean;\n}\n\n/**\n * Bounded surface used to group related content. Per the refined-minimal\n * rules, cards on the page have a hairline border and no shadow.\n *\n * @example Header + content + footer\n * <Card>\n * <CardHeader>\n * <CardTitle>Storage</CardTitle>\n * <CardDescription>Usage across all projects.</CardDescription>\n * </CardHeader>\n * <CardContent>…</CardContent>\n * <CardFooter><Button>Upgrade</Button></CardFooter>\n * </Card>\n *\n * @example Clickable list row\n * <Card variant=\"interactive\" onClick={open}>…</Card>\n *\n * @do Use the `border` style. If a card needs to \"float\" (modal, dropdown),\n * it isn't a Card — it's a Popover/Dialog.\n * @dont Add `shadow-lg` to Cards. Shadows on inline surfaces violate the\n * refined-minimal direction.\n */\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, variant, padding, onClick, onKeyDown, role, tabIndex, asChild, ...props },\n ref,\n) {\n const Comp = asChild ? Slot : 'div';\n // An interactive card with a click handler must be reachable and operable\n // from the keyboard: expose it as a button and map Enter/Space to click.\n const isButtonLike = variant === 'interactive' && typeof onClick === 'function';\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || !isButtonLike) return;\n // Only when the card itself is focused — nested controls handle their own keys.\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.currentTarget.click();\n }\n };\n return (\n <Comp\n ref={ref}\n role={role ?? (isButtonLike ? 'button' : undefined)}\n tabIndex={tabIndex ?? (isButtonLike ? 0 : undefined)}\n onClick={onClick}\n onKeyDown={isButtonLike || onKeyDown ? handleKeyDown : undefined}\n className={cn(card({ variant, padding }), className)}\n {...props}\n />\n );\n});\nCard.displayName = 'Card';\n\nexport const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function CardHeader({ className, ...props }, ref) {\n return <div ref={ref} className={cn('flex flex-col gap-1 pb-4', className)} {...props} />;\n },\n);\nCardHeader.displayName = 'CardHeader';\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(\n function CardTitle({ className, children, ...props }, ref) {\n return (\n <h3\n ref={ref}\n className={cn('text-foreground text-base leading-tight font-semibold', className)}\n {...props}\n >\n {children}\n </h3>\n );\n },\n);\nCardTitle.displayName = 'CardTitle';\n\nexport const CardDescription = forwardRef<\n HTMLParagraphElement,\n HTMLAttributes<HTMLParagraphElement>\n>(function CardDescription({ className, ...props }, ref) {\n return <p ref={ref} className={cn('text-foreground-muted text-sm', className)} {...props} />;\n});\nCardDescription.displayName = 'CardDescription';\n\nexport const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function CardContent({ className, ...props }, ref) {\n return <div ref={ref} className={cn('text-foreground text-sm', className)} {...props} />;\n },\n);\nCardContent.displayName = 'CardContent';\n\nexport const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function CardFooter({ className, ...props }, ref) {\n return <div ref={ref} className={cn('flex items-center gap-2 pt-4', className)} {...props} />;\n },\n);\nCardFooter.displayName = 'CardFooter';\n"],"names":["card","cva","Card","forwardRef","className","variant","padding","onClick","onKeyDown","role","tabIndex","asChild","props","ref","Comp","Slot","isButtonLike","jsx","event","cn","CardHeader","CardTitle","children","CardDescription","CardContent","CardFooter"],"mappings":";;;;;;AAOA,MAAMA,IAAOC;AAAA,EACX;AAAA,IACE;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,MAEF,SAAS;AAAA,QACP,MAAM;AAAA,QACN,IAAI;AAAA;AAAA,QAEJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ,GA6BaC,IAAOC,EAAsC,SACxD,EAAE,WAAAC,GAAW,SAAAC,GAAS,SAAAC,GAAS,SAAAC,GAAS,WAAAC,GAAW,MAAAC,GAAM,UAAAC,GAAU,SAAAC,GAAS,GAAGC,EAAA,GAC/EC,GACA;AACA,QAAMC,IAAOH,IAAUI,IAAO,OAGxBC,IAAeX,MAAY,iBAAiB,OAAOE,KAAY;AAWrE,SACE,gBAAAU;AAAA,IAACH;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,MAAMJ,MAASO,IAAe,WAAW;AAAA,MACzC,UAAUN,MAAaM,IAAe,IAAI;AAAA,MAC1C,SAAAT;AAAA,MACA,WAAWS,KAAgBR,IAhBT,CAACU,MAAyC;AAE9D,QADAV,KAAA,QAAAA,EAAYU,IACR,EAAAA,EAAM,oBAAoB,CAACF,MAE3BE,EAAM,WAAWA,EAAM,kBACvBA,EAAM,QAAQ,WAAWA,EAAM,QAAQ,SACzCA,EAAM,eAAA,GACNA,EAAM,cAAc,MAAA;AAAA,MAExB,IAO2D;AAAA,MACvD,WAAWC,EAAGnB,EAAK,EAAE,SAAAK,GAAS,SAAAC,EAAA,CAAS,GAAGF,CAAS;AAAA,MAClD,GAAGQ;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AACDV,EAAK,cAAc;AAEZ,MAAMkB,IAAajB;AAAA,EACxB,SAAoB,EAAE,WAAAC,GAAW,GAAGQ,EAAA,GAASC,GAAK;AAChD,WAAO,gBAAAI,EAAC,SAAI,KAAAJ,GAAU,WAAWM,EAAG,4BAA4Bf,CAAS,GAAI,GAAGQ,GAAO;AAAA,EACzF;AACF;AACAQ,EAAW,cAAc;AAElB,MAAMC,IAAYlB;AAAA,EACvB,SAAmB,EAAE,WAAAC,GAAW,UAAAkB,GAAU,GAAGV,EAAA,GAASC,GAAK;AACzD,WACE,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAJ;AAAA,QACA,WAAWM,EAAG,yDAAyDf,CAAS;AAAA,QAC/E,GAAGQ;AAAA,QAEH,UAAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AACAD,EAAU,cAAc;AAEjB,MAAME,IAAkBpB,EAG7B,SAAyB,EAAE,WAAAC,GAAW,GAAGQ,EAAA,GAASC,GAAK;AACvD,SAAO,gBAAAI,EAAC,OAAE,KAAAJ,GAAU,WAAWM,EAAG,iCAAiCf,CAAS,GAAI,GAAGQ,GAAO;AAC5F,CAAC;AACDW,EAAgB,cAAc;AAEvB,MAAMC,IAAcrB;AAAA,EACzB,SAAqB,EAAE,WAAAC,GAAW,GAAGQ,EAAA,GAASC,GAAK;AACjD,WAAO,gBAAAI,EAAC,SAAI,KAAAJ,GAAU,WAAWM,EAAG,2BAA2Bf,CAAS,GAAI,GAAGQ,GAAO;AAAA,EACxF;AACF;AACAY,EAAY,cAAc;AAEnB,MAAMC,IAAatB;AAAA,EACxB,SAAoB,EAAE,WAAAC,GAAW,GAAGQ,EAAA,GAASC,GAAK;AAChD,WAAO,gBAAAI,EAAC,SAAI,KAAAJ,GAAU,WAAWM,EAAG,gCAAgCf,CAAS,GAAI,GAAGQ,GAAO;AAAA,EAC7F;AACF;AACAa,EAAW,cAAc;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { default as useEmblaCarousel, UseEmblaCarouselType } from 'embla-carousel-react';
|
|
3
|
+
type CarouselApi = UseEmblaCarouselType[1];
|
|
4
|
+
type CarouselOptions = Parameters<typeof useEmblaCarousel>[0];
|
|
5
|
+
export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
opts?: CarouselOptions;
|
|
7
|
+
orientation?: 'horizontal' | 'vertical';
|
|
8
|
+
setApi?: (api: CarouselApi) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Embla-backed carousel. Compose with `<CarouselContent>`, `<CarouselItem>`,
|
|
12
|
+
* `<CarouselPrevious>`, and `<CarouselNext>`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const Carousel: import('react').ForwardRefExoticComponent<CarouselProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
15
|
+
export declare const CarouselContent: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import('react').RefAttributes<HTMLDivElement>>;
|
|
16
|
+
export interface CarouselItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
17
|
+
/** 0-based position, used for the default "{i+1} of {n}" label. */
|
|
18
|
+
index?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const CarouselItem: import('react').ForwardRefExoticComponent<CarouselItemProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export declare const CarouselPrevious: import('react').ForwardRefExoticComponent<Omit<Omit<import('./IconButton.js').IconButtonProps & import('react').RefAttributes<HTMLButtonElement>, "ref">, "aria-label" | "icon"> & {
|
|
22
|
+
'aria-label'?: string;
|
|
23
|
+
} & import('react').RefAttributes<HTMLButtonElement>>;
|
|
24
|
+
export declare const CarouselNext: import('react').ForwardRefExoticComponent<Omit<Omit<import('./IconButton.js').IconButtonProps & import('react').RefAttributes<HTMLButtonElement>, "ref">, "aria-label" | "icon"> & {
|
|
25
|
+
'aria-label'?: string;
|
|
26
|
+
} & import('react').RefAttributes<HTMLButtonElement>>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as c } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as m, useState as x, useCallback as O, useEffect as P, createContext as T, useContext as U, version as _ } from "react";
|
|
4
|
+
import q from "embla-carousel-react";
|
|
5
|
+
import { ChevronRight as F, ChevronLeft as G } from "../../icons/index.js";
|
|
6
|
+
import { cn as C } from "../../lib/utils.js";
|
|
7
|
+
import { useStrings as b } from "../../hooks/use-strings.js";
|
|
8
|
+
import { formatString as H } from "../../lib/strings.js";
|
|
9
|
+
import { IconButton as g } from "./IconButton.js";
|
|
10
|
+
const J = {
|
|
11
|
+
inert: Number(_.split(".")[0]) >= 19 ? !0 : ""
|
|
12
|
+
}, p = T(null);
|
|
13
|
+
function h() {
|
|
14
|
+
const f = U(p);
|
|
15
|
+
if (!f) throw new Error("Carousel components must be used inside <Carousel>");
|
|
16
|
+
return f;
|
|
17
|
+
}
|
|
18
|
+
const K = m(function({
|
|
19
|
+
opts: l,
|
|
20
|
+
orientation: o = "horizontal",
|
|
21
|
+
setApi: t,
|
|
22
|
+
className: s,
|
|
23
|
+
children: n,
|
|
24
|
+
onKeyDown: a,
|
|
25
|
+
"aria-label": d,
|
|
26
|
+
"aria-labelledby": i,
|
|
27
|
+
...v
|
|
28
|
+
}, w) {
|
|
29
|
+
const N = b(), [I, e] = q({
|
|
30
|
+
...l,
|
|
31
|
+
axis: o === "horizontal" ? "x" : "y"
|
|
32
|
+
}), [y, z] = x(!1), [S, R] = x(!1), [k, V] = x(0), [E, L] = x([]), u = O((r) => {
|
|
33
|
+
r && (z(r.canScrollPrev()), R(r.canScrollNext()), L(r.slidesInView()));
|
|
34
|
+
}, []), j = (r) => {
|
|
35
|
+
if (a == null || a(r), r.defaultPrevented) return;
|
|
36
|
+
const B = o === "horizontal" ? "ArrowLeft" : "ArrowUp", D = o === "horizontal" ? "ArrowRight" : "ArrowDown";
|
|
37
|
+
r.key === B ? (r.preventDefault(), e == null || e.scrollPrev()) : r.key === D && (r.preventDefault(), e == null || e.scrollNext());
|
|
38
|
+
};
|
|
39
|
+
return P(() => {
|
|
40
|
+
if (e)
|
|
41
|
+
return t == null || t(e), u(e), e.on("reInit", u).on("select", u).on("slidesInView", u), () => {
|
|
42
|
+
e.off("reInit", u).off("select", u).off("slidesInView", u);
|
|
43
|
+
};
|
|
44
|
+
}, [e, u, t]), P(() => {
|
|
45
|
+
if (!e) return;
|
|
46
|
+
const r = () => V(e.slideNodes().length);
|
|
47
|
+
return r(), e.on("reInit", r), () => {
|
|
48
|
+
e.off("reInit", r);
|
|
49
|
+
};
|
|
50
|
+
}, [e]), /* @__PURE__ */ c(
|
|
51
|
+
p.Provider,
|
|
52
|
+
{
|
|
53
|
+
value: {
|
|
54
|
+
carouselRef: I,
|
|
55
|
+
api: e,
|
|
56
|
+
canPrev: y,
|
|
57
|
+
canNext: S,
|
|
58
|
+
scrollPrev: () => e == null ? void 0 : e.scrollPrev(),
|
|
59
|
+
scrollNext: () => e == null ? void 0 : e.scrollNext(),
|
|
60
|
+
orientation: o,
|
|
61
|
+
slideCount: k,
|
|
62
|
+
inView: E
|
|
63
|
+
},
|
|
64
|
+
children: /* @__PURE__ */ c(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
ref: w,
|
|
68
|
+
className: C("relative isolate", s),
|
|
69
|
+
role: "region",
|
|
70
|
+
"aria-roledescription": "carousel",
|
|
71
|
+
"aria-label": d ?? (i ? void 0 : N.carousel.label),
|
|
72
|
+
"aria-labelledby": i,
|
|
73
|
+
onKeyDown: j,
|
|
74
|
+
...v,
|
|
75
|
+
children: n
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
K.displayName = "Carousel";
|
|
82
|
+
const M = m(
|
|
83
|
+
function({ className: l, ...o }, t) {
|
|
84
|
+
const { carouselRef: s, orientation: n } = h();
|
|
85
|
+
return /* @__PURE__ */ c("div", { ref: s, className: "overflow-hidden", children: /* @__PURE__ */ c(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
ref: t,
|
|
89
|
+
className: C(
|
|
90
|
+
"flex",
|
|
91
|
+
n === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
|
92
|
+
l
|
|
93
|
+
),
|
|
94
|
+
...o
|
|
95
|
+
}
|
|
96
|
+
) });
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
M.displayName = "CarouselContent";
|
|
100
|
+
const Q = m(function({ className: l, index: o, "aria-label": t, ...s }, n) {
|
|
101
|
+
const a = b(), { orientation: d, slideCount: i, inView: v } = h(), w = o !== void 0 && i > 0 ? H(a.carousel.slide, { index: o + 1, count: i }) : void 0, N = o !== void 0 && v.length > 0 && !v.includes(o);
|
|
102
|
+
return /* @__PURE__ */ c(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
ref: n,
|
|
106
|
+
role: "group",
|
|
107
|
+
"aria-roledescription": "slide",
|
|
108
|
+
"aria-label": t ?? w,
|
|
109
|
+
"aria-hidden": N || void 0,
|
|
110
|
+
...N ? J : void 0,
|
|
111
|
+
className: C(
|
|
112
|
+
"min-w-0 shrink-0 grow-0 basis-full",
|
|
113
|
+
d === "horizontal" ? "pl-4" : "pt-4",
|
|
114
|
+
l
|
|
115
|
+
),
|
|
116
|
+
...s
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
Q.displayName = "CarouselItem";
|
|
121
|
+
const W = m(
|
|
122
|
+
function({ className: l, "aria-label": o, ...t }, s) {
|
|
123
|
+
const { canPrev: n, scrollPrev: a, orientation: d } = h(), i = b();
|
|
124
|
+
return /* @__PURE__ */ c(
|
|
125
|
+
g,
|
|
126
|
+
{
|
|
127
|
+
ref: s,
|
|
128
|
+
"aria-label": o ?? i.carousel.previous,
|
|
129
|
+
variant: "outline",
|
|
130
|
+
disabled: !n,
|
|
131
|
+
onClick: a,
|
|
132
|
+
icon: /* @__PURE__ */ c(G, {}),
|
|
133
|
+
className: C(
|
|
134
|
+
"absolute z-10",
|
|
135
|
+
d === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
136
|
+
l
|
|
137
|
+
),
|
|
138
|
+
...t
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
W.displayName = "CarouselPrevious";
|
|
144
|
+
const X = m(
|
|
145
|
+
function({ className: l, "aria-label": o, ...t }, s) {
|
|
146
|
+
const { canNext: n, scrollNext: a, orientation: d } = h(), i = b();
|
|
147
|
+
return /* @__PURE__ */ c(
|
|
148
|
+
g,
|
|
149
|
+
{
|
|
150
|
+
ref: s,
|
|
151
|
+
"aria-label": o ?? i.carousel.next,
|
|
152
|
+
variant: "outline",
|
|
153
|
+
disabled: !n,
|
|
154
|
+
onClick: a,
|
|
155
|
+
icon: /* @__PURE__ */ c(F, {}),
|
|
156
|
+
className: C(
|
|
157
|
+
"absolute z-10",
|
|
158
|
+
d === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
159
|
+
l
|
|
160
|
+
),
|
|
161
|
+
...t
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
X.displayName = "CarouselNext";
|
|
167
|
+
export {
|
|
168
|
+
K as Carousel,
|
|
169
|
+
M as CarouselContent,
|
|
170
|
+
Q as CarouselItem,
|
|
171
|
+
X as CarouselNext,
|
|
172
|
+
W as CarouselPrevious
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=Carousel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Carousel.js","sources":["../../../src/components/ui/Carousel.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useState,\n type HTMLAttributes,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n} from 'react';\nimport { version as reactVersion } from 'react';\nimport useEmblaCarousel, { type UseEmblaCarouselType } from 'embla-carousel-react';\nimport { ChevronLeft, ChevronRight } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { formatString } from '@/lib/strings';\nimport { IconButton } from './IconButton';\n\n// `inert` is a boolean attribute. React 19 renders `inert={true}` as `inert=\"\"`;\n// React 18 has no knowledge of it and only forwards a string, so pass `''`\n// there (it would drop `true` with a warning). Typed loosely for both.\nconst INERT_PROPS = {\n inert: Number(reactVersion.split('.')[0]) >= 19 ? true : '',\n} as Record<string, unknown>;\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype CarouselOptions = Parameters<typeof useEmblaCarousel>[0];\n\ninterface CarouselContextValue {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: CarouselApi;\n canPrev: boolean;\n canNext: boolean;\n scrollPrev: () => void;\n scrollNext: () => void;\n orientation: 'horizontal' | 'vertical';\n slideCount: number;\n /** Indices of slides currently in view (others are inert). */\n inView: number[];\n}\n\nconst CarouselContext = createContext<CarouselContextValue | null>(null);\n\nfunction useCarousel() {\n const ctx = useContext(CarouselContext);\n if (!ctx) throw new Error('Carousel components must be used inside <Carousel>');\n return ctx;\n}\n\nexport interface CarouselProps extends HTMLAttributes<HTMLDivElement> {\n opts?: CarouselOptions;\n orientation?: 'horizontal' | 'vertical';\n setApi?: (api: CarouselApi) => void;\n}\n\n/**\n * Embla-backed carousel. Compose with `<CarouselContent>`, `<CarouselItem>`,\n * `<CarouselPrevious>`, and `<CarouselNext>`.\n */\nexport const Carousel = forwardRef<HTMLDivElement, CarouselProps>(function Carousel(\n {\n opts,\n orientation = 'horizontal',\n setApi,\n className,\n children,\n onKeyDown,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const [carouselRef, api] = useEmblaCarousel({\n ...opts,\n axis: orientation === 'horizontal' ? 'x' : 'y',\n });\n const [canPrev, setCanPrev] = useState(false);\n const [canNext, setCanNext] = useState(false);\n const [slideCount, setSlideCount] = useState(0);\n const [inView, setInView] = useState<number[]>([]);\n\n const onSelect = useCallback((api: CarouselApi) => {\n if (!api) return;\n setCanPrev(api.canScrollPrev());\n setCanNext(api.canScrollNext());\n setInView(api.slidesInView());\n }, []);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n const prevKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';\n const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';\n if (event.key === prevKey) {\n event.preventDefault();\n api?.scrollPrev();\n } else if (event.key === nextKey) {\n event.preventDefault();\n api?.scrollNext();\n }\n };\n\n useEffect(() => {\n if (!api) return;\n setApi?.(api);\n onSelect(api);\n api.on('reInit', onSelect).on('select', onSelect).on('slidesInView', onSelect);\n return () => {\n api.off('reInit', onSelect).off('select', onSelect).off('slidesInView', onSelect);\n };\n }, [api, onSelect, setApi]);\n\n useEffect(() => {\n if (!api) return;\n const count = () => setSlideCount(api.slideNodes().length);\n count();\n api.on('reInit', count);\n return () => {\n api.off('reInit', count);\n };\n }, [api]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api,\n canPrev,\n canNext,\n scrollPrev: () => api?.scrollPrev(),\n scrollNext: () => api?.scrollNext(),\n orientation,\n slideCount,\n inView,\n }}\n >\n {/* Arrow keys act only while focus is inside the region (it is not itself\n focusable); the buttons remain the primary control. */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <div\n ref={ref}\n className={cn('relative isolate', className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n aria-label={ariaLabel ?? (ariaLabelledby ? undefined : strings.carousel.label)}\n aria-labelledby={ariaLabelledby}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n});\nCarousel.displayName = 'Carousel';\n\nexport const CarouselContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function CarouselContent({ className, ...props }, ref) {\n const { carouselRef, orientation } = useCarousel();\n return (\n <div ref={carouselRef} className=\"overflow-hidden\">\n <div\n ref={ref}\n className={cn(\n 'flex',\n orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n className,\n )}\n {...props}\n />\n </div>\n );\n },\n);\nCarouselContent.displayName = 'CarouselContent';\n\nexport interface CarouselItemProps extends HTMLAttributes<HTMLDivElement> {\n /** 0-based position, used for the default \"{i+1} of {n}\" label. */\n index?: number;\n}\n\nexport const CarouselItem = forwardRef<HTMLDivElement, CarouselItemProps>(function CarouselItem(\n { className, index, 'aria-label': ariaLabel, ...props },\n ref,\n) {\n const strings = useStrings();\n const { orientation, slideCount, inView } = useCarousel();\n const defaultLabel =\n index !== undefined && slideCount > 0\n ? formatString(strings.carousel.slide, { index: index + 1, count: slideCount })\n : undefined;\n // Off-screen slides are inert: not in the tab order, not read by AT. Only\n // possible when `index` is provided (unknown index → always live).\n const offscreen = index !== undefined && inView.length > 0 && !inView.includes(index);\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label={ariaLabel ?? defaultLabel}\n aria-hidden={offscreen || undefined}\n {...(offscreen ? INERT_PROPS : undefined)}\n className={cn(\n 'min-w-0 shrink-0 grow-0 basis-full',\n orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n className,\n )}\n {...props}\n />\n );\n});\nCarouselItem.displayName = 'CarouselItem';\n\ntype CarouselButtonProps = Omit<\n ComponentPropsWithoutRef<typeof IconButton>,\n 'icon' | 'aria-label'\n> & { 'aria-label'?: string };\n\nexport const CarouselPrevious = forwardRef<HTMLButtonElement, CarouselButtonProps>(\n function CarouselPrevious({ className, 'aria-label': ariaLabel, ...props }, ref) {\n const { canPrev, scrollPrev, orientation } = useCarousel();\n const strings = useStrings();\n return (\n <IconButton\n ref={ref}\n aria-label={ariaLabel ?? strings.carousel.previous}\n variant=\"outline\"\n disabled={!canPrev}\n onClick={scrollPrev}\n icon={<ChevronLeft />}\n className={cn(\n 'absolute z-10',\n orientation === 'horizontal'\n ? 'top-1/2 -left-12 -translate-y-1/2'\n : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n className,\n )}\n {...props}\n />\n );\n },\n);\nCarouselPrevious.displayName = 'CarouselPrevious';\n\nexport const CarouselNext = forwardRef<HTMLButtonElement, CarouselButtonProps>(\n function CarouselNext({ className, 'aria-label': ariaLabel, ...props }, ref) {\n const { canNext, scrollNext, orientation } = useCarousel();\n const strings = useStrings();\n return (\n <IconButton\n ref={ref}\n aria-label={ariaLabel ?? strings.carousel.next}\n variant=\"outline\"\n disabled={!canNext}\n onClick={scrollNext}\n icon={<ChevronRight />}\n className={cn(\n 'absolute z-10',\n orientation === 'horizontal'\n ? 'top-1/2 -right-12 -translate-y-1/2'\n : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n className,\n )}\n {...props}\n />\n );\n },\n);\nCarouselNext.displayName = 'CarouselNext';\n"],"names":["INERT_PROPS","reactVersion","CarouselContext","createContext","useCarousel","ctx","useContext","Carousel","forwardRef","opts","orientation","setApi","className","children","onKeyDown","ariaLabel","ariaLabelledby","props","ref","strings","useStrings","carouselRef","api","useEmblaCarousel","canPrev","setCanPrev","useState","canNext","setCanNext","slideCount","setSlideCount","inView","setInView","onSelect","useCallback","handleKeyDown","event","prevKey","nextKey","useEffect","count","jsx","cn","CarouselContent","CarouselItem","index","defaultLabel","formatString","offscreen","CarouselPrevious","scrollPrev","IconButton","ChevronLeft","CarouselNext","scrollNext","ChevronRight"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Tiny, dependency-free chart primitives for inline dashboards — sparklines,
|
|
4
|
+
* KPI cards, at-a-glance trends. For heavy interactive visualisations reach for
|
|
5
|
+
* a charting library. The SVG is drawn in real pixels: a ResizeObserver
|
|
6
|
+
* measures the container width, so points and bars are never distorted.
|
|
7
|
+
*
|
|
8
|
+
* Keyboard: one tab stop per series; ←/→ (Home/End) move between points and
|
|
9
|
+
* the active point is announced via a polite live region.
|
|
10
|
+
*/
|
|
11
|
+
export interface ChartPoint {
|
|
12
|
+
x: string | number;
|
|
13
|
+
/** `null` breaks the line (a gap) and is skipped in bars/tables. */
|
|
14
|
+
y: number | null;
|
|
15
|
+
}
|
|
16
|
+
export interface ChartSeries {
|
|
17
|
+
/** Name used in the accessible summary, table header and tooltips. */
|
|
18
|
+
name?: string;
|
|
19
|
+
data: ChartPoint[];
|
|
20
|
+
}
|
|
21
|
+
export type ChartState = 'loading' | 'empty' | 'error';
|
|
22
|
+
/** Text builders for the accessible summary + point labels (i18n). */
|
|
23
|
+
export interface ChartLabels {
|
|
24
|
+
/** `<desc>` sentence per series. */
|
|
25
|
+
summary: (s: {
|
|
26
|
+
name: string;
|
|
27
|
+
count: number;
|
|
28
|
+
min: number;
|
|
29
|
+
max: number;
|
|
30
|
+
lastX: string | number | undefined;
|
|
31
|
+
lastY: number | undefined;
|
|
32
|
+
}) => string;
|
|
33
|
+
/** Label of one data point (tooltip, live region). */
|
|
34
|
+
point: (name: string | undefined, x: string | number, y: number) => string;
|
|
35
|
+
/** Accessible name of a series' tab stop. */
|
|
36
|
+
series: (name: string, count: number) => string;
|
|
37
|
+
}
|
|
38
|
+
export declare const defaultChartLabels: ChartLabels;
|
|
39
|
+
export interface ChartProps {
|
|
40
|
+
/** Primary series. Ignored when `series` is provided. */
|
|
41
|
+
data?: ChartPoint[];
|
|
42
|
+
/** Multiple series drawn on the same scale. */
|
|
43
|
+
series?: ChartSeries[];
|
|
44
|
+
/** Per-series colours. Defaults to `--chart-1..6` (categorical; never the accent). */
|
|
45
|
+
colors?: string[];
|
|
46
|
+
/** Drawing height in px. */
|
|
47
|
+
height?: number;
|
|
48
|
+
/** Defaults to fluid 100%. */
|
|
49
|
+
width?: number | string;
|
|
50
|
+
/** Visible caption rendered above the chart. Doubles as the accessible name. */
|
|
51
|
+
caption?: ReactNode;
|
|
52
|
+
/** Accessible name when there is no `caption`. */
|
|
53
|
+
'aria-label'?: string;
|
|
54
|
+
/** Alias of `aria-label` (SVG `<title>`). */
|
|
55
|
+
title?: string;
|
|
56
|
+
/** Faint horizontal gridlines. Default true. */
|
|
57
|
+
grid?: boolean;
|
|
58
|
+
/** Y-axis tick labels + first/last x labels. Default true. */
|
|
59
|
+
showAxis?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Render a visually-hidden `<table>` of the data after the SVG so screen
|
|
62
|
+
* readers and copy/paste get the numbers. Default true.
|
|
63
|
+
*/
|
|
64
|
+
tableFallback?: boolean;
|
|
65
|
+
/** Show a small "View as table" toggle that reveals the fallback table. */
|
|
66
|
+
showTableToggle?: boolean;
|
|
67
|
+
/** Replace the drawing with a loading skeleton / empty / error message. */
|
|
68
|
+
state?: ChartState;
|
|
69
|
+
/** Override the generated accessible text (English defaults). */
|
|
70
|
+
labels?: Partial<ChartLabels>;
|
|
71
|
+
className?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare const DEFAULT_CHART_COLORS: string[];
|
|
74
|
+
/** 1234 → "1.2K", 3_400_000 → "3.4M". */
|
|
75
|
+
export declare function abbreviateNumber(n: number): string;
|
|
76
|
+
/**
|
|
77
|
+
* "Nice" axis: expands [min, max] to multiples of a 1/2/2.5/5 step so ticks
|
|
78
|
+
* read as round numbers (never `754.5`).
|
|
79
|
+
*/
|
|
80
|
+
export declare function niceTicks(min: number, max: number, maxTicks?: number): {
|
|
81
|
+
min: number;
|
|
82
|
+
max: number;
|
|
83
|
+
ticks: number[];
|
|
84
|
+
};
|
|
85
|
+
export declare function LineChart({ data, series, colors, height, width, caption, 'aria-label': ariaLabel, title, grid, showAxis, tableFallback, showTableToggle, state, labels: labelsProp, className, }: ChartProps): import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
export declare function AreaChart(props: ChartProps): import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
export declare function BarChart({ data, series, colors, height, width, caption, 'aria-label': ariaLabel, title, grid, showAxis, tableFallback, showTableToggle, state, labels: labelsProp, className, }: ChartProps): import("react/jsx-runtime").JSX.Element;
|