@misoto22/design 0.3.1 → 0.6.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 +124 -0
- package/bin/misoto22-design.mjs +205 -0
- package/dist/agent/Accordion.md +41 -0
- package/dist/agent/Alert.md +42 -0
- package/dist/agent/AppShell.md +40 -0
- package/dist/agent/AreaChart.md +68 -0
- package/dist/agent/Article.md +41 -0
- package/dist/agent/Avatar.md +32 -0
- package/dist/agent/Badge.md +40 -0
- package/dist/agent/BarChart.md +65 -0
- package/dist/agent/BarList.md +40 -0
- package/dist/agent/BigNumber.md +45 -0
- package/dist/agent/BoxPlot.md +58 -0
- package/dist/agent/Breadcrumb.md +32 -0
- package/dist/agent/BulletChart.md +48 -0
- package/dist/agent/Button.md +56 -0
- package/dist/agent/Calendar.md +42 -0
- package/dist/agent/Card.md +49 -0
- package/dist/agent/Checkbox.md +35 -0
- package/dist/agent/Collapsible.md +46 -0
- package/dist/agent/Combobox.md +50 -0
- package/dist/agent/Command.md +93 -0
- package/dist/agent/ComposedChart.md +57 -0
- package/dist/agent/ContextMenu.md +59 -0
- package/dist/agent/DatePicker.md +75 -0
- package/dist/agent/Diagram.md +46 -0
- package/dist/agent/Dialog.md +52 -0
- package/dist/agent/DropdownMenu.md +70 -0
- package/dist/agent/EmptyState.md +37 -0
- package/dist/agent/ErrorState.md +41 -0
- package/dist/agent/Facet.md +82 -0
- package/dist/agent/Field.md +37 -0
- package/dist/agent/FigureBand.md +37 -0
- package/dist/agent/FloatingIconButton.md +38 -0
- package/dist/agent/FunnelChart.md +52 -0
- package/dist/agent/Heatmap.md +53 -0
- package/dist/agent/Histogram.md +63 -0
- package/dist/agent/Input.md +30 -0
- package/dist/agent/Kbd.md +29 -0
- package/dist/agent/LineChart.md +60 -0
- package/dist/agent/LinkArrow.md +29 -0
- package/dist/agent/NativeSelect.md +40 -0
- package/dist/agent/NavItem.md +35 -0
- package/dist/agent/Pagination.md +43 -0
- package/dist/agent/PieChart.md +55 -0
- package/dist/agent/Popover.md +57 -0
- package/dist/agent/Progress.md +33 -0
- package/dist/agent/RadarChart.md +54 -0
- package/dist/agent/RadialChart.md +57 -0
- package/dist/agent/RadioGroup.md +44 -0
- package/dist/agent/SankeyChart.md +61 -0
- package/dist/agent/ScatterChart.md +54 -0
- package/dist/agent/ScrollArea.md +40 -0
- package/dist/agent/SearchableMenu.md +46 -0
- package/dist/agent/Select.md +75 -0
- package/dist/agent/Separator.md +35 -0
- package/dist/agent/Sheet.md +57 -0
- package/dist/agent/Skeleton.md +73 -0
- package/dist/agent/Slider.md +39 -0
- package/dist/agent/Sparkline.md +45 -0
- package/dist/agent/Spinner.md +39 -0
- package/dist/agent/StatusDot.md +36 -0
- package/dist/agent/StatusPill.md +28 -0
- package/dist/agent/Steps.md +44 -0
- package/dist/agent/Switch.md +29 -0
- package/dist/agent/Table.md +85 -0
- package/dist/agent/Tabs.md +48 -0
- package/dist/agent/Tag.md +35 -0
- package/dist/agent/Textarea.md +26 -0
- package/dist/agent/Toast.md +26 -0
- package/dist/agent/ToggleGroup.md +44 -0
- package/dist/agent/Tooltip.md +51 -0
- package/dist/agent/TreemapChart.md +52 -0
- package/dist/agent/WaterfallChart.md +62 -0
- package/dist/agent/catalog.json +1791 -0
- package/dist/agent/index.md +131 -0
- package/dist/article.css +393 -0
- package/dist/charts/AreaChart/AreaChart.d.ts +191 -0
- package/dist/charts/AreaChart/AreaChart.js +480 -0
- package/dist/charts/AreaChart/AreaChart.js.map +1 -0
- package/dist/charts/BarChart/BarChart.d.ts +179 -0
- package/dist/charts/BarChart/BarChart.js +591 -0
- package/dist/charts/BarChart/BarChart.js.map +1 -0
- package/dist/charts/BarList/BarList.d.ts +57 -0
- package/dist/charts/BarList/BarList.js +71 -0
- package/dist/charts/BarList/BarList.js.map +1 -0
- package/dist/charts/BigNumber/BigNumber.d.ts +56 -0
- package/dist/charts/BigNumber/BigNumber.js +31 -0
- package/dist/charts/BigNumber/BigNumber.js.map +1 -0
- package/dist/charts/BoxPlot/BoxPlot.d.ts +177 -0
- package/dist/charts/BoxPlot/BoxPlot.js +495 -0
- package/dist/charts/BoxPlot/BoxPlot.js.map +1 -0
- package/dist/charts/BulletChart/BulletChart.d.ts +100 -0
- package/dist/charts/BulletChart/BulletChart.js +162 -0
- package/dist/charts/BulletChart/BulletChart.js.map +1 -0
- package/dist/charts/ComposedChart/ComposedChart.d.ts +171 -0
- package/dist/charts/ComposedChart/ComposedChart.js +667 -0
- package/dist/charts/ComposedChart/ComposedChart.js.map +1 -0
- package/dist/charts/Facet/Facet.d.ts +240 -0
- package/dist/charts/Facet/Facet.js +133 -0
- package/dist/charts/Facet/Facet.js.map +1 -0
- package/dist/charts/Facet/panels.d.ts +187 -0
- package/dist/charts/Facet/panels.js +203 -0
- package/dist/charts/Facet/panels.js.map +1 -0
- package/dist/charts/FunnelChart/FunnelChart.d.ts +101 -0
- package/dist/charts/FunnelChart/FunnelChart.js +147 -0
- package/dist/charts/FunnelChart/FunnelChart.js.map +1 -0
- package/dist/charts/Heatmap/Heatmap.d.ts +78 -0
- package/dist/charts/Heatmap/Heatmap.js +108 -0
- package/dist/charts/Heatmap/Heatmap.js.map +1 -0
- package/dist/charts/Histogram/Histogram.d.ts +165 -0
- package/dist/charts/Histogram/Histogram.js +350 -0
- package/dist/charts/Histogram/Histogram.js.map +1 -0
- package/dist/charts/LineChart/LineChart.d.ts +167 -0
- package/dist/charts/LineChart/LineChart.js +486 -0
- package/dist/charts/LineChart/LineChart.js.map +1 -0
- package/dist/charts/PieChart/PieChart.d.ts +128 -0
- package/dist/charts/PieChart/PieChart.js +309 -0
- package/dist/charts/PieChart/PieChart.js.map +1 -0
- package/dist/charts/RadarChart/RadarChart.d.ts +123 -0
- package/dist/charts/RadarChart/RadarChart.js +299 -0
- package/dist/charts/RadarChart/RadarChart.js.map +1 -0
- package/dist/charts/RadialChart/RadialChart.d.ts +124 -0
- package/dist/charts/RadialChart/RadialChart.js +257 -0
- package/dist/charts/RadialChart/RadialChart.js.map +1 -0
- package/dist/charts/SankeyChart/SankeyChart.d.ts +137 -0
- package/dist/charts/SankeyChart/SankeyChart.js +501 -0
- package/dist/charts/SankeyChart/SankeyChart.js.map +1 -0
- package/dist/charts/ScatterChart/ScatterChart.d.ts +128 -0
- package/dist/charts/ScatterChart/ScatterChart.js +269 -0
- package/dist/charts/ScatterChart/ScatterChart.js.map +1 -0
- package/dist/charts/Sparkline/Sparkline.d.ts +53 -0
- package/dist/charts/Sparkline/Sparkline.js +93 -0
- package/dist/charts/Sparkline/Sparkline.js.map +1 -0
- package/dist/charts/TreemapChart/TreemapChart.d.ts +93 -0
- package/dist/charts/TreemapChart/TreemapChart.js +118 -0
- package/dist/charts/TreemapChart/TreemapChart.js.map +1 -0
- package/dist/charts/WaterfallChart/WaterfallChart.d.ts +164 -0
- package/dist/charts/WaterfallChart/WaterfallChart.js +319 -0
- package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -0
- package/dist/charts/index.d.ts +41 -0
- package/dist/charts/index.js +128 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/lib/annotations.d.ts +103 -0
- package/dist/charts/lib/annotations.js +99 -0
- package/dist/charts/lib/annotations.js.map +1 -0
- package/dist/charts/lib/axis.d.ts +18 -0
- package/dist/charts/lib/axis.js +21 -0
- package/dist/charts/lib/axis.js.map +1 -0
- package/dist/charts/lib/background.d.ts +33 -0
- package/dist/charts/lib/background.js +173 -0
- package/dist/charts/lib/background.js.map +1 -0
- package/dist/charts/lib/brush.d.ts +94 -0
- package/dist/charts/lib/brush.js +535 -0
- package/dist/charts/lib/brush.js.map +1 -0
- package/dist/charts/lib/chart.d.ts +94 -0
- package/dist/charts/lib/chart.js +134 -0
- package/dist/charts/lib/chart.js.map +1 -0
- package/dist/charts/lib/dot.d.ts +29 -0
- package/dist/charts/lib/dot.js +78 -0
- package/dist/charts/lib/dot.js.map +1 -0
- package/dist/charts/lib/empty.d.ts +31 -0
- package/dist/charts/lib/empty.js +13 -0
- package/dist/charts/lib/empty.js.map +1 -0
- package/dist/charts/lib/export.d.ts +103 -0
- package/dist/charts/lib/export.js +204 -0
- package/dist/charts/lib/export.js.map +1 -0
- package/dist/charts/lib/figure.d.ts +61 -0
- package/dist/charts/lib/figure.js +59 -0
- package/dist/charts/lib/figure.js.map +1 -0
- package/dist/charts/lib/format.d.ts +46 -0
- package/dist/charts/lib/format.js +56 -0
- package/dist/charts/lib/format.js.map +1 -0
- package/dist/charts/lib/legend.d.ts +45 -0
- package/dist/charts/lib/legend.js +117 -0
- package/dist/charts/lib/legend.js.map +1 -0
- package/dist/charts/lib/loading.d.ts +48 -0
- package/dist/charts/lib/loading.js +105 -0
- package/dist/charts/lib/loading.js.map +1 -0
- package/dist/charts/lib/paint.d.ts +154 -0
- package/dist/charts/lib/paint.js +551 -0
- package/dist/charts/lib/paint.js.map +1 -0
- package/dist/charts/lib/sonify-control.d.ts +163 -0
- package/dist/charts/lib/sonify-control.js +172 -0
- package/dist/charts/lib/sonify-control.js.map +1 -0
- package/dist/charts/lib/sonify.d.ts +248 -0
- package/dist/charts/lib/sonify.js +262 -0
- package/dist/charts/lib/sonify.js.map +1 -0
- package/dist/charts/lib/toolbar.d.ts +143 -0
- package/dist/charts/lib/toolbar.js +156 -0
- package/dist/charts/lib/toolbar.js.map +1 -0
- package/dist/charts/lib/tooltip.d.ts +59 -0
- package/dist/charts/lib/tooltip.js +135 -0
- package/dist/charts/lib/tooltip.js.map +1 -0
- package/dist/charts/lib/values.d.ts +56 -0
- package/dist/charts/lib/values.js +71 -0
- package/dist/charts/lib/values.js.map +1 -0
- package/dist/charts/lib/zoom.d.ts +139 -0
- package/dist/charts/lib/zoom.js +314 -0
- package/dist/charts/lib/zoom.js.map +1 -0
- package/dist/components/AppShell/AppShell.d.ts +15 -1
- package/dist/components/AppShell/AppShell.js +8 -4
- package/dist/components/AppShell/AppShell.js.map +1 -1
- package/dist/components/Article/Article.d.ts +51 -0
- package/dist/components/Article/Article.js +21 -0
- package/dist/components/Article/Article.js.map +1 -0
- package/dist/components/Button/Button.js +2 -2
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Calendar/Calendar.d.ts +1 -1
- package/dist/components/Calendar/Calendar.js +259 -24
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.js +1 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/Collapsible/Collapsible.d.ts +15 -1
- package/dist/components/Collapsible/Collapsible.js +18 -0
- package/dist/components/Collapsible/Collapsible.js.map +1 -1
- package/dist/components/Combobox/Combobox.js +1 -1
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Command/Command.js +7 -7
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js +2 -2
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/Diagram/Diagram.d.ts +81 -0
- package/dist/components/Diagram/Diagram.js +139 -0
- package/dist/components/Diagram/Diagram.js.map +1 -0
- package/dist/components/Dialog/Dialog.js +1 -1
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +2 -2
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/FigureBand/FigureBand.js +41 -30
- package/dist/components/FigureBand/FigureBand.js.map +1 -1
- package/dist/components/Popover/Popover.js +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Select/Select.js +2 -2
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Sheet/Sheet.js +1 -1
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/Slider/Slider.js +1 -1
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/Steps/Steps.d.ts +65 -0
- package/dist/components/Steps/Steps.js +55 -0
- package/dist/components/Steps/Steps.js.map +1 -0
- package/dist/components/Switch/Switch.js +1 -1
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +1 -1
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/useSelectionIndicator.js +14 -10
- package/dist/lib/useSelectionIndicator.js.map +1 -1
- package/dist/semantic.css +19 -0
- package/dist/styles.css +1 -1
- package/dist/themes.css +103 -7
- package/dist/tokens.css +193 -10
- package/dist/tokens.d.ts +25 -0
- package/dist/tokens.js +154 -8
- package/dist/tokens.json +154 -8
- package/package.json +45 -20
- package/skills/misoto22-design/SKILL.md +171 -0
- package/skills/misoto22-design/evals/claims.json +95 -0
- package/skills/misoto22-design/rules/a11y.md +115 -0
- package/skills/misoto22-design/rules/composition.md +108 -0
- package/skills/misoto22-design/rules/forms.md +128 -0
- package/skills/misoto22-design/rules/naming.md +155 -0
- package/skills/misoto22-design/rules/tokens.md +131 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ChartTooltipSlotProps, ChartLegendSlotProps } from '../AreaChart/AreaChart.js';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode, ComponentProps } from 'react';
|
|
4
|
+
import { ScatterChart as ScatterChart$1, Scatter, XAxis, YAxis, ZAxis, CartesianGrid } from 'recharts';
|
|
5
|
+
import { ChartConfig } from '../lib/chart.js';
|
|
6
|
+
import { ChartBackgroundVariant } from '../lib/background.js';
|
|
7
|
+
import { ReferenceLine, ReferenceBand, Annotation } from '../lib/annotations.js';
|
|
8
|
+
import '../lib/empty.js';
|
|
9
|
+
import '../lib/values.js';
|
|
10
|
+
import '../lib/sonify-control.js';
|
|
11
|
+
import '../lib/sonify.js';
|
|
12
|
+
import '../lib/brush.js';
|
|
13
|
+
import '../lib/toolbar.js';
|
|
14
|
+
import '../lib/zoom.js';
|
|
15
|
+
import '../lib/figure.js';
|
|
16
|
+
import '../lib/paint.js';
|
|
17
|
+
import '../lib/dot.js';
|
|
18
|
+
import '../lib/legend.js';
|
|
19
|
+
import '../lib/tooltip.js';
|
|
20
|
+
import 'recharts/types/component/DefaultTooltipContent';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The mark each point is drawn as.
|
|
24
|
+
*
|
|
25
|
+
* Shape is doing what hue does elsewhere: with the monochrome ramp, two
|
|
26
|
+
* overlapping clouds separate far better by circle-versus-cross than by two
|
|
27
|
+
* steps of grey, and shape survives overprinting where a lightness step does
|
|
28
|
+
* not.
|
|
29
|
+
*/
|
|
30
|
+
type ScatterShape = 'circle' | 'square' | 'triangle' | 'diamond' | 'cross' | 'ring';
|
|
31
|
+
/** How a point is filled — solid, hollow, or the series gradient. */
|
|
32
|
+
type ScatterVariant = 'solid' | 'outline' | 'gradient';
|
|
33
|
+
/** The rows a scatter chart reads its hidden table view from. */
|
|
34
|
+
interface ScatterTable {
|
|
35
|
+
rows: Record<string, unknown>[];
|
|
36
|
+
/** The field naming each row. */
|
|
37
|
+
rowKey?: string;
|
|
38
|
+
columns: {
|
|
39
|
+
key: string;
|
|
40
|
+
label: ReactNode;
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
interface ScatterChartProps {
|
|
44
|
+
/** Series keys → their label and paint. Declaration order is ramp order. */
|
|
45
|
+
config: ChartConfig;
|
|
46
|
+
/**
|
|
47
|
+
* What the chart shows, in a sentence a reader could act on. Required, and
|
|
48
|
+
* announced to a screen reader even when it is not printed.
|
|
49
|
+
*/
|
|
50
|
+
title: string;
|
|
51
|
+
/** Prints the title above the plot instead of hiding it from sight. */
|
|
52
|
+
showTitle?: boolean;
|
|
53
|
+
/** A line under the title — the unit, the window, the caveat. */
|
|
54
|
+
description?: ReactNode;
|
|
55
|
+
/** The composed parts — axes, grid, tooltip, legend, and the marks themselves. */
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
/** Merged onto the figure, last, so a call site can size or space it. */
|
|
58
|
+
className?: string;
|
|
59
|
+
/** Escape hatch onto the raw Recharts chart element. */
|
|
60
|
+
chartProps?: ComponentProps<typeof ScatterChart$1>;
|
|
61
|
+
/** The series lit on first render. Selection dims every other series. */
|
|
62
|
+
defaultSelectedDataKey?: string | null;
|
|
63
|
+
/** Fires when the selection changes, and with null when it is cleared. */
|
|
64
|
+
onSelectionChange?: (selectedDataKey: string | null) => void;
|
|
65
|
+
/**
|
|
66
|
+
* The rows behind the hidden table view, with the fields to print. Scatter
|
|
67
|
+
* data lives on each `<Scatter>` rather than on the root, so unlike every
|
|
68
|
+
* other chart here the table cannot be inferred — it is declared.
|
|
69
|
+
*/
|
|
70
|
+
table?: ScatterTable | false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Two measures against each other, one mark per observation — the shape for
|
|
74
|
+
* "is there a relationship here".
|
|
75
|
+
*
|
|
76
|
+
* The only chart in the set whose x axis is a NUMBER rather than a category,
|
|
77
|
+
* which is the whole point: a scatter answers correlation, clustering and
|
|
78
|
+
* outliers, and none of those questions survive being bucketed into a bar.
|
|
79
|
+
*
|
|
80
|
+
* Past three series, shape stops separating them and the answer is small
|
|
81
|
+
* multiples — one chart per series, same axes — rather than a fourth mark.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <ScatterChart title="Load time against bundle size" config={config}>
|
|
85
|
+
* <ScatterChart.Grid />
|
|
86
|
+
* <ScatterChart.XAxis dataKey="kb" name="Bundle" unit=" kB" />
|
|
87
|
+
* <ScatterChart.YAxis dataKey="ms" name="Load" unit=" ms" />
|
|
88
|
+
* <ScatterChart.Tooltip />
|
|
89
|
+
* <ScatterChart.Scatter dataKey="desktop" data={desktop} />
|
|
90
|
+
* </ScatterChart>
|
|
91
|
+
*/
|
|
92
|
+
declare function ScatterChart({ config, title, showTitle, description, children, className, chartProps, defaultSelectedDataKey, onSelectionChange, table, }: ScatterChartProps): react.JSX.Element;
|
|
93
|
+
declare namespace ScatterChart {
|
|
94
|
+
var Scatter: ({ dataKey, data, shape, variant, size, glowing, isClickable, scatterProps, }: ScatterProps) => react.JSX.Element;
|
|
95
|
+
var XAxis: ({ type, tickLine, axisLine, tickMargin, tickFormatter, label, ...rest }: Omit<ComponentProps<typeof XAxis>, "label"> & {
|
|
96
|
+
label?: string;
|
|
97
|
+
}) => react.JSX.Element;
|
|
98
|
+
var YAxis: ({ type, tickLine, axisLine, tickMargin, width, tickFormatter, label, ...rest }: Omit<ComponentProps<typeof YAxis>, "label"> & {
|
|
99
|
+
label?: string;
|
|
100
|
+
}) => react.JSX.Element;
|
|
101
|
+
var ZAxis: ({ range, ...rest }: ComponentProps<typeof ZAxis>) => react.JSX.Element;
|
|
102
|
+
var Grid: ({ strokeDasharray, ...rest }: ComponentProps<typeof CartesianGrid>) => react.JSX.Element;
|
|
103
|
+
var Tooltip: ({ variant, roundness, defaultIndex, cursor }: ChartTooltipSlotProps) => react.JSX.Element;
|
|
104
|
+
var Legend: ({ variant, align, verticalAlign, isClickable, }: ChartLegendSlotProps) => react.JSX.Element;
|
|
105
|
+
var ReferenceLine: typeof ReferenceLine;
|
|
106
|
+
var ReferenceBand: typeof ReferenceBand;
|
|
107
|
+
var Annotation: typeof Annotation;
|
|
108
|
+
var Background: ({ variant }: {
|
|
109
|
+
variant: ChartBackgroundVariant;
|
|
110
|
+
}) => react.JSX.Element;
|
|
111
|
+
}
|
|
112
|
+
interface ScatterProps {
|
|
113
|
+
/** The series key. Must exist on the config. */
|
|
114
|
+
dataKey: string;
|
|
115
|
+
/** This series' observations. */
|
|
116
|
+
data: Record<string, unknown>[];
|
|
117
|
+
shape?: ScatterShape;
|
|
118
|
+
variant?: ScatterVariant;
|
|
119
|
+
/** Mark radius in pixels. Ignored when a `<ScatterChart.ZAxis>` sizes them. */
|
|
120
|
+
size?: number;
|
|
121
|
+
/** A halo behind the cloud — for the one series that is the point. */
|
|
122
|
+
glowing?: boolean;
|
|
123
|
+
/** Lets a click on this series select it, dimming the rest. */
|
|
124
|
+
isClickable?: boolean;
|
|
125
|
+
scatterProps?: Omit<ComponentProps<typeof Scatter>, 'data' | 'dataKey'>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { ScatterChart, type ScatterChartProps, type ScatterProps, type ScatterShape, type ScatterTable, type ScatterVariant, ScatterChart as default };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useId,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState
|
|
10
|
+
} from "react";
|
|
11
|
+
import {
|
|
12
|
+
CartesianGrid,
|
|
13
|
+
Scatter as RechartsScatter,
|
|
14
|
+
ScatterChart as RechartsScatterChart,
|
|
15
|
+
XAxis as RechartsXAxis,
|
|
16
|
+
YAxis as RechartsYAxis,
|
|
17
|
+
ZAxis as RechartsZAxis
|
|
18
|
+
} from "recharts";
|
|
19
|
+
import { ChartContainer } from "../lib/chart.js";
|
|
20
|
+
import { ChartFigure } from "../lib/figure.js";
|
|
21
|
+
import { ChartBackground } from "../lib/background.js";
|
|
22
|
+
import { ChartLegend, ChartLegendContent } from "../lib/legend.js";
|
|
23
|
+
import { ChartTooltip, ChartTooltipContent } from "../lib/tooltip.js";
|
|
24
|
+
import { GlowFilter, SeriesGradient } from "../lib/paint.js";
|
|
25
|
+
import { Annotation, ReferenceBand, ReferenceLine } from "../lib/annotations.js";
|
|
26
|
+
import { axisLabel } from "../lib/axis.js";
|
|
27
|
+
import { defaultTick } from "../lib/format.js";
|
|
28
|
+
const CHART_MARGIN = { top: 12, right: 16, bottom: 0, left: 8 };
|
|
29
|
+
const ScatterChartContext = createContext(null);
|
|
30
|
+
function useScatterChart() {
|
|
31
|
+
const context = useContext(ScatterChartContext);
|
|
32
|
+
if (!context) throw new Error("A scatter chart part must be rendered inside <ScatterChart>");
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
function ScatterChart({
|
|
36
|
+
config,
|
|
37
|
+
title,
|
|
38
|
+
showTitle,
|
|
39
|
+
description,
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
chartProps,
|
|
43
|
+
defaultSelectedDataKey = null,
|
|
44
|
+
onSelectionChange,
|
|
45
|
+
table
|
|
46
|
+
}) {
|
|
47
|
+
const chartId = useId().replace(/:/g, "");
|
|
48
|
+
const [selectedDataKey, setSelectedDataKey] = useState(defaultSelectedDataKey);
|
|
49
|
+
const selectDataKey = useCallback(
|
|
50
|
+
(next) => {
|
|
51
|
+
setSelectedDataKey(next);
|
|
52
|
+
onSelectionChange?.(next);
|
|
53
|
+
},
|
|
54
|
+
[onSelectionChange]
|
|
55
|
+
);
|
|
56
|
+
const context = useMemo(
|
|
57
|
+
() => ({ config, isLoading: false, selectedDataKey, selectDataKey }),
|
|
58
|
+
[config, selectDataKey, selectedDataKey]
|
|
59
|
+
);
|
|
60
|
+
return /* @__PURE__ */ jsx(ScatterChartContext.Provider, { value: context, children: /* @__PURE__ */ jsx(
|
|
61
|
+
ChartFigure,
|
|
62
|
+
{
|
|
63
|
+
title,
|
|
64
|
+
showTitle,
|
|
65
|
+
description,
|
|
66
|
+
className,
|
|
67
|
+
table,
|
|
68
|
+
children: /* @__PURE__ */ jsx(ChartContainer, { config, children: /* @__PURE__ */ jsx(RechartsScatterChart, { id: chartId, accessibilityLayer: true, margin: CHART_MARGIN, ...chartProps, children }) })
|
|
69
|
+
}
|
|
70
|
+
) });
|
|
71
|
+
}
|
|
72
|
+
function shapePath(shape, r) {
|
|
73
|
+
switch (shape) {
|
|
74
|
+
case "square":
|
|
75
|
+
return `M${-r},${-r}h${r * 2}v${r * 2}h${-r * 2}z`;
|
|
76
|
+
case "triangle":
|
|
77
|
+
return `M0,${-r}L${r},${r}L${-r},${r}z`;
|
|
78
|
+
case "diamond":
|
|
79
|
+
return `M0,${-r}L${r},0L0,${r}L${-r},0z`;
|
|
80
|
+
case "cross": {
|
|
81
|
+
const a = r * 0.34;
|
|
82
|
+
return `M${-a},${-r}h${a * 2}v${r - a}h${r - a}v${a * 2}h${-(r - a)}v${r - a}h${-a * 2}v${-(r - a)}h${-(r - a)}v${-a * 2}h${r - a}z`;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
return `M0,${-r}A${r},${r} 0 1,1 0,${r}A${r},${r} 0 1,1 0,${-r}z`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function Scatter({
|
|
89
|
+
dataKey,
|
|
90
|
+
data,
|
|
91
|
+
shape = "circle",
|
|
92
|
+
variant = "solid",
|
|
93
|
+
size = 5,
|
|
94
|
+
glowing = false,
|
|
95
|
+
isClickable = false,
|
|
96
|
+
scatterProps
|
|
97
|
+
}) {
|
|
98
|
+
const { config, selectedDataKey, selectDataKey } = useScatterChart();
|
|
99
|
+
const id = useId().replace(/:/g, "");
|
|
100
|
+
const isSelected = selectedDataKey === dataKey;
|
|
101
|
+
const dimmed = selectedDataKey !== null && !isSelected;
|
|
102
|
+
const paint = `url(#${id}-colors-${dataKey})`;
|
|
103
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
RechartsScatter,
|
|
106
|
+
{
|
|
107
|
+
name: dataKey,
|
|
108
|
+
dataKey,
|
|
109
|
+
data,
|
|
110
|
+
isAnimationActive: false,
|
|
111
|
+
style: isClickable ? { cursor: "pointer" } : void 0,
|
|
112
|
+
onClick: () => {
|
|
113
|
+
if (!isClickable) return;
|
|
114
|
+
selectDataKey(isSelected ? null : dataKey);
|
|
115
|
+
},
|
|
116
|
+
shape: (props) => {
|
|
117
|
+
const { cx, cy } = props;
|
|
118
|
+
if (cx === void 0 || cy === void 0) return /* @__PURE__ */ jsx("g", {});
|
|
119
|
+
const radius = typeof props.r === "number" && props.r > 0 ? props.r : size;
|
|
120
|
+
const hollow = variant === "outline" || shape === "ring";
|
|
121
|
+
return /* @__PURE__ */ jsx(
|
|
122
|
+
"g",
|
|
123
|
+
{
|
|
124
|
+
transform: `translate(${cx}, ${cy})`,
|
|
125
|
+
opacity: dimmed ? 0.2 : 1,
|
|
126
|
+
filter: glowing ? `url(#${id}-glow-${dataKey})` : void 0,
|
|
127
|
+
className: "transition-opacity duration-(--duration-base)",
|
|
128
|
+
children: shape === "ring" ? /* @__PURE__ */ jsx(
|
|
129
|
+
"circle",
|
|
130
|
+
{
|
|
131
|
+
r: radius,
|
|
132
|
+
fill: "none",
|
|
133
|
+
stroke: paint,
|
|
134
|
+
strokeWidth: Math.max(1.2, radius * 0.32)
|
|
135
|
+
}
|
|
136
|
+
) : /* @__PURE__ */ jsx(
|
|
137
|
+
"path",
|
|
138
|
+
{
|
|
139
|
+
d: shapePath(shape, radius),
|
|
140
|
+
fill: hollow ? "none" : paint,
|
|
141
|
+
stroke: hollow ? paint : "var(--chart-surface)",
|
|
142
|
+
strokeWidth: hollow ? 1.4 : 1
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
...scatterProps
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
152
|
+
/* @__PURE__ */ jsx(SeriesGradient, { id, dataKey, config }),
|
|
153
|
+
glowing && /* @__PURE__ */ jsx(GlowFilter, { id, dataKey, spread: 5, intensity: 0.8 })
|
|
154
|
+
] })
|
|
155
|
+
] });
|
|
156
|
+
}
|
|
157
|
+
function XAxis({
|
|
158
|
+
type = "number",
|
|
159
|
+
tickLine = false,
|
|
160
|
+
axisLine = false,
|
|
161
|
+
tickMargin = 8,
|
|
162
|
+
tickFormatter = defaultTick,
|
|
163
|
+
label,
|
|
164
|
+
...rest
|
|
165
|
+
}) {
|
|
166
|
+
return /* @__PURE__ */ jsx(
|
|
167
|
+
RechartsXAxis,
|
|
168
|
+
{
|
|
169
|
+
type,
|
|
170
|
+
tickLine,
|
|
171
|
+
axisLine,
|
|
172
|
+
tickMargin,
|
|
173
|
+
tickFormatter,
|
|
174
|
+
...rest,
|
|
175
|
+
children: axisLabel(label, "x")
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function YAxis({
|
|
180
|
+
type = "number",
|
|
181
|
+
tickLine = false,
|
|
182
|
+
axisLine = false,
|
|
183
|
+
tickMargin = 8,
|
|
184
|
+
width = "auto",
|
|
185
|
+
tickFormatter = defaultTick,
|
|
186
|
+
label,
|
|
187
|
+
...rest
|
|
188
|
+
}) {
|
|
189
|
+
return /* @__PURE__ */ jsx(
|
|
190
|
+
RechartsYAxis,
|
|
191
|
+
{
|
|
192
|
+
type,
|
|
193
|
+
tickLine,
|
|
194
|
+
axisLine,
|
|
195
|
+
tickMargin,
|
|
196
|
+
width,
|
|
197
|
+
tickFormatter,
|
|
198
|
+
...rest,
|
|
199
|
+
children: axisLabel(label, "y")
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
function ZAxis({
|
|
204
|
+
range = [40, 400],
|
|
205
|
+
...rest
|
|
206
|
+
}) {
|
|
207
|
+
return /* @__PURE__ */ jsx(RechartsZAxis, { range, ...rest });
|
|
208
|
+
}
|
|
209
|
+
function Grid({
|
|
210
|
+
strokeDasharray = "3 3",
|
|
211
|
+
...rest
|
|
212
|
+
}) {
|
|
213
|
+
return /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray, ...rest });
|
|
214
|
+
}
|
|
215
|
+
function Tooltip({ variant, roundness, defaultIndex, cursor = true }) {
|
|
216
|
+
const { selectedDataKey } = useScatterChart();
|
|
217
|
+
return /* @__PURE__ */ jsx(
|
|
218
|
+
ChartTooltip,
|
|
219
|
+
{
|
|
220
|
+
defaultIndex,
|
|
221
|
+
cursor: cursor ? { strokeDasharray: "3 3" } : false,
|
|
222
|
+
content: /* @__PURE__ */ jsx(ChartTooltipContent, { selected: selectedDataKey, roundness, variant })
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
function Legend({
|
|
227
|
+
variant,
|
|
228
|
+
align = "right",
|
|
229
|
+
verticalAlign = "top",
|
|
230
|
+
isClickable = false
|
|
231
|
+
}) {
|
|
232
|
+
const { selectedDataKey, selectDataKey } = useScatterChart();
|
|
233
|
+
return /* @__PURE__ */ jsx(
|
|
234
|
+
ChartLegend,
|
|
235
|
+
{
|
|
236
|
+
verticalAlign,
|
|
237
|
+
align,
|
|
238
|
+
content: /* @__PURE__ */ jsx(
|
|
239
|
+
ChartLegendContent,
|
|
240
|
+
{
|
|
241
|
+
selected: selectedDataKey,
|
|
242
|
+
onSelectChange: selectDataKey,
|
|
243
|
+
isClickable,
|
|
244
|
+
variant
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
function Background({ variant }) {
|
|
251
|
+
return /* @__PURE__ */ jsx(ChartBackground, { variant });
|
|
252
|
+
}
|
|
253
|
+
ScatterChart.Scatter = Scatter;
|
|
254
|
+
ScatterChart.XAxis = XAxis;
|
|
255
|
+
ScatterChart.YAxis = YAxis;
|
|
256
|
+
ScatterChart.ZAxis = ZAxis;
|
|
257
|
+
ScatterChart.Grid = Grid;
|
|
258
|
+
ScatterChart.Tooltip = Tooltip;
|
|
259
|
+
ScatterChart.Legend = Legend;
|
|
260
|
+
ScatterChart.ReferenceLine = ReferenceLine;
|
|
261
|
+
ScatterChart.ReferenceBand = ReferenceBand;
|
|
262
|
+
ScatterChart.Annotation = Annotation;
|
|
263
|
+
ScatterChart.Background = Background;
|
|
264
|
+
var ScatterChart_default = ScatterChart;
|
|
265
|
+
export {
|
|
266
|
+
ScatterChart,
|
|
267
|
+
ScatterChart_default as default
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=ScatterChart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/charts/ScatterChart/ScatterChart.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n type ComponentProps,\n type ReactNode,\n} from 'react'\nimport {\n CartesianGrid,\n Scatter as RechartsScatter,\n ScatterChart as RechartsScatterChart,\n XAxis as RechartsXAxis,\n YAxis as RechartsYAxis,\n ZAxis as RechartsZAxis,\n} from 'recharts'\nimport { ChartContainer, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { ChartBackground, type ChartBackgroundVariant } from '../lib/background'\nimport { ChartLegend, ChartLegendContent } from '../lib/legend'\nimport { ChartTooltip, ChartTooltipContent } from '../lib/tooltip'\nimport { GlowFilter, SeriesGradient } from '../lib/paint'\nimport { Annotation, ReferenceBand, ReferenceLine } from '../lib/annotations'\nimport { axisLabel } from '../lib/axis'\nimport { defaultTick } from '../lib/format'\nimport type { ChartLegendSlotProps, ChartTooltipSlotProps } from '../AreaChart/AreaChart'\n\nconst CHART_MARGIN = { top: 12, right: 16, bottom: 0, left: 8 }\n\n/**\n * The mark each point is drawn as.\n *\n * Shape is doing what hue does elsewhere: with the monochrome ramp, two\n * overlapping clouds separate far better by circle-versus-cross than by two\n * steps of grey, and shape survives overprinting where a lightness step does\n * not.\n */\nexport type ScatterShape = 'circle' | 'square' | 'triangle' | 'diamond' | 'cross' | 'ring'\n\n/** How a point is filled — solid, hollow, or the series gradient. */\nexport type ScatterVariant = 'solid' | 'outline' | 'gradient'\n\ninterface ScatterChartContextValue {\n config: ChartConfig\n isLoading: boolean\n selectedDataKey: string | null\n selectDataKey: (dataKey: string | null) => void\n}\n\nconst ScatterChartContext = createContext<ScatterChartContextValue | null>(null)\n\nfunction useScatterChart(): ScatterChartContextValue {\n const context = useContext(ScatterChartContext)\n if (!context) throw new Error('A scatter chart part must be rendered inside <ScatterChart>')\n return context\n}\n\n/** The rows a scatter chart reads its hidden table view from. */\nexport interface ScatterTable {\n rows: Record<string, unknown>[]\n /** The field naming each row. */\n rowKey?: string\n columns: { key: string; label: ReactNode }[]\n}\n\nexport interface ScatterChartProps {\n /** Series keys → their label and paint. Declaration order is ramp order. */\n config: ChartConfig\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** The composed parts — axes, grid, tooltip, legend, and the marks themselves. */\n children: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n /** Escape hatch onto the raw Recharts chart element. */\n chartProps?: ComponentProps<typeof RechartsScatterChart>\n /** The series lit on first render. Selection dims every other series. */\n defaultSelectedDataKey?: string | null\n /** Fires when the selection changes, and with null when it is cleared. */\n onSelectionChange?: (selectedDataKey: string | null) => void\n /**\n * The rows behind the hidden table view, with the fields to print. Scatter\n * data lives on each `<Scatter>` rather than on the root, so unlike every\n * other chart here the table cannot be inferred — it is declared.\n */\n table?: ScatterTable | false\n}\n\n/**\n * Two measures against each other, one mark per observation — the shape for\n * \"is there a relationship here\".\n *\n * The only chart in the set whose x axis is a NUMBER rather than a category,\n * which is the whole point: a scatter answers correlation, clustering and\n * outliers, and none of those questions survive being bucketed into a bar.\n *\n * Past three series, shape stops separating them and the answer is small\n * multiples — one chart per series, same axes — rather than a fourth mark.\n *\n * @example\n * <ScatterChart title=\"Load time against bundle size\" config={config}>\n * <ScatterChart.Grid />\n * <ScatterChart.XAxis dataKey=\"kb\" name=\"Bundle\" unit=\" kB\" />\n * <ScatterChart.YAxis dataKey=\"ms\" name=\"Load\" unit=\" ms\" />\n * <ScatterChart.Tooltip />\n * <ScatterChart.Scatter dataKey=\"desktop\" data={desktop} />\n * </ScatterChart>\n */\nexport function ScatterChart({\n config,\n title,\n showTitle,\n description,\n children,\n className,\n chartProps,\n defaultSelectedDataKey = null,\n onSelectionChange,\n table,\n}: ScatterChartProps) {\n const chartId = useId().replace(/:/g, '')\n const [selectedDataKey, setSelectedDataKey] = useState<string | null>(defaultSelectedDataKey)\n\n const selectDataKey = useCallback(\n (next: string | null) => {\n setSelectedDataKey(next)\n onSelectionChange?.(next)\n },\n [onSelectionChange],\n )\n\n const context = useMemo<ScatterChartContextValue>(\n () => ({ config, isLoading: false, selectedDataKey, selectDataKey }),\n [config, selectDataKey, selectedDataKey],\n )\n\n return (\n <ScatterChartContext.Provider value={context}>\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={table}\n >\n <ChartContainer config={config}>\n <RechartsScatterChart id={chartId} accessibilityLayer margin={CHART_MARGIN} {...chartProps}>\n {children}\n </RechartsScatterChart>\n </ChartContainer>\n </ChartFigure>\n </ScatterChartContext.Provider>\n )\n}\n\n/** The SVG path for one mark, centred on the origin at radius `r`. */\nfunction shapePath(shape: ScatterShape, r: number): string {\n switch (shape) {\n case 'square':\n return `M${-r},${-r}h${r * 2}v${r * 2}h${-r * 2}z`\n case 'triangle':\n return `M0,${-r}L${r},${r}L${-r},${r}z`\n case 'diamond':\n return `M0,${-r}L${r},0L0,${r}L${-r},0z`\n case 'cross': {\n const a = r * 0.34\n return `M${-a},${-r}h${a * 2}v${r - a}h${r - a}v${a * 2}h${-(r - a)}v${r - a}h${-a * 2}v${-(r - a)}h${-(r - a)}v${-a * 2}h${r - a}z`\n }\n default:\n return `M0,${-r}A${r},${r} 0 1,1 0,${r}A${r},${r} 0 1,1 0,${-r}z`\n }\n}\n\nexport interface ScatterProps {\n /** The series key. Must exist on the config. */\n dataKey: string\n /** This series' observations. */\n data: Record<string, unknown>[]\n shape?: ScatterShape\n variant?: ScatterVariant\n /** Mark radius in pixels. Ignored when a `<ScatterChart.ZAxis>` sizes them. */\n size?: number\n /** A halo behind the cloud — for the one series that is the point. */\n glowing?: boolean\n /** Lets a click on this series select it, dimming the rest. */\n isClickable?: boolean\n scatterProps?: Omit<ComponentProps<typeof RechartsScatter>, 'data' | 'dataKey'>\n}\n\n/** One cloud of observations. */\nfunction Scatter({\n dataKey,\n data,\n shape = 'circle',\n variant = 'solid',\n size = 5,\n glowing = false,\n isClickable = false,\n scatterProps,\n}: ScatterProps) {\n const { config, selectedDataKey, selectDataKey } = useScatterChart()\n const id = useId().replace(/:/g, '')\n\n const isSelected = selectedDataKey === dataKey\n const dimmed = selectedDataKey !== null && !isSelected\n const paint = `url(#${id}-colors-${dataKey})`\n\n return (\n <>\n <RechartsScatter\n name={dataKey}\n dataKey={dataKey}\n data={data}\n isAnimationActive={false}\n style={isClickable ? { cursor: 'pointer' } : undefined}\n onClick={() => {\n if (!isClickable) return\n selectDataKey(isSelected ? null : dataKey)\n }}\n shape={(props: { cx?: number; cy?: number; payload?: unknown; r?: number }) => {\n const { cx, cy } = props\n if (cx === undefined || cy === undefined) return <g />\n // `r` arrives only when a ZAxis is sizing the marks.\n const radius = typeof props.r === 'number' && props.r > 0 ? props.r : size\n const hollow = variant === 'outline' || shape === 'ring'\n\n return (\n <g\n transform={`translate(${cx}, ${cy})`}\n opacity={dimmed ? 0.2 : 1}\n filter={glowing ? `url(#${id}-glow-${dataKey})` : undefined}\n className=\"transition-opacity duration-(--duration-base)\"\n >\n {shape === 'ring' ? (\n <circle\n r={radius}\n fill=\"none\"\n stroke={paint}\n strokeWidth={Math.max(1.2, radius * 0.32)}\n />\n ) : (\n <path\n d={shapePath(shape, radius)}\n fill={hollow ? 'none' : paint}\n stroke={hollow ? paint : 'var(--chart-surface)'}\n // A surface-coloured ring on a solid mark is the 2px spacer\n // that keeps two overlapping observations countable.\n strokeWidth={hollow ? 1.4 : 1}\n />\n )}\n </g>\n )\n }}\n {...scatterProps}\n />\n <defs>\n <SeriesGradient id={id} dataKey={dataKey} config={config} />\n {glowing && <GlowFilter id={id} dataKey={dataKey} spread={5} intensity={0.8} />}\n </defs>\n </>\n )\n}\n\n/** The horizontal measure. Numeric by default — a scatter has no categories. */\nfunction XAxis({\n type = 'number',\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n tickFormatter = defaultTick,\n label,\n ...rest\n}: Omit<ComponentProps<typeof RechartsXAxis>, 'label'> & { label?: string }) {\n return (\n <RechartsXAxis\n type={type}\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n tickFormatter={tickFormatter}\n {...rest}\n >\n {axisLabel(label, 'x')}\n </RechartsXAxis>\n )\n}\n\n/** The vertical measure. */\nfunction YAxis({\n type = 'number',\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n width = 'auto',\n tickFormatter = defaultTick,\n label,\n ...rest\n}: Omit<ComponentProps<typeof RechartsYAxis>, 'label'> & { label?: string }) {\n return (\n <RechartsYAxis\n type={type}\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n width={width}\n tickFormatter={tickFormatter}\n {...rest}\n >\n {axisLabel(label, 'y')}\n </RechartsYAxis>\n )\n}\n\n/**\n * A third measure, mapped to each mark's AREA rather than its radius.\n *\n * Radius is the wrong encoding and the common mistake: doubling a radius\n * quadruples the ink, so a value twice as large reads as four times as large.\n * `range` is in area units, which is what Recharts scales — so the default\n * below is a ×4 area span, not a ×4 radius.\n */\nfunction ZAxis({\n range = [40, 400],\n ...rest\n}: ComponentProps<typeof RechartsZAxis>) {\n return <RechartsZAxis range={range} {...rest} />\n}\n\n/** Rules on both axes — a scatter is read in two directions. */\nfunction Grid({\n strokeDasharray = '3 3',\n ...rest\n}: ComponentProps<typeof CartesianGrid>) {\n return <CartesianGrid strokeDasharray={strokeDasharray} {...rest} />\n}\n\n/**\n * The hover panel, with a crosshair on both axes.\n *\n * `cursor` is a pair of rules rather than a band: a scatter point is located by\n * two coordinates, and a single vertical cursor answers only half of that.\n */\nfunction Tooltip({ variant, roundness, defaultIndex, cursor = true }: ChartTooltipSlotProps) {\n const { selectedDataKey } = useScatterChart()\n\n return (\n <ChartTooltip\n defaultIndex={defaultIndex}\n cursor={cursor ? { strokeDasharray: '3 3' } : false}\n content={\n <ChartTooltipContent selected={selectedDataKey} roundness={roundness} variant={variant} />\n }\n />\n )\n}\n\n/** The key. Required above one series. */\nfunction Legend({\n variant,\n align = 'right',\n verticalAlign = 'top',\n isClickable = false,\n}: ChartLegendSlotProps) {\n const { selectedDataKey, selectDataKey } = useScatterChart()\n\n return (\n <ChartLegend\n verticalAlign={verticalAlign}\n align={align}\n content={\n <ChartLegendContent\n selected={selectedDataKey}\n onSelectChange={selectDataKey}\n isClickable={isClickable}\n variant={variant}\n />\n }\n />\n )\n}\n\n/** The decorative plate behind the plot. */\nfunction Background({ variant }: { variant: ChartBackgroundVariant }) {\n return <ChartBackground variant={variant} />\n}\n\nScatterChart.Scatter = Scatter\nScatterChart.XAxis = XAxis\nScatterChart.YAxis = YAxis\nScatterChart.ZAxis = ZAxis\nScatterChart.Grid = Grid\nScatterChart.Tooltip = Tooltip\nScatterChart.Legend = Legend\nScatterChart.ReferenceLine = ReferenceLine\nScatterChart.ReferenceBand = ReferenceBand\nScatterChart.Annotation = Annotation\nScatterChart.Background = Background\n\nexport default ScatterChart\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/** The mark a sparkline is drawn with. */
|
|
5
|
+
type SparklineVariant = 'line' | 'area' | 'bars';
|
|
6
|
+
interface SparklineProps {
|
|
7
|
+
/** The numbers, in order. Anything shorter than two points draws nothing. */
|
|
8
|
+
data: number[];
|
|
9
|
+
/**
|
|
10
|
+
* What the run is, in a sentence. Required, and it is the whole accessible
|
|
11
|
+
* name: a sparkline has no axes and no legend, so nothing else describes it.
|
|
12
|
+
*/
|
|
13
|
+
label: string;
|
|
14
|
+
variant?: SparklineVariant;
|
|
15
|
+
/** Height in pixels. The width comes from the container. */
|
|
16
|
+
height?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The domain, as `[min, max]`. Derived from the data when omitted.
|
|
19
|
+
*
|
|
20
|
+
* Pin it whenever a column of sparklines is meant to be compared: on
|
|
21
|
+
* independent domains every row peaks and troughs identically, which is the
|
|
22
|
+
* one way a table of sparklines can be actively misleading.
|
|
23
|
+
*/
|
|
24
|
+
domain?: [number, number];
|
|
25
|
+
/** Marks the last point, which is usually the one being asked about. */
|
|
26
|
+
showLast?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The value announced alongside the label — the current reading, formatted.
|
|
29
|
+
* Falls back to the last number.
|
|
30
|
+
*/
|
|
31
|
+
value?: ReactNode;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A run of numbers at the size of a word — in a table cell, beside a figure, at
|
|
36
|
+
* the end of a row.
|
|
37
|
+
*
|
|
38
|
+
* Deliberately axis-less, gridless and label-less: a sparkline answers "what
|
|
39
|
+
* shape has this been", and every piece of chrome that would make it answer
|
|
40
|
+
* "what value exactly" also makes it too big to sit inline, which was the only
|
|
41
|
+
* reason to reach for it. When the exact value matters, print the number beside
|
|
42
|
+
* it — `value` does — and when the trend needs reading precisely, it wants a
|
|
43
|
+
* `<LineChart>` and its own space.
|
|
44
|
+
*
|
|
45
|
+
* No rendering engine: it is one `<path>` over a normalised viewBox, so it
|
|
46
|
+
* costs nothing to put a hundred of them in a table.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* <Sparkline label="Weekly signups" data={[12, 18, 9, 24, 30, 22, 41]} value="41" />
|
|
50
|
+
*/
|
|
51
|
+
declare function Sparkline({ data, label, variant, height, domain, showLast, value, className, }: SparklineProps): react.JSX.Element;
|
|
52
|
+
|
|
53
|
+
export { Sparkline, type SparklineProps, type SparklineVariant, Sparkline as default };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
function points(data, min, max) {
|
|
4
|
+
const span = max - min || 1;
|
|
5
|
+
const step = data.length > 1 ? 100 / (data.length - 1) : 0;
|
|
6
|
+
return data.map((value, index) => ({
|
|
7
|
+
x: index * step,
|
|
8
|
+
// SVG y grows downward; the data does not.
|
|
9
|
+
y: 100 - (value - min) / span * 100
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
function Sparkline({
|
|
13
|
+
data,
|
|
14
|
+
label,
|
|
15
|
+
variant = "line",
|
|
16
|
+
height = 28,
|
|
17
|
+
domain,
|
|
18
|
+
showLast = true,
|
|
19
|
+
value,
|
|
20
|
+
className
|
|
21
|
+
}) {
|
|
22
|
+
const clean = data.filter((entry) => Number.isFinite(entry));
|
|
23
|
+
const [min, max] = domain ?? [Math.min(...clean), Math.max(...clean)];
|
|
24
|
+
const reading = value ?? clean.at(-1)?.toLocaleString();
|
|
25
|
+
if (clean.length < 2) {
|
|
26
|
+
return /* @__PURE__ */ jsxs("span", { className: cn("mono-meta text-(--ink-3-aa)", className), children: [
|
|
27
|
+
label,
|
|
28
|
+
": not enough data"
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
const plotted = points(clean, min, max);
|
|
32
|
+
const line = plotted.map((point, index) => `${index === 0 ? "M" : "L"}${point.x},${point.y}`).join(" ");
|
|
33
|
+
const last = plotted.at(-1);
|
|
34
|
+
return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex w-full items-center gap-2", className), children: [
|
|
35
|
+
/* @__PURE__ */ jsxs(
|
|
36
|
+
"svg",
|
|
37
|
+
{
|
|
38
|
+
role: "img",
|
|
39
|
+
"aria-label": reading === void 0 ? label : `${label}: ${reading}`,
|
|
40
|
+
viewBox: "0 0 100 100",
|
|
41
|
+
preserveAspectRatio: "none",
|
|
42
|
+
height,
|
|
43
|
+
className: "min-w-0 flex-1 overflow-visible text-(--series-1)",
|
|
44
|
+
children: [
|
|
45
|
+
variant === "bars" ? plotted.map((point, index) => {
|
|
46
|
+
const width = 100 / (plotted.length * 1.6);
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
"rect",
|
|
49
|
+
{
|
|
50
|
+
x: point.x - width / 2,
|
|
51
|
+
y: point.y,
|
|
52
|
+
width,
|
|
53
|
+
height: Math.max(1, 100 - point.y),
|
|
54
|
+
fill: "currentColor",
|
|
55
|
+
fillOpacity: 0.85
|
|
56
|
+
},
|
|
57
|
+
index
|
|
58
|
+
);
|
|
59
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
variant === "area" && /* @__PURE__ */ jsx(
|
|
61
|
+
"path",
|
|
62
|
+
{
|
|
63
|
+
d: `${line} L100,100 L0,100 Z`,
|
|
64
|
+
fill: "currentColor",
|
|
65
|
+
fillOpacity: "var(--chart-fill)"
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"path",
|
|
70
|
+
{
|
|
71
|
+
d: line,
|
|
72
|
+
fill: "none",
|
|
73
|
+
stroke: "currentColor",
|
|
74
|
+
strokeWidth: 1.5,
|
|
75
|
+
vectorEffect: "non-scaling-stroke",
|
|
76
|
+
strokeLinejoin: "round",
|
|
77
|
+
strokeLinecap: "round"
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] }),
|
|
81
|
+
showLast && variant !== "bars" && /* @__PURE__ */ jsx("circle", { cx: last.x, cy: last.y, r: 2, fill: "currentColor", vectorEffect: "non-scaling-stroke" })
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
value !== void 0 && /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-xs tabular-nums text-(--ink)", children: value })
|
|
86
|
+
] });
|
|
87
|
+
}
|
|
88
|
+
var Sparkline_default = Sparkline;
|
|
89
|
+
export {
|
|
90
|
+
Sparkline,
|
|
91
|
+
Sparkline_default as default
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=Sparkline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/charts/Sparkline/Sparkline.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/** The mark a sparkline is drawn with. */\nexport type SparklineVariant = 'line' | 'area' | 'bars'\n\nexport interface SparklineProps {\n /** The numbers, in order. Anything shorter than two points draws nothing. */\n data: number[]\n /**\n * What the run is, in a sentence. Required, and it is the whole accessible\n * name: a sparkline has no axes and no legend, so nothing else describes it.\n */\n label: string\n variant?: SparklineVariant\n /** Height in pixels. The width comes from the container. */\n height?: number\n /**\n * The domain, as `[min, max]`. Derived from the data when omitted.\n *\n * Pin it whenever a column of sparklines is meant to be compared: on\n * independent domains every row peaks and troughs identically, which is the\n * one way a table of sparklines can be actively misleading.\n */\n domain?: [number, number]\n /** Marks the last point, which is usually the one being asked about. */\n showLast?: boolean\n /**\n * The value announced alongside the label — the current reading, formatted.\n * Falls back to the last number.\n */\n value?: ReactNode\n className?: string\n}\n\n/** The polyline through a normalised run, in a 0–100 by 0–100 box. */\nfunction points(data: number[], min: number, max: number): { x: number; y: number }[] {\n const span = max - min || 1\n const step = data.length > 1 ? 100 / (data.length - 1) : 0\n return data.map((value, index) => ({\n x: index * step,\n // SVG y grows downward; the data does not.\n y: 100 - ((value - min) / span) * 100,\n }))\n}\n\n/**\n * A run of numbers at the size of a word — in a table cell, beside a figure, at\n * the end of a row.\n *\n * Deliberately axis-less, gridless and label-less: a sparkline answers \"what\n * shape has this been\", and every piece of chrome that would make it answer\n * \"what value exactly\" also makes it too big to sit inline, which was the only\n * reason to reach for it. When the exact value matters, print the number beside\n * it — `value` does — and when the trend needs reading precisely, it wants a\n * `<LineChart>` and its own space.\n *\n * No rendering engine: it is one `<path>` over a normalised viewBox, so it\n * costs nothing to put a hundred of them in a table.\n *\n * @example\n * <Sparkline label=\"Weekly signups\" data={[12, 18, 9, 24, 30, 22, 41]} value=\"41\" />\n */\nexport function Sparkline({\n data,\n label,\n variant = 'line',\n height = 28,\n domain,\n showLast = true,\n value,\n className,\n}: SparklineProps) {\n const clean = data.filter((entry) => Number.isFinite(entry))\n const [min, max] = domain ?? [Math.min(...clean), Math.max(...clean)]\n const reading = value ?? clean.at(-1)?.toLocaleString()\n\n if (clean.length < 2) {\n return (\n <span className={cn('mono-meta text-(--ink-3-aa)', className)}>\n {label}: not enough data\n </span>\n )\n }\n\n const plotted = points(clean, min, max)\n const line = plotted.map((point, index) => `${index === 0 ? 'M' : 'L'}${point.x},${point.y}`).join(' ')\n const last = plotted.at(-1)!\n\n return (\n <span className={cn('inline-flex w-full items-center gap-2', className)}>\n <svg\n role=\"img\"\n aria-label={reading === undefined ? label : `${label}: ${reading}`}\n viewBox=\"0 0 100 100\"\n // Not `xMidYMid`: the run has to fill the cell it is given, and a\n // sparkline's aspect ratio carries no meaning to preserve.\n preserveAspectRatio=\"none\"\n height={height}\n className=\"min-w-0 flex-1 overflow-visible text-(--series-1)\"\n >\n {variant === 'bars' ? (\n plotted.map((point, index) => {\n const width = 100 / (plotted.length * 1.6)\n return (\n <rect\n key={index}\n x={point.x - width / 2}\n y={point.y}\n width={width}\n height={Math.max(1, 100 - point.y)}\n fill=\"currentColor\"\n // The bars carry the reading on their own; a wash would only\n // add a second, weaker version of it.\n fillOpacity={0.85}\n />\n )\n })\n ) : (\n <>\n {variant === 'area' && (\n <path\n d={`${line} L100,100 L0,100 Z`}\n fill=\"currentColor\"\n fillOpacity=\"var(--chart-fill)\"\n />\n )}\n <path\n d={line}\n fill=\"none\"\n stroke=\"currentColor\"\n // The viewBox is stretched to the cell, so a stroke in user units\n // would be stretched with it. `non-scaling-stroke` is what keeps\n // the line the same weight in a narrow cell and a wide one.\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n </>\n )}\n {showLast && variant !== 'bars' && (\n <circle cx={last.x} cy={last.y} r={2} fill=\"currentColor\" vectorEffect=\"non-scaling-stroke\" />\n )}\n </svg>\n {value !== undefined && (\n <span className=\"shrink-0 font-mono text-xs tabular-nums text-(--ink)\">{value}</span>\n )}\n </span>\n )\n}\n\nexport default Sparkline\n"],"mappings":"AA+EM,SAwCI,UAdI,KA1BR;AA9EN,SAAS,UAAU;AAmCnB,SAAS,OAAO,MAAgB,KAAa,KAAyC;AACpF,QAAM,OAAO,MAAM,OAAO;AAC1B,QAAM,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,KAAK;AACzD,SAAO,KAAK,IAAI,CAAC,OAAO,WAAW;AAAA,IACjC,GAAG,QAAQ;AAAA;AAAA,IAEX,GAAG,OAAQ,QAAQ,OAAO,OAAQ;AAAA,EACpC,EAAE;AACJ;AAmBO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,SAAS;AAAA,EACT;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,QAAQ,KAAK,OAAO,CAAC,UAAU,OAAO,SAAS,KAAK,CAAC;AAC3D,QAAM,CAAC,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,IAAI,GAAG,KAAK,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AACpE,QAAM,UAAU,SAAS,MAAM,GAAG,EAAE,GAAG,eAAe;AAEtD,MAAI,MAAM,SAAS,GAAG;AACpB,WACE,qBAAC,UAAK,WAAW,GAAG,+BAA+B,SAAS,GACzD;AAAA;AAAA,MAAM;AAAA,OACT;AAAA,EAEJ;AAEA,QAAM,UAAU,OAAO,OAAO,KAAK,GAAG;AACtC,QAAM,OAAO,QAAQ,IAAI,CAAC,OAAO,UAAU,GAAG,UAAU,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,EAAE,KAAK,GAAG;AACtG,QAAM,OAAO,QAAQ,GAAG,EAAE;AAE1B,SACE,qBAAC,UAAK,WAAW,GAAG,yCAAyC,SAAS,GACpE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY,YAAY,SAAY,QAAQ,GAAG,KAAK,KAAK,OAAO;AAAA,QAChE,SAAQ;AAAA,QAGR,qBAAoB;AAAA,QACpB;AAAA,QACA,WAAU;AAAA,QAET;AAAA,sBAAY,SACX,QAAQ,IAAI,CAAC,OAAO,UAAU;AAC5B,kBAAM,QAAQ,OAAO,QAAQ,SAAS;AACtC,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,GAAG,MAAM,IAAI,QAAQ;AAAA,gBACrB,GAAG,MAAM;AAAA,gBACT;AAAA,gBACA,QAAQ,KAAK,IAAI,GAAG,MAAM,MAAM,CAAC;AAAA,gBACjC,MAAK;AAAA,gBAGL,aAAa;AAAA;AAAA,cARR;AAAA,YASP;AAAA,UAEJ,CAAC,IAED,iCACG;AAAA,wBAAY,UACX;AAAA,cAAC;AAAA;AAAA,gBACC,GAAG,GAAG,IAAI;AAAA,gBACV,MAAK;AAAA,gBACL,aAAY;AAAA;AAAA,YACd;AAAA,YAEF;AAAA,cAAC;AAAA;AAAA,gBACC,GAAG;AAAA,gBACH,MAAK;AAAA,gBACL,QAAO;AAAA,gBAIP,aAAa;AAAA,gBACb,cAAa;AAAA,gBACb,gBAAe;AAAA,gBACf,eAAc;AAAA;AAAA,YAChB;AAAA,aACF;AAAA,UAED,YAAY,YAAY,UACvB,oBAAC,YAAO,IAAI,KAAK,GAAG,IAAI,KAAK,GAAG,GAAG,GAAG,MAAK,gBAAe,cAAa,sBAAqB;AAAA;AAAA;AAAA,IAEhG;AAAA,IACC,UAAU,UACT,oBAAC,UAAK,WAAU,wDAAwD,iBAAM;AAAA,KAElF;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
|