@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.
Files changed (109) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +122 -0
  4. package/dist/core/Chart.js +1 -0
  5. package/dist/core/Chart.js.map +1 -0
  6. package/dist/core/ChartData.js +1 -0
  7. package/dist/core/ChartData.js.map +1 -0
  8. package/dist/core/ChartDefinition.js +1 -0
  9. package/dist/core/ChartDefinition.js.map +1 -0
  10. package/dist/core/ChartSelection.js +1 -0
  11. package/dist/core/ChartSelection.js.map +1 -0
  12. package/dist/core/ChartTooltip.js +1 -0
  13. package/dist/core/ChartTooltip.js.map +1 -0
  14. package/dist/core/InteractionController.js +1 -0
  15. package/dist/core/InteractionController.js.map +1 -0
  16. package/dist/core/NormalizeAnnotations.js +1 -0
  17. package/dist/core/NormalizeAnnotations.js.map +1 -0
  18. package/dist/core/Options.js +1 -0
  19. package/dist/core/Options.js.map +1 -0
  20. package/dist/core/builders/Builder.js +1 -0
  21. package/dist/core/builders/Builder.js.map +1 -0
  22. package/dist/core/builders/BuilderArguments.js +1 -0
  23. package/dist/core/builders/BuilderArguments.js.map +1 -0
  24. package/dist/core/builders/BuilderScopes.js +1 -0
  25. package/dist/core/builders/BuilderScopes.js.map +1 -0
  26. package/dist/core/builders/BuilderState.js +1 -0
  27. package/dist/core/builders/BuilderState.js.map +1 -0
  28. package/dist/core/builders/BuilderValidation.js +1 -0
  29. package/dist/core/builders/BuilderValidation.js.map +1 -0
  30. package/dist/core/builders/CartesianBuilders.js +1 -0
  31. package/dist/core/builders/CartesianBuilders.js.map +1 -0
  32. package/dist/core/builders/CompositionBuilders.js +1 -0
  33. package/dist/core/builders/CompositionBuilders.js.map +1 -0
  34. package/dist/core/builders/TemporalBuilders.js +1 -0
  35. package/dist/core/builders/TemporalBuilders.js.map +1 -0
  36. package/dist/index.js +1 -0
  37. package/dist/renderers/ChartRendering.js +1 -0
  38. package/dist/renderers/ChartRendering.js.map +1 -0
  39. package/dist/renderers/LegendRenderer.js +1 -0
  40. package/dist/renderers/LegendRenderer.js.map +1 -0
  41. package/dist/renderers/SvgSurface.js +1 -0
  42. package/dist/renderers/SvgSurface.js.map +1 -0
  43. package/dist/renderers/cartesian/CartesianAxesRenderer.js +1 -0
  44. package/dist/renderers/cartesian/CartesianAxesRenderer.js.map +1 -0
  45. package/dist/renderers/cartesian/CartesianInspectorRenderer.js +1 -0
  46. package/dist/renderers/cartesian/CartesianInspectorRenderer.js.map +1 -0
  47. package/dist/renderers/cartesian/CartesianLayout.js +1 -0
  48. package/dist/renderers/cartesian/CartesianLayout.js.map +1 -0
  49. package/dist/renderers/cartesian/CartesianRendering.js +1 -0
  50. package/dist/renderers/cartesian/CartesianRendering.js.map +1 -0
  51. package/dist/renderers/cartesian/CartesianSeriesRendering.js +1 -0
  52. package/dist/renderers/cartesian/CartesianSeriesRendering.js.map +1 -0
  53. package/dist/renderers/composition/AggregationRendering.js +1 -0
  54. package/dist/renderers/composition/AggregationRendering.js.map +1 -0
  55. package/dist/renderers/composition/Composition.js +1 -0
  56. package/dist/renderers/composition/Composition.js.map +1 -0
  57. package/dist/renderers/composition/PolarAreaRendering.js +1 -0
  58. package/dist/renderers/composition/PolarAreaRendering.js.map +1 -0
  59. package/dist/renderers/composition/RadarRendering.js +1 -0
  60. package/dist/renderers/composition/RadarRendering.js.map +1 -0
  61. package/dist/renderers/temporal/HeatmapRendering.js +1 -0
  62. package/dist/renderers/temporal/HeatmapRendering.js.map +1 -0
  63. package/dist/renderers/temporal/TimesheetLayout.js +1 -0
  64. package/dist/renderers/temporal/TimesheetLayout.js.map +1 -0
  65. package/dist/renderers/temporal/TimesheetRendering.js +1 -0
  66. package/dist/renderers/temporal/TimesheetRendering.js.map +1 -0
  67. package/dist/support/Constants.js +1 -0
  68. package/dist/support/Constants.js.map +1 -0
  69. package/dist/support/Dom.js +1 -0
  70. package/dist/support/Dom.js.map +1 -0
  71. package/dist/support/Normalize.js +2 -0
  72. package/dist/support/Normalize.js.map +1 -0
  73. package/dist/support/Validation.js +1 -0
  74. package/dist/support/Validation.js.map +1 -0
  75. package/dist/support/geometry/CartesianGeometry.js +1 -0
  76. package/dist/support/geometry/CartesianGeometry.js.map +1 -0
  77. package/dist/support/geometry/Scale.js +1 -0
  78. package/dist/support/geometry/Scale.js.map +1 -0
  79. package/dist/support/geometry/SectorGeometry.js +1 -0
  80. package/dist/support/geometry/SectorGeometry.js.map +1 -0
  81. package/dist/support/presentation/Formatting.js +1 -0
  82. package/dist/support/presentation/Formatting.js.map +1 -0
  83. package/dist/support/presentation/Presentation.js +1 -0
  84. package/dist/support/presentation/Presentation.js.map +1 -0
  85. package/dist/support/presentation/Time.js +1 -0
  86. package/dist/support/presentation/Time.js.map +1 -0
  87. package/docs/api-reference.md +322 -0
  88. package/docs/bar.md +110 -0
  89. package/docs/bubble.md +76 -0
  90. package/docs/chart-types.md +35 -0
  91. package/docs/customization.md +178 -0
  92. package/docs/donut.md +55 -0
  93. package/docs/exporting.md +87 -0
  94. package/docs/frameworks.md +203 -0
  95. package/docs/getting-started.md +121 -0
  96. package/docs/heatmap.md +95 -0
  97. package/docs/line.md +148 -0
  98. package/docs/mixed.md +114 -0
  99. package/docs/percentage.md +48 -0
  100. package/docs/pie.md +58 -0
  101. package/docs/polar-area.md +46 -0
  102. package/docs/radar.md +49 -0
  103. package/docs/readme.md +37 -0
  104. package/docs/scatter.md +83 -0
  105. package/docs/timesheet.md +125 -0
  106. package/docs/updates-and-interaction.md +124 -0
  107. package/package.json +133 -0
  108. package/src/index.d.ts +481 -0
  109. 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.