@olympusoss/canvas 2.20.1 → 4.0.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 (208) hide show
  1. package/package.json +41 -177
  2. package/src/cn.ts +3 -0
  3. package/src/index.ts +12 -603
  4. package/src/theme.ts +41 -0
  5. package/src/tokens.ts +11 -0
  6. package/styles/base.css +17 -0
  7. package/styles/canvas.css +69 -52
  8. package/styles/components/alert.css +66 -0
  9. package/styles/components/app-shell.css +46 -0
  10. package/styles/components/avatar.css +15 -0
  11. package/styles/components/badge.css +83 -0
  12. package/styles/components/breadcrumb.css +35 -0
  13. package/styles/components/button-group.css +23 -0
  14. package/styles/components/button.css +107 -0
  15. package/styles/components/calendar.css +73 -0
  16. package/styles/components/card.css +58 -0
  17. package/styles/components/checkbox.css +55 -0
  18. package/styles/components/code-block.css +18 -0
  19. package/styles/components/combobox.css +75 -0
  20. package/styles/components/command.css +94 -0
  21. package/styles/components/data-table.css +142 -0
  22. package/styles/components/dialog.css +72 -0
  23. package/styles/components/dropdown.css +54 -0
  24. package/styles/components/empty-state.css +17 -0
  25. package/styles/components/field.css +27 -0
  26. package/styles/components/filter-panel.css +58 -0
  27. package/styles/components/form.css +27 -0
  28. package/styles/components/icon.css +8 -0
  29. package/styles/components/input-group.css +45 -0
  30. package/styles/components/input.css +56 -0
  31. package/styles/components/kbd.css +15 -0
  32. package/styles/components/page-header.css +52 -0
  33. package/styles/components/pagination.css +48 -0
  34. package/styles/components/popover.css +14 -0
  35. package/styles/components/radio.css +28 -0
  36. package/styles/components/row-menu.css +69 -0
  37. package/styles/components/section-card.css +49 -0
  38. package/styles/components/select.css +57 -0
  39. package/styles/components/separator.css +32 -0
  40. package/styles/components/sheet.css +70 -0
  41. package/styles/components/sidebar.css +146 -0
  42. package/styles/components/skeleton.css +32 -0
  43. package/styles/components/spinner.css +26 -0
  44. package/styles/components/stat-card.css +71 -0
  45. package/styles/components/stepper.css +63 -0
  46. package/styles/components/switch.css +45 -0
  47. package/styles/components/tabs.css +40 -0
  48. package/styles/components/textarea.css +31 -0
  49. package/styles/components/toast.css +95 -0
  50. package/styles/components/tooltip.css +53 -0
  51. package/styles/components/topbar.css +24 -0
  52. package/styles/components/typography.css +105 -0
  53. package/styles/patterns/backdrops.css +35 -0
  54. package/styles/patterns/density.css +66 -0
  55. package/styles/patterns/focus.css +38 -0
  56. package/styles/patterns/glass.css +85 -0
  57. package/styles/patterns/high-contrast.css +70 -0
  58. package/styles/patterns/reduced-motion.css +12 -0
  59. package/styles/patterns/scrollbar.css +10 -0
  60. package/styles/reset.css +89 -0
  61. package/styles/tokens/colors.css +106 -0
  62. package/styles/tokens/motion.css +33 -0
  63. package/styles/tokens/radius.css +10 -0
  64. package/styles/tokens/shadows.css +35 -0
  65. package/styles/tokens/spacing.css +19 -0
  66. package/styles/tokens/typography.css +6 -0
  67. package/styles/tokens/z-index.css +12 -0
  68. package/tsconfig.json +20 -21
  69. package/README.md +0 -60
  70. package/src/components/atoms/README.md +0 -11
  71. package/src/components/atoms/aspect-ratio.tsx +0 -32
  72. package/src/components/atoms/avatar.tsx +0 -98
  73. package/src/components/atoms/badge.tsx +0 -44
  74. package/src/components/atoms/brand-mark.tsx +0 -74
  75. package/src/components/atoms/button.tsx +0 -105
  76. package/src/components/atoms/checkbox.tsx +0 -63
  77. package/src/components/atoms/flex-box.tsx +0 -105
  78. package/src/components/atoms/icon.tsx +0 -34
  79. package/src/components/atoms/input.tsx +0 -92
  80. package/src/components/atoms/label.tsx +0 -41
  81. package/src/components/atoms/logo.tsx +0 -89
  82. package/src/components/atoms/progress.tsx +0 -55
  83. package/src/components/atoms/radio-group.tsx +0 -122
  84. package/src/components/atoms/scroll-area.tsx +0 -106
  85. package/src/components/atoms/section.tsx +0 -48
  86. package/src/components/atoms/separator.tsx +0 -45
  87. package/src/components/atoms/skeleton.tsx +0 -17
  88. package/src/components/atoms/slider.tsx +0 -93
  89. package/src/components/atoms/spinner.tsx +0 -47
  90. package/src/components/atoms/switch.tsx +0 -60
  91. package/src/components/atoms/textarea.tsx +0 -78
  92. package/src/components/atoms/toggle.tsx +0 -80
  93. package/src/components/charts/activity-heatmap.tsx +0 -186
  94. package/src/components/charts/axes.tsx +0 -21
  95. package/src/components/charts/chart-container.tsx +0 -254
  96. package/src/components/charts/chart-legend.tsx +0 -67
  97. package/src/components/charts/chart-tooltip.tsx +0 -161
  98. package/src/components/charts/chart-types.tsx +0 -49
  99. package/src/components/charts/containers.tsx +0 -11
  100. package/src/components/charts/data.tsx +0 -16
  101. package/src/components/charts/details.tsx +0 -25
  102. package/src/components/charts/dot-pulse.tsx +0 -61
  103. package/src/components/charts/gauge.tsx +0 -106
  104. package/src/components/charts/grids.tsx +0 -8
  105. package/src/components/charts/index.ts +0 -62
  106. package/src/components/charts/labeled-bar-list.tsx +0 -85
  107. package/src/components/charts/metric-breakdown.tsx +0 -316
  108. package/src/components/charts/references.tsx +0 -8
  109. package/src/components/charts/service-health-list.tsx +0 -85
  110. package/src/components/charts/sparkline-area.tsx +0 -80
  111. package/src/components/charts/sparkline.tsx +0 -52
  112. package/src/components/charts/stacked-bar.tsx +0 -104
  113. package/src/components/charts/text.tsx +0 -10
  114. package/src/components/charts/world-heat-map-inner.tsx +0 -317
  115. package/src/components/charts/world-heat-map.tsx +0 -184
  116. package/src/components/molecules/README.md +0 -12
  117. package/src/components/molecules/action-bar.tsx +0 -73
  118. package/src/components/molecules/activity-item.tsx +0 -74
  119. package/src/components/molecules/alert.tsx +0 -86
  120. package/src/components/molecules/animated-background.tsx +0 -92
  121. package/src/components/molecules/auth-shell.tsx +0 -95
  122. package/src/components/molecules/brand-lockup.tsx +0 -48
  123. package/src/components/molecules/breadcrumb.tsx +0 -157
  124. package/src/components/molecules/button-group.tsx +0 -104
  125. package/src/components/molecules/calendar.tsx +0 -217
  126. package/src/components/molecules/card.tsx +0 -102
  127. package/src/components/molecules/client-brand.tsx +0 -95
  128. package/src/components/molecules/code-block.tsx +0 -86
  129. package/src/components/molecules/countdown-button.tsx +0 -92
  130. package/src/components/molecules/empty-state.tsx +0 -56
  131. package/src/components/molecules/error-state.tsx +0 -42
  132. package/src/components/molecules/field-display.tsx +0 -35
  133. package/src/components/molecules/input-otp.tsx +0 -74
  134. package/src/components/molecules/launcher-card.tsx +0 -152
  135. package/src/components/molecules/loading-state.tsx +0 -36
  136. package/src/components/molecules/notification-item.tsx +0 -67
  137. package/src/components/molecules/notification-list.tsx +0 -45
  138. package/src/components/molecules/number-badge.tsx +0 -53
  139. package/src/components/molecules/or-separator.tsx +0 -38
  140. package/src/components/molecules/page-header.tsx +0 -88
  141. package/src/components/molecules/page-tabs.tsx +0 -94
  142. package/src/components/molecules/pagination.tsx +0 -150
  143. package/src/components/molecules/password-input.tsx +0 -83
  144. package/src/components/molecules/password-strength-meter.tsx +0 -104
  145. package/src/components/molecules/phone-input.tsx +0 -200
  146. package/src/components/molecules/search-bar.tsx +0 -64
  147. package/src/components/molecules/secret-field.tsx +0 -158
  148. package/src/components/molecules/section-card.tsx +0 -91
  149. package/src/components/molecules/social-buttons.tsx +0 -165
  150. package/src/components/molecules/stat-card.tsx +0 -100
  151. package/src/components/molecules/status-badge.tsx +0 -42
  152. package/src/components/molecules/stepper.tsx +0 -96
  153. package/src/components/molecules/table.tsx +0 -157
  154. package/src/components/molecules/terminal.tsx +0 -74
  155. package/src/components/molecules/toggle-group.tsx +0 -145
  156. package/src/components/molecules/tooltip.tsx +0 -155
  157. package/src/components/molecules/user-avatar-chip.tsx +0 -71
  158. package/src/components/organisms/README.md +0 -14
  159. package/src/components/organisms/accordion.tsx +0 -154
  160. package/src/components/organisms/alert-dialog.tsx +0 -277
  161. package/src/components/organisms/carousel.tsx +0 -244
  162. package/src/components/organisms/collapsible.tsx +0 -69
  163. package/src/components/organisms/command.tsx +0 -144
  164. package/src/components/organisms/context-menu.tsx +0 -339
  165. package/src/components/organisms/dashboard-grid.tsx +0 -369
  166. package/src/components/organisms/data-table.tsx +0 -330
  167. package/src/components/organisms/dialog.tsx +0 -312
  168. package/src/components/organisms/drawer.tsx +0 -123
  169. package/src/components/organisms/dropdown-menu.tsx +0 -440
  170. package/src/components/organisms/editors/code-editor.tsx +0 -144
  171. package/src/components/organisms/editors/index.ts +0 -4
  172. package/src/components/organisms/editors/markdown-editor.tsx +0 -153
  173. package/src/components/organisms/editors/markdown-renderer.ts +0 -27
  174. package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
  175. package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
  176. package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
  177. package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
  178. package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
  179. package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
  180. package/src/components/organisms/error-boundary.tsx +0 -61
  181. package/src/components/organisms/form.tsx +0 -174
  182. package/src/components/organisms/hover-card.tsx +0 -115
  183. package/src/components/organisms/menubar.tsx +0 -498
  184. package/src/components/organisms/navbar.tsx +0 -104
  185. package/src/components/organisms/navigation-menu.tsx +0 -235
  186. package/src/components/organisms/popover.tsx +0 -149
  187. package/src/components/organisms/resizable.tsx +0 -58
  188. package/src/components/organisms/schema-form.tsx +0 -232
  189. package/src/components/organisms/select.tsx +0 -309
  190. package/src/components/organisms/sheet.tsx +0 -265
  191. package/src/components/organisms/sidebar.tsx +0 -1040
  192. package/src/components/organisms/sonner.tsx +0 -96
  193. package/src/components/organisms/tabs.tsx +0 -133
  194. package/src/components/organisms/theme-provider.tsx +0 -101
  195. package/src/hooks/use-mobile.tsx +0 -19
  196. package/src/lib/portal-container.tsx +0 -35
  197. package/src/lib/utils.ts +0 -6
  198. package/src/native.ts +0 -23
  199. package/src/tokens/colors.ts +0 -91
  200. package/src/tokens/index.ts +0 -3
  201. package/src/tokens/spacing.ts +0 -55
  202. package/src/tokens/typography.ts +0 -27
  203. package/styles/dashboard-grid.css +0 -47
  204. package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
  205. package/styles/glass.css +0 -171
  206. package/styles/leaflet.css +0 -13
  207. package/styles/tokens.css +0 -317
  208. package/tailwind.config.ts +0 -70
@@ -1,316 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export type MetricBreakdownTone = "success" | "warning" | "error" | "neutral";
6
-
7
- const TONE_HSL: Record<MetricBreakdownTone, string> = {
8
- success: "143 70% 40%",
9
- warning: "38 92% 50%",
10
- error: "0 80% 60%",
11
- neutral: "var(--muted-foreground)",
12
- };
13
-
14
- export interface MetricBreakdownRow {
15
- /** Row label (often a code or category key). Used as the React key. */
16
- label: React.ReactNode;
17
- /** Numeric value rendered to the right of the label. */
18
- value: number;
19
- /**
20
- * Period-over-period change as a percentage. Positive renders as `▲ N%` in
21
- * the success hue, negative as `▼ N%` in the error hue. Omit to hide the
22
- * delta column for that row.
23
- */
24
- delta?: number;
25
- /**
26
- * CSS variable name (without leading `--`) used for the row's swatch and
27
- * bar fill. Falls back to the wrapper's `defaultColorVar`.
28
- */
29
- colorVar?: string;
30
- }
31
-
32
- export interface MetricBreakdownChip {
33
- /** Chip label, typically a code or short tag. Used as the React key. */
34
- label: React.ReactNode;
35
- /** Optional count rendered after a thin separator. */
36
- count?: number;
37
- /** Visual tone. Default `error`. */
38
- tone?: MetricBreakdownTone;
39
- }
40
-
41
- export interface MetricBreakdownProps extends React.HTMLAttributes<HTMLDivElement> {
42
- /** Primary metric value rendered as the headline number. */
43
- value: React.ReactNode;
44
- /** Caption rendered below the headline value (uppercase muted). */
45
- label: React.ReactNode;
46
- /** Secondary metric rendered top-right (e.g. an error rate). */
47
- rate?: React.ReactNode;
48
- /** Caption for the secondary metric (uppercase muted). */
49
- rateLabel?: React.ReactNode;
50
- /** Tone driving the secondary metric color. Default `neutral`. */
51
- rateTone?: MetricBreakdownTone;
52
- /** Sparkline data points. Rendered as an SVG line + area-fill ramp. */
53
- spark?: number[];
54
- /** Unit suffix rendered next to the last value of the sparkline, e.g. `req/s`. */
55
- sparkUnit?: React.ReactNode;
56
- /**
57
- * CSS variable name (without leading `--`) for the sparkline color. Default
58
- * `chart-1`. Also used as the fallback for breakdown rows that omit `colorVar`.
59
- */
60
- defaultColorVar?: string;
61
- /** Pixel height of the sparkline. Default `36`. */
62
- sparkHeight?: number;
63
- /** Category breakdown rows. */
64
- breakdown?: MetricBreakdownRow[];
65
- /** Format breakdown row values. Default `toLocaleString`. */
66
- valueFormatter?: (value: number) => string;
67
- /** Trailing chip row (e.g. recent error codes). */
68
- chips?: MetricBreakdownChip[];
69
- /**
70
- * Label rendered before the chip row (uppercase muted). Default `"Errors"`.
71
- * Pass `null` to hide.
72
- */
73
- chipsLabel?: React.ReactNode;
74
- }
75
-
76
- /**
77
- * Multi-section metric card: a headline value with an optional rate, a small
78
- * inline trend sparkline, a per-category breakdown with delta arrows, and a
79
- * trailing chip row for recent issues.
80
- *
81
- * Designed for "throughput-style" dashboards (token issuance, API request
82
- * volume, job throughput, sign-ups by source): anywhere a single metric needs
83
- * to be decomposed by category and contextualized by trend and notable issues
84
- * in one card. Identity-agnostic; bring your own labels and color tokens.
85
- */
86
- export const MetricBreakdown = React.forwardRef<HTMLDivElement, MetricBreakdownProps>(
87
- (
88
- {
89
- value,
90
- label,
91
- rate,
92
- rateLabel,
93
- rateTone = "neutral",
94
- spark,
95
- sparkUnit,
96
- defaultColorVar = "chart-1",
97
- sparkHeight = 36,
98
- breakdown,
99
- valueFormatter = (v) => v.toLocaleString(),
100
- chips,
101
- chipsLabel = "Errors",
102
- className,
103
- ...props
104
- },
105
- ref,
106
- ) => {
107
- const rateColor =
108
- rateTone === "neutral" ? "hsl(var(--muted-foreground))" : `hsl(${TONE_HSL[rateTone]})`;
109
- const showChipsLabel = chipsLabel !== null && chipsLabel !== undefined && chipsLabel !== "";
110
- return (
111
- <div ref={ref} className={cn("w-full", className)} {...props}>
112
- <div className="mb-2.5 flex items-baseline justify-between gap-3">
113
- <div>
114
- <div className="font-mono text-[22px] font-semibold leading-[1.1] tabular-nums">
115
- {value}
116
- </div>
117
- <div className="text-[11px] uppercase tracking-[0.04em] text-muted-foreground">
118
- {label}
119
- </div>
120
- </div>
121
- {(rate !== undefined || rateLabel) && (
122
- <div className="text-right">
123
- <div
124
- className="font-mono text-[13px] font-medium tabular-nums"
125
- style={{ color: rateColor }}
126
- >
127
- {rate}
128
- </div>
129
- {rateLabel && (
130
- <div className="text-[11px] uppercase tracking-[0.04em] text-muted-foreground">
131
- {rateLabel}
132
- </div>
133
- )}
134
- </div>
135
- )}
136
- </div>
137
-
138
- {spark && spark.length > 1 && (
139
- <MetricBreakdownSpark
140
- data={spark}
141
- height={sparkHeight}
142
- colorVar={defaultColorVar}
143
- unit={sparkUnit}
144
- />
145
- )}
146
-
147
- {breakdown && breakdown.length > 0 && (
148
- <MetricBreakdownRows
149
- rows={breakdown}
150
- defaultColorVar={defaultColorVar}
151
- valueFormatter={valueFormatter}
152
- />
153
- )}
154
-
155
- {chips && chips.length > 0 && (
156
- <div
157
- className={cn(
158
- "flex flex-wrap items-center gap-1.5",
159
- (breakdown && breakdown.length > 0) || spark
160
- ? "mt-3 border-t border-border pt-2.5"
161
- : "",
162
- )}
163
- >
164
- {showChipsLabel && (
165
- <span className="mr-0.5 text-[10.5px] uppercase tracking-[0.04em] text-muted-foreground">
166
- {chipsLabel}
167
- </span>
168
- )}
169
- {chips.map((chip, i) => {
170
- const tone = chip.tone ?? "error";
171
- const hsl = TONE_HSL[tone];
172
- return (
173
- <span
174
- key={`${i}-${typeof chip.label === "string" ? chip.label : i}`}
175
- className="inline-flex items-center gap-1 rounded font-mono text-[10.5px]"
176
- style={{
177
- padding: "2px 6px",
178
- background: `hsl(${hsl} / 0.1)`,
179
- color: `hsl(${hsl})`,
180
- }}
181
- >
182
- {chip.label}
183
- {chip.count !== undefined && <span className="opacity-70">·{chip.count}</span>}
184
- </span>
185
- );
186
- })}
187
- </div>
188
- )}
189
- </div>
190
- );
191
- },
192
- );
193
- MetricBreakdown.displayName = "MetricBreakdown";
194
-
195
- interface MetricBreakdownSparkProps {
196
- data: number[];
197
- height: number;
198
- colorVar: string;
199
- unit?: React.ReactNode;
200
- }
201
-
202
- function MetricBreakdownSpark({ data, height, colorVar, unit }: MetricBreakdownSparkProps) {
203
- const max = Math.max(...data);
204
- const min = Math.min(...data);
205
- const range = max - min || 1;
206
- const w = (data.length - 1) * 10;
207
- const h = height;
208
- const pts = data
209
- .map((v, i) => {
210
- const x = i * 10;
211
- const y = h - 2 - ((v - min) / range) * (h - 6);
212
- return `${x},${y}`;
213
- })
214
- .join(" ");
215
- const area = `0,${h} ${pts} ${w},${h}`;
216
- const last = data[data.length - 1];
217
- const lastY = h - 2 - ((last - min) / range) * (h - 6);
218
- const fillId = React.useId();
219
- return (
220
- <div className="relative mb-3.5" style={{ height }}>
221
- <svg
222
- viewBox={`0 0 ${w} ${h}`}
223
- preserveAspectRatio="none"
224
- className="h-full w-full overflow-visible"
225
- aria-hidden
226
- >
227
- <defs>
228
- <linearGradient id={fillId} x1="0" x2="0" y1="0" y2="1">
229
- <stop offset="0%" stopColor={`hsl(var(--${colorVar}))`} stopOpacity="0.25" />
230
- <stop offset="100%" stopColor={`hsl(var(--${colorVar}))`} stopOpacity="0" />
231
- </linearGradient>
232
- </defs>
233
- <polygon points={area} fill={`url(#${fillId})`} />
234
- <polyline
235
- points={pts}
236
- fill="none"
237
- stroke={`hsl(var(--${colorVar}))`}
238
- strokeWidth="1.5"
239
- vectorEffect="non-scaling-stroke"
240
- />
241
- <circle cx={w} cy={lastY} r="2.5" fill={`hsl(var(--${colorVar}))`} />
242
- </svg>
243
- {unit !== undefined && (
244
- <div
245
- className="absolute right-0 top-0 bg-card px-1 font-mono text-[11px]"
246
- style={{ color: `hsl(var(--${colorVar}))` }}
247
- >
248
- {last}
249
- {typeof unit === "string" ? ` ${unit}` : <> {unit}</>}
250
- </div>
251
- )}
252
- </div>
253
- );
254
- }
255
-
256
- interface MetricBreakdownRowsProps {
257
- rows: MetricBreakdownRow[];
258
- defaultColorVar: string;
259
- valueFormatter: (value: number) => string;
260
- }
261
-
262
- function MetricBreakdownRows({ rows, defaultColorVar, valueFormatter }: MetricBreakdownRowsProps) {
263
- const total = rows.reduce((sum, r) => sum + r.value, 0) || 1;
264
- return (
265
- <div className="flex flex-col gap-2">
266
- {rows.map((row, i) => {
267
- const colorVar = row.colorVar ?? defaultColorVar;
268
- const pct = (row.value / total) * 100;
269
- const hasDelta = row.delta !== undefined;
270
- const up = hasDelta && (row.delta as number) >= 0;
271
- const deltaHsl = up ? TONE_HSL.success : TONE_HSL.error;
272
- return (
273
- <div
274
- key={`${i}-${typeof row.label === "string" ? row.label : i}`}
275
- className="text-[12.5px]"
276
- >
277
- <div className="mb-1 flex items-center gap-2">
278
- <span
279
- className="size-2 shrink-0 rounded-sm"
280
- style={{ background: `hsl(var(--${colorVar}))` }}
281
- aria-hidden
282
- />
283
- <span className="flex-1 truncate font-mono text-[11.5px] text-foreground">
284
- {row.label}
285
- </span>
286
- <span className="font-mono text-[11.5px] tabular-nums">
287
- {valueFormatter(row.value)}
288
- </span>
289
- {hasDelta && (
290
- <span
291
- className="min-w-[38px] text-right font-mono text-[10.5px] tabular-nums"
292
- style={{ color: `hsl(${deltaHsl})` }}
293
- >
294
- {up ? "▲" : "▼"} {Math.abs(row.delta as number)}%
295
- </span>
296
- )}
297
- </div>
298
- <div
299
- className="overflow-hidden rounded-full bg-muted"
300
- style={{ height: 3 }}
301
- aria-hidden
302
- >
303
- <div
304
- className="h-full rounded-full"
305
- style={{
306
- width: `${pct}%`,
307
- background: `hsl(var(--${colorVar}))`,
308
- }}
309
- />
310
- </div>
311
- </div>
312
- );
313
- })}
314
- </div>
315
- );
316
- }
@@ -1,8 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * Reference primitives — pure pass-throughs. Theming via CSS in
5
- * `<ChartContainer>` (`[&_.recharts-reference-line_[stroke='#ccc']]:stroke-border`).
6
- * Wrapping breaks Recharts' child-by-type detection.
7
- */
8
- export { ReferenceArea, ReferenceDot, ReferenceLine } from "recharts";
@@ -1,85 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export type ServiceHealthStatus = "healthy" | "degraded" | "down";
6
-
7
- export interface ServiceHealthItem {
8
- /** Service name shown in the row. Used as the React key. */
9
- name: string;
10
- /** Status — drives the dot color. */
11
- status: ServiceHealthStatus;
12
- /**
13
- * Right-aligned meta cells, monospace, muted-foreground. Pass an array of
14
- * primitives or React nodes — each renders as its own cell.
15
- */
16
- meta?: React.ReactNode[];
17
- }
18
-
19
- export interface ServiceHealthListProps extends React.HTMLAttributes<HTMLDivElement> {
20
- /** Service rows rendered top-to-bottom. */
21
- items: ServiceHealthItem[];
22
- /** Caption rendered below the list. */
23
- caption?: React.ReactNode;
24
- }
25
-
26
- const DOT_TOKENS: Record<ServiceHealthStatus, string> = {
27
- healthy: "143 70% 45%",
28
- degraded: "38 92% 50%",
29
- down: "0 80% 60%",
30
- };
31
-
32
- /**
33
- * Vertical list of services with a colored status dot, name, and optional
34
- * monospace meta cells (latency, uptime, region, …). The dot has a 3-px halo
35
- * matching the status hue at 18% opacity to pull focus.
36
- */
37
- export const ServiceHealthList = React.forwardRef<HTMLDivElement, ServiceHealthListProps>(
38
- ({ items, caption, className, ...props }, ref) => {
39
- return (
40
- <div ref={ref} className={cn("w-full", className)} {...props}>
41
- <ul className="flex flex-col gap-2.5">
42
- {items.map((item) => {
43
- const hsl = DOT_TOKENS[item.status];
44
- const isHealthy = item.status === "healthy";
45
- return (
46
- <li key={item.name} className="flex items-center gap-2.5 text-[13px]">
47
- <span className="relative flex size-2 shrink-0">
48
- {isHealthy && (
49
- <span
50
- aria-hidden
51
- className="absolute inline-flex h-full w-full animate-ping rounded-full opacity-75"
52
- style={{ background: `hsl(${hsl})` }}
53
- />
54
- )}
55
- <span
56
- role="img"
57
- aria-label={`Status: ${item.status}`}
58
- className="relative inline-flex size-2 rounded-full"
59
- style={{
60
- background: `hsl(${hsl})`,
61
- boxShadow: isHealthy
62
- ? `0 0 6px hsl(${hsl}), 0 0 0 3px hsl(${hsl} / 0.18)`
63
- : `0 0 0 3px hsl(${hsl} / 0.18)`,
64
- }}
65
- />
66
- </span>
67
- <span className="flex-1 font-medium">{item.name}</span>
68
- {item.meta?.map((cell, i) => (
69
- <span
70
- key={`${item.name}-meta-${i}`}
71
- className="font-mono text-[11.5px] text-muted-foreground"
72
- >
73
- {cell}
74
- </span>
75
- ))}
76
- </li>
77
- );
78
- })}
79
- </ul>
80
- {caption && <p className="mt-3 text-xs text-muted-foreground">{caption}</p>}
81
- </div>
82
- );
83
- },
84
- );
85
- ServiceHealthList.displayName = "ServiceHealthList";
@@ -1,80 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface SparklineAreaProps extends React.HTMLAttributes<HTMLDivElement> {
6
- /** Values to plot. Each entry maps to a point on the area line. Needs at least 2 points. */
7
- data: number[];
8
- /** Pixel height of the chart area. */
9
- height?: number;
10
- /**
11
- * CSS variable name (without leading `--`) used for the stroke and fill.
12
- * Default `chart-1`. The variable should resolve to an HSL triplet.
13
- */
14
- colorVar?: string;
15
- /** Caption rendered below the chart. */
16
- caption?: React.ReactNode;
17
- }
18
-
19
- /**
20
- * Pure-SVG area sparkline for inline embedding in `<StatCard>` /
21
- * `<SectionCard>`. Renders a filled area with a gradient fade, a stroke line,
22
- * and a dot on the last data point. Decorative only (no tooltips, no axes).
23
- */
24
- export const SparklineArea = React.forwardRef<HTMLDivElement, SparklineAreaProps>(
25
- ({ data, height = 48, colorVar = "chart-1", caption, className, ...props }, ref) => {
26
- const fillId = React.useId();
27
-
28
- if (data.length < 2) return null;
29
-
30
- const max = Math.max(...data);
31
- const min = Math.min(...data);
32
- const range = max - min || 1;
33
- const w = (data.length - 1) * 10;
34
- const h = height;
35
- const padding = 3; // vertical padding for the end-dot
36
-
37
- const pts = data
38
- .map((v, i) => {
39
- const x = i * 10;
40
- const y = h - padding - ((v - min) / range) * (h - padding * 2);
41
- return `${x},${y}`;
42
- })
43
- .join(" ");
44
-
45
- const area = `0,${h} ${pts} ${w},${h}`;
46
- const last = data[data.length - 1];
47
- const lastY = h - padding - ((last - min) / range) * (h - padding * 2);
48
- const color = `hsl(var(--${colorVar}))`;
49
-
50
- return (
51
- <div ref={ref} className={cn("w-full", className)} {...props}>
52
- <svg
53
- viewBox={`0 0 ${w} ${h}`}
54
- preserveAspectRatio="none"
55
- className="h-full w-full overflow-visible"
56
- style={{ height }}
57
- aria-hidden
58
- >
59
- <defs>
60
- <linearGradient id={fillId} x1="0" x2="0" y1="0" y2="1">
61
- <stop offset="0%" stopColor={color} stopOpacity="0.25" />
62
- <stop offset="100%" stopColor={color} stopOpacity="0" />
63
- </linearGradient>
64
- </defs>
65
- <polygon points={area} fill={`url(#${fillId})`} />
66
- <polyline
67
- points={pts}
68
- fill="none"
69
- stroke={color}
70
- strokeWidth="1.5"
71
- vectorEffect="non-scaling-stroke"
72
- />
73
- <circle cx={w} cy={lastY} r="2.5" fill={color} />
74
- </svg>
75
- {caption && <p className="mt-2 text-xs text-muted-foreground">{caption}</p>}
76
- </div>
77
- );
78
- },
79
- );
80
- SparklineArea.displayName = "SparklineArea";
@@ -1,52 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface SparklineProps extends React.HTMLAttributes<HTMLDivElement> {
6
- /** Values to plot. Each entry maps to a bar. */
7
- data: number[];
8
- /** Pixel height of the chart. */
9
- height?: number;
10
- /**
11
- * CSS variable name (without leading `--`) used for the bar fill. Default
12
- * `chart-1`. The variable should resolve to an HSL triplet for `hsl()`.
13
- */
14
- colorVar?: string;
15
- /** Pixel gap between bars. */
16
- gap?: number;
17
- /** Caption rendered below the bars. */
18
- caption?: React.ReactNode;
19
- }
20
-
21
- /**
22
- * Pure-CSS mini bar chart for inline embedding in `<StatCard>` /
23
- * `<SectionCard>`. Decorative — for live data, use a `<BarChart>` from the
24
- * charts tier with proper axes and tooltips.
25
- */
26
- export const Sparkline = React.forwardRef<HTMLDivElement, SparklineProps>(
27
- ({ data, height = 160, colorVar = "chart-1", gap = 4, caption, className, ...props }, ref) => {
28
- const max = Math.max(1, ...data);
29
- return (
30
- <div ref={ref} className={cn("w-full", className)} {...props}>
31
- <div className="flex w-full items-end" style={{ height, gap }}>
32
- {data.map((value, i) => {
33
- const pct = Math.max(0, Math.min(100, (value / max) * 100));
34
- return (
35
- <div
36
- key={`bar-${i}-${value}`}
37
- className="flex-1 rounded-sm"
38
- style={{
39
- height: `${pct}%`,
40
- background: `hsl(var(--${colorVar}))`,
41
- }}
42
- aria-hidden
43
- />
44
- );
45
- })}
46
- </div>
47
- {caption && <p className="mt-3 text-xs text-muted-foreground">{caption}</p>}
48
- </div>
49
- );
50
- },
51
- );
52
- Sparkline.displayName = "Sparkline";
@@ -1,104 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface StackedBarSegment {
6
- /** Label shown in the legend. Used as the React key. */
7
- label: string;
8
- /** Numeric value (raw count or percentage). Segments size proportionally to the sum. */
9
- value: number;
10
- /**
11
- * CSS variable name (without leading `--`) used for the segment fill. Default
12
- * `chart-1`. Each segment can override; unspecified segments fall through
13
- * to the wrapper's `defaultColorVar`.
14
- */
15
- colorVar?: string;
16
- }
17
-
18
- export interface StackedBarProps extends React.HTMLAttributes<HTMLDivElement> {
19
- /** Segments rendered left-to-right with width proportional to `value`. */
20
- segments: StackedBarSegment[];
21
- /**
22
- * Fallback CSS variable name for segments that omit `colorVar`. Default
23
- * `chart-1`.
24
- */
25
- defaultColorVar?: string;
26
- /** Pixel height of the bar pill. Default `10`. */
27
- height?: number;
28
- /** Show the legend list below the bar. Default `true`. */
29
- showLegend?: boolean;
30
- /** Format the per-segment value in the legend. Default `(v) => `${v}%``. */
31
- valueFormatter?: (value: number) => string;
32
- /** Caption rendered below the legend. */
33
- caption?: React.ReactNode;
34
- }
35
-
36
- /**
37
- * Horizontal stacked-percent bar with an optional swatch legend below. Segments
38
- * are sized proportionally to their value's share of the total — pass raw
39
- * counts or pre-computed percentages, the rendering is the same.
40
- */
41
- export const StackedBar = React.forwardRef<HTMLDivElement, StackedBarProps>(
42
- (
43
- {
44
- segments,
45
- defaultColorVar = "chart-1",
46
- height = 10,
47
- showLegend = true,
48
- valueFormatter = (v) => `${v}%`,
49
- caption,
50
- className,
51
- ...props
52
- },
53
- ref,
54
- ) => {
55
- const total = segments.reduce((sum, s) => sum + s.value, 0) || 1;
56
- return (
57
- <div ref={ref} className={cn("w-full", className)} {...props}>
58
- <div
59
- className="flex w-full overflow-hidden rounded-full bg-muted"
60
- style={{ height }}
61
- role="presentation"
62
- >
63
- {segments.map((seg) => {
64
- const pct = (seg.value / total) * 100;
65
- const colorVar = seg.colorVar ?? defaultColorVar;
66
- return (
67
- <div
68
- key={seg.label}
69
- style={{
70
- width: `${pct}%`,
71
- background: `hsl(var(--${colorVar}))`,
72
- }}
73
- title={`${seg.label}: ${valueFormatter(seg.value)}`}
74
- aria-hidden
75
- />
76
- );
77
- })}
78
- </div>
79
- {showLegend && (
80
- <ul className="mt-3 flex flex-col gap-2">
81
- {segments.map((seg) => {
82
- const colorVar = seg.colorVar ?? defaultColorVar;
83
- return (
84
- <li key={seg.label} className="flex items-center gap-2.5 text-[13px]">
85
- <span
86
- className="size-2 shrink-0 rounded-full"
87
- style={{ background: `hsl(var(--${colorVar}))` }}
88
- aria-hidden
89
- />
90
- <span className="flex-1">{seg.label}</span>
91
- <span className="font-mono text-xs text-muted-foreground">
92
- {valueFormatter(seg.value)}
93
- </span>
94
- </li>
95
- );
96
- })}
97
- </ul>
98
- )}
99
- {caption && <p className="mt-3 text-xs text-muted-foreground">{caption}</p>}
100
- </div>
101
- );
102
- },
103
- );
104
- StackedBar.displayName = "StackedBar";
@@ -1,10 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * Text-rendering primitives. `Label` is aliased to `ChartLabel` to avoid
5
- * collision with canvas's form `Label` atom.
6
- *
7
- * Re-exported via `export { … }` so TypeScript doesn't have to name internal
8
- * Recharts prop types in the d.ts emit.
9
- */
10
- export { Label as ChartLabel, LabelList, Text } from "recharts";