@olympusoss/canvas 2.20.2 → 3.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.
Files changed (214) hide show
  1. package/README.md +69 -35
  2. package/package.json +45 -177
  3. package/src/cn.ts +3 -0
  4. package/src/index.ts +12 -603
  5. package/src/theme.ts +62 -0
  6. package/src/tokens.ts +11 -0
  7. package/styles/base.css +17 -0
  8. package/styles/canvas.css +77 -52
  9. package/styles/components/alert.css +66 -0
  10. package/styles/components/app-shell.css +46 -0
  11. package/styles/components/avatar.css +22 -0
  12. package/styles/components/badge.css +83 -0
  13. package/styles/components/breadcrumb.css +35 -0
  14. package/styles/components/button-group.css +23 -0
  15. package/styles/components/button.css +107 -0
  16. package/styles/components/calendar.css +73 -0
  17. package/styles/components/card.css +58 -0
  18. package/styles/components/checkbox.css +55 -0
  19. package/styles/components/code-block.css +18 -0
  20. package/styles/components/combobox.css +75 -0
  21. package/styles/components/command.css +94 -0
  22. package/styles/components/data-table.css +142 -0
  23. package/styles/components/dialog.css +72 -0
  24. package/styles/components/dropdown.css +54 -0
  25. package/styles/components/empty-state.css +17 -0
  26. package/styles/components/field.css +27 -0
  27. package/styles/components/filter-panel.css +58 -0
  28. package/styles/components/form.css +27 -0
  29. package/styles/components/icon.css +8 -0
  30. package/styles/components/input-group.css +45 -0
  31. package/styles/components/input.css +56 -0
  32. package/styles/components/kbd.css +15 -0
  33. package/styles/components/page-header.css +52 -0
  34. package/styles/components/pagination.css +48 -0
  35. package/styles/components/popover.css +14 -0
  36. package/styles/components/radio.css +28 -0
  37. package/styles/components/row-menu.css +69 -0
  38. package/styles/components/section-card.css +49 -0
  39. package/styles/components/select.css +57 -0
  40. package/styles/components/separator.css +32 -0
  41. package/styles/components/sheet.css +70 -0
  42. package/styles/components/sidebar.css +146 -0
  43. package/styles/components/skeleton.css +32 -0
  44. package/styles/components/spinner.css +26 -0
  45. package/styles/components/stat-card.css +71 -0
  46. package/styles/components/stepper.css +63 -0
  47. package/styles/components/switch.css +45 -0
  48. package/styles/components/tabs.css +40 -0
  49. package/styles/components/textarea.css +31 -0
  50. package/styles/components/toast.css +95 -0
  51. package/styles/components/tooltip.css +53 -0
  52. package/styles/components/topbar.css +24 -0
  53. package/styles/components/typography.css +105 -0
  54. package/styles/patterns/backdrops.css +35 -0
  55. package/styles/patterns/density.css +66 -0
  56. package/styles/patterns/focus.css +22 -0
  57. package/styles/patterns/glass.css +85 -0
  58. package/styles/patterns/high-contrast.css +70 -0
  59. package/styles/patterns/reduced-motion.css +12 -0
  60. package/styles/patterns/scrollbar.css +10 -0
  61. package/styles/reset.css +89 -0
  62. package/styles/tokens/colors.css +106 -0
  63. package/styles/tokens/motion.css +33 -0
  64. package/styles/tokens/radius.css +10 -0
  65. package/styles/tokens/shadows.css +35 -0
  66. package/styles/tokens/spacing.css +19 -0
  67. package/styles/tokens/typography.css +6 -0
  68. package/styles/tokens/z-index.css +12 -0
  69. package/styles/utilities/display.css +66 -0
  70. package/styles/utilities/flexbox.css +240 -0
  71. package/styles/utilities/gap.css +288 -0
  72. package/styles/utilities/grid.css +138 -0
  73. package/styles/utilities/position.css +78 -0
  74. package/styles/utilities/sizing.css +138 -0
  75. package/tsconfig.json +20 -21
  76. package/src/components/atoms/README.md +0 -11
  77. package/src/components/atoms/aspect-ratio.tsx +0 -32
  78. package/src/components/atoms/avatar.tsx +0 -98
  79. package/src/components/atoms/badge.tsx +0 -44
  80. package/src/components/atoms/brand-mark.tsx +0 -74
  81. package/src/components/atoms/button.tsx +0 -105
  82. package/src/components/atoms/checkbox.tsx +0 -63
  83. package/src/components/atoms/flex-box.tsx +0 -105
  84. package/src/components/atoms/icon.tsx +0 -34
  85. package/src/components/atoms/input.tsx +0 -92
  86. package/src/components/atoms/label.tsx +0 -41
  87. package/src/components/atoms/logo.tsx +0 -89
  88. package/src/components/atoms/progress.tsx +0 -55
  89. package/src/components/atoms/radio-group.tsx +0 -122
  90. package/src/components/atoms/scroll-area.tsx +0 -106
  91. package/src/components/atoms/section.tsx +0 -48
  92. package/src/components/atoms/separator.tsx +0 -45
  93. package/src/components/atoms/skeleton.tsx +0 -17
  94. package/src/components/atoms/slider.tsx +0 -93
  95. package/src/components/atoms/spinner.tsx +0 -47
  96. package/src/components/atoms/switch.tsx +0 -60
  97. package/src/components/atoms/textarea.tsx +0 -78
  98. package/src/components/atoms/toggle.tsx +0 -80
  99. package/src/components/charts/activity-heatmap.tsx +0 -186
  100. package/src/components/charts/axes.tsx +0 -21
  101. package/src/components/charts/chart-container.tsx +0 -254
  102. package/src/components/charts/chart-legend.tsx +0 -67
  103. package/src/components/charts/chart-tooltip.tsx +0 -161
  104. package/src/components/charts/chart-types.tsx +0 -49
  105. package/src/components/charts/containers.tsx +0 -11
  106. package/src/components/charts/data.tsx +0 -16
  107. package/src/components/charts/details.tsx +0 -25
  108. package/src/components/charts/dot-pulse.tsx +0 -61
  109. package/src/components/charts/gauge.tsx +0 -106
  110. package/src/components/charts/grids.tsx +0 -8
  111. package/src/components/charts/index.ts +0 -62
  112. package/src/components/charts/labeled-bar-list.tsx +0 -85
  113. package/src/components/charts/metric-breakdown.tsx +0 -316
  114. package/src/components/charts/references.tsx +0 -8
  115. package/src/components/charts/service-health-list.tsx +0 -85
  116. package/src/components/charts/sparkline-area.tsx +0 -80
  117. package/src/components/charts/sparkline.tsx +0 -52
  118. package/src/components/charts/stacked-bar.tsx +0 -104
  119. package/src/components/charts/text.tsx +0 -10
  120. package/src/components/charts/world-heat-map-inner.tsx +0 -317
  121. package/src/components/charts/world-heat-map.tsx +0 -184
  122. package/src/components/molecules/README.md +0 -12
  123. package/src/components/molecules/action-bar.tsx +0 -73
  124. package/src/components/molecules/activity-item.tsx +0 -74
  125. package/src/components/molecules/alert.tsx +0 -86
  126. package/src/components/molecules/animated-background.tsx +0 -92
  127. package/src/components/molecules/auth-shell.tsx +0 -95
  128. package/src/components/molecules/brand-lockup.tsx +0 -48
  129. package/src/components/molecules/breadcrumb.tsx +0 -157
  130. package/src/components/molecules/button-group.tsx +0 -104
  131. package/src/components/molecules/calendar.tsx +0 -217
  132. package/src/components/molecules/card.tsx +0 -102
  133. package/src/components/molecules/client-brand.tsx +0 -95
  134. package/src/components/molecules/code-block.tsx +0 -86
  135. package/src/components/molecules/countdown-button.tsx +0 -92
  136. package/src/components/molecules/empty-state.tsx +0 -56
  137. package/src/components/molecules/error-state.tsx +0 -42
  138. package/src/components/molecules/field-display.tsx +0 -35
  139. package/src/components/molecules/input-otp.tsx +0 -74
  140. package/src/components/molecules/launcher-card.tsx +0 -152
  141. package/src/components/molecules/loading-state.tsx +0 -36
  142. package/src/components/molecules/notification-item.tsx +0 -67
  143. package/src/components/molecules/notification-list.tsx +0 -45
  144. package/src/components/molecules/number-badge.tsx +0 -53
  145. package/src/components/molecules/or-separator.tsx +0 -38
  146. package/src/components/molecules/page-header.tsx +0 -88
  147. package/src/components/molecules/page-tabs.tsx +0 -94
  148. package/src/components/molecules/pagination.tsx +0 -150
  149. package/src/components/molecules/password-input.tsx +0 -83
  150. package/src/components/molecules/password-strength-meter.tsx +0 -104
  151. package/src/components/molecules/phone-input.tsx +0 -200
  152. package/src/components/molecules/search-bar.tsx +0 -64
  153. package/src/components/molecules/secret-field.tsx +0 -158
  154. package/src/components/molecules/section-card.tsx +0 -91
  155. package/src/components/molecules/social-buttons.tsx +0 -165
  156. package/src/components/molecules/stat-card.tsx +0 -100
  157. package/src/components/molecules/status-badge.tsx +0 -42
  158. package/src/components/molecules/stepper.tsx +0 -96
  159. package/src/components/molecules/table.tsx +0 -157
  160. package/src/components/molecules/terminal.tsx +0 -74
  161. package/src/components/molecules/toggle-group.tsx +0 -145
  162. package/src/components/molecules/tooltip.tsx +0 -155
  163. package/src/components/molecules/user-avatar-chip.tsx +0 -71
  164. package/src/components/organisms/README.md +0 -14
  165. package/src/components/organisms/accordion.tsx +0 -154
  166. package/src/components/organisms/alert-dialog.tsx +0 -277
  167. package/src/components/organisms/carousel.tsx +0 -244
  168. package/src/components/organisms/collapsible.tsx +0 -69
  169. package/src/components/organisms/command.tsx +0 -144
  170. package/src/components/organisms/context-menu.tsx +0 -339
  171. package/src/components/organisms/dashboard-grid.tsx +0 -369
  172. package/src/components/organisms/data-table.tsx +0 -330
  173. package/src/components/organisms/dialog.tsx +0 -312
  174. package/src/components/organisms/drawer.tsx +0 -123
  175. package/src/components/organisms/dropdown-menu.tsx +0 -440
  176. package/src/components/organisms/editors/code-editor.tsx +0 -144
  177. package/src/components/organisms/editors/index.ts +0 -4
  178. package/src/components/organisms/editors/markdown-editor.tsx +0 -153
  179. package/src/components/organisms/editors/markdown-renderer.ts +0 -27
  180. package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
  181. package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
  182. package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
  183. package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
  184. package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
  185. package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
  186. package/src/components/organisms/error-boundary.tsx +0 -61
  187. package/src/components/organisms/form.tsx +0 -174
  188. package/src/components/organisms/hover-card.tsx +0 -115
  189. package/src/components/organisms/menubar.tsx +0 -498
  190. package/src/components/organisms/navbar.tsx +0 -104
  191. package/src/components/organisms/navigation-menu.tsx +0 -235
  192. package/src/components/organisms/popover.tsx +0 -149
  193. package/src/components/organisms/resizable.tsx +0 -58
  194. package/src/components/organisms/schema-form.tsx +0 -232
  195. package/src/components/organisms/select.tsx +0 -309
  196. package/src/components/organisms/sheet.tsx +0 -265
  197. package/src/components/organisms/sidebar.tsx +0 -1040
  198. package/src/components/organisms/sonner.tsx +0 -96
  199. package/src/components/organisms/tabs.tsx +0 -133
  200. package/src/components/organisms/theme-provider.tsx +0 -101
  201. package/src/hooks/use-mobile.tsx +0 -19
  202. package/src/lib/portal-container.tsx +0 -35
  203. package/src/lib/utils.ts +0 -6
  204. package/src/native.ts +0 -23
  205. package/src/tokens/colors.ts +0 -91
  206. package/src/tokens/index.ts +0 -3
  207. package/src/tokens/spacing.ts +0 -55
  208. package/src/tokens/typography.ts +0 -27
  209. package/styles/dashboard-grid.css +0 -47
  210. package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
  211. package/styles/glass.css +0 -175
  212. package/styles/leaflet.css +0 -13
  213. package/styles/tokens.css +0 -317
  214. 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";