@comitor/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/dist/chart/chart.d.ts +88 -0
- package/dist/chart/chart.js +199 -0
- package/dist/chart/chart.js.map +1 -0
- package/dist/chart/comitor-chart.d.ts +109 -0
- package/dist/chart/comitor-chart.js +189 -0
- package/dist/chart/comitor-chart.js.map +1 -0
- package/dist/chart/index.d.ts +4 -0
- package/dist/chart/index.js +3 -0
- package/dist/chart/index.js.map +1 -0
- package/dist/components/composite/avatar-group.d.ts +33 -0
- package/dist/components/composite/avatar-group.js +86 -0
- package/dist/components/composite/avatar-group.js.map +1 -0
- package/dist/components/composite/combobox.d.ts +70 -0
- package/dist/components/composite/combobox.js +291 -0
- package/dist/components/composite/combobox.js.map +1 -0
- package/dist/components/composite/command-palette.d.ts +56 -0
- package/dist/components/composite/command-palette.js +131 -0
- package/dist/components/composite/command-palette.js.map +1 -0
- package/dist/components/composite/confirm-dialog.d.ts +40 -0
- package/dist/components/composite/confirm-dialog.js +96 -0
- package/dist/components/composite/confirm-dialog.js.map +1 -0
- package/dist/components/composite/copy-button.d.ts +27 -0
- package/dist/components/composite/copy-button.js +80 -0
- package/dist/components/composite/copy-button.js.map +1 -0
- package/dist/components/composite/data-table.d.ts +136 -0
- package/dist/components/composite/data-table.js +300 -0
- package/dist/components/composite/data-table.js.map +1 -0
- package/dist/components/composite/date-picker.d.ts +77 -0
- package/dist/components/composite/date-picker.js +268 -0
- package/dist/components/composite/date-picker.js.map +1 -0
- package/dist/components/composite/empty-state.d.ts +38 -0
- package/dist/components/composite/empty-state.js +35 -0
- package/dist/components/composite/empty-state.js.map +1 -0
- package/dist/components/composite/filter-chips.d.ts +39 -0
- package/dist/components/composite/filter-chips.js +79 -0
- package/dist/components/composite/filter-chips.js.map +1 -0
- package/dist/components/composite/form-field.d.ts +58 -0
- package/dist/components/composite/form-field.js +81 -0
- package/dist/components/composite/form-field.js.map +1 -0
- package/dist/components/composite/form-section.d.ts +33 -0
- package/dist/components/composite/form-section.js +85 -0
- package/dist/components/composite/form-section.js.map +1 -0
- package/dist/components/composite/inline-edit.d.ts +38 -0
- package/dist/components/composite/inline-edit.js +165 -0
- package/dist/components/composite/inline-edit.js.map +1 -0
- package/dist/components/composite/keyboard-hint.d.ts +31 -0
- package/dist/components/composite/keyboard-hint.js +72 -0
- package/dist/components/composite/keyboard-hint.js.map +1 -0
- package/dist/components/composite/letter-avatar.d.ts +47 -0
- package/dist/components/composite/letter-avatar.js +66 -0
- package/dist/components/composite/letter-avatar.js.map +1 -0
- package/dist/components/composite/page-container.d.ts +25 -0
- package/dist/components/composite/page-container.js +23 -0
- package/dist/components/composite/page-container.js.map +1 -0
- package/dist/components/composite/page-header.d.ts +49 -0
- package/dist/components/composite/page-header.js +75 -0
- package/dist/components/composite/page-header.js.map +1 -0
- package/dist/components/composite/relative-time.d.ts +28 -0
- package/dist/components/composite/relative-time.js +44 -0
- package/dist/components/composite/relative-time.js.map +1 -0
- package/dist/components/composite/search-filter-bar.d.ts +38 -0
- package/dist/components/composite/search-filter-bar.js +135 -0
- package/dist/components/composite/search-filter-bar.js.map +1 -0
- package/dist/components/composite/stat-card.d.ts +48 -0
- package/dist/components/composite/stat-card.js +76 -0
- package/dist/components/composite/stat-card.js.map +1 -0
- package/dist/components/composite/status-config.d.ts +85 -0
- package/dist/components/composite/status-config.js +41 -0
- package/dist/components/composite/status-config.js.map +1 -0
- package/dist/components/composite/status-pill.d.ts +26 -0
- package/dist/components/composite/status-pill.js +59 -0
- package/dist/components/composite/status-pill.js.map +1 -0
- package/dist/components/composite/table-pagination.d.ts +31 -0
- package/dist/components/composite/table-pagination.js +103 -0
- package/dist/components/composite/table-pagination.js.map +1 -0
- package/dist/components/composite/text-utils.d.ts +38 -0
- package/dist/components/composite/text-utils.js +40 -0
- package/dist/components/composite/text-utils.js.map +1 -0
- package/dist/components/composite/toast.d.ts +56 -0
- package/dist/components/composite/toast.js +63 -0
- package/dist/components/composite/toast.js.map +1 -0
- package/dist/components/ui/accordion.d.ts +17 -0
- package/dist/components/ui/accordion.js +53 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +34 -0
- package/dist/components/ui/alert-dialog.js +103 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert.d.ts +16 -0
- package/dist/components/ui/alert.js +50 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +7 -0
- package/dist/components/ui/aspect-ratio.js +10 -0
- package/dist/components/ui/aspect-ratio.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +11 -0
- package/dist/components/ui/avatar.js +33 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +15 -0
- package/dist/components/ui/badge.js +37 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +30 -0
- package/dist/components/ui/breadcrumb.js +89 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/button-group.d.ts +21 -0
- package/dist/components/ui/button-group.js +62 -0
- package/dist/components/ui/button-group.js.map +1 -0
- package/dist/components/ui/button.d.ts +21 -0
- package/dist/components/ui/button.js +70 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +26 -0
- package/dist/components/ui/calendar.js +148 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +33 -0
- package/dist/components/ui/card.js +70 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/carousel.d.ts +46 -0
- package/dist/components/ui/carousel.js +173 -0
- package/dist/components/ui/carousel.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +17 -0
- package/dist/components/ui/checkbox.js +67 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +17 -0
- package/dist/components/ui/collapsible.js +18 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/command.d.ts +41 -0
- package/dist/components/ui/command.js +122 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +51 -0
- package/dist/components/ui/context-menu.js +166 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +48 -0
- package/dist/components/ui/dialog.js +131 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +32 -0
- package/dist/components/ui/drawer.js +102 -0
- package/dist/components/ui/drawer.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +52 -0
- package/dist/components/ui/dropdown-menu.js +167 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/empty.d.ts +22 -0
- package/dist/components/ui/empty.js +87 -0
- package/dist/components/ui/empty.js.map +1 -0
- package/dist/components/ui/field.d.ts +46 -0
- package/dist/components/ui/field.js +197 -0
- package/dist/components/ui/field.js.map +1 -0
- package/dist/components/ui/form.d.ts +50 -0
- package/dist/components/ui/form.js +100 -0
- package/dist/components/ui/form.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +17 -0
- package/dist/components/ui/hover-card.js +31 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/input-group.d.ts +26 -0
- package/dist/components/ui/input-group.js +148 -0
- package/dist/components/ui/input-group.js.map +1 -0
- package/dist/components/ui/input-otp.d.ts +23 -0
- package/dist/components/ui/input-otp.js +54 -0
- package/dist/components/ui/input-otp.js.map +1 -0
- package/dist/components/ui/input.d.ts +21 -0
- package/dist/components/ui/input.js +62 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/item.d.ts +39 -0
- package/dist/components/ui/item.js +142 -0
- package/dist/components/ui/item.js.map +1 -0
- package/dist/components/ui/kbd.d.ts +8 -0
- package/dist/components/ui/kbd.js +25 -0
- package/dist/components/ui/kbd.js.map +1 -0
- package/dist/components/ui/label.d.ts +7 -0
- package/dist/components/ui/label.js +21 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +51 -0
- package/dist/components/ui/menubar.js +196 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +39 -0
- package/dist/components/ui/navigation-menu.js +123 -0
- package/dist/components/ui/navigation-menu.js.map +1 -0
- package/dist/components/ui/pagination.d.ts +40 -0
- package/dist/components/ui/pagination.js +95 -0
- package/dist/components/ui/pagination.js.map +1 -0
- package/dist/components/ui/popover.d.ts +19 -0
- package/dist/components/ui/popover.js +35 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +12 -0
- package/dist/components/ui/progress.js +27 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +9 -0
- package/dist/components/ui/radio-group.js +34 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/resizable.d.ts +19 -0
- package/dist/components/ui/resizable.js +38 -0
- package/dist/components/ui/resizable.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +13 -0
- package/dist/components/ui/scroll-area.js +46 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +33 -0
- package/dist/components/ui/select.js +133 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.d.ts +7 -0
- package/dist/components/ui/separator.js +23 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +38 -0
- package/dist/components/ui/sheet.js +96 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +6 -0
- package/dist/components/ui/skeleton.js +9 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +12 -0
- package/dist/components/ui/slider.js +54 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +9 -0
- package/dist/components/ui/sonner.js +25 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/spinner.d.ts +6 -0
- package/dist/components/ui/spinner.js +10 -0
- package/dist/components/ui/spinner.js.map +1 -0
- package/dist/components/ui/switch.d.ts +14 -0
- package/dist/components/ui/switch.js +65 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.d.ts +30 -0
- package/dist/components/ui/table.js +87 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +35 -0
- package/dist/components/ui/tabs.js +54 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +6 -0
- package/dist/components/ui/textarea.js +19 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +18 -0
- package/dist/components/ui/toggle-group.js +52 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +15 -0
- package/dist/components/ui/toggle.js +33 -0
- package/dist/components/ui/toggle.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +21 -0
- package/dist/components/ui/tooltip.js +38 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/form.d.ts +5 -0
- package/dist/form.js +2 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +16 -0
- package/dist/lib/cn.js +9 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/shell/app-header.d.ts +41 -0
- package/dist/shell/app-header.js +136 -0
- package/dist/shell/app-header.js.map +1 -0
- package/dist/shell/app-launcher.d.ts +33 -0
- package/dist/shell/app-launcher.js +189 -0
- package/dist/shell/app-launcher.js.map +1 -0
- package/dist/shell/app-shell.d.ts +44 -0
- package/dist/shell/app-shell.js +99 -0
- package/dist/shell/app-shell.js.map +1 -0
- package/dist/shell/comitor-lockup.d.ts +33 -0
- package/dist/shell/comitor-lockup.js +112 -0
- package/dist/shell/comitor-lockup.js.map +1 -0
- package/dist/shell/comitor-logo.d.ts +28 -0
- package/dist/shell/comitor-logo.js +92 -0
- package/dist/shell/comitor-logo.js.map +1 -0
- package/dist/shell/index.d.ts +24 -0
- package/dist/shell/index.js +23 -0
- package/dist/shell/index.js.map +1 -0
- package/dist/shell/mobile-menu.d.ts +26 -0
- package/dist/shell/mobile-menu.js +56 -0
- package/dist/shell/mobile-menu.js.map +1 -0
- package/dist/shell/notifications-button.d.ts +22 -0
- package/dist/shell/notifications-button.js +39 -0
- package/dist/shell/notifications-button.js.map +1 -0
- package/dist/shell/shell-context.d.ts +91 -0
- package/dist/shell/shell-context.js +233 -0
- package/dist/shell/shell-context.js.map +1 -0
- package/dist/shell/shell-link.d.ts +19 -0
- package/dist/shell/shell-link.js +18 -0
- package/dist/shell/shell-link.js.map +1 -0
- package/dist/shell/sidebar-group.d.ts +21 -0
- package/dist/shell/sidebar-group.js +43 -0
- package/dist/shell/sidebar-group.js.map +1 -0
- package/dist/shell/sidebar-menu-item.d.ts +27 -0
- package/dist/shell/sidebar-menu-item.js +152 -0
- package/dist/shell/sidebar-menu-item.js.map +1 -0
- package/dist/shell/sidebar.d.ts +26 -0
- package/dist/shell/sidebar.js +92 -0
- package/dist/shell/sidebar.js.map +1 -0
- package/dist/shell/theme-provider.d.ts +18 -0
- package/dist/shell/theme-provider.js +29 -0
- package/dist/shell/theme-provider.js.map +1 -0
- package/dist/shell/theme-toggle.d.ts +28 -0
- package/dist/shell/theme-toggle.js +67 -0
- package/dist/shell/theme-toggle.js.map +1 -0
- package/dist/shell/types.d.ts +168 -0
- package/dist/shell/types.js +38 -0
- package/dist/shell/types.js.map +1 -0
- package/dist/shell/user-menu.d.ts +42 -0
- package/dist/shell/user-menu.js +132 -0
- package/dist/shell/user-menu.js.map +1 -0
- package/dist/shell/utils.d.ts +62 -0
- package/dist/shell/utils.js +73 -0
- package/dist/shell/utils.js.map +1 -0
- package/dist/shell/workspace-switcher.d.ts +24 -0
- package/dist/shell/workspace-switcher.js +219 -0
- package/dist/shell/workspace-switcher.js.map +1 -0
- package/dist/tokens.d.ts +173 -0
- package/dist/tokens.js +161 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +151 -0
- package/styles.css +357 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as RechartsPrimitive from "recharts";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
const THEMES = { light: "", dark: ".dark" };
|
|
7
|
+
const ChartContext = React.createContext(null);
|
|
8
|
+
function useChart() {
|
|
9
|
+
const context = React.useContext(ChartContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error("useChart ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong <ChartContainer />");
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
function ChartContainer({ id, className, children, config, ...props }) {
|
|
16
|
+
const uniqueId = React.useId();
|
|
17
|
+
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
18
|
+
return /* @__PURE__ */ jsx(ChartContext.Provider, { value: { config }, children: /* @__PURE__ */ jsxs(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
"data-slot": "chart",
|
|
22
|
+
"data-chart": chartId,
|
|
23
|
+
className: cn(
|
|
24
|
+
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
|
|
25
|
+
className
|
|
26
|
+
),
|
|
27
|
+
...props,
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsx(ChartStyle, { id: chartId, config }),
|
|
30
|
+
/* @__PURE__ */ jsx(RechartsPrimitive.ResponsiveContainer, { children })
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
) });
|
|
34
|
+
}
|
|
35
|
+
const ChartStyle = ({ id, config }) => {
|
|
36
|
+
const colorConfig = Object.entries(config).filter(([, itemConfig]) => itemConfig.theme || itemConfig.color);
|
|
37
|
+
if (!colorConfig.length) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const css = Object.entries(THEMES).map(([theme, prefix]) => {
|
|
41
|
+
const declarations = colorConfig.map(([key, itemConfig]) => {
|
|
42
|
+
const color = itemConfig.theme?.[theme] || itemConfig.color;
|
|
43
|
+
return color ? ` --color-${key}: ${color};` : null;
|
|
44
|
+
}).filter(Boolean).join("\n");
|
|
45
|
+
return `${prefix} [data-chart=${id}] {
|
|
46
|
+
${declarations}
|
|
47
|
+
}`;
|
|
48
|
+
}).join("\n");
|
|
49
|
+
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: css } });
|
|
50
|
+
};
|
|
51
|
+
const ChartTooltip = RechartsPrimitive.Tooltip;
|
|
52
|
+
function ChartTooltipContent({
|
|
53
|
+
active,
|
|
54
|
+
payload,
|
|
55
|
+
className,
|
|
56
|
+
indicator = "dot",
|
|
57
|
+
hideLabel = false,
|
|
58
|
+
hideIndicator = false,
|
|
59
|
+
label,
|
|
60
|
+
labelFormatter,
|
|
61
|
+
labelClassName,
|
|
62
|
+
formatter,
|
|
63
|
+
color,
|
|
64
|
+
nameKey,
|
|
65
|
+
labelKey
|
|
66
|
+
}) {
|
|
67
|
+
const { config } = useChart();
|
|
68
|
+
const tooltipLabel = React.useMemo(() => {
|
|
69
|
+
if (hideLabel || !payload?.length) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const [item] = payload;
|
|
73
|
+
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
|
|
74
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
75
|
+
const value = !labelKey && typeof label === "string" ? config[label]?.label || label : itemConfig?.label;
|
|
76
|
+
if (labelFormatter) {
|
|
77
|
+
return /* @__PURE__ */ jsx("div", { className: cn("font-medium", labelClassName), children: labelFormatter(value, payload) });
|
|
78
|
+
}
|
|
79
|
+
if (!value) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ jsx("div", { className: cn("font-medium", labelClassName), children: value });
|
|
83
|
+
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
84
|
+
if (!active || !payload?.length) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
88
|
+
return /* @__PURE__ */ jsxs(
|
|
89
|
+
"div",
|
|
90
|
+
{
|
|
91
|
+
className: cn(
|
|
92
|
+
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-overlay",
|
|
93
|
+
className
|
|
94
|
+
),
|
|
95
|
+
children: [
|
|
96
|
+
!nestLabel ? tooltipLabel : null,
|
|
97
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-1.5", children: payload.map((item, index) => {
|
|
98
|
+
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
|
99
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
100
|
+
const indicatorColor = color || item.payload?.fill || item.color;
|
|
101
|
+
const rowKey = `${item.dataKey ?? item.name ?? index}`;
|
|
102
|
+
return /* @__PURE__ */ jsx(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
className: cn(
|
|
106
|
+
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
|
|
107
|
+
indicator === "dot" && "items-center"
|
|
108
|
+
),
|
|
109
|
+
children: formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, payload) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
110
|
+
itemConfig?.icon ? /* @__PURE__ */ jsx(itemConfig.icon, {}) : !hideIndicator && /* @__PURE__ */ jsx(
|
|
111
|
+
"div",
|
|
112
|
+
{
|
|
113
|
+
className: cn("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", {
|
|
114
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
115
|
+
"w-1": indicator === "line",
|
|
116
|
+
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
|
117
|
+
"my-0.5": nestLabel && indicator === "dashed"
|
|
118
|
+
}),
|
|
119
|
+
style: {
|
|
120
|
+
"--color-bg": indicatorColor,
|
|
121
|
+
"--color-border": indicatorColor
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
/* @__PURE__ */ jsxs(
|
|
126
|
+
"div",
|
|
127
|
+
{
|
|
128
|
+
className: cn("flex flex-1 justify-between leading-none", nestLabel ? "items-end" : "items-center"),
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-1.5", children: [
|
|
131
|
+
nestLabel ? tooltipLabel : null,
|
|
132
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: itemConfig?.label || item.name })
|
|
133
|
+
] }),
|
|
134
|
+
item.value !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-foreground font-mono font-medium tabular-nums", children: item.value.toLocaleString("vi-VN") })
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] })
|
|
139
|
+
},
|
|
140
|
+
rowKey
|
|
141
|
+
);
|
|
142
|
+
}) })
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const ChartLegend = RechartsPrimitive.Legend;
|
|
148
|
+
function ChartLegendContent({
|
|
149
|
+
className,
|
|
150
|
+
hideIcon = false,
|
|
151
|
+
payload,
|
|
152
|
+
verticalAlign = "bottom",
|
|
153
|
+
nameKey
|
|
154
|
+
}) {
|
|
155
|
+
const { config } = useChart();
|
|
156
|
+
if (!payload?.length) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center gap-4", verticalAlign === "top" ? "pb-3" : "pt-3", className), children: payload.map((item, index) => {
|
|
160
|
+
const key = `${nameKey || item.dataKey || "value"}`;
|
|
161
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
162
|
+
const rowKey = `${item.value ?? item.dataKey ?? index}`;
|
|
163
|
+
return /* @__PURE__ */ jsxs("div", { className: "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3", children: [
|
|
164
|
+
itemConfig?.icon && !hideIcon ? /* @__PURE__ */ jsx(itemConfig.icon, {}) : /* @__PURE__ */ jsx(
|
|
165
|
+
"div",
|
|
166
|
+
{
|
|
167
|
+
className: "h-2 w-2 shrink-0 rounded-[2px]",
|
|
168
|
+
style: {
|
|
169
|
+
backgroundColor: item.color
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
itemConfig?.label
|
|
174
|
+
] }, rowKey);
|
|
175
|
+
}) });
|
|
176
|
+
}
|
|
177
|
+
function getPayloadConfigFromPayload(config, payload, key) {
|
|
178
|
+
if (typeof payload !== "object" || payload === null) {
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
const payloadPayload = "payload" in payload && typeof payload.payload === "object" && payload.payload !== null ? payload.payload : void 0;
|
|
182
|
+
let configLabelKey = key;
|
|
183
|
+
if (key in payload && typeof payload[key] === "string") {
|
|
184
|
+
configLabelKey = payload[key];
|
|
185
|
+
} else if (payloadPayload && key in payloadPayload && typeof payloadPayload[key] === "string") {
|
|
186
|
+
configLabelKey = payloadPayload[key];
|
|
187
|
+
}
|
|
188
|
+
return configLabelKey in config ? config[configLabelKey] : config[key];
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
ChartContainer,
|
|
192
|
+
ChartLegend,
|
|
193
|
+
ChartLegendContent,
|
|
194
|
+
ChartStyle,
|
|
195
|
+
ChartTooltip,
|
|
196
|
+
ChartTooltipContent,
|
|
197
|
+
useChart
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/chart/chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as RechartsPrimitive from \"recharts\";\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Chart primitive của shadcn/ui (style new-york) — port nguyên trạng từ comitor-ds để\n * `shadcn add` vẫn dùng lại được. Đây là tầng \"thô\": bạn tự lắp recharts bên trong.\n * Muốn dựng nhanh 3 loại biểu đồ quen thuộc thì xem `comitor-chart.tsx`.\n *\n * Cơ chế màu (điểm hay nhất, và cũng dễ hiểu sai nhất):\n * `<ChartContainer>` render kèm `<ChartStyle>` — thẻ này sinh ra biến CSS `--color-<key>`\n * cho từng key trong `config`, giới hạn phạm vi bằng `[data-chart=<id>]`. Series recharts\n * chỉ việc trỏ `fill=\"var(--color-desktop)\"`. Nhờ vậy màu vẫn nằm trong TOKEN (không hex\n * rải trong JSX) và đổi được theo light/dark khi config khai `theme: { light, dark }`.\n */\n\n// Ánh xạ { TÊN_THEME: CSS_SELECTOR }. Chuỗi rỗng = light (không cần selector bọc ngoài).\nconst THEMES = { light: \"\", dark: \".dark\" } as const;\n\n/**\n * Khai báo hiển thị cho từng series: nhãn, icon và màu.\n * Dùng `color` cho màu dùng chung, hoặc `theme` khi cần màu khác nhau giữa light/dark.\n * Giá trị màu nên là token: `\"var(--chart-1)\"`, `\"var(--teal)\"`… — đừng ghi hex.\n */\nexport type ChartConfig = {\n [k in string]: {\n label?: React.ReactNode;\n icon?: React.ComponentType;\n } & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });\n};\n\ntype ChartContextProps = {\n config: ChartConfig;\n};\n\nconst ChartContext = React.createContext<ChartContextProps | null>(null);\n\n/** Đọc `config` của `<ChartContainer>` gần nhất. Ném lỗi nếu gọi ngoài container. */\nfunction useChart(): ChartContextProps {\n const context = React.useContext(ChartContext);\n\n if (!context) {\n throw new Error(\"useChart phải được gọi bên trong <ChartContainer />\");\n }\n\n return context;\n}\n\nexport type ChartContainerProps = React.ComponentProps<\"div\"> & {\n /** Khai báo nhãn/màu cho từng series — nguồn của biến `--color-<key>`. */\n config: ChartConfig;\n children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>[\"children\"];\n};\n\n/**\n * Khung biểu đồ: cấp context `config`, bơm biến màu, và bọc sẵn `ResponsiveContainer`.\n * Tỉ lệ mặc định `aspect-video`; muốn cao cố định thì truyền `className=\"h-64 aspect-auto\"`.\n */\nfunction ChartContainer({ id, className, children, config, ...props }: ChartContainerProps) {\n const uniqueId = React.useId();\n const chartId = `chart-${id || uniqueId.replace(/:/g, \"\")}`;\n\n return (\n <ChartContext.Provider value={{ config }}>\n <div\n data-slot=\"chart\"\n data-chart={chartId}\n className={cn(\n \"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden\",\n className\n )}\n {...props}\n >\n <ChartStyle id={chartId} config={config} />\n <RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n );\n}\n\nexport type ChartStyleProps = {\n /** Giá trị của thuộc tính `data-chart` trên container — dùng làm phạm vi cho biến màu. */\n id: string;\n config: ChartConfig;\n};\n\n/**\n * Sinh thẻ `<style>` chứa `--color-<key>` cho từng series, phạm vi `[data-chart=<id>]`.\n * `ChartContainer` đã render sẵn; chỉ export riêng cho ai tự dựng container.\n */\nconst ChartStyle = ({ id, config }: ChartStyleProps) => {\n const colorConfig = Object.entries(config).filter(([, itemConfig]) => itemConfig.theme || itemConfig.color);\n\n if (!colorConfig.length) {\n return null;\n }\n\n // Một khối CSS cho mỗi theme: light không cần selector bọc ngoài, dark bọc bằng `.dark`.\n const css = Object.entries(THEMES)\n .map(([theme, prefix]) => {\n const declarations = colorConfig\n .map(([key, itemConfig]) => {\n const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;\n return color ? ` --color-${key}: ${color};` : null;\n })\n .filter(Boolean)\n .join(\"\\n\");\n\n return `${prefix} [data-chart=${id}] {\\n${declarations}\\n}`;\n })\n .join(\"\\n\");\n\n // biome-ignore lint/security/noDangerouslySetInnerHtml: phải bơm nguyên khối CSS gồm nhiều selector (light + `.dark`) — React không có API nào khác cho việc này; nội dung ghép từ `config` do chính app khai, không phải dữ liệu người dùng nhập.\n return <style dangerouslySetInnerHTML={{ __html: css }} />;\n};\n\n/** `Tooltip` của recharts — đặt `content={<ChartTooltipContent />}` để có giao diện theo token. */\nconst ChartTooltip = RechartsPrimitive.Tooltip;\n\nexport type ChartTooltipContentProps = React.ComponentProps<typeof RechartsPrimitive.Tooltip> &\n React.ComponentProps<\"div\"> & {\n /** Ẩn dòng nhãn (trục X) ở đầu tooltip. */\n hideLabel?: boolean;\n /** Ẩn ô màu đứng trước tên series. */\n hideIndicator?: boolean;\n /** Kiểu ô màu: chấm tròn / vạch đứng / vạch đứt. */\n indicator?: \"line\" | \"dot\" | \"dashed\";\n /** Khoá dùng để tra `config` cho TÊN series (mặc định `name` rồi tới `dataKey`). */\n nameKey?: string;\n /** Khoá dùng để tra `config` cho NHÃN trục X. */\n labelKey?: string;\n };\n\n/** Nội dung tooltip theo design token Comitor: nền `bg-background`, viền `border-border/50`. */\nfunction ChartTooltipContent({\n active,\n payload,\n className,\n indicator = \"dot\",\n hideLabel = false,\n hideIndicator = false,\n label,\n labelFormatter,\n labelClassName,\n formatter,\n color,\n nameKey,\n labelKey\n}: ChartTooltipContentProps) {\n const { config } = useChart();\n\n const tooltipLabel = React.useMemo(() => {\n if (hideLabel || !payload?.length) {\n return null;\n }\n\n const [item] = payload;\n const key = `${labelKey || item?.dataKey || item?.name || \"value\"}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const value = !labelKey && typeof label === \"string\" ? config[label]?.label || label : itemConfig?.label;\n\n if (labelFormatter) {\n return <div className={cn(\"font-medium\", labelClassName)}>{labelFormatter(value, payload)}</div>;\n }\n\n if (!value) {\n return null;\n }\n\n return <div className={cn(\"font-medium\", labelClassName)}>{value}</div>;\n }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);\n\n if (!active || !payload?.length) {\n return null;\n }\n\n // Chỉ một series + không phải chấm tròn → nhét nhãn vào trong hàng cho gọn.\n const nestLabel = payload.length === 1 && indicator !== \"dot\";\n\n return (\n <div\n className={cn(\n \"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-overlay\",\n className\n )}\n >\n {!nestLabel ? tooltipLabel : null}\n <div className=\"grid gap-1.5\">\n {payload.map((item, index) => {\n const key = `${nameKey || item.name || item.dataKey || \"value\"}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const indicatorColor = color || item.payload?.fill || item.color;\n const rowKey = `${item.dataKey ?? item.name ?? index}`;\n\n return (\n <div\n key={rowKey}\n className={cn(\n \"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5\",\n indicator === \"dot\" && \"items-center\"\n )}\n >\n {formatter && item?.value !== undefined && item.name ? (\n formatter(item.value, item.name, item, index, payload)\n ) : (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n !hideIndicator && (\n <div\n className={cn(\"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)\", {\n \"h-2.5 w-2.5\": indicator === \"dot\",\n \"w-1\": indicator === \"line\",\n \"w-0 border-[1.5px] border-dashed bg-transparent\": indicator === \"dashed\",\n \"my-0.5\": nestLabel && indicator === \"dashed\"\n })}\n style={\n {\n \"--color-bg\": indicatorColor,\n \"--color-border\": indicatorColor\n } as React.CSSProperties\n }\n />\n )\n )}\n <div\n className={cn(\"flex flex-1 justify-between leading-none\", nestLabel ? \"items-end\" : \"items-center\")}\n >\n <div className=\"grid gap-1.5\">\n {nestLabel ? tooltipLabel : null}\n <span className=\"text-muted-foreground\">{itemConfig?.label || item.name}</span>\n </div>\n {item.value !== undefined && (\n <span className=\"text-foreground font-mono font-medium tabular-nums\">\n {item.value.toLocaleString(\"vi-VN\")}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n/** `Legend` của recharts — đặt `content={<ChartLegendContent />}` để lấy nhãn từ `config`. */\nconst ChartLegend = RechartsPrimitive.Legend;\n\nexport type ChartLegendContentProps = React.ComponentProps<\"div\"> &\n Pick<RechartsPrimitive.LegendProps, \"payload\" | \"verticalAlign\"> & {\n /** Ẩn icon riêng của series (nếu `config` có khai `icon`). */\n hideIcon?: boolean;\n /** Khoá dùng để tra `config` (mặc định `dataKey`). */\n nameKey?: string;\n };\n\n/** Chú giải nằm ngang, nhãn lấy từ `config` chứ không phải `dataKey` thô. */\nfunction ChartLegendContent({\n className,\n hideIcon = false,\n payload,\n verticalAlign = \"bottom\",\n nameKey\n}: ChartLegendContentProps) {\n const { config } = useChart();\n\n if (!payload?.length) {\n return null;\n }\n\n return (\n <div className={cn(\"flex items-center justify-center gap-4\", verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\", className)}>\n {payload.map((item, index) => {\n const key = `${nameKey || item.dataKey || \"value\"}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const rowKey = `${item.value ?? item.dataKey ?? index}`;\n\n return (\n <div key={rowKey} className=\"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3\">\n {itemConfig?.icon && !hideIcon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"h-2 w-2 shrink-0 rounded-[2px]\"\n style={{\n backgroundColor: item.color\n }}\n />\n )}\n {itemConfig?.label}\n </div>\n );\n })}\n </div>\n );\n}\n\n/**\n * Tra `config` từ một phần tử payload của recharts.\n * Payload có 2 tầng (`item` và `item.payload`) và khoá tra có thể nằm ở tầng nào cũng được —\n * hàm này thử cả hai trước khi rơi về chính `key`.\n */\nfunction getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {\n if (typeof payload !== \"object\" || payload === null) {\n return undefined;\n }\n\n const payloadPayload =\n \"payload\" in payload && typeof payload.payload === \"object\" && payload.payload !== null\n ? payload.payload\n : undefined;\n\n let configLabelKey: string = key;\n\n if (key in payload && typeof payload[key as keyof typeof payload] === \"string\") {\n configLabelKey = payload[key as keyof typeof payload] as string;\n } else if (\n payloadPayload &&\n key in payloadPayload &&\n typeof payloadPayload[key as keyof typeof payloadPayload] === \"string\"\n ) {\n configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;\n }\n\n return configLabelKey in config ? config[configLabelKey] : config[key];\n}\n\nexport { ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, useChart };\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TooltipProps } from 'recharts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Biểu đồ "lắp sẵn" của Comitor — dựng nhanh line/bar/area chỉ với `data` + `series`.
|
|
6
|
+
* Khác `chart.tsx` (primitive shadcn thô, tự lắp recharts), file này là tầng tiện dụng:
|
|
7
|
+
* đã chọn sẵn trục, lưới, tooltip và bảng màu theo token.
|
|
8
|
+
*
|
|
9
|
+
* ⚠ Tên export ĐÃ ĐỔI so với comitor-ds vì hai file đụng nhau trong cùng entry `/chart`:
|
|
10
|
+
* `ChartContainer` → `ComitorChartContainer`, `ChartTooltip` → `ComitorChartTooltip`.
|
|
11
|
+
* Tên trần `ChartContainer`/`ChartTooltip` để dành cho primitive shadcn (giữ tương thích
|
|
12
|
+
* `shadcn add`). Code mẫu cũ trong trang docs comitor-ds phải sửa theo.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Bảng màu series mặc định — trỏ vào `--chart-1…5` trong `styles.css`.
|
|
16
|
+
*
|
|
17
|
+
* Vì sao không lấy thẳng `var(--gold-300)`, `var(--teal)`… như comitor-ds: bảng màu biểu đồ
|
|
18
|
+
* cần đổi ĐỘC LẬP với brand (app đổi `--chart-*` ở `<body>` là xong), và `--chart-3` có bản
|
|
19
|
+
* dark riêng (navy nguyên bản chìm vào nền tối).
|
|
20
|
+
*
|
|
21
|
+
* Dùng `var(--chart-N)` chứ KHÔNG dùng `var(--color-chart-N)`: bản `--chart-*` được khai
|
|
22
|
+
* tường minh trong `:root` của `styles.css` nên chắc chắn tồn tại, còn `--color-chart-*` là
|
|
23
|
+
* biến do `@theme inline` của Tailwind sinh ra.
|
|
24
|
+
*/
|
|
25
|
+
declare const CHART_SERIES_COLORS: readonly ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
26
|
+
/**
|
|
27
|
+
* Màu gọi theo tên — dùng khi muốn chỉ định màu cho một series cụ thể
|
|
28
|
+
* (`series: [{ key: "loi", color: CHART_COLORS.red }]`).
|
|
29
|
+
*
|
|
30
|
+
* ⚠ Khoá `gray` của comitor-ds đổi thành `ash`: thang xám của package là `--ash-*`,
|
|
31
|
+
* `gray-*` để nguyên cho Tailwind (xem chú thích thang màu trong `styles.css`).
|
|
32
|
+
*/
|
|
33
|
+
declare const CHART_COLORS: {
|
|
34
|
+
readonly gold: "var(--chart-1)";
|
|
35
|
+
readonly goldLight: "var(--gold-100)";
|
|
36
|
+
readonly teal: "var(--chart-2)";
|
|
37
|
+
readonly navy: "var(--chart-3)";
|
|
38
|
+
readonly green: "var(--chart-4)";
|
|
39
|
+
readonly red: "var(--chart-5)";
|
|
40
|
+
readonly ash: "var(--ash-300)";
|
|
41
|
+
};
|
|
42
|
+
type ChartColorName = keyof typeof CHART_COLORS;
|
|
43
|
+
interface ComitorChartTooltipProps {
|
|
44
|
+
/** Do recharts bơm vào — đừng truyền tay. */
|
|
45
|
+
active?: boolean;
|
|
46
|
+
/** Do recharts bơm vào — đừng truyền tay. */
|
|
47
|
+
payload?: TooltipProps<number, string>["payload"];
|
|
48
|
+
/** Do recharts bơm vào — đừng truyền tay. */
|
|
49
|
+
label?: string;
|
|
50
|
+
/** Định dạng nhãn trục X (ví dụ đổi "2026-01" → "Th01/2026"). */
|
|
51
|
+
labelFormatter?: (label: string) => string;
|
|
52
|
+
/** Định dạng giá trị (ví dụ thêm "₫", "%"). Mặc định: `toLocaleString("vi-VN")`. */
|
|
53
|
+
valueFormatter?: (value: number) => string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Tooltip theo token Comitor. Truyền vào recharts dưới dạng element:
|
|
57
|
+
* `<Tooltip content={<ComitorChartTooltip valueFormatter={f} />} />`.
|
|
58
|
+
*/
|
|
59
|
+
declare function ComitorChartTooltip({ active, payload, label, labelFormatter, valueFormatter }: ComitorChartTooltipProps): React.JSX.Element | null;
|
|
60
|
+
interface ComitorChartContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
61
|
+
/** Chiều cao khung. Số → px. Mặc định 300. */
|
|
62
|
+
height?: number | string;
|
|
63
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Khung `ResponsiveContainer` cao cố định. Khác `ChartContainer` của shadcn ở chỗ:
|
|
67
|
+
* không cấp context `config`, không sinh biến `--color-<key>` — chỉ lo kích thước.
|
|
68
|
+
*/
|
|
69
|
+
declare function ComitorChartContainer({ height, className, children, ref, ...props }: ComitorChartContainerProps): React.JSX.Element;
|
|
70
|
+
/** Một series (một đường/cột/vùng) trong biểu đồ. */
|
|
71
|
+
interface DataSeries {
|
|
72
|
+
/** Tên trường trong `data`. */
|
|
73
|
+
key: string;
|
|
74
|
+
/** Nhãn hiển thị ở tooltip/chú giải. Mặc định lấy `key`. */
|
|
75
|
+
name?: string;
|
|
76
|
+
/** Màu — nên lấy từ `CHART_COLORS` hoặc `var(--chart-N)`, đừng ghi hex. */
|
|
77
|
+
color?: string;
|
|
78
|
+
}
|
|
79
|
+
interface BaseChartProps {
|
|
80
|
+
data: Record<string, unknown>[];
|
|
81
|
+
/** Tên trường dùng cho trục X. */
|
|
82
|
+
xAxisKey: string;
|
|
83
|
+
height?: number | string;
|
|
84
|
+
className?: string;
|
|
85
|
+
showGrid?: boolean;
|
|
86
|
+
showLegend?: boolean;
|
|
87
|
+
valueFormatter?: (value: number) => string;
|
|
88
|
+
}
|
|
89
|
+
interface ComitorLineChartProps extends BaseChartProps {
|
|
90
|
+
series: DataSeries[];
|
|
91
|
+
}
|
|
92
|
+
/** Biểu đồ đường — dùng cho xu hướng theo thời gian. */
|
|
93
|
+
declare function ComitorLineChart({ data, xAxisKey, series, height, className, showGrid, showLegend, valueFormatter }: ComitorLineChartProps): React.JSX.Element;
|
|
94
|
+
interface ComitorBarChartProps extends BaseChartProps {
|
|
95
|
+
series: DataSeries[];
|
|
96
|
+
/** Chồng các series lên nhau thay vì xếp cạnh. */
|
|
97
|
+
stacked?: boolean;
|
|
98
|
+
}
|
|
99
|
+
/** Biểu đồ cột — dùng cho so sánh giữa các hạng mục. Lưới chỉ kẻ ngang. */
|
|
100
|
+
declare function ComitorBarChart({ data, xAxisKey, series, height, className, showGrid, showLegend, stacked, valueFormatter }: ComitorBarChartProps): React.JSX.Element;
|
|
101
|
+
interface ComitorAreaChartProps extends BaseChartProps {
|
|
102
|
+
series: DataSeries[];
|
|
103
|
+
/** Chồng các series lên nhau (tổng tích luỹ). */
|
|
104
|
+
stacked?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** Biểu đồ vùng — xu hướng kèm cảm giác khối lượng; `fillOpacity` 0.15 để chồng lớp còn đọc được. */
|
|
107
|
+
declare function ComitorAreaChart({ data, xAxisKey, series, height, className, showGrid, showLegend, stacked, valueFormatter }: ComitorAreaChartProps): React.JSX.Element;
|
|
108
|
+
|
|
109
|
+
export { type BaseChartProps, CHART_COLORS, CHART_SERIES_COLORS, type ChartColorName, ComitorAreaChart, type ComitorAreaChartProps, ComitorBarChart, type ComitorBarChartProps, ComitorChartContainer, type ComitorChartContainerProps, ComitorChartTooltip, type ComitorChartTooltipProps, ComitorLineChart, type ComitorLineChartProps, type DataSeries };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
Area,
|
|
5
|
+
AreaChart,
|
|
6
|
+
Bar,
|
|
7
|
+
BarChart,
|
|
8
|
+
CartesianGrid,
|
|
9
|
+
Legend,
|
|
10
|
+
Line,
|
|
11
|
+
LineChart,
|
|
12
|
+
ResponsiveContainer,
|
|
13
|
+
Tooltip,
|
|
14
|
+
XAxis,
|
|
15
|
+
YAxis
|
|
16
|
+
} from "recharts";
|
|
17
|
+
import { cn } from "../lib/cn.js";
|
|
18
|
+
const CHART_SERIES_COLORS = [
|
|
19
|
+
"var(--chart-1)",
|
|
20
|
+
"var(--chart-2)",
|
|
21
|
+
"var(--chart-3)",
|
|
22
|
+
"var(--chart-4)",
|
|
23
|
+
"var(--chart-5)"
|
|
24
|
+
];
|
|
25
|
+
const CHART_COLORS = {
|
|
26
|
+
gold: "var(--chart-1)",
|
|
27
|
+
goldLight: "var(--gold-100)",
|
|
28
|
+
teal: "var(--chart-2)",
|
|
29
|
+
navy: "var(--chart-3)",
|
|
30
|
+
green: "var(--chart-4)",
|
|
31
|
+
red: "var(--chart-5)",
|
|
32
|
+
ash: "var(--ash-300)"
|
|
33
|
+
};
|
|
34
|
+
function seriesColor(series, index) {
|
|
35
|
+
return series.color ?? CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length] ?? CHART_SERIES_COLORS[0];
|
|
36
|
+
}
|
|
37
|
+
function ComitorChartTooltip({
|
|
38
|
+
active,
|
|
39
|
+
payload,
|
|
40
|
+
label,
|
|
41
|
+
labelFormatter,
|
|
42
|
+
valueFormatter
|
|
43
|
+
}) {
|
|
44
|
+
if (!active || !payload?.length) return null;
|
|
45
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "comitor-chart-tooltip", className: "rounded-lg border border-border bg-card px-3 py-2 shadow-card", children: [
|
|
46
|
+
label && /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-foreground mb-1.5", children: labelFormatter ? labelFormatter(label) : label }),
|
|
47
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: payload.map((entry, i) => {
|
|
48
|
+
const value = entry.value;
|
|
49
|
+
const display = value === void 0 ? "\u2014" : valueFormatter?.(value) ?? value.toLocaleString("vi-VN");
|
|
50
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
51
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full shrink-0", style: { backgroundColor: entry.color } }),
|
|
52
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
53
|
+
entry.name,
|
|
54
|
+
":"
|
|
55
|
+
] }),
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: display })
|
|
57
|
+
] }, `${entry.dataKey ?? entry.name ?? i}`);
|
|
58
|
+
}) })
|
|
59
|
+
] });
|
|
60
|
+
}
|
|
61
|
+
function ComitorChartContainer({
|
|
62
|
+
height = 300,
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
ref,
|
|
66
|
+
...props
|
|
67
|
+
}) {
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
ref,
|
|
72
|
+
"data-slot": "comitor-chart",
|
|
73
|
+
className: cn("w-full", className),
|
|
74
|
+
style: { height: typeof height === "number" ? `${height}px` : height },
|
|
75
|
+
...props,
|
|
76
|
+
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children })
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const AXIS_TICK = { fontSize: 11, fill: "var(--muted-foreground)" };
|
|
81
|
+
const AXIS_LINE = { stroke: "var(--border)" };
|
|
82
|
+
const CHART_MARGIN = { top: 8, right: 8, left: 0, bottom: 0 };
|
|
83
|
+
function ComitorLineChart({
|
|
84
|
+
data,
|
|
85
|
+
xAxisKey,
|
|
86
|
+
series,
|
|
87
|
+
height = 300,
|
|
88
|
+
className,
|
|
89
|
+
showGrid = true,
|
|
90
|
+
showLegend = false,
|
|
91
|
+
valueFormatter
|
|
92
|
+
}) {
|
|
93
|
+
return /* @__PURE__ */ jsx(ComitorChartContainer, { height, className, children: /* @__PURE__ */ jsxs(LineChart, { data, margin: CHART_MARGIN, children: [
|
|
94
|
+
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--border)" }),
|
|
95
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xAxisKey, tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
96
|
+
/* @__PURE__ */ jsx(YAxis, { tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
97
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ComitorChartTooltip, { valueFormatter }) }),
|
|
98
|
+
showLegend && /* @__PURE__ */ jsx(Legend, {}),
|
|
99
|
+
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
100
|
+
Line,
|
|
101
|
+
{
|
|
102
|
+
type: "monotone",
|
|
103
|
+
dataKey: s.key,
|
|
104
|
+
name: s.name || s.key,
|
|
105
|
+
stroke: seriesColor(s, i),
|
|
106
|
+
strokeWidth: 2,
|
|
107
|
+
dot: false,
|
|
108
|
+
activeDot: { r: 4, strokeWidth: 0 }
|
|
109
|
+
},
|
|
110
|
+
s.key
|
|
111
|
+
))
|
|
112
|
+
] }) });
|
|
113
|
+
}
|
|
114
|
+
function ComitorBarChart({
|
|
115
|
+
data,
|
|
116
|
+
xAxisKey,
|
|
117
|
+
series,
|
|
118
|
+
height = 300,
|
|
119
|
+
className,
|
|
120
|
+
showGrid = true,
|
|
121
|
+
showLegend = false,
|
|
122
|
+
stacked = false,
|
|
123
|
+
valueFormatter
|
|
124
|
+
}) {
|
|
125
|
+
return /* @__PURE__ */ jsx(ComitorChartContainer, { height, className, children: /* @__PURE__ */ jsxs(BarChart, { data, margin: CHART_MARGIN, children: [
|
|
126
|
+
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--border)", vertical: false }),
|
|
127
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xAxisKey, tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
128
|
+
/* @__PURE__ */ jsx(YAxis, { tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
129
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ComitorChartTooltip, { valueFormatter }) }),
|
|
130
|
+
showLegend && /* @__PURE__ */ jsx(Legend, {}),
|
|
131
|
+
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
132
|
+
Bar,
|
|
133
|
+
{
|
|
134
|
+
dataKey: s.key,
|
|
135
|
+
name: s.name || s.key,
|
|
136
|
+
fill: seriesColor(s, i),
|
|
137
|
+
radius: [4, 4, 0, 0],
|
|
138
|
+
stackId: stacked ? "stack" : void 0
|
|
139
|
+
},
|
|
140
|
+
s.key
|
|
141
|
+
))
|
|
142
|
+
] }) });
|
|
143
|
+
}
|
|
144
|
+
function ComitorAreaChart({
|
|
145
|
+
data,
|
|
146
|
+
xAxisKey,
|
|
147
|
+
series,
|
|
148
|
+
height = 300,
|
|
149
|
+
className,
|
|
150
|
+
showGrid = true,
|
|
151
|
+
showLegend = false,
|
|
152
|
+
stacked = false,
|
|
153
|
+
valueFormatter
|
|
154
|
+
}) {
|
|
155
|
+
return /* @__PURE__ */ jsx(ComitorChartContainer, { height, className, children: /* @__PURE__ */ jsxs(AreaChart, { data, margin: CHART_MARGIN, children: [
|
|
156
|
+
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--border)" }),
|
|
157
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xAxisKey, tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
158
|
+
/* @__PURE__ */ jsx(YAxis, { tick: AXIS_TICK, axisLine: AXIS_LINE, tickLine: AXIS_LINE }),
|
|
159
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ComitorChartTooltip, { valueFormatter }) }),
|
|
160
|
+
showLegend && /* @__PURE__ */ jsx(Legend, {}),
|
|
161
|
+
series.map((s, i) => {
|
|
162
|
+
const color = seriesColor(s, i);
|
|
163
|
+
return /* @__PURE__ */ jsx(
|
|
164
|
+
Area,
|
|
165
|
+
{
|
|
166
|
+
type: "monotone",
|
|
167
|
+
dataKey: s.key,
|
|
168
|
+
name: s.name || s.key,
|
|
169
|
+
stroke: color,
|
|
170
|
+
fill: color,
|
|
171
|
+
fillOpacity: 0.15,
|
|
172
|
+
strokeWidth: 2,
|
|
173
|
+
stackId: stacked ? "stack" : void 0
|
|
174
|
+
},
|
|
175
|
+
s.key
|
|
176
|
+
);
|
|
177
|
+
})
|
|
178
|
+
] }) });
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
CHART_COLORS,
|
|
182
|
+
CHART_SERIES_COLORS,
|
|
183
|
+
ComitorAreaChart,
|
|
184
|
+
ComitorBarChart,
|
|
185
|
+
ComitorChartContainer,
|
|
186
|
+
ComitorChartTooltip,
|
|
187
|
+
ComitorLineChart
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=comitor-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/chart/comitor-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Legend,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n type TooltipProps,\n XAxis,\n YAxis\n} from \"recharts\";\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Biểu đồ \"lắp sẵn\" của Comitor — dựng nhanh line/bar/area chỉ với `data` + `series`.\n * Khác `chart.tsx` (primitive shadcn thô, tự lắp recharts), file này là tầng tiện dụng:\n * đã chọn sẵn trục, lưới, tooltip và bảng màu theo token.\n *\n * ⚠ Tên export ĐÃ ĐỔI so với comitor-ds vì hai file đụng nhau trong cùng entry `/chart`:\n * `ChartContainer` → `ComitorChartContainer`, `ChartTooltip` → `ComitorChartTooltip`.\n * Tên trần `ChartContainer`/`ChartTooltip` để dành cho primitive shadcn (giữ tương thích\n * `shadcn add`). Code mẫu cũ trong trang docs comitor-ds phải sửa theo.\n */\n\n/**\n * Bảng màu series mặc định — trỏ vào `--chart-1…5` trong `styles.css`.\n *\n * Vì sao không lấy thẳng `var(--gold-300)`, `var(--teal)`… như comitor-ds: bảng màu biểu đồ\n * cần đổi ĐỘC LẬP với brand (app đổi `--chart-*` ở `<body>` là xong), và `--chart-3` có bản\n * dark riêng (navy nguyên bản chìm vào nền tối).\n *\n * Dùng `var(--chart-N)` chứ KHÔNG dùng `var(--color-chart-N)`: bản `--chart-*` được khai\n * tường minh trong `:root` của `styles.css` nên chắc chắn tồn tại, còn `--color-chart-*` là\n * biến do `@theme inline` của Tailwind sinh ra.\n */\nexport const CHART_SERIES_COLORS = [\n \"var(--chart-1)\",\n \"var(--chart-2)\",\n \"var(--chart-3)\",\n \"var(--chart-4)\",\n \"var(--chart-5)\"\n] as const;\n\n/**\n * Màu gọi theo tên — dùng khi muốn chỉ định màu cho một series cụ thể\n * (`series: [{ key: \"loi\", color: CHART_COLORS.red }]`).\n *\n * ⚠ Khoá `gray` của comitor-ds đổi thành `ash`: thang xám của package là `--ash-*`,\n * `gray-*` để nguyên cho Tailwind (xem chú thích thang màu trong `styles.css`).\n */\nexport const CHART_COLORS = {\n gold: \"var(--chart-1)\",\n goldLight: \"var(--gold-100)\",\n teal: \"var(--chart-2)\",\n navy: \"var(--chart-3)\",\n green: \"var(--chart-4)\",\n red: \"var(--chart-5)\",\n ash: \"var(--ash-300)\"\n} as const;\n\nexport type ChartColorName = keyof typeof CHART_COLORS;\n\n/** Lấy màu cho series thứ `index`: ưu tiên màu tự khai, không có thì xoay vòng bảng mặc định. */\nfunction seriesColor(series: DataSeries, index: number): string {\n return series.color ?? CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length] ?? CHART_SERIES_COLORS[0];\n}\n\n// ─── Tooltip ────────────────────────────────────────────────────────────────\n\nexport interface ComitorChartTooltipProps {\n /** Do recharts bơm vào — đừng truyền tay. */\n active?: boolean;\n /** Do recharts bơm vào — đừng truyền tay. */\n payload?: TooltipProps<number, string>[\"payload\"];\n /** Do recharts bơm vào — đừng truyền tay. */\n label?: string;\n /** Định dạng nhãn trục X (ví dụ đổi \"2026-01\" → \"Th01/2026\"). */\n labelFormatter?: (label: string) => string;\n /** Định dạng giá trị (ví dụ thêm \"₫\", \"%\"). Mặc định: `toLocaleString(\"vi-VN\")`. */\n valueFormatter?: (value: number) => string;\n}\n\n/**\n * Tooltip theo token Comitor. Truyền vào recharts dưới dạng element:\n * `<Tooltip content={<ComitorChartTooltip valueFormatter={f} />} />`.\n */\nexport function ComitorChartTooltip({\n active,\n payload,\n label,\n labelFormatter,\n valueFormatter\n}: ComitorChartTooltipProps) {\n if (!active || !payload?.length) return null;\n\n return (\n <div data-slot=\"comitor-chart-tooltip\" className=\"rounded-lg border border-border bg-card px-3 py-2 shadow-card\">\n {label && (\n <p className=\"text-xs font-medium text-foreground mb-1.5\">{labelFormatter ? labelFormatter(label) : label}</p>\n )}\n <div className=\"space-y-1\">\n {payload.map((entry, i) => {\n const value = entry.value;\n const display = value === undefined ? \"—\" : (valueFormatter?.(value) ?? value.toLocaleString(\"vi-VN\"));\n\n return (\n <div key={`${entry.dataKey ?? entry.name ?? i}`} className=\"flex items-center gap-2 text-xs\">\n <span className=\"h-2 w-2 rounded-full shrink-0\" style={{ backgroundColor: entry.color }} />\n <span className=\"text-muted-foreground\">{entry.name}:</span>\n <span className=\"font-medium text-foreground\">{display}</span>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\n// ─── Khung ──────────────────────────────────────────────────────────────────\n\nexport interface ComitorChartContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Chiều cao khung. Số → px. Mặc định 300. */\n height?: number | string;\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * Khung `ResponsiveContainer` cao cố định. Khác `ChartContainer` của shadcn ở chỗ:\n * không cấp context `config`, không sinh biến `--color-<key>` — chỉ lo kích thước.\n */\nexport function ComitorChartContainer({\n height = 300,\n className,\n children,\n ref,\n ...props\n}: ComitorChartContainerProps) {\n return (\n <div\n ref={ref}\n data-slot=\"comitor-chart\"\n className={cn(\"w-full\", className)}\n style={{ height: typeof height === \"number\" ? `${height}px` : height }}\n {...props}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children as React.ReactElement}\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ─── Kiểu dùng chung ────────────────────────────────────────────────────────\n\n/** Một series (một đường/cột/vùng) trong biểu đồ. */\nexport interface DataSeries {\n /** Tên trường trong `data`. */\n key: string;\n /** Nhãn hiển thị ở tooltip/chú giải. Mặc định lấy `key`. */\n name?: string;\n /** Màu — nên lấy từ `CHART_COLORS` hoặc `var(--chart-N)`, đừng ghi hex. */\n color?: string;\n}\n\nexport interface BaseChartProps {\n data: Record<string, unknown>[];\n /** Tên trường dùng cho trục X. */\n xAxisKey: string;\n height?: number | string;\n className?: string;\n showGrid?: boolean;\n showLegend?: boolean;\n valueFormatter?: (value: number) => string;\n}\n\n/** Style dùng chung cho tick của cả hai trục — gom lại để 3 biểu đồ không lệch nhau. */\nconst AXIS_TICK = { fontSize: 11, fill: \"var(--muted-foreground)\" } as const;\nconst AXIS_LINE = { stroke: \"var(--border)\" } as const;\nconst CHART_MARGIN = { top: 8, right: 8, left: 0, bottom: 0 } as const;\n\n// ─── Line ───────────────────────────────────────────────────────────────────\n\nexport interface ComitorLineChartProps extends BaseChartProps {\n series: DataSeries[];\n}\n\n/** Biểu đồ đường — dùng cho xu hướng theo thời gian. */\nexport function ComitorLineChart({\n data,\n xAxisKey,\n series,\n height = 300,\n className,\n showGrid = true,\n showLegend = false,\n valueFormatter\n}: ComitorLineChartProps) {\n return (\n <ComitorChartContainer height={height} className={className}>\n <LineChart data={data} margin={CHART_MARGIN}>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--border)\" />}\n <XAxis dataKey={xAxisKey} tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <YAxis tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <Tooltip content={<ComitorChartTooltip valueFormatter={valueFormatter} />} />\n {showLegend && <Legend />}\n {series.map((s, i) => (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.name || s.key}\n stroke={seriesColor(s, i)}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 4, strokeWidth: 0 }}\n />\n ))}\n </LineChart>\n </ComitorChartContainer>\n );\n}\n\n// ─── Bar ────────────────────────────────────────────────────────────────────\n\nexport interface ComitorBarChartProps extends BaseChartProps {\n series: DataSeries[];\n /** Chồng các series lên nhau thay vì xếp cạnh. */\n stacked?: boolean;\n}\n\n/** Biểu đồ cột — dùng cho so sánh giữa các hạng mục. Lưới chỉ kẻ ngang. */\nexport function ComitorBarChart({\n data,\n xAxisKey,\n series,\n height = 300,\n className,\n showGrid = true,\n showLegend = false,\n stacked = false,\n valueFormatter\n}: ComitorBarChartProps) {\n return (\n <ComitorChartContainer height={height} className={className}>\n <BarChart data={data} margin={CHART_MARGIN}>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--border)\" vertical={false} />}\n <XAxis dataKey={xAxisKey} tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <YAxis tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <Tooltip content={<ComitorChartTooltip valueFormatter={valueFormatter} />} />\n {showLegend && <Legend />}\n {series.map((s, i) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.name || s.key}\n fill={seriesColor(s, i)}\n radius={[4, 4, 0, 0]}\n stackId={stacked ? \"stack\" : undefined}\n />\n ))}\n </BarChart>\n </ComitorChartContainer>\n );\n}\n\n// ─── Area ───────────────────────────────────────────────────────────────────\n\nexport interface ComitorAreaChartProps extends BaseChartProps {\n series: DataSeries[];\n /** Chồng các series lên nhau (tổng tích luỹ). */\n stacked?: boolean;\n}\n\n/** Biểu đồ vùng — xu hướng kèm cảm giác khối lượng; `fillOpacity` 0.15 để chồng lớp còn đọc được. */\nexport function ComitorAreaChart({\n data,\n xAxisKey,\n series,\n height = 300,\n className,\n showGrid = true,\n showLegend = false,\n stacked = false,\n valueFormatter\n}: ComitorAreaChartProps) {\n return (\n <ComitorChartContainer height={height} className={className}>\n <AreaChart data={data} margin={CHART_MARGIN}>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--border)\" />}\n <XAxis dataKey={xAxisKey} tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <YAxis tick={AXIS_TICK} axisLine={AXIS_LINE} tickLine={AXIS_LINE} />\n <Tooltip content={<ComitorChartTooltip valueFormatter={valueFormatter} />} />\n {showLegend && <Legend />}\n {series.map((s, i) => {\n const color = seriesColor(s, i);\n return (\n <Area\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.name || s.key}\n stroke={color}\n fill={color}\n fillOpacity={0.15}\n strokeWidth={2}\n stackId={stacked ? \"stack\" : undefined}\n />\n );\n })}\n </AreaChart>\n </ComitorChartContainer>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ChartConfig, ChartContainer, ChartContainerProps, ChartLegend, ChartLegendContent, ChartLegendContentProps, ChartStyle, ChartStyleProps, ChartTooltip, ChartTooltipContent, ChartTooltipContentProps, useChart } from './chart.js';
|
|
2
|
+
export { BaseChartProps, CHART_COLORS, CHART_SERIES_COLORS, ChartColorName, ComitorAreaChart, ComitorAreaChartProps, ComitorBarChart, ComitorBarChartProps, ComitorChartContainer, ComitorChartContainerProps, ComitorChartTooltip, ComitorChartTooltipProps, ComitorLineChart, ComitorLineChartProps, DataSeries } from './comitor-chart.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import 'recharts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/chart/index.ts"],"sourcesContent":["/**\n * Entry \"@comitor/ui/chart\" — Chart* (shadcn) + ComitorChart* + CHART_COLORS.\n *\n * Tách riêng vì `recharts` nặng (~100KB) và là peer OPTIONAL; đa số trang không vẽ biểu đồ.\n *\n * ⚠ Va tên đã xử lý ở tầng file: `chart.tsx` giữ tên trần `ChartContainer`/`ChartTooltip`\n * (API shadcn); bản tiện dụng của comitor-ds mang tiền tố `ComitorChartContainer`/\n * `ComitorChartTooltip`. Đừng đặt lại tên trần trong `comitor-chart.tsx`, barrel này\n * dùng `export *` nên sẽ vỡ ngay lúc build.\n */\nexport * from \"./chart.js\";\nexport * from \"./comitor-chart.js\";\n"],"mappings":"AAUA,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import { LetterAvatarSize } from './letter-avatar.js';
|
|
4
|
+
|
|
5
|
+
interface AvatarGroupItem {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
src?: string | null;
|
|
9
|
+
/** Dòng phụ trong tooltip (vd email, vai trò) */
|
|
10
|
+
caption?: string;
|
|
11
|
+
}
|
|
12
|
+
interface AvatarGroupProps extends Omit<ComponentProps<"div">, "children"> {
|
|
13
|
+
items: AvatarGroupItem[];
|
|
14
|
+
/** Số avatar hiện tối đa; phần dư gộp vào ô "+k" (mặc định 4) */
|
|
15
|
+
max?: number;
|
|
16
|
+
size?: LetterAvatarSize;
|
|
17
|
+
/** Bấm vào ô "+k" — vd mở danh sách đầy đủ */
|
|
18
|
+
onOverflowClick?: () => void;
|
|
19
|
+
/** Nhãn của cả nhóm cho trình đọc màn hình (mặc định "Thành viên") */
|
|
20
|
+
label?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Chồng avatar + ô "+k" — dùng cho người thực hiện (Tasks), người tham gia (Chat), người theo dõi (CRM).
|
|
24
|
+
*
|
|
25
|
+
* Tooltip hiện tên đầy đủ vì avatar chồng nhau thì initials bị che một phần; không có tooltip
|
|
26
|
+
* thì nhóm avatar chỉ là trang trí, người dùng không biết ai đang ở trong việc.
|
|
27
|
+
*
|
|
28
|
+
* Mỗi avatar là một nút focusable (Tab tới được) và mang tên thành viên cho trình đọc màn hình
|
|
29
|
+
* — xem chú thích ở trigger bên dưới.
|
|
30
|
+
*/
|
|
31
|
+
declare function AvatarGroup({ items, max, size, onOverflowClick, label, className, ...props }: AvatarGroupProps): React.JSX.Element;
|
|
32
|
+
|
|
33
|
+
export { AvatarGroup, type AvatarGroupItem, type AvatarGroupProps };
|