@pushwoosh/kit-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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/ai-documentation.md +378 -0
  3. package/core/Chart/Axes.d.ts +19 -0
  4. package/core/Chart/Axes.js +204 -0
  5. package/core/Chart/Chart.d.ts +33 -0
  6. package/core/Chart/Chart.js +616 -0
  7. package/core/Chart/Chart.styles.d.ts +109 -0
  8. package/core/Chart/Chart.styles.js +227 -0
  9. package/core/Chart/ChartErrorBoundary.d.ts +15 -0
  10. package/core/Chart/ChartErrorBoundary.js +27 -0
  11. package/core/Chart/Hinted.d.ts +11 -0
  12. package/core/Chart/Hinted.js +63 -0
  13. package/core/Chart/Legend.d.ts +15 -0
  14. package/core/Chart/Legend.js +92 -0
  15. package/core/Chart/PlotDecorations.d.ts +17 -0
  16. package/core/Chart/PlotDecorations.js +172 -0
  17. package/core/Chart/StackLabels.d.ts +6 -0
  18. package/core/Chart/StackLabels.js +85 -0
  19. package/core/Chart/Titles.d.ts +6 -0
  20. package/core/Chart/Titles.js +67 -0
  21. package/core/Chart/Tooltip.d.ts +13 -0
  22. package/core/Chart/Tooltip.js +87 -0
  23. package/core/Chart/useSize.d.ts +5 -0
  24. package/core/Chart/useSize.js +45 -0
  25. package/core/animation/motion.d.ts +20 -0
  26. package/core/animation/motion.js +20 -0
  27. package/core/animation/useTweenedNumbers.d.ts +1 -0
  28. package/core/animation/useTweenedNumbers.js +51 -0
  29. package/core/data/points.d.ts +14 -0
  30. package/core/data/points.js +73 -0
  31. package/core/format/time.d.ts +2 -0
  32. package/core/format/time.js +38 -0
  33. package/core/geometry/path.d.ts +19 -0
  34. package/core/geometry/path.js +76 -0
  35. package/core/layout/legend-layout.d.ts +21 -0
  36. package/core/layout/legend-layout.js +103 -0
  37. package/core/layout/measure-text.d.ts +9 -0
  38. package/core/layout/measure-text.js +57 -0
  39. package/core/layout/metrics.d.ts +63 -0
  40. package/core/layout/metrics.js +66 -0
  41. package/core/layout/point-width.d.ts +11 -0
  42. package/core/layout/point-width.js +86 -0
  43. package/core/layout/title-layout.d.ts +30 -0
  44. package/core/layout/title-layout.js +99 -0
  45. package/core/model/build-model.d.ts +69 -0
  46. package/core/model/build-model.js +556 -0
  47. package/core/model/palette.d.ts +15 -0
  48. package/core/model/palette.js +17 -0
  49. package/core/options/normalize.d.ts +8 -0
  50. package/core/options/normalize.js +44 -0
  51. package/core/registry/registry.d.ts +5 -0
  52. package/core/registry/registry.js +32 -0
  53. package/core/scale/band.d.ts +10 -0
  54. package/core/scale/band.js +33 -0
  55. package/core/scale/color.d.ts +9 -0
  56. package/core/scale/color.js +69 -0
  57. package/core/scale/linear.d.ts +17 -0
  58. package/core/scale/linear.js +64 -0
  59. package/core/scale/time.d.ts +19 -0
  60. package/core/scale/time.js +187 -0
  61. package/core/stack/stack.d.ts +9 -0
  62. package/core/stack/stack.js +76 -0
  63. package/core/types/formatter.d.ts +59 -0
  64. package/core/types/formatter.js +1 -0
  65. package/core/types/geometry.d.ts +16 -0
  66. package/core/types/geometry.js +1 -0
  67. package/core/types/options.d.ts +331 -0
  68. package/core/types/options.js +2 -0
  69. package/core/types/renderer.d.ts +101 -0
  70. package/core/types/renderer.js +1 -0
  71. package/core/types/scale.d.ts +34 -0
  72. package/core/types/scale.js +1 -0
  73. package/index.d.ts +16 -0
  74. package/index.js +10 -0
  75. package/package.json +64 -0
  76. package/presets/AreaChart.d.ts +3 -0
  77. package/presets/AreaChart.js +13 -0
  78. package/presets/ColumnChart.d.ts +3 -0
  79. package/presets/ColumnChart.js +13 -0
  80. package/presets/LineChart.d.ts +3 -0
  81. package/presets/LineChart.js +13 -0
  82. package/presets/PieChart.d.ts +3 -0
  83. package/presets/PieChart.js +13 -0
  84. package/presets/StackedColumnChart.d.ts +7 -0
  85. package/presets/StackedColumnChart.js +31 -0
  86. package/presets/index.d.ts +5 -0
  87. package/presets/index.js +5 -0
  88. package/series/area/area.d.ts +2 -0
  89. package/series/area/area.js +57 -0
  90. package/series/area/index.d.ts +1 -0
  91. package/series/area/index.js +1 -0
  92. package/series/column/column.d.ts +2 -0
  93. package/series/column/column.js +163 -0
  94. package/series/column/index.d.ts +1 -0
  95. package/series/column/index.js +1 -0
  96. package/series/heatmap/heatmap.d.ts +2 -0
  97. package/series/heatmap/heatmap.js +172 -0
  98. package/series/heatmap/index.d.ts +1 -0
  99. package/series/heatmap/index.js +1 -0
  100. package/series/line/index.d.ts +1 -0
  101. package/series/line/index.js +1 -0
  102. package/series/line/line.d.ts +2 -0
  103. package/series/line/line.js +49 -0
  104. package/series/pie/index.d.ts +1 -0
  105. package/series/pie/index.js +1 -0
  106. package/series/pie/pie.d.ts +2 -0
  107. package/series/pie/pie.js +224 -0
  108. package/series/shared/DataLabels.d.ts +14 -0
  109. package/series/shared/DataLabels.js +124 -0
  110. package/series/shared/Markers.d.ts +9 -0
  111. package/series/shared/Markers.js +34 -0
  112. package/series/shared/color.d.ts +1 -0
  113. package/series/shared/color.js +19 -0
  114. package/series/shared/domain.d.ts +3 -0
  115. package/series/shared/domain.js +14 -0
  116. package/series/shared/hit.d.ts +3 -0
  117. package/series/shared/hit.js +33 -0
  118. package/series/shared/legend.d.ts +3 -0
  119. package/series/shared/legend.js +12 -0
@@ -0,0 +1,10 @@
1
+ import { type BandScale } from '../types/scale';
2
+ export interface BandScaleOptions {
3
+ domain: readonly string[];
4
+ range: readonly [number, number];
5
+ paddingInner?: number;
6
+ paddingOuter?: number;
7
+ /** Assign slots from the far end of the range, keeping the domain order intact. */
8
+ reversed?: boolean;
9
+ }
10
+ export declare function createBandScale(options: BandScaleOptions): BandScale;
@@ -0,0 +1,33 @@
1
+ export function createBandScale(options) {
2
+ const {
3
+ domain,
4
+ range,
5
+ paddingInner = 0.2,
6
+ paddingOuter = 0.1,
7
+ reversed = false
8
+ } = options;
9
+ const [r0, r1] = range;
10
+ const count = domain.length;
11
+ const width = r1 - r0;
12
+ const step = count > 0 ? width / Math.max(1e-6, count - paddingInner + 2 * paddingOuter) : width;
13
+ const bandwidth = step * (1 - paddingInner);
14
+ const start = r0 + step * paddingOuter;
15
+ const index = new Map(domain.map((value, i) => [value, i]));
16
+ const slotOf = i => reversed ? count - 1 - i : i;
17
+ return {
18
+ kind: 'band',
19
+ map: value => {
20
+ const i = index.get(value);
21
+ return i === undefined ? NaN : start + slotOf(i) * step;
22
+ },
23
+ invert: px => {
24
+ const slot = Math.floor((px - start) / step);
25
+ return slot >= 0 && slot < count ? domain[slotOf(slot)] : undefined;
26
+ },
27
+ ticks: () => [...domain],
28
+ domain: () => domain,
29
+ range: () => [r0, r1],
30
+ bandwidth: () => bandwidth,
31
+ step: () => step
32
+ };
33
+ }
@@ -0,0 +1,9 @@
1
+ import { type ColorScale, type ColorStop } from '../types/scale';
2
+ export interface ColorScaleOptions {
3
+ domain: readonly [number, number];
4
+ stops: readonly ColorStop[];
5
+ }
6
+ type Rgba = [number, number, number, number];
7
+ export declare const parseColor: (color: string) => Rgba | null;
8
+ export declare function createColorScale(options: ColorScaleOptions): ColorScale;
9
+ export {};
@@ -0,0 +1,69 @@
1
+ const clampChannel = value => Math.min(255, Math.max(0, Math.round(value)));
2
+ const parseHex = color => {
3
+ const match = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(color);
4
+ if (!match) {
5
+ return null;
6
+ }
7
+ const raw = match[1];
8
+ const short = raw.length === 3 || raw.length === 4;
9
+ const hex = short ? raw.split('').map(char => char + char).join('') : raw;
10
+ const channel = at => parseInt(hex.slice(at, at + 2), 16);
11
+ return [channel(0), channel(2), channel(4), hex.length === 8 ? channel(6) / 255 : 1];
12
+ };
13
+ const parseFunctional = color => {
14
+ const match = /^rgba?\(([^)]+)\)$/i.exec(color);
15
+ if (!match) {
16
+ return null;
17
+ }
18
+ const parts = match[1].split(/[,/\s]+/).filter(Boolean).map(Number);
19
+ if (parts.length < 3 || parts.some(part => !Number.isFinite(part))) {
20
+ return null;
21
+ }
22
+ return [parts[0], parts[1], parts[2], parts[3] === undefined ? 1 : parts[3]];
23
+ };
24
+ export const parseColor = color => parseHex(color.trim()) ?? parseFunctional(color.trim());
25
+ const toCss = ([r, g, b, a]) => a >= 1 ? `#${[r, g, b].map(channel => clampChannel(channel).toString(16).padStart(2, '0')).join('')}` : `rgba(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)}, ${Number(a.toFixed(3))})`;
26
+ const mix = (from, to, t) => [from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t, from[2] + (to[2] - from[2]) * t, from[3] + (to[3] - from[3]) * t];
27
+ // A stop whose colour cannot be parsed still holds its position: it is returned
28
+ // verbatim on an exact hit and skipped when interpolating a neighbour.
29
+ const resolveStops = stops => stops.map(([at, color]) => ({
30
+ at,
31
+ color,
32
+ rgba: parseColor(color) ?? [0, 0, 0, 0]
33
+ })).sort((left, right) => left.at - right.at);
34
+ export function createColorScale(options) {
35
+ const stops = resolveStops(options.stops);
36
+ const [min, max] = options.domain;
37
+ const span = max - min;
38
+ const positionOf = value => {
39
+ if (!Number.isFinite(value) || !Number.isFinite(span) || span === 0) {
40
+ return 0;
41
+ }
42
+ return Math.min(1, Math.max(0, (value - min) / span));
43
+ };
44
+ return {
45
+ kind: 'color',
46
+ map: value => {
47
+ if (!stops.length) {
48
+ return 'transparent';
49
+ }
50
+ const at = positionOf(value);
51
+ const upperIndex = stops.findIndex(stop => stop.at >= at);
52
+ if (upperIndex === -1) {
53
+ return stops[stops.length - 1].color;
54
+ }
55
+ const upper = stops[upperIndex];
56
+ const lower = stops[upperIndex - 1];
57
+ if (!lower || upper.at === at) {
58
+ return upper.color;
59
+ }
60
+ const local = (at - lower.at) / (upper.at - lower.at);
61
+ return toCss(mix(lower.rgba, upper.rgba, local));
62
+ },
63
+ domain: () => [min, max],
64
+ stops: () => stops.map(({
65
+ at,
66
+ color
67
+ }) => [at, color])
68
+ };
69
+ }
@@ -0,0 +1,17 @@
1
+ import { type ContinuousScale } from '../types/scale';
2
+ /** Which ends of the domain may be rounded outwards to a whole tick step. */
3
+ export interface NiceEnds {
4
+ min?: boolean;
5
+ max?: boolean;
6
+ }
7
+ export interface LinearScaleOptions {
8
+ domain: readonly [number, number];
9
+ range: readonly [number, number];
10
+ nice?: boolean | NiceEnds;
11
+ tickHint?: number;
12
+ /** False keeps every tick a whole number, as a count axis needs. */
13
+ allowDecimals?: boolean;
14
+ }
15
+ export declare function niceStep(rough: number, allowDecimals?: boolean): number;
16
+ export declare function linearTicks(min: number, max: number, count: number, allowDecimals?: boolean): number[];
17
+ export declare function createLinearScale(options: LinearScaleOptions): ContinuousScale;
@@ -0,0 +1,64 @@
1
+ const STEPS = [1, 2, 2.5, 5, 10];
2
+ export function niceStep(rough, allowDecimals = true) {
3
+ if (!(rough > 0)) {
4
+ return 1;
5
+ }
6
+ const magnitude = 10 ** Math.floor(Math.log10(rough));
7
+ const normalized = rough / magnitude;
8
+ // A whole-number axis keeps only the candidates that stay whole at this
9
+ // magnitude — below magnitude 1 that leaves 10, which lands the step on 1.
10
+ const steps = allowDecimals ? STEPS : STEPS.filter(candidate => Number.isInteger(candidate * magnitude));
11
+ const step = (steps.find(candidate => normalized <= candidate) ?? 10) * magnitude;
12
+ return allowDecimals ? step : Math.max(1, step);
13
+ }
14
+ export function linearTicks(min, max, count, allowDecimals = true) {
15
+ if (!Number.isFinite(min) || !Number.isFinite(max) || count < 1) {
16
+ return [];
17
+ }
18
+ if (min === max) {
19
+ return [min];
20
+ }
21
+ const step = niceStep((max - min) / count, allowDecimals);
22
+ const first = Math.ceil(min / step) * step;
23
+ const ticks = [];
24
+ for (let i = 0; first + i * step <= max + step * 1e-9; i += 1) {
25
+ ticks.push(Number((first + i * step).toPrecision(12)));
26
+ }
27
+ return ticks;
28
+ }
29
+ export function createLinearScale(options) {
30
+ const {
31
+ range,
32
+ nice = false,
33
+ tickHint = 5,
34
+ allowDecimals = true
35
+ } = options;
36
+ let [min, max] = options.domain;
37
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
38
+ [min, max] = [0, 1];
39
+ } else if (min === max) {
40
+ const pad = Math.abs(min) || 1;
41
+ [min, max] = [min - pad, max + pad];
42
+ }
43
+ const niceMin = nice === true || typeof nice === 'object' && nice.min === true;
44
+ const niceMax = nice === true || typeof nice === 'object' && nice.max === true;
45
+ if (niceMin || niceMax) {
46
+ const step = niceStep((max - min) / tickHint, allowDecimals);
47
+ if (niceMin) {
48
+ min = Math.floor(min / step) * step;
49
+ }
50
+ if (niceMax) {
51
+ max = Math.ceil(max / step) * step;
52
+ }
53
+ }
54
+ const [r0, r1] = range;
55
+ const scale = (r1 - r0) / (max - min);
56
+ return {
57
+ kind: 'linear',
58
+ map: value => r0 + (value - min) * scale,
59
+ invert: px => min + (px - r0) / scale,
60
+ ticks: (hint = tickHint) => linearTicks(min, max, hint, allowDecimals),
61
+ domain: () => [min, max],
62
+ range: () => [r0, r1]
63
+ };
64
+ }
@@ -0,0 +1,19 @@
1
+ import { type ContinuousScale, type TimeUnit } from '../types/scale';
2
+ export declare const SECOND = 1000;
3
+ export declare const MINUTE: number;
4
+ export declare const HOUR: number;
5
+ export declare const DAY: number;
6
+ export declare const WEEK: number;
7
+ export interface TimeTicksOptions {
8
+ count?: number;
9
+ unit?: TimeUnit;
10
+ }
11
+ export declare function timeTicks(min: number, max: number, options?: TimeTicksOptions): number[];
12
+ export declare function defaultTimeFormat(min: number, max: number): string;
13
+ export interface TimeScaleOptions {
14
+ domain: readonly [number, number];
15
+ range: readonly [number, number];
16
+ tickHint?: number;
17
+ tickUnit?: TimeUnit;
18
+ }
19
+ export declare function createTimeScale(options: TimeScaleOptions): ContinuousScale;
@@ -0,0 +1,187 @@
1
+ export const SECOND = 1000;
2
+ export const MINUTE = 60 * SECOND;
3
+ export const HOUR = 60 * MINUTE;
4
+ export const DAY = 24 * HOUR;
5
+ export const WEEK = 7 * DAY;
6
+ const LADDER = [{
7
+ unit: 'second',
8
+ step: 1,
9
+ approx: SECOND
10
+ }, {
11
+ unit: 'second',
12
+ step: 5,
13
+ approx: 5 * SECOND
14
+ }, {
15
+ unit: 'second',
16
+ step: 15,
17
+ approx: 15 * SECOND
18
+ }, {
19
+ unit: 'second',
20
+ step: 30,
21
+ approx: 30 * SECOND
22
+ }, {
23
+ unit: 'minute',
24
+ step: 1,
25
+ approx: MINUTE
26
+ }, {
27
+ unit: 'minute',
28
+ step: 5,
29
+ approx: 5 * MINUTE
30
+ }, {
31
+ unit: 'minute',
32
+ step: 15,
33
+ approx: 15 * MINUTE
34
+ }, {
35
+ unit: 'minute',
36
+ step: 30,
37
+ approx: 30 * MINUTE
38
+ }, {
39
+ unit: 'hour',
40
+ step: 1,
41
+ approx: HOUR
42
+ }, {
43
+ unit: 'hour',
44
+ step: 3,
45
+ approx: 3 * HOUR
46
+ }, {
47
+ unit: 'hour',
48
+ step: 6,
49
+ approx: 6 * HOUR
50
+ }, {
51
+ unit: 'hour',
52
+ step: 12,
53
+ approx: 12 * HOUR
54
+ }, {
55
+ unit: 'day',
56
+ step: 1,
57
+ approx: DAY
58
+ }, {
59
+ unit: 'day',
60
+ step: 2,
61
+ approx: 2 * DAY
62
+ }, {
63
+ unit: 'week',
64
+ step: 1,
65
+ approx: WEEK
66
+ }, {
67
+ unit: 'month',
68
+ step: 1,
69
+ approx: 30 * DAY,
70
+ calendar: true
71
+ }, {
72
+ unit: 'month',
73
+ step: 3,
74
+ approx: 91 * DAY,
75
+ calendar: true
76
+ }, {
77
+ unit: 'month',
78
+ step: 6,
79
+ approx: 182 * DAY,
80
+ calendar: true
81
+ }, {
82
+ unit: 'year',
83
+ step: 1,
84
+ approx: 365 * DAY,
85
+ calendar: true
86
+ }];
87
+ const pickRung = (span, count) => {
88
+ const target = span / Math.max(1, count);
89
+ return LADDER.find(rung => rung.approx >= target) ?? LADDER[LADDER.length - 1];
90
+ };
91
+ const alignUp = (from, rung) => {
92
+ const date = new Date(from);
93
+ if (rung.unit === 'year') {
94
+ const year = date.getUTCFullYear();
95
+ const aligned = Date.UTC(Math.ceil(year / rung.step) * rung.step, 0, 1);
96
+ return aligned;
97
+ }
98
+ if (rung.unit === 'month') {
99
+ const month = date.getUTCMonth();
100
+ const stepped = Math.ceil(month / rung.step) * rung.step;
101
+ return Date.UTC(date.getUTCFullYear(), stepped, 1);
102
+ }
103
+ if (rung.unit === 'week') {
104
+ const midnight = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
105
+ const shift = (8 - (new Date(midnight).getUTCDay() || 7)) % 7;
106
+ return midnight + shift * DAY;
107
+ }
108
+ const size = rung.approx;
109
+ const midnight = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
110
+ const sinceMidnight = from - midnight;
111
+ return midnight + Math.ceil(sinceMidnight / size) * size;
112
+ };
113
+ const advance = (timestamp, rung) => {
114
+ if (!rung.calendar) {
115
+ return timestamp + rung.approx;
116
+ }
117
+ const date = new Date(timestamp);
118
+ const months = rung.unit === 'year' ? rung.step * 12 : rung.step;
119
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
120
+ };
121
+ const rungFor = unit => LADDER.find(rung => rung.unit === unit && rung.step === 1) ?? LADDER[LADDER.length - 1];
122
+ export function timeTicks(min, max, options = {}) {
123
+ const {
124
+ count = 6,
125
+ unit
126
+ } = options;
127
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {
128
+ return [];
129
+ }
130
+ if (min === max) {
131
+ return [min];
132
+ }
133
+ const rung = unit ? rungFor(unit) : pickRung(max - min, count);
134
+ const ticks = [];
135
+ // Calendar alignment lands on the start of the current unit, which can sit
136
+ // before the domain — a tick there would be drawn outside the plot.
137
+ let cursor = alignUp(min, rung);
138
+ if (cursor < min) {
139
+ cursor = advance(cursor, rung);
140
+ }
141
+ for (let i = 0; cursor <= max && i < 1000; i += 1) {
142
+ ticks.push(cursor);
143
+ cursor = advance(cursor, rung);
144
+ }
145
+ return ticks.length ? ticks : [min, max];
146
+ }
147
+ const sameUtcDay = (a, b) => {
148
+ const left = new Date(a);
149
+ const right = new Date(b);
150
+ return left.getUTCFullYear() === right.getUTCFullYear() && left.getUTCMonth() === right.getUTCMonth() && left.getUTCDate() === right.getUTCDate();
151
+ };
152
+ export function defaultTimeFormat(min, max) {
153
+ const span = max - min;
154
+ if (span <= 2 * DAY) {
155
+ return sameUtcDay(min, max) ? '%H:%M' : '%b %d, %H:%M';
156
+ }
157
+ if (span <= 200 * DAY) {
158
+ return '%b %d';
159
+ }
160
+ return '%b, %Y';
161
+ }
162
+ export function createTimeScale(options) {
163
+ const {
164
+ range,
165
+ tickHint = 6,
166
+ tickUnit
167
+ } = options;
168
+ let [min, max] = options.domain;
169
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
170
+ [min, max] = [0, DAY];
171
+ } else if (min === max) {
172
+ [min, max] = [min - HOUR, max + HOUR];
173
+ }
174
+ const [r0, r1] = range;
175
+ const scale = (r1 - r0) / (max - min);
176
+ return {
177
+ kind: 'time',
178
+ map: value => r0 + (value - min) * scale,
179
+ invert: px => min + (px - r0) / scale,
180
+ ticks: (hint = tickHint) => timeTicks(min, max, {
181
+ count: hint,
182
+ unit: tickUnit
183
+ }),
184
+ domain: () => [min, max],
185
+ range: () => [r0, r1]
186
+ };
187
+ }
@@ -0,0 +1,9 @@
1
+ import { type NormalizedPoint } from '../data/points';
2
+ import { type SeriesOptionsBase } from '../types/options';
3
+ import { type StackedValue } from '../types/renderer';
4
+ export interface StackInput {
5
+ index: number;
6
+ options: SeriesOptionsBase;
7
+ points: readonly NormalizedPoint[];
8
+ }
9
+ export declare function computeStacks(series: readonly StackInput[]): Map<number, Array<StackedValue | undefined>>;
@@ -0,0 +1,76 @@
1
+ const groupKey = series => series.stacking ? `${series.yAxis ?? 0}:${series.stacking}` : null;
2
+ const keyOf = point => String(point.x);
3
+ const emptyRunning = () => ({
4
+ positive: 0,
5
+ negative: 0
6
+ });
7
+ export function computeStacks(series) {
8
+ const result = new Map();
9
+ const groups = new Map();
10
+ series.forEach(entry => {
11
+ const key = groupKey(entry.options);
12
+ if (key === null) {
13
+ return;
14
+ }
15
+ const existing = groups.get(key);
16
+ if (existing) {
17
+ existing.push(entry);
18
+ } else {
19
+ groups.set(key, [entry]);
20
+ }
21
+ });
22
+ groups.forEach((members, key) => {
23
+ const percent = key.endsWith(':percent');
24
+ const totals = new Map();
25
+ members.forEach(({
26
+ points
27
+ }) => {
28
+ points.forEach(point => {
29
+ if (point.y === null) {
30
+ return;
31
+ }
32
+ const running = totals.get(keyOf(point)) ?? {
33
+ signed: 0,
34
+ absolute: 0
35
+ };
36
+ running.signed += point.y;
37
+ running.absolute += Math.abs(point.y);
38
+ totals.set(keyOf(point), running);
39
+ });
40
+ });
41
+ const cursors = new Map();
42
+ members.forEach(({
43
+ index,
44
+ points
45
+ }) => {
46
+ const stacked = points.map(point => {
47
+ if (point.y === null) {
48
+ return undefined;
49
+ }
50
+ const pointKey = keyOf(point);
51
+ const cursor = cursors.get(pointKey) ?? emptyRunning();
52
+ const total = totals.get(pointKey) ?? {
53
+ signed: 0,
54
+ absolute: 0
55
+ };
56
+ const negative = point.y < 0;
57
+ const from = negative ? cursor.negative : cursor.positive;
58
+ const magnitude = percent && total.absolute !== 0 ? point.y / total.absolute * 100 : point.y;
59
+ const to = from + magnitude;
60
+ if (negative) {
61
+ cursor.negative = to;
62
+ } else {
63
+ cursor.positive = to;
64
+ }
65
+ cursors.set(pointKey, cursor);
66
+ return {
67
+ from,
68
+ to,
69
+ total: total.signed
70
+ };
71
+ });
72
+ result.set(index, stacked);
73
+ });
74
+ });
75
+ return result;
76
+ }
@@ -0,0 +1,59 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ScaleKind } from './scale';
3
+ export interface AxisLabelContext {
4
+ value: number | string;
5
+ index: number;
6
+ isFirst: boolean;
7
+ isLast: boolean;
8
+ axis: {
9
+ kind: ScaleKind;
10
+ min: number | undefined;
11
+ max: number | undefined;
12
+ };
13
+ }
14
+ export type AxisLabelFormatter = (ctx: AxisLabelContext) => ReactNode;
15
+ export interface LegendLabelContext<TSeriesMeta = unknown> {
16
+ seriesIndex: number;
17
+ name: string;
18
+ color: string;
19
+ visible: boolean;
20
+ meta: TSeriesMeta | undefined;
21
+ }
22
+ export type LegendLabelFormatter<TSeriesMeta = unknown> = (ctx: LegendLabelContext<TSeriesMeta>) => ReactNode;
23
+ export interface DataLabelContext<TSeriesMeta = unknown> {
24
+ x: number | string;
25
+ y: number | null;
26
+ value: number | null;
27
+ pointIndex: number;
28
+ seriesIndex: number;
29
+ seriesName: string;
30
+ meta: TSeriesMeta | undefined;
31
+ }
32
+ export type DataLabelFormatter<TSeriesMeta = unknown> = (ctx: DataLabelContext<TSeriesMeta>) => ReactNode;
33
+ export interface TooltipNeighbour {
34
+ x: number | string;
35
+ y: number | null;
36
+ index: number;
37
+ }
38
+ export interface TooltipPoint<TSeriesMeta = unknown> {
39
+ seriesIndex: number;
40
+ seriesName: string;
41
+ color: string;
42
+ x: number | string;
43
+ y: number | null;
44
+ value: number | null;
45
+ index: number;
46
+ meta: TSeriesMeta | undefined;
47
+ prev: TooltipNeighbour | undefined;
48
+ next: TooltipNeighbour | undefined;
49
+ }
50
+ export interface TooltipContext<TChartMeta = unknown, TSeriesMeta = unknown> {
51
+ x: number | string;
52
+ points: readonly TooltipPoint<TSeriesMeta>[];
53
+ chart: {
54
+ title: string | undefined;
55
+ meta: TChartMeta | undefined;
56
+ };
57
+ }
58
+ export type TooltipFormatter<TChartMeta = unknown, TSeriesMeta = unknown> = (ctx: TooltipContext<TChartMeta, TSeriesMeta>) => ReactNode;
59
+ export type TooltipCellFormatter<TSeriesMeta = unknown> = (point: TooltipPoint<TSeriesMeta>) => ReactNode;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface Size {
6
+ width: number;
7
+ height: number;
8
+ }
9
+ export interface Rect extends Point, Size {
10
+ }
11
+ export type Spacing = [number, number, number, number];
12
+ export interface SlotGeometry {
13
+ width: number;
14
+ offset: number;
15
+ step: number;
16
+ }
@@ -0,0 +1 @@
1
+ export {};