@orchidsoftware/charts 0.0.1
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/CHANGELOG.md +71 -0
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/core/Chart.js +1 -0
- package/dist/core/Chart.js.map +1 -0
- package/dist/core/ChartData.js +1 -0
- package/dist/core/ChartData.js.map +1 -0
- package/dist/core/ChartDefinition.js +1 -0
- package/dist/core/ChartDefinition.js.map +1 -0
- package/dist/core/ChartSelection.js +1 -0
- package/dist/core/ChartSelection.js.map +1 -0
- package/dist/core/ChartTooltip.js +1 -0
- package/dist/core/ChartTooltip.js.map +1 -0
- package/dist/core/InteractionController.js +1 -0
- package/dist/core/InteractionController.js.map +1 -0
- package/dist/core/NormalizeAnnotations.js +1 -0
- package/dist/core/NormalizeAnnotations.js.map +1 -0
- package/dist/core/Options.js +1 -0
- package/dist/core/Options.js.map +1 -0
- package/dist/core/builders/Builder.js +1 -0
- package/dist/core/builders/Builder.js.map +1 -0
- package/dist/core/builders/BuilderArguments.js +1 -0
- package/dist/core/builders/BuilderArguments.js.map +1 -0
- package/dist/core/builders/BuilderScopes.js +1 -0
- package/dist/core/builders/BuilderScopes.js.map +1 -0
- package/dist/core/builders/BuilderState.js +1 -0
- package/dist/core/builders/BuilderState.js.map +1 -0
- package/dist/core/builders/BuilderValidation.js +1 -0
- package/dist/core/builders/BuilderValidation.js.map +1 -0
- package/dist/core/builders/CartesianBuilders.js +1 -0
- package/dist/core/builders/CartesianBuilders.js.map +1 -0
- package/dist/core/builders/CompositionBuilders.js +1 -0
- package/dist/core/builders/CompositionBuilders.js.map +1 -0
- package/dist/core/builders/TemporalBuilders.js +1 -0
- package/dist/core/builders/TemporalBuilders.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/renderers/ChartRendering.js +1 -0
- package/dist/renderers/ChartRendering.js.map +1 -0
- package/dist/renderers/LegendRenderer.js +1 -0
- package/dist/renderers/LegendRenderer.js.map +1 -0
- package/dist/renderers/SvgSurface.js +1 -0
- package/dist/renderers/SvgSurface.js.map +1 -0
- package/dist/renderers/cartesian/CartesianAxesRenderer.js +1 -0
- package/dist/renderers/cartesian/CartesianAxesRenderer.js.map +1 -0
- package/dist/renderers/cartesian/CartesianInspectorRenderer.js +1 -0
- package/dist/renderers/cartesian/CartesianInspectorRenderer.js.map +1 -0
- package/dist/renderers/cartesian/CartesianLayout.js +1 -0
- package/dist/renderers/cartesian/CartesianLayout.js.map +1 -0
- package/dist/renderers/cartesian/CartesianRendering.js +1 -0
- package/dist/renderers/cartesian/CartesianRendering.js.map +1 -0
- package/dist/renderers/cartesian/CartesianSeriesRendering.js +1 -0
- package/dist/renderers/cartesian/CartesianSeriesRendering.js.map +1 -0
- package/dist/renderers/composition/AggregationRendering.js +1 -0
- package/dist/renderers/composition/AggregationRendering.js.map +1 -0
- package/dist/renderers/composition/Composition.js +1 -0
- package/dist/renderers/composition/Composition.js.map +1 -0
- package/dist/renderers/composition/PolarAreaRendering.js +1 -0
- package/dist/renderers/composition/PolarAreaRendering.js.map +1 -0
- package/dist/renderers/composition/RadarRendering.js +1 -0
- package/dist/renderers/composition/RadarRendering.js.map +1 -0
- package/dist/renderers/temporal/HeatmapRendering.js +1 -0
- package/dist/renderers/temporal/HeatmapRendering.js.map +1 -0
- package/dist/renderers/temporal/TimesheetLayout.js +1 -0
- package/dist/renderers/temporal/TimesheetLayout.js.map +1 -0
- package/dist/renderers/temporal/TimesheetRendering.js +1 -0
- package/dist/renderers/temporal/TimesheetRendering.js.map +1 -0
- package/dist/support/Constants.js +1 -0
- package/dist/support/Constants.js.map +1 -0
- package/dist/support/Dom.js +1 -0
- package/dist/support/Dom.js.map +1 -0
- package/dist/support/Normalize.js +2 -0
- package/dist/support/Normalize.js.map +1 -0
- package/dist/support/Validation.js +1 -0
- package/dist/support/Validation.js.map +1 -0
- package/dist/support/geometry/CartesianGeometry.js +1 -0
- package/dist/support/geometry/CartesianGeometry.js.map +1 -0
- package/dist/support/geometry/Scale.js +1 -0
- package/dist/support/geometry/Scale.js.map +1 -0
- package/dist/support/geometry/SectorGeometry.js +1 -0
- package/dist/support/geometry/SectorGeometry.js.map +1 -0
- package/dist/support/presentation/Formatting.js +1 -0
- package/dist/support/presentation/Formatting.js.map +1 -0
- package/dist/support/presentation/Presentation.js +1 -0
- package/dist/support/presentation/Presentation.js.map +1 -0
- package/dist/support/presentation/Time.js +1 -0
- package/dist/support/presentation/Time.js.map +1 -0
- package/docs/api-reference.md +322 -0
- package/docs/bar.md +110 -0
- package/docs/bubble.md +76 -0
- package/docs/chart-types.md +35 -0
- package/docs/customization.md +178 -0
- package/docs/donut.md +55 -0
- package/docs/exporting.md +87 -0
- package/docs/frameworks.md +203 -0
- package/docs/getting-started.md +121 -0
- package/docs/heatmap.md +95 -0
- package/docs/line.md +148 -0
- package/docs/mixed.md +114 -0
- package/docs/percentage.md +48 -0
- package/docs/pie.md +58 -0
- package/docs/polar-area.md +46 -0
- package/docs/radar.md +49 -0
- package/docs/readme.md +37 -0
- package/docs/scatter.md +83 -0
- package/docs/timesheet.md +125 -0
- package/docs/updates-and-interaction.md +124 -0
- package/package.json +133 -0
- package/src/index.d.ts +481 -0
- package/src/styles.css +350 -0
package/docs/bubble.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Bubble charts
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Bubble charts compare three measurements at once. The `x` and `y` properties
|
|
6
|
+
position each point, while `r` controls its radius.
|
|
7
|
+
|
|
8
|
+
## Creating a bubble chart
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import { BubbleChart } from "@orchidsoftware/charts";
|
|
12
|
+
import "@orchidsoftware/charts/style.css";
|
|
13
|
+
|
|
14
|
+
const chart = BubbleChart.make("#accounts")
|
|
15
|
+
.dataset("Accounts", [
|
|
16
|
+
{ x: 12, y: 38, r: 6 },
|
|
17
|
+
{ x: 18, y: 51, r: 10 },
|
|
18
|
+
{ x: 25, y: 63, r: 15 },
|
|
19
|
+
])
|
|
20
|
+
.render();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The value returned by `render()` is the mounted chart and may be updated,
|
|
24
|
+
selected, or exported.
|
|
25
|
+
|
|
26
|
+
## Comparing datasets
|
|
27
|
+
|
|
28
|
+
Name each dataset so the legend and tooltip can distinguish its bubbles:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import { BubbleChart } from "@orchidsoftware/charts";
|
|
32
|
+
|
|
33
|
+
BubbleChart.make("#accounts")
|
|
34
|
+
.dataset("Self-serve", [
|
|
35
|
+
{ x: 12, y: 38, r: 6 },
|
|
36
|
+
{ x: 18, y: 51, r: 10 },
|
|
37
|
+
])
|
|
38
|
+
.dataset("Sales-led", [
|
|
39
|
+
{ x: 16, y: 58, r: 12 },
|
|
40
|
+
{ x: 24, y: 72, r: 16 },
|
|
41
|
+
])
|
|
42
|
+
.colors(["#2563eb", "#f59e0b"])
|
|
43
|
+
.render();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The radius must be finite and non-negative. Keep the range of radii modest so
|
|
47
|
+
large bubbles do not hide smaller ones.
|
|
48
|
+
|
|
49
|
+
## Point visibility
|
|
50
|
+
|
|
51
|
+
Use `dots(false)` to hide bubble marks while retaining the scale and any
|
|
52
|
+
annotations:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import { BubbleChart } from "@orchidsoftware/charts";
|
|
56
|
+
|
|
57
|
+
BubbleChart.make("#accounts").dataset(bubbles).dots(false).render();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Dataset appearance
|
|
61
|
+
|
|
62
|
+
A bubble dataset callback supports `color()`, `opacity()`, and
|
|
63
|
+
`formatValue()`:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import { BubbleChart } from "@orchidsoftware/charts";
|
|
67
|
+
|
|
68
|
+
BubbleChart.make("#accounts")
|
|
69
|
+
.dataset("Accounts", bubbles, (dataset) => {
|
|
70
|
+
dataset.color("#2563eb").opacity(0.75);
|
|
71
|
+
})
|
|
72
|
+
.render();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Bubble charts also support the shared [cartesian presentation, formatting, and
|
|
76
|
+
annotation methods](./customization.md).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Chart types
|
|
2
|
+
|
|
3
|
+
Charts2 includes twelve chart types for common product questions. Choose the
|
|
4
|
+
question you need to answer, then open the corresponding guide.
|
|
5
|
+
|
|
6
|
+
## Change and comparison
|
|
7
|
+
|
|
8
|
+
- [Line charts](./line.md) show change across an ordered sequence.
|
|
9
|
+
- [Bar charts](./bar.md) compare categories, groups, and totals.
|
|
10
|
+
- [Mixed charts](./mixed.md) place lines, bars, and scatter points on one scale.
|
|
11
|
+
|
|
12
|
+
## Relationships
|
|
13
|
+
|
|
14
|
+
- [Scatter charts](./scatter.md) compare two measurements.
|
|
15
|
+
- [Bubble charts](./bubble.md) add a third measurement through radius.
|
|
16
|
+
|
|
17
|
+
## Composition and profiles
|
|
18
|
+
|
|
19
|
+
- [Pie charts](./pie.md) divide a whole into a few slices.
|
|
20
|
+
- [Donut charts](./donut.md) show the same relationship as a ring.
|
|
21
|
+
- [Percentage charts](./percentage.md) show composition in a compact strip.
|
|
22
|
+
- [Radar charts](./radar.md) compare profiles across a shared scale.
|
|
23
|
+
- [Polar area charts](./polar-area.md) compare magnitude with equal angles.
|
|
24
|
+
|
|
25
|
+
## Activity and planning
|
|
26
|
+
|
|
27
|
+
- [Calendar heatmaps](./heatmap.md) show daily activity and recurring patterns.
|
|
28
|
+
- [Timesheet charts](./timesheet.md) place tasks and phases on a timeline.
|
|
29
|
+
|
|
30
|
+
If two types seem suitable, prefer the one that communicates the answer with
|
|
31
|
+
fewer visual concepts. A bar chart is usually easier to compare than a pie, and
|
|
32
|
+
a line is usually clearer than a radar for change over time.
|
|
33
|
+
|
|
34
|
+
Once you have chosen a type, use [Customization](./customization.md) for shared
|
|
35
|
+
colors, formatting, tooltips, and annotations.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Customization
|
|
2
|
+
|
|
3
|
+
Charts2 starts with product-ready defaults. Add only the methods that change
|
|
4
|
+
the result you need.
|
|
5
|
+
|
|
6
|
+
## Set the size
|
|
7
|
+
|
|
8
|
+
Charts are responsive by default: they follow the width of their host element.
|
|
9
|
+
Set a fixed drawing width only when the chart should not follow its container.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
13
|
+
|
|
14
|
+
LineChart.make("#revenue").dataset([42, 48, 57]).height(280).render();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- `height(pixels)` sets the chart height.
|
|
18
|
+
- `width(pixels)` sets an explicit width.
|
|
19
|
+
|
|
20
|
+
For a wide calendar heatmap on a small screen, let its host scroll:
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<div class="charts2-scrollable-heatmap">
|
|
24
|
+
<div id="activity"></div>
|
|
25
|
+
</div>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Choose chart colors
|
|
29
|
+
|
|
30
|
+
Pass colors in the order they should be used. The palette repeats when there
|
|
31
|
+
are more series or categories than colors.
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
import { BarChart } from "@orchidsoftware/charts";
|
|
35
|
+
|
|
36
|
+
BarChart.make("#orders")
|
|
37
|
+
.labels(["Web", "Retail", "Partners"])
|
|
38
|
+
.dataset("Orders", [124, 86, 43])
|
|
39
|
+
.colors(["#2563eb", "#8b5cf6", "#f59e0b"])
|
|
40
|
+
.render();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For series charts, colors are assigned by dataset. For pie, donut, percentage,
|
|
44
|
+
and polar area charts, colors are assigned by category. A heatmap uses the
|
|
45
|
+
palette as a low-to-high intensity scale.
|
|
46
|
+
|
|
47
|
+
To change one dataset without changing the chart palette, pass a color directly:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
51
|
+
|
|
52
|
+
LineChart.make("#revenue")
|
|
53
|
+
.dataset("Actual", [42, 48, 57], "#2563eb")
|
|
54
|
+
.dataset("Plan", [45, 50, 55], "#94a3b8")
|
|
55
|
+
.render();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Match the surrounding interface
|
|
59
|
+
|
|
60
|
+
Charts2 uses CSS variables for shared surface colors. Override them on a page,
|
|
61
|
+
card, or chart host:
|
|
62
|
+
|
|
63
|
+
```css
|
|
64
|
+
.analytics-card {
|
|
65
|
+
--charts-label-color: #e2e8f0;
|
|
66
|
+
--charts-secondary-label-color: #94a3b8;
|
|
67
|
+
--charts-axis-line-color: #334155;
|
|
68
|
+
--charts-tooltip-bg: rgb(15 23 42 / 96%);
|
|
69
|
+
--charts-tooltip-value: #f8fafc;
|
|
70
|
+
--charts-focus-ring: #38bdf8;
|
|
71
|
+
--charts-mark-separator: #0f172a;
|
|
72
|
+
--charts-point-fill: #0f172a;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This is usually enough for a dark card or branded product surface. Keep data
|
|
77
|
+
colors in `colors()` so they remain explicit and readable.
|
|
78
|
+
|
|
79
|
+
## Change the presentation
|
|
80
|
+
|
|
81
|
+
Common chart-wide methods describe the result directly:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
85
|
+
|
|
86
|
+
const compactTrend = LineChart.make("#trend")
|
|
87
|
+
.dataset([12, 18, 16, 25])
|
|
88
|
+
.height(90)
|
|
89
|
+
.smooth()
|
|
90
|
+
.gradient()
|
|
91
|
+
.frameless()
|
|
92
|
+
.render();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- Line: `smooth()`, `gradient()`, `area()`, `dots(false)`, `strokeWidth(value)`.
|
|
96
|
+
- Bar: `horizontal()`, `stacked()`, `radius(value)`.
|
|
97
|
+
- Cartesian: `axes(false)`, `grid(false)`, `valueLabels(false)`, `frameless()`.
|
|
98
|
+
- Pie and donut: `maxSlices(value)`, `startAngle(value)`, `padAngle(value)`,
|
|
99
|
+
`cornerRadius(value)`.
|
|
100
|
+
- Percentage: `maxSlices(value)`, `radius(value)`.
|
|
101
|
+
- Polar area: `padAngle(value)`, `cornerRadius(value)`.
|
|
102
|
+
- Radar: `strokeWidth(value)`.
|
|
103
|
+
- Heatmap: `countLabel(value)`, `radius(value)`.
|
|
104
|
+
- Timesheet: `axes(false)`, `grid(false)`, `valueLabels(false)`, `radius(value)`.
|
|
105
|
+
|
|
106
|
+
Boolean conventions use the short form when enabled. Their optional boolean
|
|
107
|
+
argument is useful for conditional code, such as `.stacked(isCompact)`.
|
|
108
|
+
|
|
109
|
+
## Format labels and values
|
|
110
|
+
|
|
111
|
+
Use chart-level formatters when the same rule should apply everywhere:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import { BarChart } from "@orchidsoftware/charts";
|
|
115
|
+
|
|
116
|
+
BarChart.make("#revenue")
|
|
117
|
+
.labels(["Starter", "Team", "Business"])
|
|
118
|
+
.dataset("MRR", [12400, 28600, 53100])
|
|
119
|
+
.formatValue((value) => `$${Math.round(value / 1000)}k`)
|
|
120
|
+
.formatLabel((label) => label.toUpperCase())
|
|
121
|
+
.render();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Customize only the tooltip when the axis should stay compact:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
128
|
+
|
|
129
|
+
LineChart.make("#revenue")
|
|
130
|
+
.dataset("Revenue", [12400, 28600, 53100])
|
|
131
|
+
.tooltip((tooltip) => {
|
|
132
|
+
tooltip.formatValue((value) => `$${value.toLocaleString()}`);
|
|
133
|
+
})
|
|
134
|
+
.render();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Pass `false` to `tooltip()` or `legend()` when that layer does not help the
|
|
138
|
+
reader.
|
|
139
|
+
|
|
140
|
+
## Add context to a cartesian chart
|
|
141
|
+
|
|
142
|
+
Markers show a target or threshold. Regions show a meaningful range.
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
146
|
+
|
|
147
|
+
LineChart.make("#response-time")
|
|
148
|
+
.labels(["Mon", "Tue", "Wed", "Thu", "Fri"])
|
|
149
|
+
.dataset("p95", [180, 210, 195, 240, 205])
|
|
150
|
+
.marker("Target", 200, "#dc2626")
|
|
151
|
+
.region("Healthy", [0, 200], "#16a34a")
|
|
152
|
+
.render();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Line, bar, scatter, bubble, and mixed charts support markers and regions.
|
|
156
|
+
|
|
157
|
+
## Customize one dataset
|
|
158
|
+
|
|
159
|
+
Use a dataset callback only when one series needs different treatment:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
163
|
+
|
|
164
|
+
LineChart.make("#revenue")
|
|
165
|
+
.dataset("Actual", [42, 48, 57], (dataset) => {
|
|
166
|
+
dataset.color("#2563eb").strokeWidth(3).dots(true);
|
|
167
|
+
})
|
|
168
|
+
.dataset("Plan", [45, 50, 55], (dataset) => {
|
|
169
|
+
dataset.color("#94a3b8").opacity(0.7).dots(false);
|
|
170
|
+
})
|
|
171
|
+
.render();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Start with chart-wide methods. Reach for a callback only when a single dataset,
|
|
175
|
+
axis, marker, region, or tooltip must differ.
|
|
176
|
+
|
|
177
|
+
Continue with [Updates and interaction](./updates-and-interaction.md) to connect
|
|
178
|
+
the finished chart to your application.
|
package/docs/donut.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Donut charts
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Donut charts show the same part-to-whole relationship as pie charts while
|
|
6
|
+
leaving the center open. Use one when the ring shape fits the surrounding
|
|
7
|
+
interface better than a solid circle.
|
|
8
|
+
|
|
9
|
+
## Creating a donut chart
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { DonutChart } from "@orchidsoftware/charts";
|
|
13
|
+
import "@orchidsoftware/charts/style.css";
|
|
14
|
+
|
|
15
|
+
const chart = DonutChart.make("#plans")
|
|
16
|
+
.labels(["Starter", "Team", "Business"])
|
|
17
|
+
.dataset([58, 31, 11])
|
|
18
|
+
.colors(["#2563eb", "#8b5cf6", "#f59e0b"])
|
|
19
|
+
.render();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Donut values must be non-negative and at least one value must be positive.
|
|
23
|
+
|
|
24
|
+
## Limiting slices
|
|
25
|
+
|
|
26
|
+
`maxSlices()` keeps the largest categories and combines the remainder:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import { DonutChart } from "@orchidsoftware/charts";
|
|
30
|
+
|
|
31
|
+
DonutChart.make("#sources").labels(sources).dataset(visits).maxSlices(5).render();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Starting angle
|
|
35
|
+
|
|
36
|
+
Use `startAngle()` to rotate the first slice to a deliberate position:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import { DonutChart } from "@orchidsoftware/charts";
|
|
40
|
+
|
|
41
|
+
DonutChart.make("#plans").labels(plans).dataset(accounts).startAngle(-90).render();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Slice spacing and corners
|
|
45
|
+
|
|
46
|
+
`padAngle()` adds spacing between slices. `cornerRadius()` rounds their corners:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import { DonutChart } from "@orchidsoftware/charts";
|
|
50
|
+
|
|
51
|
+
DonutChart.make("#plans").labels(plans).dataset(accounts).padAngle(2).cornerRadius(4).render();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Donut charts also support `formatLabel()`, `formatValue()`, `legend(false)`, and
|
|
55
|
+
the shared [colors, dimensions, tooltip, and selection methods](./customization.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Exporting SVG
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Every Charts2 chart is rendered as SVG. The runtime API can return that SVG as
|
|
6
|
+
a string or download it as a file. Export always uses the chart's current data
|
|
7
|
+
and presentation.
|
|
8
|
+
|
|
9
|
+
## Getting the SVG source
|
|
10
|
+
|
|
11
|
+
The `toSvg()` method returns the complete SVG markup as a string:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
15
|
+
import "@orchidsoftware/charts/style.css";
|
|
16
|
+
|
|
17
|
+
const chart = LineChart.make("#revenue")
|
|
18
|
+
.labels(["Jan", "Feb", "Mar", "Apr"])
|
|
19
|
+
.dataset("Revenue", [42, 48, 57, 63])
|
|
20
|
+
.render();
|
|
21
|
+
|
|
22
|
+
const source = chart.toSvg();
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Charts2 copies the computed presentation styles into the exported SVG. This
|
|
26
|
+
makes the result self-contained when it is opened outside the current page.
|
|
27
|
+
|
|
28
|
+
You may send the string to your own storage or download service:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
await fetch("/reports/revenue.svg", {
|
|
32
|
+
method: "PUT",
|
|
33
|
+
headers: { "Content-Type": "image/svg+xml" },
|
|
34
|
+
body: chart.toSvg(),
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Downloading an SVG file
|
|
39
|
+
|
|
40
|
+
The `download()` method starts a browser download:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import { BarChart } from "@orchidsoftware/charts";
|
|
44
|
+
import "@orchidsoftware/charts/style.css";
|
|
45
|
+
|
|
46
|
+
const chart = BarChart.make("#orders")
|
|
47
|
+
.labels(["Web", "Retail", "Partners"])
|
|
48
|
+
.dataset("Orders", [124, 86, 43])
|
|
49
|
+
.render();
|
|
50
|
+
|
|
51
|
+
chart.download("orders-by-channel");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Charts2 adds the `.svg` extension when it is not supplied. If the filename is
|
|
55
|
+
omitted, the chart title is used; a chart without a title downloads as
|
|
56
|
+
`Chart.svg`.
|
|
57
|
+
|
|
58
|
+
The filename must not be empty or contain `/` or `\\` path separators.
|
|
59
|
+
|
|
60
|
+
## Exporting updated data
|
|
61
|
+
|
|
62
|
+
Export reflects the latest successful update:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
66
|
+
import "@orchidsoftware/charts/style.css";
|
|
67
|
+
|
|
68
|
+
const chart = LineChart.make("#revenue")
|
|
69
|
+
.labels(["Jan", "Feb", "Mar"])
|
|
70
|
+
.dataset("Revenue", [42, 48, 57])
|
|
71
|
+
.render();
|
|
72
|
+
|
|
73
|
+
chart.update({
|
|
74
|
+
labels: ["Feb", "Mar", "Apr"],
|
|
75
|
+
datasets: [{ name: "Revenue", values: [48, 57, 63] }],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
chart.download("latest-revenue");
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
An invalid update leaves the previous chart unchanged, so a later export still
|
|
82
|
+
contains the last successfully rendered data.
|
|
83
|
+
|
|
84
|
+
## Exporting after cleanup
|
|
85
|
+
|
|
86
|
+
Do not call `toSvg()` or `download()` after `destroy()`. Destroying a chart
|
|
87
|
+
releases its SVG and browser resources, and the chart can no longer be used.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# React, Vue, and Hotwire
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Charts2 does not require a framework wrapper. A chart owns one host element and
|
|
6
|
+
returns a small lifecycle that maps directly to component mounting, updates,
|
|
7
|
+
and cleanup.
|
|
8
|
+
|
|
9
|
+
## React
|
|
10
|
+
|
|
11
|
+
Create the chart after the host element mounts. Keep the chart in a ref so data
|
|
12
|
+
changes can call `update()`, then destroy it from the effect cleanup:
|
|
13
|
+
|
|
14
|
+
```jsx
|
|
15
|
+
import { useEffect, useRef } from "react";
|
|
16
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
17
|
+
import "@orchidsoftware/charts/style.css";
|
|
18
|
+
|
|
19
|
+
export function RevenueChart({ labels, values }) {
|
|
20
|
+
const host = useRef(null);
|
|
21
|
+
const chart = useRef(null);
|
|
22
|
+
const initialData = useRef({ labels, values });
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const initial = initialData.current;
|
|
26
|
+
|
|
27
|
+
chart.current = LineChart.make(host.current)
|
|
28
|
+
.labels(initial.labels)
|
|
29
|
+
.dataset("Revenue", initial.values)
|
|
30
|
+
.gradient()
|
|
31
|
+
.render();
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
chart.current?.destroy();
|
|
35
|
+
chart.current = null;
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
chart.current?.update({
|
|
41
|
+
labels,
|
|
42
|
+
datasets: [{ name: "Revenue", values }],
|
|
43
|
+
});
|
|
44
|
+
}, [labels, values]);
|
|
45
|
+
|
|
46
|
+
return <div ref={host} />;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The first effect owns the chart lifecycle. The second replaces its data without
|
|
51
|
+
recreating the SVG or its event listeners.
|
|
52
|
+
|
|
53
|
+
If presentation methods such as `gradient()` or `height()` depend on props,
|
|
54
|
+
recreate the chart when those presentation props change. `update()` replaces
|
|
55
|
+
data; it does not reconfigure the chart type or builder options.
|
|
56
|
+
|
|
57
|
+
## Vue
|
|
58
|
+
|
|
59
|
+
Mount the chart in `onMounted()`, watch the data that may change, and destroy
|
|
60
|
+
the chart before the component unmounts:
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<script setup>
|
|
64
|
+
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
65
|
+
import { BarChart } from "@orchidsoftware/charts";
|
|
66
|
+
import "@orchidsoftware/charts/style.css";
|
|
67
|
+
|
|
68
|
+
const props = defineProps({
|
|
69
|
+
labels: { type: Array, required: true },
|
|
70
|
+
values: { type: Array, required: true },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const host = ref(null);
|
|
74
|
+
let chart;
|
|
75
|
+
|
|
76
|
+
onMounted(() => {
|
|
77
|
+
chart = BarChart.make(host.value)
|
|
78
|
+
.labels(props.labels)
|
|
79
|
+
.dataset("Orders", props.values)
|
|
80
|
+
.horizontal()
|
|
81
|
+
.render();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
watch(
|
|
85
|
+
() => [props.labels, props.values],
|
|
86
|
+
([labels, values]) => {
|
|
87
|
+
chart?.update({
|
|
88
|
+
labels,
|
|
89
|
+
datasets: [{ name: "Orders", values }],
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
onBeforeUnmount(() => chart?.destroy());
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<div ref="host" />
|
|
99
|
+
</template>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Hotwire and Stimulus
|
|
103
|
+
|
|
104
|
+
Let a Stimulus controller own the chart element. Values keep server-rendered
|
|
105
|
+
data in the HTML, value callbacks update a controller preserved by a Turbo
|
|
106
|
+
morph, and `disconnect()` handles frame or page replacement.
|
|
107
|
+
|
|
108
|
+
Import the Charts2 stylesheet once from your JavaScript entry point:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// app/javascript/application.js
|
|
112
|
+
import "@orchidsoftware/charts/style.css";
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Put the chart data in Stimulus values. The `turbo:before-cache` action removes
|
|
116
|
+
the generated SVG before Turbo takes its snapshot, so restoring the page never
|
|
117
|
+
duplicates a cached chart:
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<div
|
|
121
|
+
class="analytics-chart"
|
|
122
|
+
data-controller="revenue-chart"
|
|
123
|
+
data-action="turbo:before-cache@document->revenue-chart#destroy"
|
|
124
|
+
data-revenue-chart-labels-value='["Jan", "Feb", "Mar", "Apr"]'
|
|
125
|
+
data-revenue-chart-values-value="[42, 48, 57, 63]"
|
|
126
|
+
></div>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// app/javascript/controllers/revenue_chart_controller.js
|
|
131
|
+
import { Controller } from "@hotwired/stimulus";
|
|
132
|
+
import { LineChart } from "@orchidsoftware/charts";
|
|
133
|
+
|
|
134
|
+
export default class extends Controller {
|
|
135
|
+
static values = {
|
|
136
|
+
labels: Array,
|
|
137
|
+
values: Array,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
chart = null;
|
|
141
|
+
updateQueued = false;
|
|
142
|
+
|
|
143
|
+
connect() {
|
|
144
|
+
this.chart = LineChart.make(this.element)
|
|
145
|
+
.labels(this.labelsValue)
|
|
146
|
+
.dataset("Revenue", this.valuesValue)
|
|
147
|
+
.gradient()
|
|
148
|
+
.render();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
disconnect() {
|
|
152
|
+
this.destroy();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
labelsValueChanged() {
|
|
156
|
+
this.queueUpdate();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
valuesValueChanged() {
|
|
160
|
+
this.queueUpdate();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
destroy() {
|
|
164
|
+
this.chart?.destroy();
|
|
165
|
+
this.chart = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
queueUpdate() {
|
|
169
|
+
if (!this.chart || this.updateQueued) return;
|
|
170
|
+
|
|
171
|
+
this.updateQueued = true;
|
|
172
|
+
|
|
173
|
+
queueMicrotask(() => {
|
|
174
|
+
this.updateQueued = false;
|
|
175
|
+
this.chart?.update({
|
|
176
|
+
labels: this.labelsValue,
|
|
177
|
+
datasets: [{ name: "Revenue", values: this.valuesValue }],
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The microtask combines adjacent label and value mutations into one update.
|
|
185
|
+
When Turbo replaces the controller element, Stimulus destroys the old chart
|
|
186
|
+
and `connect()` renders the new one; no Turbo-specific global listener is
|
|
187
|
+
needed.
|
|
188
|
+
|
|
189
|
+
## Resizing
|
|
190
|
+
|
|
191
|
+
Do not calculate the component width for Charts2. Unless `width()` is set, the
|
|
192
|
+
chart follows its host element and observes width changes automatically. Use
|
|
193
|
+
ordinary CSS to size the host:
|
|
194
|
+
|
|
195
|
+
```css
|
|
196
|
+
.analytics-chart {
|
|
197
|
+
width: 100%;
|
|
198
|
+
min-width: 0;
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Continue with [Updates and interaction](./updates-and-interaction.md) for data,
|
|
203
|
+
selection, point snapshots, and cleanup details.
|