@olympusoss/canvas 2.20.2 → 3.2.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/README.md +73 -35
- package/package.json +46 -177
- package/src/cn.ts +3 -0
- package/src/index.ts +12 -603
- package/src/theme.ts +62 -0
- package/src/tokens.ts +11 -0
- package/styles/atoms/avatar.css +22 -0
- package/styles/atoms/badge.css +83 -0
- package/styles/atoms/breadcrumb.css +35 -0
- package/styles/atoms/button-group.css +23 -0
- package/styles/atoms/button.css +107 -0
- package/styles/atoms/checkbox.css +55 -0
- package/styles/atoms/combobox.css +75 -0
- package/styles/atoms/dropdown.css +54 -0
- package/styles/atoms/icon.css +8 -0
- package/styles/atoms/input-group.css +45 -0
- package/styles/atoms/input.css +56 -0
- package/styles/atoms/kbd.css +15 -0
- package/styles/atoms/pagination.css +48 -0
- package/styles/atoms/popover.css +14 -0
- package/styles/atoms/radio.css +28 -0
- package/styles/atoms/select.css +57 -0
- package/styles/atoms/separator.css +32 -0
- package/styles/atoms/skeleton.css +32 -0
- package/styles/atoms/spinner.css +26 -0
- package/styles/atoms/switch.css +45 -0
- package/styles/atoms/textarea.css +31 -0
- package/styles/atoms/tooltip.css +53 -0
- package/styles/atoms/typography.css +105 -0
- package/styles/base.css +17 -0
- package/styles/canvas.css +77 -52
- package/styles/molecules/alert.css +66 -0
- package/styles/molecules/card.css +58 -0
- package/styles/molecules/code-block.css +18 -0
- package/styles/molecules/empty-state.css +17 -0
- package/styles/molecules/field.css +27 -0
- package/styles/molecules/form.css +27 -0
- package/styles/molecules/page-header.css +52 -0
- package/styles/molecules/section-card.css +49 -0
- package/styles/molecules/stat-card.css +71 -0
- package/styles/molecules/toast.css +95 -0
- package/styles/organisms/app-shell.css +46 -0
- package/styles/organisms/calendar.css +73 -0
- package/styles/organisms/command.css +94 -0
- package/styles/organisms/data-table.css +142 -0
- package/styles/organisms/dialog.css +72 -0
- package/styles/organisms/filter-panel.css +58 -0
- package/styles/organisms/row-menu.css +69 -0
- package/styles/organisms/sheet.css +70 -0
- package/styles/organisms/sidebar.css +146 -0
- package/styles/organisms/stepper.css +63 -0
- package/styles/organisms/tabs.css +40 -0
- package/styles/organisms/topbar.css +24 -0
- package/styles/patterns/backdrops.css +35 -0
- package/styles/patterns/density.css +66 -0
- package/styles/patterns/focus.css +22 -0
- package/styles/patterns/glass.css +85 -0
- package/styles/patterns/high-contrast.css +70 -0
- package/styles/patterns/reduced-motion.css +12 -0
- package/styles/patterns/scrollbar.css +10 -0
- package/styles/reset.css +89 -0
- package/styles/tokens/colors.css +106 -0
- package/styles/tokens/motion.css +33 -0
- package/styles/tokens/radius.css +10 -0
- package/styles/tokens/shadows.css +35 -0
- package/styles/tokens/spacing.css +19 -0
- package/styles/tokens/typography.css +6 -0
- package/styles/tokens/z-index.css +12 -0
- package/styles/utilities/display.css +66 -0
- package/styles/utilities/flexbox.css +240 -0
- package/styles/utilities/gap.css +288 -0
- package/styles/utilities/grid.css +138 -0
- package/styles/utilities/position.css +78 -0
- package/styles/utilities/sizing.css +138 -0
- package/tsconfig.json +20 -21
- package/src/components/atoms/README.md +0 -11
- package/src/components/atoms/aspect-ratio.tsx +0 -32
- package/src/components/atoms/avatar.tsx +0 -98
- package/src/components/atoms/badge.tsx +0 -44
- package/src/components/atoms/brand-mark.tsx +0 -74
- package/src/components/atoms/button.tsx +0 -105
- package/src/components/atoms/checkbox.tsx +0 -63
- package/src/components/atoms/flex-box.tsx +0 -105
- package/src/components/atoms/icon.tsx +0 -34
- package/src/components/atoms/input.tsx +0 -92
- package/src/components/atoms/label.tsx +0 -41
- package/src/components/atoms/logo.tsx +0 -89
- package/src/components/atoms/progress.tsx +0 -55
- package/src/components/atoms/radio-group.tsx +0 -122
- package/src/components/atoms/scroll-area.tsx +0 -106
- package/src/components/atoms/section.tsx +0 -48
- package/src/components/atoms/separator.tsx +0 -45
- package/src/components/atoms/skeleton.tsx +0 -17
- package/src/components/atoms/slider.tsx +0 -93
- package/src/components/atoms/spinner.tsx +0 -47
- package/src/components/atoms/switch.tsx +0 -60
- package/src/components/atoms/textarea.tsx +0 -78
- package/src/components/atoms/toggle.tsx +0 -80
- package/src/components/charts/activity-heatmap.tsx +0 -186
- package/src/components/charts/axes.tsx +0 -21
- package/src/components/charts/chart-container.tsx +0 -254
- package/src/components/charts/chart-legend.tsx +0 -67
- package/src/components/charts/chart-tooltip.tsx +0 -161
- package/src/components/charts/chart-types.tsx +0 -49
- package/src/components/charts/containers.tsx +0 -11
- package/src/components/charts/data.tsx +0 -16
- package/src/components/charts/details.tsx +0 -25
- package/src/components/charts/dot-pulse.tsx +0 -61
- package/src/components/charts/gauge.tsx +0 -106
- package/src/components/charts/grids.tsx +0 -8
- package/src/components/charts/index.ts +0 -62
- package/src/components/charts/labeled-bar-list.tsx +0 -85
- package/src/components/charts/metric-breakdown.tsx +0 -316
- package/src/components/charts/references.tsx +0 -8
- package/src/components/charts/service-health-list.tsx +0 -85
- package/src/components/charts/sparkline-area.tsx +0 -80
- package/src/components/charts/sparkline.tsx +0 -52
- package/src/components/charts/stacked-bar.tsx +0 -104
- package/src/components/charts/text.tsx +0 -10
- package/src/components/charts/world-heat-map-inner.tsx +0 -317
- package/src/components/charts/world-heat-map.tsx +0 -184
- package/src/components/molecules/README.md +0 -12
- package/src/components/molecules/action-bar.tsx +0 -73
- package/src/components/molecules/activity-item.tsx +0 -74
- package/src/components/molecules/alert.tsx +0 -86
- package/src/components/molecules/animated-background.tsx +0 -92
- package/src/components/molecules/auth-shell.tsx +0 -95
- package/src/components/molecules/brand-lockup.tsx +0 -48
- package/src/components/molecules/breadcrumb.tsx +0 -157
- package/src/components/molecules/button-group.tsx +0 -104
- package/src/components/molecules/calendar.tsx +0 -217
- package/src/components/molecules/card.tsx +0 -102
- package/src/components/molecules/client-brand.tsx +0 -95
- package/src/components/molecules/code-block.tsx +0 -86
- package/src/components/molecules/countdown-button.tsx +0 -92
- package/src/components/molecules/empty-state.tsx +0 -56
- package/src/components/molecules/error-state.tsx +0 -42
- package/src/components/molecules/field-display.tsx +0 -35
- package/src/components/molecules/input-otp.tsx +0 -74
- package/src/components/molecules/launcher-card.tsx +0 -152
- package/src/components/molecules/loading-state.tsx +0 -36
- package/src/components/molecules/notification-item.tsx +0 -67
- package/src/components/molecules/notification-list.tsx +0 -45
- package/src/components/molecules/number-badge.tsx +0 -53
- package/src/components/molecules/or-separator.tsx +0 -38
- package/src/components/molecules/page-header.tsx +0 -88
- package/src/components/molecules/page-tabs.tsx +0 -94
- package/src/components/molecules/pagination.tsx +0 -150
- package/src/components/molecules/password-input.tsx +0 -83
- package/src/components/molecules/password-strength-meter.tsx +0 -104
- package/src/components/molecules/phone-input.tsx +0 -200
- package/src/components/molecules/search-bar.tsx +0 -64
- package/src/components/molecules/secret-field.tsx +0 -158
- package/src/components/molecules/section-card.tsx +0 -91
- package/src/components/molecules/social-buttons.tsx +0 -165
- package/src/components/molecules/stat-card.tsx +0 -100
- package/src/components/molecules/status-badge.tsx +0 -42
- package/src/components/molecules/stepper.tsx +0 -96
- package/src/components/molecules/table.tsx +0 -157
- package/src/components/molecules/terminal.tsx +0 -74
- package/src/components/molecules/toggle-group.tsx +0 -145
- package/src/components/molecules/tooltip.tsx +0 -155
- package/src/components/molecules/user-avatar-chip.tsx +0 -71
- package/src/components/organisms/README.md +0 -14
- package/src/components/organisms/accordion.tsx +0 -154
- package/src/components/organisms/alert-dialog.tsx +0 -277
- package/src/components/organisms/carousel.tsx +0 -244
- package/src/components/organisms/collapsible.tsx +0 -69
- package/src/components/organisms/command.tsx +0 -144
- package/src/components/organisms/context-menu.tsx +0 -339
- package/src/components/organisms/dashboard-grid.tsx +0 -369
- package/src/components/organisms/data-table.tsx +0 -330
- package/src/components/organisms/dialog.tsx +0 -312
- package/src/components/organisms/drawer.tsx +0 -123
- package/src/components/organisms/dropdown-menu.tsx +0 -440
- package/src/components/organisms/editors/code-editor.tsx +0 -144
- package/src/components/organisms/editors/index.ts +0 -4
- package/src/components/organisms/editors/markdown-editor.tsx +0 -153
- package/src/components/organisms/editors/markdown-renderer.ts +0 -27
- package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
- package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
- package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
- package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
- package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
- package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
- package/src/components/organisms/error-boundary.tsx +0 -61
- package/src/components/organisms/form.tsx +0 -174
- package/src/components/organisms/hover-card.tsx +0 -115
- package/src/components/organisms/menubar.tsx +0 -498
- package/src/components/organisms/navbar.tsx +0 -104
- package/src/components/organisms/navigation-menu.tsx +0 -235
- package/src/components/organisms/popover.tsx +0 -149
- package/src/components/organisms/resizable.tsx +0 -58
- package/src/components/organisms/schema-form.tsx +0 -232
- package/src/components/organisms/select.tsx +0 -309
- package/src/components/organisms/sheet.tsx +0 -265
- package/src/components/organisms/sidebar.tsx +0 -1040
- package/src/components/organisms/sonner.tsx +0 -96
- package/src/components/organisms/tabs.tsx +0 -133
- package/src/components/organisms/theme-provider.tsx +0 -101
- package/src/hooks/use-mobile.tsx +0 -19
- package/src/lib/portal-container.tsx +0 -35
- package/src/lib/utils.ts +0 -6
- package/src/native.ts +0 -23
- package/src/tokens/colors.ts +0 -91
- package/src/tokens/index.ts +0 -3
- package/src/tokens/spacing.ts +0 -55
- package/src/tokens/typography.ts +0 -27
- package/styles/dashboard-grid.css +0 -47
- package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
- package/styles/glass.css +0 -175
- package/styles/leaflet.css +0 -13
- package/styles/tokens.css +0 -317
- package/tailwind.config.ts +0 -70
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import * as RechartsPrimitive from "recharts";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
|
|
8
|
-
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
9
|
-
const THEMES = { light: "", dark: ".dark" } as const;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Recharts data primitives that should auto-cycle through `--chart-N`
|
|
13
|
-
* colours when the consumer didn't pass an explicit `fill` / `stroke`.
|
|
14
|
-
*
|
|
15
|
-
* We compare by reference (the actual class component from `recharts`) — the
|
|
16
|
-
* deep-clone walker uses `child.type === T` to detect them. This works because
|
|
17
|
-
* canvas re-exports these symbols from `recharts` directly (see `data.tsx`).
|
|
18
|
-
*/
|
|
19
|
-
const PALETTE_TARGETS = new Set<unknown>([
|
|
20
|
-
RechartsPrimitive.Line,
|
|
21
|
-
RechartsPrimitive.Bar,
|
|
22
|
-
RechartsPrimitive.Area,
|
|
23
|
-
RechartsPrimitive.Pie,
|
|
24
|
-
RechartsPrimitive.Scatter,
|
|
25
|
-
RechartsPrimitive.Radar,
|
|
26
|
-
RechartsPrimitive.RadialBar,
|
|
27
|
-
RechartsPrimitive.Funnel,
|
|
28
|
-
]);
|
|
29
|
-
|
|
30
|
-
const PALETTE_SIZE = 5;
|
|
31
|
-
|
|
32
|
-
function nextColour(counter: { i: number }) {
|
|
33
|
-
const idx = counter.i++ % PALETTE_SIZE;
|
|
34
|
-
return `hsl(var(--chart-${idx + 1}))`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Walk a Pie's `<Cell>` children and inject per-cell palette fills when none
|
|
39
|
-
* is set, so each slice gets a distinct hue without requiring the consumer to
|
|
40
|
-
* write `<Cell fill={...} />` for every entry.
|
|
41
|
-
*/
|
|
42
|
-
function paintPieCells(children: React.ReactNode, counter: { i: number }): React.ReactNode {
|
|
43
|
-
return React.Children.map(children, (child) => {
|
|
44
|
-
if (!React.isValidElement(child)) return child;
|
|
45
|
-
const cellProps = child.props as { fill?: unknown };
|
|
46
|
-
if (child.type === RechartsPrimitive.Cell && cellProps.fill === undefined) {
|
|
47
|
-
return React.cloneElement(child, { fill: nextColour(counter) } as Partial<typeof cellProps>);
|
|
48
|
-
}
|
|
49
|
-
return child;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Inject per-row `fill` on Funnel data when the consumer didn't supply one.
|
|
55
|
-
* Recharts Funnel reads each datum's `fill` to colour the stage, so we map
|
|
56
|
-
* the array and assign palette colours in order.
|
|
57
|
-
*/
|
|
58
|
-
function paintFunnelData<T extends { fill?: unknown }>(data: T[], counter: { i: number }): T[] {
|
|
59
|
-
return data.map((row) => (row.fill === undefined ? { ...row, fill: nextColour(counter) } : row));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Recursively walk the children, cloning any data primitive that's missing
|
|
64
|
-
* `fill`/`stroke` and injecting an `hsl(var(--chart-N))` default. Counter is
|
|
65
|
-
* passed by reference so order is deterministic across the entire tree.
|
|
66
|
-
*/
|
|
67
|
-
function applyPalette(children: React.ReactNode, counter: { i: number }): React.ReactNode {
|
|
68
|
-
return React.Children.map(children, (child) => {
|
|
69
|
-
if (!React.isValidElement(child)) return child;
|
|
70
|
-
const props = child.props as {
|
|
71
|
-
fill?: unknown;
|
|
72
|
-
stroke?: unknown;
|
|
73
|
-
children?: React.ReactNode;
|
|
74
|
-
data?: unknown;
|
|
75
|
-
label?: unknown;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Pie: walk Cell children and assign per-slice palette colours so each
|
|
79
|
-
// slice is distinct. Also default `label={true}` so slices render their
|
|
80
|
-
// data-key value at the perimeter without needing a `<LabelList>`.
|
|
81
|
-
if (child.type === RechartsPrimitive.Pie) {
|
|
82
|
-
const next: Record<string, unknown> = {};
|
|
83
|
-
if (props.children !== undefined) {
|
|
84
|
-
next.children = paintPieCells(props.children, counter);
|
|
85
|
-
} else if (props.fill === undefined && props.stroke === undefined) {
|
|
86
|
-
const colour = nextColour(counter);
|
|
87
|
-
next.fill = colour;
|
|
88
|
-
next.stroke = colour;
|
|
89
|
-
}
|
|
90
|
-
if (props.label === undefined) next.label = true;
|
|
91
|
-
return React.cloneElement(child, next as Partial<typeof props>);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Funnel: distribute palette colours across the data array (Recharts
|
|
95
|
-
// reads `fill` from each datum, not from the <Funnel> element).
|
|
96
|
-
if (child.type === RechartsPrimitive.Funnel && Array.isArray(props.data)) {
|
|
97
|
-
return React.cloneElement(child, {
|
|
98
|
-
data: paintFunnelData(props.data as { fill?: unknown }[], counter),
|
|
99
|
-
} as Partial<typeof props>);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (PALETTE_TARGETS.has(child.type)) {
|
|
103
|
-
if (props.fill === undefined && props.stroke === undefined) {
|
|
104
|
-
const colour = nextColour(counter);
|
|
105
|
-
// Pie/Radar/Area also benefit from a matching stroke; the
|
|
106
|
-
// component decides which one applies (Bar reads fill, Line
|
|
107
|
-
// reads stroke, etc.).
|
|
108
|
-
return React.cloneElement(child, {
|
|
109
|
-
fill: colour,
|
|
110
|
-
stroke: colour,
|
|
111
|
-
} as Partial<typeof props>);
|
|
112
|
-
}
|
|
113
|
-
return child;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Recurse into children of layout / chart-type / fragment elements so
|
|
117
|
-
// data primitives nested inside (e.g. <BarChart><Bar/></BarChart>)
|
|
118
|
-
// still get assigned a palette colour.
|
|
119
|
-
if (props.children !== undefined) {
|
|
120
|
-
return React.cloneElement(
|
|
121
|
-
child,
|
|
122
|
-
{} as Partial<typeof props>,
|
|
123
|
-
applyPalette(props.children, counter),
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return child;
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export type ChartConfig = {
|
|
131
|
-
[k in string]: {
|
|
132
|
-
label?: React.ReactNode;
|
|
133
|
-
icon?: React.ComponentType;
|
|
134
|
-
} & (
|
|
135
|
-
| { color?: string; theme?: never }
|
|
136
|
-
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
|
137
|
-
);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
interface ChartContextProps {
|
|
141
|
-
config: ChartConfig;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const ChartContext = React.createContext<ChartContextProps | null>(null);
|
|
145
|
-
|
|
146
|
-
export function useChart() {
|
|
147
|
-
const context = React.useContext(ChartContext);
|
|
148
|
-
|
|
149
|
-
if (!context) {
|
|
150
|
-
throw new Error("useChart must be used within a <ChartContainer />");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return context;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const ChartContainer = React.forwardRef<
|
|
157
|
-
HTMLDivElement,
|
|
158
|
-
React.ComponentProps<"div"> & {
|
|
159
|
-
config: ChartConfig;
|
|
160
|
-
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
161
|
-
}
|
|
162
|
-
>(({ id, className, children, config, ...props }, ref) => {
|
|
163
|
-
const uniqueId = React.useId();
|
|
164
|
-
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
165
|
-
|
|
166
|
-
const themedChildren = React.useMemo(
|
|
167
|
-
() => applyPalette(children, { i: 0 }) as typeof children,
|
|
168
|
-
[children],
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
return (
|
|
172
|
-
<ChartContext.Provider value={{ config }}>
|
|
173
|
-
<div
|
|
174
|
-
data-chart={chartId}
|
|
175
|
-
ref={ref}
|
|
176
|
-
className={cn(
|
|
177
|
-
"flex aspect-video w-full justify-center text-xs [&_.recharts-cartesian-axis-line]:stroke-border [&_.recharts-cartesian-axis-tick-line]:stroke-transparent [&_.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-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.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 [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
|
178
|
-
className,
|
|
179
|
-
)}
|
|
180
|
-
{...props}
|
|
181
|
-
>
|
|
182
|
-
<ChartStyle id={chartId} config={config} />
|
|
183
|
-
<RechartsPrimitive.ResponsiveContainer>
|
|
184
|
-
{themedChildren}
|
|
185
|
-
</RechartsPrimitive.ResponsiveContainer>
|
|
186
|
-
</div>
|
|
187
|
-
</ChartContext.Provider>
|
|
188
|
-
);
|
|
189
|
-
});
|
|
190
|
-
ChartContainer.displayName = "Chart";
|
|
191
|
-
|
|
192
|
-
export const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|
193
|
-
const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
|
|
194
|
-
|
|
195
|
-
if (!colorConfig.length) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<style
|
|
201
|
-
dangerouslySetInnerHTML={{
|
|
202
|
-
__html: Object.entries(THEMES)
|
|
203
|
-
.map(
|
|
204
|
-
([theme, prefix]) => `
|
|
205
|
-
${prefix} [data-chart=${id}] {
|
|
206
|
-
${colorConfig
|
|
207
|
-
.map(([key, itemConfig]) => {
|
|
208
|
-
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
|
|
209
|
-
return color ? ` --color-${key}: ${color};` : null;
|
|
210
|
-
})
|
|
211
|
-
.join("\n")}
|
|
212
|
-
}
|
|
213
|
-
`,
|
|
214
|
-
)
|
|
215
|
-
.join("\n"),
|
|
216
|
-
}}
|
|
217
|
-
/>
|
|
218
|
-
);
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Look up the canvas config entry that matches a Recharts payload item.
|
|
223
|
-
* Used by tooltip and legend components to resolve labels / icons / colours
|
|
224
|
-
* from the ChartConfig prop.
|
|
225
|
-
*/
|
|
226
|
-
export function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
|
|
227
|
-
/* c8 ignore next 3 -- defensive guard: payload is always a recharts item object at call sites */
|
|
228
|
-
if (typeof payload !== "object" || payload === null) {
|
|
229
|
-
return undefined;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const payloadPayload =
|
|
233
|
-
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
|
|
234
|
-
? payload.payload
|
|
235
|
-
: undefined;
|
|
236
|
-
|
|
237
|
-
let configLabelKey: string = key;
|
|
238
|
-
|
|
239
|
-
/* c8 ignore next 3 -- defensive name-key lookup: real recharts payloads don't carry this shape */
|
|
240
|
-
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
|
|
241
|
-
configLabelKey = payload[key as keyof typeof payload] as string;
|
|
242
|
-
} else if (
|
|
243
|
-
/* c8 ignore next 3 -- defensive nested-name lookup: only reachable with custom dataset shape */
|
|
244
|
-
payloadPayload &&
|
|
245
|
-
key in payloadPayload &&
|
|
246
|
-
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
|
247
|
-
) {
|
|
248
|
-
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export { ChartContainer };
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import * as RechartsPrimitive from "recharts";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
import { getPayloadConfigFromPayload, useChart } from "./chart-container";
|
|
8
|
-
|
|
9
|
-
const ChartLegend = RechartsPrimitive.Legend;
|
|
10
|
-
|
|
11
|
-
const ChartLegendContent = React.forwardRef<
|
|
12
|
-
HTMLDivElement,
|
|
13
|
-
React.ComponentProps<"div"> &
|
|
14
|
-
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
|
15
|
-
hideIcon?: boolean;
|
|
16
|
-
nameKey?: string;
|
|
17
|
-
}
|
|
18
|
-
>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
|
|
19
|
-
const { config } = useChart();
|
|
20
|
-
|
|
21
|
-
if (!payload?.length) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div
|
|
27
|
-
ref={ref}
|
|
28
|
-
className={cn(
|
|
29
|
-
"flex items-center justify-center gap-4",
|
|
30
|
-
verticalAlign === "top" ? "pb-3" : "pt-3",
|
|
31
|
-
className,
|
|
32
|
-
)}
|
|
33
|
-
>
|
|
34
|
-
{payload
|
|
35
|
-
.filter((item) => item.type !== "none")
|
|
36
|
-
.map((item) => {
|
|
37
|
-
/* c8 ignore next -- prefers nameKey when provided; remaining fallbacks are recharts-internal edge cases */
|
|
38
|
-
const key = `${nameKey || item.dataKey || "value"}`;
|
|
39
|
-
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div
|
|
43
|
-
key={item.value}
|
|
44
|
-
className={cn(
|
|
45
|
-
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
|
|
46
|
-
)}
|
|
47
|
-
>
|
|
48
|
-
{itemConfig?.icon && !hideIcon ? (
|
|
49
|
-
<itemConfig.icon />
|
|
50
|
-
) : (
|
|
51
|
-
<div
|
|
52
|
-
className="h-2 w-2 shrink-0 rounded-[2px]"
|
|
53
|
-
style={{
|
|
54
|
-
backgroundColor: item.color,
|
|
55
|
-
}}
|
|
56
|
-
/>
|
|
57
|
-
)}
|
|
58
|
-
{itemConfig?.label}
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
})}
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
ChartLegendContent.displayName = "ChartLegend";
|
|
66
|
-
|
|
67
|
-
export { ChartLegend, ChartLegendContent };
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import * as RechartsPrimitive from "recharts";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
import { getPayloadConfigFromPayload, useChart } from "./chart-container";
|
|
8
|
-
|
|
9
|
-
const ChartTooltip = RechartsPrimitive.Tooltip;
|
|
10
|
-
|
|
11
|
-
const ChartTooltipContent = React.forwardRef<
|
|
12
|
-
HTMLDivElement,
|
|
13
|
-
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
|
14
|
-
React.ComponentProps<"div"> & {
|
|
15
|
-
hideLabel?: boolean;
|
|
16
|
-
hideIndicator?: boolean;
|
|
17
|
-
indicator?: "line" | "dot" | "dashed";
|
|
18
|
-
nameKey?: string;
|
|
19
|
-
labelKey?: string;
|
|
20
|
-
}
|
|
21
|
-
>(
|
|
22
|
-
(
|
|
23
|
-
{
|
|
24
|
-
active,
|
|
25
|
-
payload,
|
|
26
|
-
className,
|
|
27
|
-
indicator = "dot",
|
|
28
|
-
hideLabel = false,
|
|
29
|
-
hideIndicator = false,
|
|
30
|
-
label,
|
|
31
|
-
labelFormatter,
|
|
32
|
-
labelClassName,
|
|
33
|
-
formatter,
|
|
34
|
-
color,
|
|
35
|
-
nameKey,
|
|
36
|
-
labelKey,
|
|
37
|
-
},
|
|
38
|
-
ref,
|
|
39
|
-
) => {
|
|
40
|
-
const { config } = useChart();
|
|
41
|
-
|
|
42
|
-
const tooltipLabel = React.useMemo(() => {
|
|
43
|
-
if (hideLabel || !payload?.length) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const [item] = payload;
|
|
48
|
-
/* c8 ignore next -- the || chain prefers labelKey when set; the remaining fallbacks are recharts-internal edge cases */
|
|
49
|
-
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
|
|
50
|
-
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
51
|
-
const value =
|
|
52
|
-
!labelKey && typeof label === "string"
|
|
53
|
-
? config[label as keyof typeof config]?.label || label
|
|
54
|
-
: itemConfig?.label;
|
|
55
|
-
|
|
56
|
-
if (labelFormatter) {
|
|
57
|
-
return (
|
|
58
|
-
<div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* c8 ignore next 3 -- defensive fallback: value is always truthy when this branch is reached in practice */
|
|
63
|
-
if (!value) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
|
68
|
-
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
69
|
-
|
|
70
|
-
if (!active || !payload?.length) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div
|
|
78
|
-
ref={ref}
|
|
79
|
-
className={cn(
|
|
80
|
-
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
|
81
|
-
className,
|
|
82
|
-
)}
|
|
83
|
-
>
|
|
84
|
-
{!nestLabel ? tooltipLabel : null}
|
|
85
|
-
<div className="grid gap-1.5">
|
|
86
|
-
{payload
|
|
87
|
-
.filter((item) => item.type !== "none")
|
|
88
|
-
.map((item, index) => {
|
|
89
|
-
/* c8 ignore next -- prefers nameKey when provided; remaining fallbacks are recharts-internal edge cases */
|
|
90
|
-
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
|
91
|
-
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
92
|
-
/* c8 ignore next -- defers to explicit color prop; remaining fallbacks cover recharts-internal edge cases */
|
|
93
|
-
const indicatorColor = color || item.payload.fill || item.color;
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div
|
|
97
|
-
key={item.dataKey}
|
|
98
|
-
className={cn(
|
|
99
|
-
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
|
100
|
-
indicator === "dot" && "items-center",
|
|
101
|
-
)}
|
|
102
|
-
>
|
|
103
|
-
{formatter && item?.value !== undefined && item.name ? (
|
|
104
|
-
formatter(item.value, item.name, item, index, item.payload)
|
|
105
|
-
) : (
|
|
106
|
-
<>
|
|
107
|
-
{itemConfig?.icon ? (
|
|
108
|
-
<itemConfig.icon />
|
|
109
|
-
) : (
|
|
110
|
-
!hideIndicator && (
|
|
111
|
-
<div
|
|
112
|
-
className={cn(
|
|
113
|
-
"shrink-0 rounded-[2px] border-[var(--color-border)] bg-[var(--color-bg)]",
|
|
114
|
-
{
|
|
115
|
-
"h-2.5 w-2.5": indicator === "dot",
|
|
116
|
-
"w-1": indicator === "line",
|
|
117
|
-
"w-0 border-[1.5px] border-dashed bg-transparent":
|
|
118
|
-
indicator === "dashed",
|
|
119
|
-
"my-0.5": nestLabel && indicator === "dashed",
|
|
120
|
-
},
|
|
121
|
-
)}
|
|
122
|
-
style={
|
|
123
|
-
{
|
|
124
|
-
"--color-bg": indicatorColor,
|
|
125
|
-
"--color-border": indicatorColor,
|
|
126
|
-
} as React.CSSProperties
|
|
127
|
-
}
|
|
128
|
-
/>
|
|
129
|
-
)
|
|
130
|
-
)}
|
|
131
|
-
<div
|
|
132
|
-
className={cn(
|
|
133
|
-
"flex flex-1 justify-between leading-none",
|
|
134
|
-
nestLabel ? "items-end" : "items-center",
|
|
135
|
-
)}
|
|
136
|
-
>
|
|
137
|
-
<div className="grid gap-1.5">
|
|
138
|
-
{nestLabel ? tooltipLabel : null}
|
|
139
|
-
<span className="text-muted-foreground">
|
|
140
|
-
{itemConfig?.label || item.name}
|
|
141
|
-
</span>
|
|
142
|
-
</div>
|
|
143
|
-
{item.value && (
|
|
144
|
-
<span className="font-mono font-medium tabular-nums text-foreground">
|
|
145
|
-
{item.value.toLocaleString()}
|
|
146
|
-
</span>
|
|
147
|
-
)}
|
|
148
|
-
</div>
|
|
149
|
-
</>
|
|
150
|
-
)}
|
|
151
|
-
</div>
|
|
152
|
-
);
|
|
153
|
-
})}
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
ChartTooltipContent.displayName = "ChartTooltip";
|
|
160
|
-
|
|
161
|
-
export { ChartTooltip, ChartTooltipContent };
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type * as React from "react";
|
|
4
|
-
import * as RechartsPrimitive from "recharts";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Chart-type wrappers. Each wraps a Recharts top-level chart component with
|
|
8
|
-
* sensible default `margin` so axis labels don't clip. Children pass through
|
|
9
|
-
* UNWRAPPED — Recharts uses `findAllByType` on its direct children to detect
|
|
10
|
-
* `<Line>` / `<XAxis>` / `<CartesianGrid>` etc., so any extra wrapper element
|
|
11
|
-
* (Context.Provider, fragment-only-on-render, etc.) breaks chart rendering.
|
|
12
|
-
*
|
|
13
|
-
* The auto-palette context is established by `<ChartContainer>` instead, so
|
|
14
|
-
* data primitives still cycle through `--chart-N` colours when no fill /
|
|
15
|
-
* stroke is supplied.
|
|
16
|
-
*
|
|
17
|
-
* Margin defaults are merged with any user-supplied margin (user wins on
|
|
18
|
-
* conflict). Everything else is straight pass-through.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const DEFAULT_MARGIN = { top: 12, right: 12, bottom: 4, left: 0 } as const;
|
|
22
|
-
|
|
23
|
-
function withDefaults<P extends { children?: React.ReactNode; margin?: object }>(
|
|
24
|
-
Comp: React.ComponentType<P>,
|
|
25
|
-
): React.FC<P> {
|
|
26
|
-
const Wrapped = ({ children, margin, ...rest }: P) => {
|
|
27
|
-
const merged = { ...DEFAULT_MARGIN, ...(margin ?? {}) };
|
|
28
|
-
const props = { ...rest, margin: merged } as any;
|
|
29
|
-
return <Comp {...props}>{children}</Comp>;
|
|
30
|
-
};
|
|
31
|
-
/* c8 ignore next -- defensive: every recharts top-level chart component ships with a `displayName`; the `?? Comp.name ?? "Anonymous"` fallback chain only fires for hand-rolled non-recharts wrappers */
|
|
32
|
-
Wrapped.displayName = `Chart(${(Comp as { displayName?: string }).displayName ?? Comp.name ?? "Anonymous"})`;
|
|
33
|
-
return Wrapped;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const LineChart = withDefaults(RechartsPrimitive.LineChart);
|
|
37
|
-
export const BarChart = withDefaults(RechartsPrimitive.BarChart);
|
|
38
|
-
export const AreaChart = withDefaults(RechartsPrimitive.AreaChart);
|
|
39
|
-
export const ComposedChart = withDefaults(RechartsPrimitive.ComposedChart);
|
|
40
|
-
export const PieChart = withDefaults(RechartsPrimitive.PieChart);
|
|
41
|
-
export const ScatterChart = withDefaults(RechartsPrimitive.ScatterChart);
|
|
42
|
-
export const RadarChart = withDefaults(RechartsPrimitive.RadarChart);
|
|
43
|
-
export const RadialBarChart = withDefaults(RechartsPrimitive.RadialBarChart);
|
|
44
|
-
export const FunnelChart = withDefaults(RechartsPrimitive.FunnelChart);
|
|
45
|
-
export const SunburstChart = withDefaults(RechartsPrimitive.SunburstChart);
|
|
46
|
-
|
|
47
|
-
// Treemap and Sankey have a different children shape — pure pass-throughs.
|
|
48
|
-
export const Treemap = RechartsPrimitive.Treemap;
|
|
49
|
-
export const Sankey = RechartsPrimitive.Sankey;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Container-level Recharts primitives. Pure pass-throughs.
|
|
5
|
-
* `ResponsiveContainer` is also re-rendered internally by `<ChartContainer>`,
|
|
6
|
-
* but consumers might want to use it directly for non-canvas-themed charts.
|
|
7
|
-
*
|
|
8
|
-
* NOTE: re-exported via `export { … }` (not `export const = …`) so TypeScript
|
|
9
|
-
* doesn't have to name internal Recharts prop types in the d.ts emit.
|
|
10
|
-
*/
|
|
11
|
-
export { Brush, Layer, ResponsiveContainer, Surface } from "recharts";
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Data-primitive re-exports. These are direct pass-throughs of the Recharts
|
|
5
|
-
* primitives — wrapping them in our own function components breaks Recharts'
|
|
6
|
-
* `findAllByType` introspection (it looks for `child.type === Bar` etc., and
|
|
7
|
-
* a wrapper's `type` is our function, not Bar). The earlier wrapped version
|
|
8
|
-
* crashed Recharts with "Cannot read properties of undefined (reading
|
|
9
|
-
* 'yAxisId')" because the chart-type couldn't find our axes / data.
|
|
10
|
-
*
|
|
11
|
-
* Auto-palette cycling happens inside `<ChartContainer>` via a deep
|
|
12
|
-
* `React.cloneElement` walk that injects `fill`/`stroke` defaults when the
|
|
13
|
-
* consumer didn't supply one. The wrapped types remain the real Recharts
|
|
14
|
-
* classes, so axis detection / chart-type child resolution keep working.
|
|
15
|
-
*/
|
|
16
|
-
export { Area, Bar, Funnel, Line, Pie, Radar, RadialBar, Scatter } from "recharts";
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Detail-level Recharts primitives. Pure pass-throughs — no theming
|
|
5
|
-
* opportunity, but re-exported here so consumers can import the entire
|
|
6
|
-
* Recharts surface from canvas.
|
|
7
|
-
*
|
|
8
|
-
* `Cell` and `Customized` are aliased to `ChartCell` / `ChartCustomized` to
|
|
9
|
-
* avoid likely future collisions with canvas-domain primitives.
|
|
10
|
-
*
|
|
11
|
-
* Re-exported via `export { … }` (not `export const = …`) so TypeScript
|
|
12
|
-
* doesn't have to name internal Recharts prop types in the d.ts emit.
|
|
13
|
-
*/
|
|
14
|
-
export {
|
|
15
|
-
Cell as ChartCell,
|
|
16
|
-
Cross,
|
|
17
|
-
Curve,
|
|
18
|
-
Customized as ChartCustomized,
|
|
19
|
-
Dot,
|
|
20
|
-
ErrorBar,
|
|
21
|
-
Polygon,
|
|
22
|
-
Rectangle,
|
|
23
|
-
Sector,
|
|
24
|
-
Trapezoid,
|
|
25
|
-
} from "recharts";
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
|
|
5
|
-
export interface DotPulseProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
|
-
/** Number of active (pulsing) dots. Clamped to `[0, total]`. */
|
|
7
|
-
count: number;
|
|
8
|
-
/** Total number of dots rendered. Default `5`. */
|
|
9
|
-
total?: number;
|
|
10
|
-
/**
|
|
11
|
-
* CSS variable name (without leading `--`) used for active dot fill.
|
|
12
|
-
* Default `chart-1`. The variable should resolve to an HSL triplet.
|
|
13
|
-
*/
|
|
14
|
-
colorVar?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Pure-CSS severity indicator: a row of small circles where the first `count`
|
|
19
|
-
* dots pulse with color and the rest are muted. Designed for `<StatCard>`
|
|
20
|
-
* slots where no time-series data exists (e.g. lockout counts).
|
|
21
|
-
*/
|
|
22
|
-
export const DotPulse = React.forwardRef<HTMLDivElement, DotPulseProps>(
|
|
23
|
-
({ count, total = 5, colorVar = "chart-1", className, ...props }, ref) => {
|
|
24
|
-
const active = Math.max(0, Math.min(total, count));
|
|
25
|
-
const color = `hsl(var(--${colorVar}))`;
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<div
|
|
29
|
-
ref={ref}
|
|
30
|
-
className={cn("flex items-center gap-2", className)}
|
|
31
|
-
role="presentation"
|
|
32
|
-
{...props}
|
|
33
|
-
>
|
|
34
|
-
{Array.from({ length: total }, (_, i) => {
|
|
35
|
-
const isActive = i < active;
|
|
36
|
-
return (
|
|
37
|
-
<span
|
|
38
|
-
key={i}
|
|
39
|
-
className={cn("inline-block size-2 rounded-full", !isActive && "bg-muted")}
|
|
40
|
-
style={
|
|
41
|
-
isActive
|
|
42
|
-
? {
|
|
43
|
-
background: color,
|
|
44
|
-
animation: `canvas-dot-pulse 1.5s ease-in-out infinite`,
|
|
45
|
-
animationDelay: `${i * 0.15}s`,
|
|
46
|
-
}
|
|
47
|
-
: undefined
|
|
48
|
-
}
|
|
49
|
-
aria-hidden
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
})}
|
|
53
|
-
<style>{`@keyframes canvas-dot-pulse {
|
|
54
|
-
0%, 100% { transform: scale(1); opacity: 1; }
|
|
55
|
-
50% { transform: scale(1.4); opacity: 0.5; }
|
|
56
|
-
}`}</style>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
DotPulse.displayName = "DotPulse";
|