@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,224 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTweenedNumbers } from '../../core/animation/useTweenedNumbers';
3
+ import { normalizePoints } from '../../core/data/points';
4
+ import { applyValueFormat } from '../../core/format/time';
5
+ import { DEFAULT_SURFACE_COLOR } from '../../core/model/palette';
6
+ import { brighten } from '../shared/color';
7
+ const RADIUS_FRACTION = 0.85;
8
+ const LABEL_OFFSET = 12;
9
+ const HOVER_BRIGHTNESS = 0.12;
10
+ const FULL_TURN = Math.PI * 2;
11
+ const positive = point => {
12
+ const raw = point.value ?? point.y;
13
+ return raw !== null && Number.isFinite(raw) && raw > 0 ? raw : 0;
14
+ };
15
+ function geometryOf(ctx, radiusScale = 1) {
16
+ const {
17
+ plot
18
+ } = ctx;
19
+ const outer = Math.min(plot.width, plot.height) / 2 * RADIUS_FRACTION * radiusScale;
20
+ const requested = ctx.series.innerSize;
21
+ const fraction = typeof requested === 'string' ? Number.parseFloat(requested) / 100 : requested ?? 0;
22
+ return {
23
+ cx: plot.x + plot.width / 2,
24
+ cy: plot.y + plot.height / 2,
25
+ outer,
26
+ inner: outer * Math.min(0.95, Math.max(0, Number.isFinite(fraction) ? fraction : 0))
27
+ };
28
+ }
29
+ function toSlices(ctx) {
30
+ const shown = point => !ctx.hiddenPoints.has(point.index);
31
+ const visibleTotal = ctx.points.reduce((sum, point) => sum + (shown(point) ? positive(point) : 0), 0);
32
+ // Nothing left on show: keep the angles the circle had and let the radius carry it
33
+ // away, so the last slice pulls into the centre rather than snapping shut.
34
+ const total = visibleTotal > 0 ? visibleTotal : ctx.points.reduce((sum, point) => sum + positive(point), 0);
35
+ if (total <= 0) {
36
+ return [];
37
+ }
38
+ const slices = [];
39
+ let cursor = -Math.PI / 2;
40
+ ctx.points.forEach((point, index) => {
41
+ const value = positive(point);
42
+ if (value <= 0) {
43
+ return;
44
+ }
45
+ // A switched-off slice takes no angle: it closes like a fan while the slices
46
+ // after it slide into the room it leaves, and the shape of the path is the same
47
+ // throughout, which is what lets the browser interpolate it.
48
+ const sweep = shown(point) || visibleTotal <= 0 ? value / total * FULL_TURN : 0;
49
+ slices.push({
50
+ point,
51
+ from: cursor,
52
+ to: cursor + sweep,
53
+ color: point.color ?? ctx.colors[index % ctx.colors.length] ?? ctx.color,
54
+ hidden: !shown(point)
55
+ });
56
+ cursor += sweep;
57
+ });
58
+ return slices;
59
+ }
60
+ const polar = (cx, cy, radius, angle) => ({
61
+ x: cx + radius * Math.cos(angle),
62
+ y: cy + radius * Math.sin(angle)
63
+ });
64
+ function slicePath(slice, geometry) {
65
+ const {
66
+ cx,
67
+ cy,
68
+ outer,
69
+ inner
70
+ } = geometry;
71
+ const sweep = slice.to - slice.from;
72
+ const large = sweep > Math.PI ? 1 : 0;
73
+ const outerStart = polar(cx, cy, outer, slice.from);
74
+ const outerEnd = polar(cx, cy, outer, slice.to);
75
+ if (sweep >= FULL_TURN - 1e-9) {
76
+ const half = polar(cx, cy, outer, slice.from + Math.PI);
77
+ const ring = `M${outerStart.x} ${outerStart.y}` + `A${outer} ${outer} 0 1 1 ${half.x} ${half.y}` + `A${outer} ${outer} 0 1 1 ${outerStart.x} ${outerStart.y}Z`;
78
+ if (inner <= 0) {
79
+ return ring;
80
+ }
81
+ const innerStart = polar(cx, cy, inner, slice.from);
82
+ const innerHalf = polar(cx, cy, inner, slice.from + Math.PI);
83
+ return `${ring}M${innerStart.x} ${innerStart.y}` + `A${inner} ${inner} 0 1 0 ${innerHalf.x} ${innerHalf.y}` + `A${inner} ${inner} 0 1 0 ${innerStart.x} ${innerStart.y}Z`;
84
+ }
85
+ if (inner <= 0) {
86
+ return `M${cx} ${cy}L${outerStart.x} ${outerStart.y}` + `A${outer} ${outer} 0 ${large} 1 ${outerEnd.x} ${outerEnd.y}Z`;
87
+ }
88
+ const innerEnd = polar(cx, cy, inner, slice.to);
89
+ const innerStart = polar(cx, cy, inner, slice.from);
90
+ return `M${outerStart.x} ${outerStart.y}` + `A${outer} ${outer} 0 ${large} 1 ${outerEnd.x} ${outerEnd.y}` + `L${innerEnd.x} ${innerEnd.y}` + `A${inner} ${inner} 0 ${large} 0 ${innerStart.x} ${innerStart.y}Z`;
91
+ }
92
+ function Labels({
93
+ ctx,
94
+ slices,
95
+ geometry
96
+ }) {
97
+ const labels = ctx.series.dataLabels;
98
+ if (!(labels !== null && labels !== void 0 && labels.enabled)) {
99
+ return null;
100
+ }
101
+ return slices.filter(slice => !slice.hidden).map(slice => {
102
+ var _labels$style, _labels$style2, _labels$style3, _labels$style4;
103
+ const mid = (slice.from + slice.to) / 2;
104
+ const at = polar(geometry.cx, geometry.cy, geometry.outer + LABEL_OFFSET, mid);
105
+ const text = labels.formatter ? labels.formatter({
106
+ x: slice.point.x,
107
+ y: slice.point.y,
108
+ value: slice.point.value,
109
+ pointIndex: slice.point.index,
110
+ seriesIndex: ctx.seriesIndex,
111
+ seriesName: ctx.series.name ?? '',
112
+ meta: ctx.series.meta
113
+ }) : applyValueFormat(labels.format ?? '{value}', slice.point.y ?? '');
114
+ return _jsx("text", {
115
+ x: at.x,
116
+ y: at.y,
117
+ textAnchor: Math.cos(mid) < 0 ? 'end' : 'start',
118
+ dominantBaseline: "middle",
119
+ fill: labels.color ?? ((_labels$style = labels.style) === null || _labels$style === void 0 ? void 0 : _labels$style.color),
120
+ fontFamily: (_labels$style2 = labels.style) === null || _labels$style2 === void 0 ? void 0 : _labels$style2.fontFamily,
121
+ fontSize: ((_labels$style3 = labels.style) === null || _labels$style3 === void 0 ? void 0 : _labels$style3.fontSize) ?? '11px',
122
+ fontWeight: (_labels$style4 = labels.style) === null || _labels$style4 === void 0 ? void 0 : _labels$style4.fontWeight,
123
+ children: text
124
+ }, `label-${slice.point.index}`);
125
+ });
126
+ }
127
+ /**
128
+ * The circle is drawn from tweened numbers: two angles per slice plus the radius of
129
+ * the whole pie. A slice that is switched off loses its angle and closes like a fan;
130
+ * a series that is switched off loses its radius and pulls into the centre. Because
131
+ * every frame regenerates the arc from those numbers, the shape is always a true
132
+ * arc — interpolating the path itself would bend it.
133
+ */
134
+ function Slices({
135
+ ctx
136
+ }) {
137
+ var _ctx$hovered;
138
+ const slices = toSlices(ctx);
139
+ const alive = ctx.points.some(point => !ctx.hiddenPoints.has(point.index));
140
+ const targets = [...slices.flatMap(slice => [slice.from, slice.to]), ctx.hidden || !alive ? 0 : 1];
141
+ const tweened = useTweenedNumbers(targets);
142
+ if (!slices.length) {
143
+ return null;
144
+ }
145
+ const radiusScale = tweened[tweened.length - 1] ?? 1;
146
+ const geometry = geometryOf(ctx, radiusScale);
147
+ const hovered = (_ctx$hovered = ctx.hovered) === null || _ctx$hovered === void 0 ? void 0 : _ctx$hovered.pointIndex;
148
+ const drawn = slices.map((slice, index) => ({
149
+ ...slice,
150
+ from: tweened[index * 2] ?? slice.from,
151
+ to: tweened[index * 2 + 1] ?? slice.to
152
+ }));
153
+ return _jsxs("g", {
154
+ children: [drawn.map(slice => _jsx("path", {
155
+ d: slicePath(slice, geometry),
156
+ opacity: slice.hidden ? 0 : 1,
157
+ fill: slice.point.index === hovered ? brighten(slice.color, HOVER_BRIGHTNESS) : slice.color,
158
+ stroke: ctx.series.borderColor ?? DEFAULT_SURFACE_COLOR,
159
+ strokeWidth: ctx.series.borderWidth ?? 1
160
+ }, `slice-${slice.point.index}`)), _jsx(Labels, {
161
+ ctx: ctx,
162
+ slices: drawn,
163
+ geometry: geometry
164
+ })]
165
+ });
166
+ }
167
+ export const pie = {
168
+ type: 'pie',
169
+ scales: {
170
+ x: 'none',
171
+ y: 'none'
172
+ },
173
+ // The renderer tweens its own geometry; CSS must not transition it as well.
174
+ exit: 'collapse',
175
+ animatesGeometry: true,
176
+ extendDomain: (_series, acc) => acc,
177
+ render: ctx => _jsx(Slices, {
178
+ ctx: ctx
179
+ }),
180
+ hitTest: (ctx, pointer) => {
181
+ const slices = toSlices(ctx).filter(slice => !slice.hidden);
182
+ if (!slices.length) {
183
+ return null;
184
+ }
185
+ const geometry = geometryOf(ctx);
186
+ const dx = pointer.x - geometry.cx;
187
+ const dy = pointer.y - geometry.cy;
188
+ const radius = Math.sqrt(dx * dx + dy * dy);
189
+ if (radius > geometry.outer || radius < geometry.inner) {
190
+ return null;
191
+ }
192
+ const raw = Math.atan2(dy, dx);
193
+ const angle = raw < -Math.PI / 2 ? raw + FULL_TURN : raw;
194
+ const hit = slices.find(slice => angle >= slice.from && angle < slice.to);
195
+ if (!hit) {
196
+ return null;
197
+ }
198
+ const mid = (hit.from + hit.to) / 2;
199
+ return [{
200
+ seriesIndex: ctx.seriesIndex,
201
+ pointIndex: hit.point.index,
202
+ x: hit.point.name ?? hit.point.x,
203
+ y: hit.point.y,
204
+ value: hit.point.value,
205
+ anchor: polar(geometry.cx, geometry.cy, (geometry.outer + geometry.inner) / 2, mid),
206
+ distance: 0
207
+ }];
208
+ },
209
+ legendItems: (series, seriesIndex, colors) => {
210
+ if (series.showInLegend === false) {
211
+ return [];
212
+ }
213
+ // One item per slice: a pie's legend keys the points, so each item toggles its
214
+ // own slice and the remaining ones share the circle out again.
215
+ return normalizePoints(series.data, series.name).map((point, index) => ({
216
+ seriesIndex,
217
+ pointIndex: point.index,
218
+ label: point.name ?? String(point.y ?? index + 1),
219
+ color: point.color ?? colors[index % colors.length] ?? '',
220
+ symbol: 'circle',
221
+ visible: true
222
+ }));
223
+ }
224
+ };
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type Projected } from '../../core/geometry/path';
3
+ import { type DataLabelsOptions } from '../../core/types/options';
4
+ import { type RenderContext } from '../../core/types/renderer';
5
+ export declare const labelAnchor: (align: DataLabelsOptions["align"]) => "start" | "middle" | "end";
6
+ export interface DataLabelsProps {
7
+ ctx: RenderContext;
8
+ projected: readonly Projected[];
9
+ positionOf: (projected: Projected) => {
10
+ x: number;
11
+ y: number;
12
+ };
13
+ }
14
+ export declare function DataLabels({ ctx, projected, positionOf }: DataLabelsProps): ReactNode;
@@ -0,0 +1,124 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { applyValueFormat } from '../../core/format/time';
3
+ import { measureText, textHeight } from '../../core/layout/measure-text';
4
+ import { CHART_EDGE_GAP } from '../../core/layout/metrics';
5
+ import { DEFAULT_TEXT_COLOR } from '../../core/model/palette';
6
+ export const labelAnchor = align => {
7
+ if (align === 'left') {
8
+ return 'start';
9
+ }
10
+ if (align === 'right') {
11
+ return 'end';
12
+ }
13
+ return 'middle';
14
+ };
15
+ const textProps = style => ({
16
+ fontFamily: style === null || style === void 0 ? void 0 : style.fontFamily,
17
+ fontSize: style === null || style === void 0 ? void 0 : style.fontSize,
18
+ fontWeight: style === null || style === void 0 ? void 0 : style.fontWeight,
19
+ fontStyle: style === null || style === void 0 ? void 0 : style.fontStyle,
20
+ paintOrder: (style === null || style === void 0 ? void 0 : style.textOutline) === 'none' ? undefined : 'stroke'
21
+ });
22
+ /** No formatter and no format string: the chart's own number rule prints it. */
23
+ const defaultLabel = (ctx, format, y) => {
24
+ if (format) {
25
+ return applyValueFormat(format, y ?? '');
26
+ }
27
+ return y === null ? '' : ctx.numberFormat(y);
28
+ };
29
+ const boxOf = (lines, at, anchor, styles) => {
30
+ const width = lines.reduce((widest, text, index) => Math.max(widest, measureText(text, styles[Math.min(index, 1)])), 0);
31
+ const height = lines.reduce((total, _text, index) => total + textHeight(styles[Math.min(index, 1)]), 0);
32
+ const left = anchor === 'start' ? at.x : at.x - (anchor === 'middle' ? width / 2 : width);
33
+ return {
34
+ left,
35
+ right: left + width,
36
+ top: at.y - textHeight(styles[0]),
37
+ bottom: at.y - textHeight(styles[0]) + height
38
+ };
39
+ };
40
+ const overlaps = (a, b) => a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
41
+ export function DataLabels({
42
+ ctx,
43
+ projected,
44
+ positionOf
45
+ }) {
46
+ const options = ctx.series.dataLabels;
47
+ if (!(options !== null && options !== void 0 && options.enabled)) {
48
+ return null;
49
+ }
50
+ const turbo = ctx.series.turboThreshold;
51
+ if (turbo !== undefined && ctx.points.length > turbo) {
52
+ return null;
53
+ }
54
+ const style = options.style;
55
+ const secondary = options.secondaryStyle;
56
+ const styles = [style, secondary ?? style];
57
+ const anchor = labelAnchor(options.align);
58
+ // A newline splits the label into stacked lines: a value with its share under
59
+ // it is one label, not two, so it moves and hides as one.
60
+ const linesOf = content => {
61
+ if (typeof content !== 'string' && typeof content !== 'number') {
62
+ return null;
63
+ }
64
+ return String(content).split('\n');
65
+ };
66
+ // Highcharts hides a label rather than let it collide. Only text can be
67
+ // measured, so a formatter returning an element is always drawn.
68
+ const placed = [];
69
+ const collides = (lines, at) => {
70
+ if (options.allowOverlap === true || !lines) {
71
+ return false;
72
+ }
73
+ const box = boxOf(lines, at, anchor, styles);
74
+ if (placed.some(other => overlaps(box, other))) {
75
+ return true;
76
+ }
77
+ placed.push(box);
78
+ return false;
79
+ };
80
+ return projected.map(item => {
81
+ const {
82
+ point
83
+ } = item;
84
+ const content = options.formatter ? options.formatter({
85
+ x: point.x,
86
+ y: point.y,
87
+ value: point.value,
88
+ pointIndex: point.index,
89
+ seriesIndex: ctx.seriesIndex,
90
+ seriesName: ctx.series.name ?? `Series ${ctx.seriesIndex + 1}`,
91
+ meta: ctx.series.meta
92
+ }) : defaultLabel(ctx, options.format, point.y);
93
+ if (content === null || content === undefined || content === '') {
94
+ return null;
95
+ }
96
+ const lines = linesOf(content);
97
+ const at = positionOf(item);
98
+ // A label may sit above the plot area — that is what the headroom is for — but
99
+ // never against the chart edge, where it reads as glued to the border. Extra
100
+ // lines grow downwards, so only the first one has to clear the edge.
101
+ const stack = lines ? lines.length - 1 : 0;
102
+ const y = Math.max(at.y - stack * textHeight(secondary ?? style), textHeight(style) + CHART_EDGE_GAP);
103
+ if (collides(lines, {
104
+ x: at.x,
105
+ y
106
+ })) {
107
+ return null;
108
+ }
109
+ return _jsx("text", {
110
+ x: at.x,
111
+ y: y,
112
+ textAnchor: anchor,
113
+ fill: options.color ?? (style === null || style === void 0 ? void 0 : style.color) ?? DEFAULT_TEXT_COLOR,
114
+ ...textProps(style),
115
+ children: lines && lines.length > 1 ? lines.map((text, index) => _jsx("tspan", {
116
+ x: at.x,
117
+ dy: index === 0 ? 0 : textHeight(secondary ?? style),
118
+ fill: index === 0 ? undefined : secondary === null || secondary === void 0 ? void 0 : secondary.color,
119
+ ...(index === 0 ? {} : textProps(secondary)),
120
+ children: text
121
+ }, `line-${index}`)) : content
122
+ }, `dl-${point.index}`);
123
+ });
124
+ }
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type Projected } from '../../core/geometry/path';
3
+ import { type MarkerOptions } from '../../core/types/options';
4
+ export interface MarkersProps {
5
+ marker: MarkerOptions | undefined;
6
+ projected: readonly Projected[];
7
+ color: string;
8
+ }
9
+ export declare function Markers({ marker, projected, color }: MarkersProps): ReactNode;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const markerRadius = marker => (marker === null || marker === void 0 ? void 0 : marker.radius) ?? (marker !== null && marker !== void 0 && marker.width ? marker.width / 2 : 3);
3
+ export function Markers({
4
+ marker,
5
+ projected,
6
+ color
7
+ }) {
8
+ if ((marker === null || marker === void 0 ? void 0 : marker.enabled) === false || (marker === null || marker === void 0 ? void 0 : marker.symbol) === 'none') {
9
+ return null;
10
+ }
11
+ const radius = markerRadius(marker);
12
+ const fill = (marker === null || marker === void 0 ? void 0 : marker.fillColor) ?? color;
13
+ const square = (marker === null || marker === void 0 ? void 0 : marker.symbol) === 'square';
14
+ return projected.map(({
15
+ point,
16
+ px,
17
+ py
18
+ }) => square ? _jsx("rect", {
19
+ x: px - radius,
20
+ y: py - radius,
21
+ width: radius * 2,
22
+ height: radius * 2,
23
+ fill: fill,
24
+ stroke: marker === null || marker === void 0 ? void 0 : marker.lineColor,
25
+ strokeWidth: marker === null || marker === void 0 ? void 0 : marker.lineWidth
26
+ }, `m-${point.index}`) : _jsx("circle", {
27
+ cx: px,
28
+ cy: py,
29
+ r: radius,
30
+ fill: fill,
31
+ stroke: marker === null || marker === void 0 ? void 0 : marker.lineColor,
32
+ strokeWidth: marker === null || marker === void 0 ? void 0 : marker.lineWidth
33
+ }, `m-${point.index}`));
34
+ }
@@ -0,0 +1 @@
1
+ export declare function brighten(color: string, amount: number): string;
@@ -0,0 +1,19 @@
1
+ const clamp = value => Math.min(255, Math.max(0, Math.round(value)));
2
+ const parseHex = color => {
3
+ const match = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim());
4
+ if (!match) {
5
+ return null;
6
+ }
7
+ const hex = match[1].length === 3 ? match[1].split('').map(char => char + char).join('') : match[1];
8
+ return [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
9
+ };
10
+ export function brighten(color, amount) {
11
+ const rgb = parseHex(color);
12
+ if (!rgb || !amount) {
13
+ return color;
14
+ }
15
+ const target = amount > 0 ? 255 : 0;
16
+ const ratio = Math.abs(amount);
17
+ const [r, g, b] = rgb.map(channel => clamp(channel + (target - channel) * ratio));
18
+ return `#${[r, g, b].map(channel => channel.toString(16).padStart(2, '0')).join('')}`;
19
+ }
@@ -0,0 +1,3 @@
1
+ import { type SeriesOptionsBase } from '../../core/types/options';
2
+ import { type DomainAcc } from '../../core/types/renderer';
3
+ export declare function includeZero(series: SeriesOptionsBase, acc: DomainAcc): DomainAcc;
@@ -0,0 +1,14 @@
1
+ export function includeZero(series, acc) {
2
+ const index = Math.min(series.yAxis ?? 0, acc.y.length - 1);
3
+ const target = acc.y[index];
4
+ if (!target) {
5
+ return acc;
6
+ }
7
+ return {
8
+ ...acc,
9
+ y: acc.y.map((range, current) => current === index ? {
10
+ min: Math.min(range.min, 0),
11
+ max: Math.max(range.max, 0)
12
+ } : range)
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import { type Point } from '../../core/types/geometry';
2
+ import { type PointHit, type RenderContext } from '../../core/types/renderer';
3
+ export declare function nearestByX(ctx: RenderContext, pointer: Point, radius?: number): PointHit[] | null;
@@ -0,0 +1,33 @@
1
+ import { pointKey } from '../../core/data/points';
2
+ import { drawable, projectSeries } from '../../core/geometry/path';
3
+ const HIT_RADIUS = 40;
4
+ export function nearestByX(ctx, pointer, radius = HIT_RADIUS) {
5
+ const projected = drawable(projectSeries(ctx));
6
+ if (!projected.length) {
7
+ return null;
8
+ }
9
+ let best;
10
+ let bestDistance = Infinity;
11
+ projected.forEach(candidate => {
12
+ const distance = Math.abs(candidate.px - pointer.x);
13
+ if (distance < bestDistance) {
14
+ best = candidate;
15
+ bestDistance = distance;
16
+ }
17
+ });
18
+ if (!best || bestDistance > radius) {
19
+ return null;
20
+ }
21
+ return [{
22
+ seriesIndex: ctx.seriesIndex,
23
+ pointIndex: best.point.index,
24
+ x: pointKey(best.point),
25
+ y: best.point.y,
26
+ value: best.point.value,
27
+ anchor: {
28
+ x: best.px,
29
+ y: best.py
30
+ },
31
+ distance: bestDistance
32
+ }];
33
+ }
@@ -0,0 +1,3 @@
1
+ import { type SeriesOptionsBase } from '../../core/types/options';
2
+ import { type LegendItem } from '../../core/types/renderer';
3
+ export declare function seriesLegend(series: SeriesOptionsBase, seriesIndex: number, symbol: LegendItem['symbol']): LegendItem[];
@@ -0,0 +1,12 @@
1
+ export function seriesLegend(series, seriesIndex, symbol) {
2
+ if (series.showInLegend === false) {
3
+ return [];
4
+ }
5
+ return [{
6
+ seriesIndex,
7
+ label: series.name ?? `Series ${seriesIndex + 1}`,
8
+ color: series.color ?? '',
9
+ symbol: series.legendSymbol ?? symbol,
10
+ visible: series.visible !== false
11
+ }];
12
+ }