@sckyzo/cosmos-charts 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +107 -22
  3. package/THIRD_PARTY_NOTICES.md +81 -0
  4. package/dist/BarChart.cjs +137 -0
  5. package/dist/BarChart.cjs.map +1 -0
  6. package/dist/BarChart.js +135 -0
  7. package/dist/BarChart.js.map +1 -0
  8. package/dist/BoxPlotChart.cjs +214 -0
  9. package/dist/BoxPlotChart.cjs.map +1 -0
  10. package/dist/BoxPlotChart.js +212 -0
  11. package/dist/BoxPlotChart.js.map +1 -0
  12. package/dist/BumpChart.cjs +328 -0
  13. package/dist/BumpChart.cjs.map +1 -0
  14. package/dist/BumpChart.js +326 -0
  15. package/dist/BumpChart.js.map +1 -0
  16. package/dist/CalendarChart.cjs +216 -0
  17. package/dist/CalendarChart.cjs.map +1 -0
  18. package/dist/CalendarChart.js +214 -0
  19. package/dist/CalendarChart.js.map +1 -0
  20. package/dist/DonutChart.cjs +122 -0
  21. package/dist/DonutChart.cjs.map +1 -0
  22. package/dist/DonutChart.js +120 -0
  23. package/dist/DonutChart.js.map +1 -0
  24. package/dist/FunnelChart.cjs +422 -0
  25. package/dist/FunnelChart.cjs.map +1 -0
  26. package/dist/FunnelChart.js +420 -0
  27. package/dist/FunnelChart.js.map +1 -0
  28. package/dist/GradientRadial.cjs +51 -0
  29. package/dist/GradientRadial.cjs.map +1 -0
  30. package/dist/GradientRadial.js +49 -0
  31. package/dist/GradientRadial.js.map +1 -0
  32. package/dist/HeatmapChart.cjs +146 -0
  33. package/dist/HeatmapChart.cjs.map +1 -0
  34. package/dist/HeatmapChart.js +144 -0
  35. package/dist/HeatmapChart.js.map +1 -0
  36. package/dist/LineAreaChart.cjs +98 -0
  37. package/dist/LineAreaChart.cjs.map +1 -0
  38. package/dist/LineAreaChart.js +96 -0
  39. package/dist/LineAreaChart.js.map +1 -0
  40. package/dist/MixedChart.cjs +195 -0
  41. package/dist/MixedChart.cjs.map +1 -0
  42. package/dist/MixedChart.js +193 -0
  43. package/dist/MixedChart.js.map +1 -0
  44. package/dist/RadarChart.cjs +109 -0
  45. package/dist/RadarChart.cjs.map +1 -0
  46. package/dist/RadarChart.js +107 -0
  47. package/dist/RadarChart.js.map +1 -0
  48. package/dist/RadialBarChart.cjs +99 -0
  49. package/dist/RadialBarChart.cjs.map +1 -0
  50. package/dist/RadialBarChart.js +97 -0
  51. package/dist/RadialBarChart.js.map +1 -0
  52. package/dist/RangeBarChart.cjs +218 -0
  53. package/dist/RangeBarChart.cjs.map +1 -0
  54. package/dist/RangeBarChart.js +216 -0
  55. package/dist/RangeBarChart.js.map +1 -0
  56. package/dist/RealtimeChart.cjs +173 -0
  57. package/dist/RealtimeChart.cjs.map +1 -0
  58. package/dist/RealtimeChart.js +171 -0
  59. package/dist/RealtimeChart.js.map +1 -0
  60. package/dist/ScatterChart.cjs +134 -0
  61. package/dist/ScatterChart.cjs.map +1 -0
  62. package/dist/ScatterChart.js +131 -0
  63. package/dist/ScatterChart.js.map +1 -0
  64. package/dist/SemiCircleGauge.cjs +142 -0
  65. package/dist/SemiCircleGauge.cjs.map +1 -0
  66. package/dist/SemiCircleGauge.js +140 -0
  67. package/dist/SemiCircleGauge.js.map +1 -0
  68. package/dist/Sparkline.cjs +117 -0
  69. package/dist/Sparkline.cjs.map +1 -0
  70. package/dist/Sparkline.js +115 -0
  71. package/dist/Sparkline.js.map +1 -0
  72. package/dist/StrokedGauge.cjs +94 -0
  73. package/dist/StrokedGauge.cjs.map +1 -0
  74. package/dist/StrokedGauge.js +92 -0
  75. package/dist/StrokedGauge.js.map +1 -0
  76. package/dist/TreemapChart.cjs +263 -0
  77. package/dist/TreemapChart.cjs.map +1 -0
  78. package/dist/TreemapChart.js +261 -0
  79. package/dist/TreemapChart.js.map +1 -0
  80. package/dist/WaterfallChart.cjs +235 -0
  81. package/dist/WaterfallChart.cjs.map +1 -0
  82. package/dist/WaterfallChart.js +233 -0
  83. package/dist/WaterfallChart.js.map +1 -0
  84. package/dist/WorldMap.cjs +2088 -0
  85. package/dist/WorldMap.cjs.map +1 -0
  86. package/dist/WorldMap.js +603 -0
  87. package/dist/WorldMap.js.map +1 -0
  88. package/dist/chunk-3N43QT36.js +60 -0
  89. package/dist/chunk-3N43QT36.js.map +1 -0
  90. package/dist/chunk-5C2RLJYZ.js +11 -0
  91. package/dist/chunk-5C2RLJYZ.js.map +1 -0
  92. package/dist/chunk-6ALKZLN2.js +127 -0
  93. package/dist/chunk-6ALKZLN2.js.map +1 -0
  94. package/dist/chunk-6BKLL7LZ.cjs +30 -0
  95. package/dist/chunk-6BKLL7LZ.cjs.map +1 -0
  96. package/dist/chunk-6NRRABPF.cjs +64 -0
  97. package/dist/chunk-6NRRABPF.cjs.map +1 -0
  98. package/dist/chunk-AAYHG7YK.cjs +30 -0
  99. package/dist/chunk-AAYHG7YK.cjs.map +1 -0
  100. package/dist/chunk-AOSO5QHL.js +19 -0
  101. package/dist/chunk-AOSO5QHL.js.map +1 -0
  102. package/dist/chunk-BFEDLXVW.js +12 -0
  103. package/dist/chunk-BFEDLXVW.js.map +1 -0
  104. package/dist/chunk-DBZQI4HM.cjs +14 -0
  105. package/dist/chunk-DBZQI4HM.cjs.map +1 -0
  106. package/dist/chunk-F2PMFYJ5.cjs +40 -0
  107. package/dist/chunk-F2PMFYJ5.cjs.map +1 -0
  108. package/dist/chunk-FUZW3H5X.js +38 -0
  109. package/dist/chunk-FUZW3H5X.js.map +1 -0
  110. package/dist/chunk-GDZLYYL2.js +27 -0
  111. package/dist/chunk-GDZLYYL2.js.map +1 -0
  112. package/dist/chunk-GVTMZYOM.cjs +30 -0
  113. package/dist/chunk-GVTMZYOM.cjs.map +1 -0
  114. package/dist/chunk-GZRMVE4X.cjs +131 -0
  115. package/dist/chunk-GZRMVE4X.cjs.map +1 -0
  116. package/dist/chunk-HMH6DI7U.js +27 -0
  117. package/dist/chunk-HMH6DI7U.js.map +1 -0
  118. package/dist/chunk-ICL5K2CW.js +280 -0
  119. package/dist/chunk-ICL5K2CW.js.map +1 -0
  120. package/dist/chunk-IW6EWB26.cjs +21 -0
  121. package/dist/chunk-IW6EWB26.cjs.map +1 -0
  122. package/dist/chunk-K3R7JGOW.cjs +18 -0
  123. package/dist/chunk-K3R7JGOW.cjs.map +1 -0
  124. package/dist/chunk-LT3UC65O.cjs +68 -0
  125. package/dist/chunk-LT3UC65O.cjs.map +1 -0
  126. package/dist/chunk-MN4CDVOS.cjs +63 -0
  127. package/dist/chunk-MN4CDVOS.cjs.map +1 -0
  128. package/dist/chunk-OCD6JIIX.js +23 -0
  129. package/dist/chunk-OCD6JIIX.js.map +1 -0
  130. package/dist/chunk-RILUG7JB.js +44 -0
  131. package/dist/chunk-RILUG7JB.js.map +1 -0
  132. package/dist/chunk-RLHGPHC7.cjs +51 -0
  133. package/dist/chunk-RLHGPHC7.cjs.map +1 -0
  134. package/dist/chunk-SGG7XKOY.js +58 -0
  135. package/dist/chunk-SGG7XKOY.js.map +1 -0
  136. package/dist/chunk-T3K6Q34U.cjs +82 -0
  137. package/dist/chunk-T3K6Q34U.cjs.map +1 -0
  138. package/dist/chunk-TPH5T3DA.cjs +282 -0
  139. package/dist/chunk-TPH5T3DA.cjs.map +1 -0
  140. package/dist/chunk-TYQWZR3S.cjs +13 -0
  141. package/dist/chunk-TYQWZR3S.cjs.map +1 -0
  142. package/dist/chunk-UATB2IRY.js +51 -0
  143. package/dist/chunk-UATB2IRY.js.map +1 -0
  144. package/dist/chunk-UJIV5PVI.js +13 -0
  145. package/dist/chunk-UJIV5PVI.js.map +1 -0
  146. package/dist/chunk-URNXLVSK.js +44 -0
  147. package/dist/chunk-URNXLVSK.js.map +1 -0
  148. package/dist/chunk-VIOSQN4Z.js +79 -0
  149. package/dist/chunk-VIOSQN4Z.js.map +1 -0
  150. package/dist/chunk-X3YZP5WF.cjs +46 -0
  151. package/dist/chunk-X3YZP5WF.cjs.map +1 -0
  152. package/dist/chunk-YWYF5CCJ.js +61 -0
  153. package/dist/chunk-YWYF5CCJ.js.map +1 -0
  154. package/dist/chunk-Z4LZFMDH.cjs +60 -0
  155. package/dist/chunk-Z4LZFMDH.cjs.map +1 -0
  156. package/dist/index.cjs +154 -1166
  157. package/dist/index.cjs.map +1 -1
  158. package/dist/index.d.cts +470 -80
  159. package/dist/index.d.ts +470 -80
  160. package/dist/index.js +23 -1144
  161. package/dist/index.js.map +1 -1
  162. package/dist/theme.cjs +88 -0
  163. package/dist/theme.cjs.map +1 -0
  164. package/dist/theme.js +84 -0
  165. package/dist/theme.js.map +1 -0
  166. package/package.json +23 -12
@@ -0,0 +1,235 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var chunk6BKLL7LZ_cjs = require('./chunk-6BKLL7LZ.cjs');
5
+ var chunkIW6EWB26_cjs = require('./chunk-IW6EWB26.cjs');
6
+ var chunkTYQWZR3S_cjs = require('./chunk-TYQWZR3S.cjs');
7
+ var chunkRLHGPHC7_cjs = require('./chunk-RLHGPHC7.cjs');
8
+ var chunkT3K6Q34U_cjs = require('./chunk-T3K6Q34U.cjs');
9
+ var chunk6NRRABPF_cjs = require('./chunk-6NRRABPF.cjs');
10
+ var chunkLT3UC65O_cjs = require('./chunk-LT3UC65O.cjs');
11
+ var react = require('react');
12
+ var recharts = require('recharts');
13
+ var jsxRuntime = require('react/jsx-runtime');
14
+
15
+ // src/waterfall.ts
16
+ var KIND_WORDS = {
17
+ increase: "Increase",
18
+ decrease: "Decrease",
19
+ subtotal: "Subtotal",
20
+ total: "Total"
21
+ };
22
+ var clean = (value) => value !== void 0 && Number.isFinite(value) ? value : 0;
23
+ var waterfallBars = (data) => {
24
+ let running = 0;
25
+ return data.map((datum) => {
26
+ if (datum.kind === "subtotal" || datum.kind === "total") {
27
+ if (datum.kind === "total" && datum.value !== void 0) running = clean(datum.value);
28
+ return {
29
+ label: datum.label,
30
+ kind: datum.kind,
31
+ value: running,
32
+ low: Math.min(0, running),
33
+ high: Math.max(0, running),
34
+ running
35
+ };
36
+ }
37
+ const change = clean(datum.value);
38
+ const before = running;
39
+ running += change;
40
+ return {
41
+ label: datum.label,
42
+ kind: change < 0 ? "decrease" : "increase",
43
+ value: change,
44
+ low: Math.min(before, running),
45
+ high: Math.max(before, running),
46
+ running
47
+ };
48
+ });
49
+ };
50
+ var signed = (value, format = String) => `${value < 0 ? "\u2212" : "+"}${format(Math.abs(value))}`;
51
+ var barText = (bar, format = String) => bar.kind === "increase" || bar.kind === "decrease" ? signed(bar.value, format) : format(bar.running);
52
+ var NARROW = 480;
53
+ var MAX_LABELED_BARS = 12;
54
+ var GAP = 0.19;
55
+ var MAX_BAR = 64;
56
+ var CHAR_WIDTH = 8;
57
+ var SUBTOTAL_OPACITY = 0.55;
58
+ var TILT_HEIGHT = 4.2;
59
+ var MAX_CHARS = 14;
60
+ var fillOf = (kind, palette) => kind === "increase" ? palette.positive : kind === "decrease" ? palette.negative : palette.total;
61
+ var waterfallShape = (palette, labels, format) => (props) => {
62
+ const row = props.payload;
63
+ const left = Math.min(props.x, props.x + props.width);
64
+ const top = Math.min(props.y, props.y + props.height);
65
+ const width = Math.abs(props.width);
66
+ const height = Math.max(Math.abs(props.height), 1);
67
+ if (!(width > 0)) return null;
68
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
69
+ /* @__PURE__ */ jsxRuntime.jsx(
70
+ "rect",
71
+ {
72
+ x: left,
73
+ y: top,
74
+ width,
75
+ height,
76
+ rx: Math.min(3, width / 2, height / 2),
77
+ fill: fillOf(row.kind, palette),
78
+ fillOpacity: row.kind === "subtotal" ? SUBTOTAL_OPACITY : 1
79
+ }
80
+ ),
81
+ labels && /* @__PURE__ */ jsxRuntime.jsx(
82
+ "text",
83
+ {
84
+ x: left + width / 2,
85
+ y: top - 6,
86
+ textAnchor: "middle",
87
+ fill: chunkLT3UC65O_cjs.TEXT,
88
+ style: {
89
+ fontSize: chunkRLHGPHC7_cjs.TEXT_SIZE,
90
+ fontWeight: 600,
91
+ fontVariantNumeric: "tabular-nums"
92
+ },
93
+ children: barText(row, format)
94
+ }
95
+ )
96
+ ] });
97
+ };
98
+ var Connectors = ({ bars }) => {
99
+ const x = recharts.useXAxisScale();
100
+ const y = recharts.useYAxisScale();
101
+ if (!x || !y) return null;
102
+ const edges = bars.map((_, i) => {
103
+ const start = x(String(i), { position: "start" });
104
+ const end = x(String(i), { position: "end" });
105
+ if (start === void 0 || end === void 0) return void 0;
106
+ const band = end - start;
107
+ const full = Math.round(band * (1 - 2 * GAP));
108
+ const size = Math.min(full, MAX_BAR);
109
+ const left = start + band * GAP + (full - size) / 2;
110
+ return { left, right: left + size };
111
+ });
112
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { "aria-hidden": true, "data-waterfall-connectors": "", children: bars.slice(0, -1).map((bar, i) => {
113
+ const from = edges[i];
114
+ const to = edges[i + 1];
115
+ const level = y(bar.running);
116
+ if (!from || !to || level === void 0) return null;
117
+ return /* @__PURE__ */ jsxRuntime.jsx(
118
+ "line",
119
+ {
120
+ x1: from.right,
121
+ x2: to.left,
122
+ y1: level,
123
+ y2: level,
124
+ stroke: chunkLT3UC65O_cjs.MUTED,
125
+ strokeOpacity: 0.7,
126
+ strokeWidth: 1,
127
+ strokeDasharray: "3 3"
128
+ },
129
+ i
130
+ );
131
+ }) });
132
+ };
133
+ var WaterfallChart = ({
134
+ data,
135
+ formatValue = String,
136
+ showLabels,
137
+ showConnectors = true,
138
+ colors,
139
+ xTitle,
140
+ yTitle,
141
+ emptyMessage = "No data",
142
+ ariaLabel,
143
+ children,
144
+ height = 320,
145
+ width
146
+ }) => {
147
+ const axis = chunkRLHGPHC7_cjs.useAxis();
148
+ const { ref, width: measured } = chunkIW6EWB26_cjs.useElementWidth();
149
+ const bars = react.useMemo(() => waterfallBars(data), [data]);
150
+ const rows = react.useMemo(
151
+ () => bars.map((bar, i) => ({ ...bar, key: String(i), range: [bar.low, bar.high] })),
152
+ [bars]
153
+ );
154
+ const name = ariaLabel ?? `Waterfall chart: ${data.map((d) => d.label).join(", ")}`;
155
+ if (data.length === 0) {
156
+ return /* @__PURE__ */ jsxRuntime.jsx(chunk6NRRABPF_cjs.ChartEmpty, { message: emptyMessage, ariaLabel: name, height, width });
157
+ }
158
+ const palette = {
159
+ positive: colors?.positive ?? chunkLT3UC65O_cjs.SUCCESS,
160
+ negative: colors?.negative ?? chunkLT3UC65O_cjs.ERROR,
161
+ total: colors?.total ?? chunkLT3UC65O_cjs.SERIES[0]
162
+ };
163
+ const narrow = measured !== void 0 && measured < NARROW;
164
+ const labels = showLabels ?? (rows.length <= MAX_LABELED_BARS && !narrow);
165
+ const longest = Math.min(MAX_CHARS, Math.max(...rows.map((row) => row.label.length)));
166
+ const band = measured === void 0 ? Infinity : (measured - 56) / rows.length;
167
+ const tilted = narrow || longest * CHAR_WIDTH > band;
168
+ const labelAxis = {
169
+ interval: 0,
170
+ tickFormatter: (_, index) => chunk6BKLL7LZ_cjs.trimLabel(rows[index]?.label ?? ""),
171
+ ...tilted ? {
172
+ angle: -40,
173
+ tick: { ...chunkRLHGPHC7_cjs.AXIS.tick, textAnchor: "end" },
174
+ height: (xTitle ? 52 : 30) + Math.round(longest * TILT_HEIGHT)
175
+ } : {}
176
+ };
177
+ const tooltip = ({ active, payload }) => {
178
+ const row = payload?.[0]?.payload;
179
+ if (!active || !row) return null;
180
+ const color = fillOf(row.kind, palette);
181
+ const step = row.kind === "increase" || row.kind === "decrease";
182
+ return /* @__PURE__ */ jsxRuntime.jsx(
183
+ chunkT3K6Q34U_cjs.TooltipCard,
184
+ {
185
+ title: row.label,
186
+ rows: [
187
+ {
188
+ name: KIND_WORDS[row.kind],
189
+ value: step ? signed(row.value, formatValue) : formatValue(row.running),
190
+ color
191
+ },
192
+ ...step ? [{ name: "Running total", value: formatValue(row.running), color: "transparent" }] : []
193
+ ]
194
+ }
195
+ );
196
+ };
197
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, style: { width: width ?? "100%", maxWidth: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
198
+ recharts.BarChart,
199
+ {
200
+ ...chunk6NRRABPF_cjs.frameProps({ height, width: "100%", ariaLabel: name }),
201
+ data: rows,
202
+ margin: { top: labels ? 24 : 4, right: 8, bottom: 0, left: 0 },
203
+ barCategoryGap: `${GAP * 100}%`,
204
+ children: [
205
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { ...chunkRLHGPHC7_cjs.GRID_PROPS }),
206
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "key", ...axis, ...chunkRLHGPHC7_cjs.xAxisTitle(xTitle), ...labelAxis }),
207
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { width: "auto", tickFormatter: formatValue, ...axis, ...chunkRLHGPHC7_cjs.yAxisTitle(yTitle) }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(
209
+ recharts.Tooltip,
210
+ {
211
+ content: tooltip,
212
+ cursor: { fill: chunkLT3UC65O_cjs.BORDER, fillOpacity: 0.35, radius: 8 },
213
+ animationDuration: 150
214
+ }
215
+ ),
216
+ /* @__PURE__ */ jsxRuntime.jsx(
217
+ recharts.Bar,
218
+ {
219
+ name: "Waterfall",
220
+ dataKey: "range",
221
+ maxBarSize: MAX_BAR,
222
+ shape: waterfallShape(palette, labels, formatValue),
223
+ ...chunkTYQWZR3S_cjs.ANIMATION
224
+ }
225
+ ),
226
+ showConnectors && /* @__PURE__ */ jsxRuntime.jsx(Connectors, { bars }),
227
+ children
228
+ ]
229
+ }
230
+ ) });
231
+ };
232
+
233
+ exports.WaterfallChart = WaterfallChart;
234
+ //# sourceMappingURL=WaterfallChart.cjs.map
235
+ //# sourceMappingURL=WaterfallChart.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/waterfall.ts","../src/WaterfallChart.tsx"],"names":["jsx","TEXT","TEXT_SIZE","useXAxisScale","useYAxisScale","MUTED","useAxis","useElementWidth","useMemo","ChartEmpty","SUCCESS","ERROR","SERIES","trimLabel","AXIS","TooltipCard","jsxs","BarChart","frameProps","CartesianGrid","GRID_PROPS","XAxis","xAxisTitle","YAxis","yAxisTitle","Tooltip","BORDER","Bar","ANIMATION"],"mappings":"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","file":"WaterfallChart.cjs","sourcesContent":["// The maths of WaterfallChart: the running total, the span each bar covers and\n// the text that names it. No DOM and no colors, so it runs in plain Node.\n\nexport type WaterfallDatum = {\n label: string;\n /** A step: the signed change. A total: sets the running total to it (an opening balance). */\n value?: number;\n /** Default `step`. `subtotal` and `total` are drawn from 0 to the running total. */\n kind?: 'step' | 'subtotal' | 'total';\n};\n\n/** What a bar means: a step reads as an increase or a decrease from its sign. */\nexport type WaterfallBarKind = 'increase' | 'decrease' | 'subtotal' | 'total';\n\nexport type WaterfallBar = {\n label: string;\n kind: WaterfallBarKind;\n /** A step: its signed change. A subtotal or a total: the running total it shows. */\n value: number;\n /** The bottom and the top of the bar, in the units of the data. */\n low: number;\n high: number;\n /** The running total after this bar. */\n running: number;\n};\n\n/** The kind in words, for the tooltip and the name a screen reader hears. */\nexport const KIND_WORDS: Record<WaterfallBarKind, string> = {\n increase: 'Increase',\n decrease: 'Decrease',\n subtotal: 'Subtotal',\n total: 'Total',\n};\n\n// A missing or non-finite value counts as 0.\nconst clean = (value: number | undefined) =>\n value !== undefined && Number.isFinite(value) ? value : 0;\n\n/**\n * One bar per datum, in the order given. A step moves the running total by its\n * signed `value` and floats between the total before and after it. A subtotal\n * is the running total at that point, from 0, never the sum since the last\n * cut. A total with a `value` sets the running total to it (the opening\n * balance, or a known balance mid-walk); without one it shows the running\n * total. Both are drawn from 0.\n */\nexport const waterfallBars = (data: readonly WaterfallDatum[]): WaterfallBar[] => {\n let running = 0;\n return data.map((datum) => {\n if (datum.kind === 'subtotal' || datum.kind === 'total') {\n if (datum.kind === 'total' && datum.value !== undefined) running = clean(datum.value);\n return {\n label: datum.label,\n kind: datum.kind,\n value: running,\n low: Math.min(0, running),\n high: Math.max(0, running),\n running,\n };\n }\n const change = clean(datum.value);\n const before = running;\n running += change;\n return {\n label: datum.label,\n kind: change < 0 ? 'decrease' : 'increase',\n value: change,\n low: Math.min(before, running),\n high: Math.max(before, running),\n running,\n };\n });\n};\n\n/** A change with its sign, in text: `+310k`, `−265k` (U+2212). `format` gets the magnitude. */\nexport const signed = (value: number, format: (value: number) => string = String) =>\n `${value < 0 ? '−' : '+'}${format(Math.abs(value))}`;\n\n/** What is written above a bar: the signed change of a step, the running total of a subtotal or a total. */\nexport const barText = (bar: WaterfallBar, format: (value: number) => string = String) =>\n bar.kind === 'increase' || bar.kind === 'decrease'\n ? signed(bar.value, format)\n : format(bar.running);\n","import { useMemo } from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Tooltip,\n useXAxisScale,\n useYAxisScale,\n XAxis,\n YAxis,\n type BarShapeProps,\n type TooltipContentProps,\n} from 'recharts';\nimport { ANIMATION } from './core/animation';\nimport { AXIS, GRID_PROPS, TEXT_SIZE, useAxis, xAxisTitle, yAxisTitle } from './core/axes';\nimport { trimLabel } from './core/categoryAxis';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { BORDER, ERROR, MUTED, SERIES, SUCCESS, TEXT } from './core/colors';\nimport { ChartEmpty, frameProps } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport type { ChartAxisTitleProps, ChartEmptyProps, ChartProps } from './types';\nimport {\n barText,\n KIND_WORDS,\n signed,\n waterfallBars,\n type WaterfallBar,\n type WaterfallDatum,\n} from './waterfall';\n\nexport type { WaterfallDatum } from './waterfall';\n\nexport type WaterfallChartProps = ChartProps &\n ChartEmptyProps &\n ChartAxisTitleProps & {\n /**\n * The bars, left to right. A step (default) carries a signed `value`. A\n * `subtotal` is drawn from 0 to the running total at that point. A\n * `total` is drawn the same way, and a `value` on it sets the running\n * total to it: an opening balance, or a known balance mid-walk.\n */\n data: WaterfallDatum[];\n /** Format the value axis, the labels and the tooltip. The sign of a step is written apart: the function gets the magnitude. */\n formatValue?: (value: number) => string;\n /**\n * Write the value above each bar. Default: on with 12 bars or fewer in a chart at least\n * 480px wide, off otherwise (the values stay in the tooltip and the keyboard walk).\n */\n showLabels?: boolean;\n /** Dashed lines from the top of a bar to the next one. Default true. */\n showConnectors?: boolean;\n /** Override the colors: increases, decreases, totals and subtotals. Defaults: the theme's success, error and accent. */\n colors?: { positive?: string; negative?: string; total?: string };\n };\n\n// Below this width the value labels go and the category names tilt.\nconst NARROW = 480;\nconst MAX_LABELED_BARS = 12;\n// The share of a category's band left empty on each side of a bar, and the\n// widest bar. Recharts places the bar from these; the connectors are drawn\n// from the same numbers.\nconst GAP = 0.19;\nconst MAX_BAR = 64;\n// Pixels one character of a category name takes, at the largest text size.\nconst CHAR_WIDTH = 8;\nconst SUBTOTAL_OPACITY = 0.55;\nconst TILT_HEIGHT = 4.2;\nconst MAX_CHARS = 14;\n\ntype Row = WaterfallBar & { key: string; range: [number, number] };\n\ntype Palette = { positive: string; negative: string; total: string };\n\nconst fillOf = (kind: WaterfallBar['kind'], palette: Palette) =>\n kind === 'increase' ? palette.positive : kind === 'decrease' ? palette.negative : palette.total;\n\n// The bar is the span between the running total before and after the step; the\n// label above it is drawn here too, so it moves with the bar while it grows.\nconst waterfallShape =\n (palette: Palette, labels: boolean, format: (value: number) => string) =>\n (props: BarShapeProps) => {\n const row = props.payload as Row;\n const left = Math.min(props.x, props.x + props.width);\n const top = Math.min(props.y, props.y + props.height);\n const width = Math.abs(props.width);\n // A step of 0 is a hairline.\n const height = Math.max(Math.abs(props.height), 1);\n if (!(width > 0)) return null;\n return (\n <g>\n <rect\n x={left}\n y={top}\n width={width}\n height={height}\n rx={Math.min(3, width / 2, height / 2)}\n fill={fillOf(row.kind, palette)}\n fillOpacity={row.kind === 'subtotal' ? SUBTOTAL_OPACITY : 1}\n />\n {labels && (\n <text\n x={left + width / 2}\n y={top - 6}\n textAnchor=\"middle\"\n fill={TEXT}\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {barText(row, format)}\n </text>\n )}\n </g>\n );\n };\n\n// From the right edge of a bar, at the running total after it, to the left edge\n// of the next one. Out of the accessibility layer: the bars say it all.\nconst Connectors = ({ bars }: { bars: readonly WaterfallBar[] }) => {\n const x = useXAxisScale();\n const y = useYAxisScale();\n if (!x || !y) return null;\n const edges = bars.map((_, i) => {\n const start = x(String(i), { position: 'start' });\n const end = x(String(i), { position: 'end' });\n if (start === undefined || end === undefined) return undefined;\n const band = end - start;\n const full = Math.round(band * (1 - 2 * GAP));\n const size = Math.min(full, MAX_BAR);\n const left = start + band * GAP + (full - size) / 2;\n return { left, right: left + size };\n });\n return (\n <g aria-hidden data-waterfall-connectors=\"\">\n {bars.slice(0, -1).map((bar, i) => {\n const from = edges[i];\n const to = edges[i + 1];\n const level = y(bar.running);\n if (!from || !to || level === undefined) return null;\n return (\n <line\n key={i}\n x1={from.right}\n x2={to.left}\n y1={level}\n y2={level}\n stroke={MUTED}\n strokeOpacity={0.7}\n strokeWidth={1}\n strokeDasharray=\"3 3\"\n />\n );\n })}\n </g>\n );\n};\n\nexport const WaterfallChart = ({\n data,\n formatValue = String,\n showLabels,\n showConnectors = true,\n colors,\n xTitle,\n yTitle,\n emptyMessage = 'No data',\n ariaLabel,\n children,\n height = 320,\n width,\n}: WaterfallChartProps) => {\n const axis = useAxis();\n const { ref, width: measured } = useElementWidth();\n const bars = useMemo(() => waterfallBars(data), [data]);\n const rows: Row[] = useMemo(\n () => bars.map((bar, i) => ({ ...bar, key: String(i), range: [bar.low, bar.high] })),\n [bars]\n );\n const name = ariaLabel ?? `Waterfall chart: ${data.map((d) => d.label).join(', ')}`;\n if (data.length === 0) {\n return <ChartEmpty message={emptyMessage} ariaLabel={name} height={height} width={width} />;\n }\n\n const palette: Palette = {\n positive: colors?.positive ?? SUCCESS,\n negative: colors?.negative ?? ERROR,\n total: colors?.total ?? SERIES[0],\n };\n const narrow = measured !== undefined && measured < NARROW;\n const labels = showLabels ?? (rows.length <= MAX_LABELED_BARS && !narrow);\n // Names tilt when the chart is narrow, or when they would not fit side by side.\n const longest = Math.min(MAX_CHARS, Math.max(...rows.map((row) => row.label.length)));\n const band = measured === undefined ? Infinity : (measured - 56) / rows.length;\n const tilted = narrow || longest * CHAR_WIDTH > band;\n const labelAxis = {\n interval: 0,\n tickFormatter: (_: unknown, index: number) => trimLabel(rows[index]?.label ?? ''),\n ...(tilted\n ? {\n angle: -40,\n tick: { ...AXIS.tick, textAnchor: 'end' },\n height: (xTitle ? 52 : 30) + Math.round(longest * TILT_HEIGHT),\n }\n : {}),\n } as const;\n\n const tooltip = ({ active, payload }: Partial<TooltipContentProps>) => {\n const row = payload?.[0]?.payload as Row | undefined;\n if (!active || !row) return null;\n const color = fillOf(row.kind, palette);\n const step = row.kind === 'increase' || row.kind === 'decrease';\n return (\n <TooltipCard\n title={row.label}\n rows={[\n {\n name: KIND_WORDS[row.kind],\n value: step ? signed(row.value, formatValue) : formatValue(row.running),\n color,\n },\n ...(step\n ? [{ name: 'Running total', value: formatValue(row.running), color: 'transparent' }]\n : []),\n ]}\n />\n );\n };\n\n return (\n <div ref={ref} style={{ width: width ?? '100%', maxWidth: '100%' }}>\n <BarChart\n {...frameProps({ height, width: '100%', ariaLabel: name })}\n data={rows}\n margin={{ top: labels ? 24 : 4, right: 8, bottom: 0, left: 0 }}\n barCategoryGap={`${GAP * 100}%`}\n >\n <CartesianGrid {...GRID_PROPS} />\n <XAxis dataKey=\"key\" {...axis} {...xAxisTitle(xTitle)} {...labelAxis} />\n <YAxis width=\"auto\" tickFormatter={formatValue} {...axis} {...yAxisTitle(yTitle)} />\n <Tooltip\n content={tooltip}\n cursor={{ fill: BORDER, fillOpacity: 0.35, radius: 8 }}\n animationDuration={150}\n />\n <Bar\n name=\"Waterfall\"\n dataKey=\"range\"\n maxBarSize={MAX_BAR}\n shape={waterfallShape(palette, labels, formatValue)}\n {...ANIMATION}\n />\n {showConnectors && <Connectors bars={bars} />}\n {children}\n </BarChart>\n </div>\n );\n};\n"]}
@@ -0,0 +1,233 @@
1
+ "use client";
2
+ import { trimLabel } from './chunk-HMH6DI7U.js';
3
+ import { useElementWidth } from './chunk-AOSO5QHL.js';
4
+ import { ANIMATION } from './chunk-5C2RLJYZ.js';
5
+ import { useAxis, AXIS, GRID_PROPS, xAxisTitle, yAxisTitle, TEXT_SIZE } from './chunk-RILUG7JB.js';
6
+ import { TooltipCard } from './chunk-VIOSQN4Z.js';
7
+ import { ChartEmpty, frameProps } from './chunk-3N43QT36.js';
8
+ import { SERIES, ERROR, SUCCESS, BORDER, TEXT, MUTED } from './chunk-UATB2IRY.js';
9
+ import { useMemo } from 'react';
10
+ import { BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar, useXAxisScale, useYAxisScale } from 'recharts';
11
+ import { jsx, jsxs } from 'react/jsx-runtime';
12
+
13
+ // src/waterfall.ts
14
+ var KIND_WORDS = {
15
+ increase: "Increase",
16
+ decrease: "Decrease",
17
+ subtotal: "Subtotal",
18
+ total: "Total"
19
+ };
20
+ var clean = (value) => value !== void 0 && Number.isFinite(value) ? value : 0;
21
+ var waterfallBars = (data) => {
22
+ let running = 0;
23
+ return data.map((datum) => {
24
+ if (datum.kind === "subtotal" || datum.kind === "total") {
25
+ if (datum.kind === "total" && datum.value !== void 0) running = clean(datum.value);
26
+ return {
27
+ label: datum.label,
28
+ kind: datum.kind,
29
+ value: running,
30
+ low: Math.min(0, running),
31
+ high: Math.max(0, running),
32
+ running
33
+ };
34
+ }
35
+ const change = clean(datum.value);
36
+ const before = running;
37
+ running += change;
38
+ return {
39
+ label: datum.label,
40
+ kind: change < 0 ? "decrease" : "increase",
41
+ value: change,
42
+ low: Math.min(before, running),
43
+ high: Math.max(before, running),
44
+ running
45
+ };
46
+ });
47
+ };
48
+ var signed = (value, format = String) => `${value < 0 ? "\u2212" : "+"}${format(Math.abs(value))}`;
49
+ var barText = (bar, format = String) => bar.kind === "increase" || bar.kind === "decrease" ? signed(bar.value, format) : format(bar.running);
50
+ var NARROW = 480;
51
+ var MAX_LABELED_BARS = 12;
52
+ var GAP = 0.19;
53
+ var MAX_BAR = 64;
54
+ var CHAR_WIDTH = 8;
55
+ var SUBTOTAL_OPACITY = 0.55;
56
+ var TILT_HEIGHT = 4.2;
57
+ var MAX_CHARS = 14;
58
+ var fillOf = (kind, palette) => kind === "increase" ? palette.positive : kind === "decrease" ? palette.negative : palette.total;
59
+ var waterfallShape = (palette, labels, format) => (props) => {
60
+ const row = props.payload;
61
+ const left = Math.min(props.x, props.x + props.width);
62
+ const top = Math.min(props.y, props.y + props.height);
63
+ const width = Math.abs(props.width);
64
+ const height = Math.max(Math.abs(props.height), 1);
65
+ if (!(width > 0)) return null;
66
+ return /* @__PURE__ */ jsxs("g", { children: [
67
+ /* @__PURE__ */ jsx(
68
+ "rect",
69
+ {
70
+ x: left,
71
+ y: top,
72
+ width,
73
+ height,
74
+ rx: Math.min(3, width / 2, height / 2),
75
+ fill: fillOf(row.kind, palette),
76
+ fillOpacity: row.kind === "subtotal" ? SUBTOTAL_OPACITY : 1
77
+ }
78
+ ),
79
+ labels && /* @__PURE__ */ jsx(
80
+ "text",
81
+ {
82
+ x: left + width / 2,
83
+ y: top - 6,
84
+ textAnchor: "middle",
85
+ fill: TEXT,
86
+ style: {
87
+ fontSize: TEXT_SIZE,
88
+ fontWeight: 600,
89
+ fontVariantNumeric: "tabular-nums"
90
+ },
91
+ children: barText(row, format)
92
+ }
93
+ )
94
+ ] });
95
+ };
96
+ var Connectors = ({ bars }) => {
97
+ const x = useXAxisScale();
98
+ const y = useYAxisScale();
99
+ if (!x || !y) return null;
100
+ const edges = bars.map((_, i) => {
101
+ const start = x(String(i), { position: "start" });
102
+ const end = x(String(i), { position: "end" });
103
+ if (start === void 0 || end === void 0) return void 0;
104
+ const band = end - start;
105
+ const full = Math.round(band * (1 - 2 * GAP));
106
+ const size = Math.min(full, MAX_BAR);
107
+ const left = start + band * GAP + (full - size) / 2;
108
+ return { left, right: left + size };
109
+ });
110
+ return /* @__PURE__ */ jsx("g", { "aria-hidden": true, "data-waterfall-connectors": "", children: bars.slice(0, -1).map((bar, i) => {
111
+ const from = edges[i];
112
+ const to = edges[i + 1];
113
+ const level = y(bar.running);
114
+ if (!from || !to || level === void 0) return null;
115
+ return /* @__PURE__ */ jsx(
116
+ "line",
117
+ {
118
+ x1: from.right,
119
+ x2: to.left,
120
+ y1: level,
121
+ y2: level,
122
+ stroke: MUTED,
123
+ strokeOpacity: 0.7,
124
+ strokeWidth: 1,
125
+ strokeDasharray: "3 3"
126
+ },
127
+ i
128
+ );
129
+ }) });
130
+ };
131
+ var WaterfallChart = ({
132
+ data,
133
+ formatValue = String,
134
+ showLabels,
135
+ showConnectors = true,
136
+ colors,
137
+ xTitle,
138
+ yTitle,
139
+ emptyMessage = "No data",
140
+ ariaLabel,
141
+ children,
142
+ height = 320,
143
+ width
144
+ }) => {
145
+ const axis = useAxis();
146
+ const { ref, width: measured } = useElementWidth();
147
+ const bars = useMemo(() => waterfallBars(data), [data]);
148
+ const rows = useMemo(
149
+ () => bars.map((bar, i) => ({ ...bar, key: String(i), range: [bar.low, bar.high] })),
150
+ [bars]
151
+ );
152
+ const name = ariaLabel ?? `Waterfall chart: ${data.map((d) => d.label).join(", ")}`;
153
+ if (data.length === 0) {
154
+ return /* @__PURE__ */ jsx(ChartEmpty, { message: emptyMessage, ariaLabel: name, height, width });
155
+ }
156
+ const palette = {
157
+ positive: colors?.positive ?? SUCCESS,
158
+ negative: colors?.negative ?? ERROR,
159
+ total: colors?.total ?? SERIES[0]
160
+ };
161
+ const narrow = measured !== void 0 && measured < NARROW;
162
+ const labels = showLabels ?? (rows.length <= MAX_LABELED_BARS && !narrow);
163
+ const longest = Math.min(MAX_CHARS, Math.max(...rows.map((row) => row.label.length)));
164
+ const band = measured === void 0 ? Infinity : (measured - 56) / rows.length;
165
+ const tilted = narrow || longest * CHAR_WIDTH > band;
166
+ const labelAxis = {
167
+ interval: 0,
168
+ tickFormatter: (_, index) => trimLabel(rows[index]?.label ?? ""),
169
+ ...tilted ? {
170
+ angle: -40,
171
+ tick: { ...AXIS.tick, textAnchor: "end" },
172
+ height: (xTitle ? 52 : 30) + Math.round(longest * TILT_HEIGHT)
173
+ } : {}
174
+ };
175
+ const tooltip = ({ active, payload }) => {
176
+ const row = payload?.[0]?.payload;
177
+ if (!active || !row) return null;
178
+ const color = fillOf(row.kind, palette);
179
+ const step = row.kind === "increase" || row.kind === "decrease";
180
+ return /* @__PURE__ */ jsx(
181
+ TooltipCard,
182
+ {
183
+ title: row.label,
184
+ rows: [
185
+ {
186
+ name: KIND_WORDS[row.kind],
187
+ value: step ? signed(row.value, formatValue) : formatValue(row.running),
188
+ color
189
+ },
190
+ ...step ? [{ name: "Running total", value: formatValue(row.running), color: "transparent" }] : []
191
+ ]
192
+ }
193
+ );
194
+ };
195
+ return /* @__PURE__ */ jsx("div", { ref, style: { width: width ?? "100%", maxWidth: "100%" }, children: /* @__PURE__ */ jsxs(
196
+ BarChart,
197
+ {
198
+ ...frameProps({ height, width: "100%", ariaLabel: name }),
199
+ data: rows,
200
+ margin: { top: labels ? 24 : 4, right: 8, bottom: 0, left: 0 },
201
+ barCategoryGap: `${GAP * 100}%`,
202
+ children: [
203
+ /* @__PURE__ */ jsx(CartesianGrid, { ...GRID_PROPS }),
204
+ /* @__PURE__ */ jsx(XAxis, { dataKey: "key", ...axis, ...xAxisTitle(xTitle), ...labelAxis }),
205
+ /* @__PURE__ */ jsx(YAxis, { width: "auto", tickFormatter: formatValue, ...axis, ...yAxisTitle(yTitle) }),
206
+ /* @__PURE__ */ jsx(
207
+ Tooltip,
208
+ {
209
+ content: tooltip,
210
+ cursor: { fill: BORDER, fillOpacity: 0.35, radius: 8 },
211
+ animationDuration: 150
212
+ }
213
+ ),
214
+ /* @__PURE__ */ jsx(
215
+ Bar,
216
+ {
217
+ name: "Waterfall",
218
+ dataKey: "range",
219
+ maxBarSize: MAX_BAR,
220
+ shape: waterfallShape(palette, labels, formatValue),
221
+ ...ANIMATION
222
+ }
223
+ ),
224
+ showConnectors && /* @__PURE__ */ jsx(Connectors, { bars }),
225
+ children
226
+ ]
227
+ }
228
+ ) });
229
+ };
230
+
231
+ export { WaterfallChart };
232
+ //# sourceMappingURL=WaterfallChart.js.map
233
+ //# sourceMappingURL=WaterfallChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/waterfall.ts","../src/WaterfallChart.tsx"],"names":[],"mappings":"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","file":"WaterfallChart.js","sourcesContent":["// The maths of WaterfallChart: the running total, the span each bar covers and\n// the text that names it. No DOM and no colors, so it runs in plain Node.\n\nexport type WaterfallDatum = {\n label: string;\n /** A step: the signed change. A total: sets the running total to it (an opening balance). */\n value?: number;\n /** Default `step`. `subtotal` and `total` are drawn from 0 to the running total. */\n kind?: 'step' | 'subtotal' | 'total';\n};\n\n/** What a bar means: a step reads as an increase or a decrease from its sign. */\nexport type WaterfallBarKind = 'increase' | 'decrease' | 'subtotal' | 'total';\n\nexport type WaterfallBar = {\n label: string;\n kind: WaterfallBarKind;\n /** A step: its signed change. A subtotal or a total: the running total it shows. */\n value: number;\n /** The bottom and the top of the bar, in the units of the data. */\n low: number;\n high: number;\n /** The running total after this bar. */\n running: number;\n};\n\n/** The kind in words, for the tooltip and the name a screen reader hears. */\nexport const KIND_WORDS: Record<WaterfallBarKind, string> = {\n increase: 'Increase',\n decrease: 'Decrease',\n subtotal: 'Subtotal',\n total: 'Total',\n};\n\n// A missing or non-finite value counts as 0.\nconst clean = (value: number | undefined) =>\n value !== undefined && Number.isFinite(value) ? value : 0;\n\n/**\n * One bar per datum, in the order given. A step moves the running total by its\n * signed `value` and floats between the total before and after it. A subtotal\n * is the running total at that point, from 0, never the sum since the last\n * cut. A total with a `value` sets the running total to it (the opening\n * balance, or a known balance mid-walk); without one it shows the running\n * total. Both are drawn from 0.\n */\nexport const waterfallBars = (data: readonly WaterfallDatum[]): WaterfallBar[] => {\n let running = 0;\n return data.map((datum) => {\n if (datum.kind === 'subtotal' || datum.kind === 'total') {\n if (datum.kind === 'total' && datum.value !== undefined) running = clean(datum.value);\n return {\n label: datum.label,\n kind: datum.kind,\n value: running,\n low: Math.min(0, running),\n high: Math.max(0, running),\n running,\n };\n }\n const change = clean(datum.value);\n const before = running;\n running += change;\n return {\n label: datum.label,\n kind: change < 0 ? 'decrease' : 'increase',\n value: change,\n low: Math.min(before, running),\n high: Math.max(before, running),\n running,\n };\n });\n};\n\n/** A change with its sign, in text: `+310k`, `−265k` (U+2212). `format` gets the magnitude. */\nexport const signed = (value: number, format: (value: number) => string = String) =>\n `${value < 0 ? '−' : '+'}${format(Math.abs(value))}`;\n\n/** What is written above a bar: the signed change of a step, the running total of a subtotal or a total. */\nexport const barText = (bar: WaterfallBar, format: (value: number) => string = String) =>\n bar.kind === 'increase' || bar.kind === 'decrease'\n ? signed(bar.value, format)\n : format(bar.running);\n","import { useMemo } from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Tooltip,\n useXAxisScale,\n useYAxisScale,\n XAxis,\n YAxis,\n type BarShapeProps,\n type TooltipContentProps,\n} from 'recharts';\nimport { ANIMATION } from './core/animation';\nimport { AXIS, GRID_PROPS, TEXT_SIZE, useAxis, xAxisTitle, yAxisTitle } from './core/axes';\nimport { trimLabel } from './core/categoryAxis';\nimport { TooltipCard } from './core/ChartTooltip';\nimport { BORDER, ERROR, MUTED, SERIES, SUCCESS, TEXT } from './core/colors';\nimport { ChartEmpty, frameProps } from './core/frame';\nimport { useElementWidth } from './core/useElementWidth';\nimport type { ChartAxisTitleProps, ChartEmptyProps, ChartProps } from './types';\nimport {\n barText,\n KIND_WORDS,\n signed,\n waterfallBars,\n type WaterfallBar,\n type WaterfallDatum,\n} from './waterfall';\n\nexport type { WaterfallDatum } from './waterfall';\n\nexport type WaterfallChartProps = ChartProps &\n ChartEmptyProps &\n ChartAxisTitleProps & {\n /**\n * The bars, left to right. A step (default) carries a signed `value`. A\n * `subtotal` is drawn from 0 to the running total at that point. A\n * `total` is drawn the same way, and a `value` on it sets the running\n * total to it: an opening balance, or a known balance mid-walk.\n */\n data: WaterfallDatum[];\n /** Format the value axis, the labels and the tooltip. The sign of a step is written apart: the function gets the magnitude. */\n formatValue?: (value: number) => string;\n /**\n * Write the value above each bar. Default: on with 12 bars or fewer in a chart at least\n * 480px wide, off otherwise (the values stay in the tooltip and the keyboard walk).\n */\n showLabels?: boolean;\n /** Dashed lines from the top of a bar to the next one. Default true. */\n showConnectors?: boolean;\n /** Override the colors: increases, decreases, totals and subtotals. Defaults: the theme's success, error and accent. */\n colors?: { positive?: string; negative?: string; total?: string };\n };\n\n// Below this width the value labels go and the category names tilt.\nconst NARROW = 480;\nconst MAX_LABELED_BARS = 12;\n// The share of a category's band left empty on each side of a bar, and the\n// widest bar. Recharts places the bar from these; the connectors are drawn\n// from the same numbers.\nconst GAP = 0.19;\nconst MAX_BAR = 64;\n// Pixels one character of a category name takes, at the largest text size.\nconst CHAR_WIDTH = 8;\nconst SUBTOTAL_OPACITY = 0.55;\nconst TILT_HEIGHT = 4.2;\nconst MAX_CHARS = 14;\n\ntype Row = WaterfallBar & { key: string; range: [number, number] };\n\ntype Palette = { positive: string; negative: string; total: string };\n\nconst fillOf = (kind: WaterfallBar['kind'], palette: Palette) =>\n kind === 'increase' ? palette.positive : kind === 'decrease' ? palette.negative : palette.total;\n\n// The bar is the span between the running total before and after the step; the\n// label above it is drawn here too, so it moves with the bar while it grows.\nconst waterfallShape =\n (palette: Palette, labels: boolean, format: (value: number) => string) =>\n (props: BarShapeProps) => {\n const row = props.payload as Row;\n const left = Math.min(props.x, props.x + props.width);\n const top = Math.min(props.y, props.y + props.height);\n const width = Math.abs(props.width);\n // A step of 0 is a hairline.\n const height = Math.max(Math.abs(props.height), 1);\n if (!(width > 0)) return null;\n return (\n <g>\n <rect\n x={left}\n y={top}\n width={width}\n height={height}\n rx={Math.min(3, width / 2, height / 2)}\n fill={fillOf(row.kind, palette)}\n fillOpacity={row.kind === 'subtotal' ? SUBTOTAL_OPACITY : 1}\n />\n {labels && (\n <text\n x={left + width / 2}\n y={top - 6}\n textAnchor=\"middle\"\n fill={TEXT}\n style={{\n fontSize: TEXT_SIZE,\n fontWeight: 600,\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n {barText(row, format)}\n </text>\n )}\n </g>\n );\n };\n\n// From the right edge of a bar, at the running total after it, to the left edge\n// of the next one. Out of the accessibility layer: the bars say it all.\nconst Connectors = ({ bars }: { bars: readonly WaterfallBar[] }) => {\n const x = useXAxisScale();\n const y = useYAxisScale();\n if (!x || !y) return null;\n const edges = bars.map((_, i) => {\n const start = x(String(i), { position: 'start' });\n const end = x(String(i), { position: 'end' });\n if (start === undefined || end === undefined) return undefined;\n const band = end - start;\n const full = Math.round(band * (1 - 2 * GAP));\n const size = Math.min(full, MAX_BAR);\n const left = start + band * GAP + (full - size) / 2;\n return { left, right: left + size };\n });\n return (\n <g aria-hidden data-waterfall-connectors=\"\">\n {bars.slice(0, -1).map((bar, i) => {\n const from = edges[i];\n const to = edges[i + 1];\n const level = y(bar.running);\n if (!from || !to || level === undefined) return null;\n return (\n <line\n key={i}\n x1={from.right}\n x2={to.left}\n y1={level}\n y2={level}\n stroke={MUTED}\n strokeOpacity={0.7}\n strokeWidth={1}\n strokeDasharray=\"3 3\"\n />\n );\n })}\n </g>\n );\n};\n\nexport const WaterfallChart = ({\n data,\n formatValue = String,\n showLabels,\n showConnectors = true,\n colors,\n xTitle,\n yTitle,\n emptyMessage = 'No data',\n ariaLabel,\n children,\n height = 320,\n width,\n}: WaterfallChartProps) => {\n const axis = useAxis();\n const { ref, width: measured } = useElementWidth();\n const bars = useMemo(() => waterfallBars(data), [data]);\n const rows: Row[] = useMemo(\n () => bars.map((bar, i) => ({ ...bar, key: String(i), range: [bar.low, bar.high] })),\n [bars]\n );\n const name = ariaLabel ?? `Waterfall chart: ${data.map((d) => d.label).join(', ')}`;\n if (data.length === 0) {\n return <ChartEmpty message={emptyMessage} ariaLabel={name} height={height} width={width} />;\n }\n\n const palette: Palette = {\n positive: colors?.positive ?? SUCCESS,\n negative: colors?.negative ?? ERROR,\n total: colors?.total ?? SERIES[0],\n };\n const narrow = measured !== undefined && measured < NARROW;\n const labels = showLabels ?? (rows.length <= MAX_LABELED_BARS && !narrow);\n // Names tilt when the chart is narrow, or when they would not fit side by side.\n const longest = Math.min(MAX_CHARS, Math.max(...rows.map((row) => row.label.length)));\n const band = measured === undefined ? Infinity : (measured - 56) / rows.length;\n const tilted = narrow || longest * CHAR_WIDTH > band;\n const labelAxis = {\n interval: 0,\n tickFormatter: (_: unknown, index: number) => trimLabel(rows[index]?.label ?? ''),\n ...(tilted\n ? {\n angle: -40,\n tick: { ...AXIS.tick, textAnchor: 'end' },\n height: (xTitle ? 52 : 30) + Math.round(longest * TILT_HEIGHT),\n }\n : {}),\n } as const;\n\n const tooltip = ({ active, payload }: Partial<TooltipContentProps>) => {\n const row = payload?.[0]?.payload as Row | undefined;\n if (!active || !row) return null;\n const color = fillOf(row.kind, palette);\n const step = row.kind === 'increase' || row.kind === 'decrease';\n return (\n <TooltipCard\n title={row.label}\n rows={[\n {\n name: KIND_WORDS[row.kind],\n value: step ? signed(row.value, formatValue) : formatValue(row.running),\n color,\n },\n ...(step\n ? [{ name: 'Running total', value: formatValue(row.running), color: 'transparent' }]\n : []),\n ]}\n />\n );\n };\n\n return (\n <div ref={ref} style={{ width: width ?? '100%', maxWidth: '100%' }}>\n <BarChart\n {...frameProps({ height, width: '100%', ariaLabel: name })}\n data={rows}\n margin={{ top: labels ? 24 : 4, right: 8, bottom: 0, left: 0 }}\n barCategoryGap={`${GAP * 100}%`}\n >\n <CartesianGrid {...GRID_PROPS} />\n <XAxis dataKey=\"key\" {...axis} {...xAxisTitle(xTitle)} {...labelAxis} />\n <YAxis width=\"auto\" tickFormatter={formatValue} {...axis} {...yAxisTitle(yTitle)} />\n <Tooltip\n content={tooltip}\n cursor={{ fill: BORDER, fillOpacity: 0.35, radius: 8 }}\n animationDuration={150}\n />\n <Bar\n name=\"Waterfall\"\n dataKey=\"range\"\n maxBarSize={MAX_BAR}\n shape={waterfallShape(palette, labels, formatValue)}\n {...ANIMATION}\n />\n {showConnectors && <Connectors bars={bars} />}\n {children}\n </BarChart>\n </div>\n );\n};\n"]}