@ledgerhq/lumen-ui-rnative-visualization 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module/lib/Components/Axis/Axis.constants.js +21 -0
- package/dist/module/lib/Components/Axis/Axis.constants.js.map +1 -0
- package/dist/module/lib/Components/Axis/index.js +5 -0
- package/dist/module/lib/Components/Axis/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js +34 -22
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js +5 -4
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js +54 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/utils.js +18 -8
- package/dist/module/lib/Components/CartesianChart/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.js +6 -1
- package/dist/module/lib/Components/Line/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.test.js +88 -0
- package/dist/module/lib/Components/Line/utils.test.js.map +1 -0
- package/dist/module/lib/Components/LineChart/LineChart.js +19 -31
- package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.stories.js +38 -0
- package/dist/module/lib/Components/LineChart/LineChart.stories.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.test.js +192 -0
- package/dist/module/lib/Components/LineChart/LineChart.test.js.map +1 -1
- package/dist/module/lib/Components/Point/Point.js +8 -2
- package/dist/module/lib/Components/Point/Point.js.map +1 -1
- package/dist/module/lib/Components/Point/constants.js +9 -0
- package/dist/module/lib/Components/Point/constants.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js +35 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js +153 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/index.js +5 -0
- package/dist/module/lib/Components/Point/pointContext/index.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js +10 -0
- package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js.map +1 -0
- package/dist/module/lib/Components/Point/utils.js +26 -6
- package/dist/module/lib/Components/Point/utils.js.map +1 -1
- package/dist/module/lib/Components/Point/utils.test.js +84 -2
- package/dist/module/lib/Components/Point/utils.test.js.map +1 -1
- package/dist/module/lib/Components/ReferenceLine/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/DefaultScrubberTooltip.test.js +4 -4
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.js +31 -10
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.js +4 -4
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js +35 -7
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/utils.js +64 -0
- package/dist/module/lib/Components/Scrubber/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/utils.test.js +128 -1
- package/dist/module/lib/Components/Scrubber/utils.test.js.map +1 -1
- package/dist/module/lib/utils/domain/domain.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.js +13 -4
- package/dist/module/lib/utils/scales/scales.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.test.js +29 -0
- package/dist/module/lib/utils/scales/scales.test.js.map +1 -1
- package/dist/module/lib/utils/ticks/ticks.js +13 -3
- package/dist/module/lib/utils/ticks/ticks.js.map +1 -1
- package/dist/module/lib/utils/ticks/ticks.test.js +34 -0
- package/dist/module/lib/utils/ticks/ticks.test.js.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/Axis.constants.d.ts +5 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts +39 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Axis/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/context/useBuildChartContext.d.ts +5 -4
- package/dist/typescript/src/lib/Components/CartesianChart/context/useBuildChartContext.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts +11 -3
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts +18 -8
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/utils.d.ts +4 -0
- package/dist/typescript/src/lib/Components/Line/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts +1 -1
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts +10 -3
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/constants.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/MagneticPointsProvider.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/MagneticPointsProvider.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts +13 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/types.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/utils.d.ts +8 -6
- package/dist/typescript/src/lib/Components/Point/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts +3 -2
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.d.ts +5 -1
- package/dist/typescript/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts +13 -0
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts +33 -3
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/domain/domain.d.ts +5 -4
- package/dist/typescript/src/lib/utils/domain/domain.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/index.d.ts +1 -1
- package/dist/typescript/src/lib/utils/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/scales/scales.d.ts +11 -5
- package/dist/typescript/src/lib/utils/scales/scales.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts +13 -5
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/types.d.ts +3 -28
- package/dist/typescript/src/lib/utils/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/lib/Components/Axis/Axis.constants.ts +23 -0
- package/src/lib/Components/Axis/Axis.types.ts +40 -0
- package/src/lib/Components/Axis/index.ts +2 -0
- package/src/lib/Components/CartesianChart/CartesianChart.tsx +39 -23
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.test.ts +56 -0
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.ts +8 -8
- package/src/lib/Components/CartesianChart/types.ts +11 -3
- package/src/lib/Components/CartesianChart/utils.ts +18 -8
- package/src/lib/Components/Line/utils.test.ts +90 -0
- package/src/lib/Components/Line/utils.ts +6 -1
- package/src/lib/Components/LineChart/LineChart.stories.tsx +36 -0
- package/src/lib/Components/LineChart/LineChart.test.tsx +156 -0
- package/src/lib/Components/LineChart/LineChart.tsx +30 -40
- package/src/lib/Components/LineChart/types.ts +10 -3
- package/src/lib/Components/Point/Point.tsx +9 -3
- package/src/lib/Components/Point/constants.ts +6 -0
- package/src/lib/Components/Point/pointContext/MagneticPointsProvider.test.tsx +162 -0
- package/src/lib/Components/Point/pointContext/MagneticPointsProvider.tsx +45 -0
- package/src/lib/Components/Point/pointContext/index.ts +5 -0
- package/src/lib/Components/Point/pointContext/magneticPointsContext.ts +19 -0
- package/src/lib/Components/Point/types.ts +7 -0
- package/src/lib/Components/Point/utils.test.ts +95 -5
- package/src/lib/Components/Point/utils.ts +35 -7
- package/src/lib/Components/ReferenceLine/utils.ts +4 -7
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/DefaultScrubberTooltip.test.tsx +4 -4
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.ts +4 -4
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.ts +29 -15
- package/src/lib/Components/Scrubber/ScrubberProvider.tsx +41 -5
- package/src/lib/Components/Scrubber/types.ts +13 -0
- package/src/lib/Components/Scrubber/utils.test.ts +150 -0
- package/src/lib/Components/Scrubber/utils.ts +93 -3
- package/src/lib/utils/domain/domain.ts +6 -5
- package/src/lib/utils/index.ts +0 -2
- package/src/lib/utils/scales/scales.test.ts +19 -0
- package/src/lib/utils/scales/scales.ts +15 -9
- package/src/lib/utils/ticks/ticks.test.ts +45 -0
- package/src/lib/utils/ticks/ticks.ts +19 -9
- package/src/lib/utils/types.ts +3 -26
|
@@ -4,10 +4,15 @@ import {
|
|
|
4
4
|
isNumericScale,
|
|
5
5
|
} from '../../utils/scales/scales';
|
|
6
6
|
import type {
|
|
7
|
-
AxisConfigProps,
|
|
8
7
|
CartesianChartContextValue,
|
|
9
8
|
ChartScaleFunction,
|
|
10
9
|
} from '../../utils/types';
|
|
10
|
+
import type { BaseAxisProps } from '../Axis';
|
|
11
|
+
|
|
12
|
+
export type MagnetEntry = {
|
|
13
|
+
index: number;
|
|
14
|
+
pixelX: number;
|
|
15
|
+
};
|
|
11
16
|
|
|
12
17
|
const isNumberArray = (arr: string[] | number[]): arr is number[] =>
|
|
13
18
|
typeof arr[0] === 'number';
|
|
@@ -49,7 +54,7 @@ const findClosestIndex = (
|
|
|
49
54
|
export const getDataIndexFromPosition = (
|
|
50
55
|
pixelX: number,
|
|
51
56
|
scale: ChartScaleFunction,
|
|
52
|
-
axisConfig: Partial<
|
|
57
|
+
axisConfig: Partial<BaseAxisProps> | undefined,
|
|
53
58
|
dataLength: number,
|
|
54
59
|
): number => {
|
|
55
60
|
if (dataLength <= 0) return 0;
|
|
@@ -116,7 +121,7 @@ export const resolvePixelY = (
|
|
|
116
121
|
export const resolvePixelX = (
|
|
117
122
|
dataIndex: number,
|
|
118
123
|
getXScale: CartesianChartContextValue['getXScale'],
|
|
119
|
-
axisConfig?:
|
|
124
|
+
axisConfig?: BaseAxisProps,
|
|
120
125
|
): number | undefined => {
|
|
121
126
|
const scale = getXScale();
|
|
122
127
|
if (!scale) return undefined;
|
|
@@ -124,3 +129,88 @@ export const resolvePixelX = (
|
|
|
124
129
|
const xValue = typeof axisValue === 'number' ? axisValue : dataIndex;
|
|
125
130
|
return getPointOnScale(xValue, scale);
|
|
126
131
|
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Resolves each magnetic index to its pixel position and returns them sorted
|
|
135
|
+
* by `pixelX` ascending. Indices that cannot be projected are filtered out.
|
|
136
|
+
* The sorted output enables early-exit in {@link applyMagnetisation}.
|
|
137
|
+
*/
|
|
138
|
+
export const buildSortedMagnets = ({
|
|
139
|
+
magneticIndices,
|
|
140
|
+
getPixelForIndex,
|
|
141
|
+
}: {
|
|
142
|
+
magneticIndices: ReadonlySet<number>;
|
|
143
|
+
getPixelForIndex: (index: number) => number | undefined;
|
|
144
|
+
}): MagnetEntry[] => {
|
|
145
|
+
if (magneticIndices.size === 0) return [];
|
|
146
|
+
|
|
147
|
+
const magnets: MagnetEntry[] = [];
|
|
148
|
+
|
|
149
|
+
for (const index of magneticIndices) {
|
|
150
|
+
const pixelX = getPixelForIndex(index);
|
|
151
|
+
if (pixelX !== undefined) magnets.push({ index, pixelX });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
magnets.sort((a, b) => a.pixelX - b.pixelX);
|
|
155
|
+
|
|
156
|
+
return magnets;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Binary search for the magnet whose `pixelX` is closest to `pixelX`.
|
|
161
|
+
* Returns -1 when the array is empty. Favors the left neighbor on ties.
|
|
162
|
+
*/
|
|
163
|
+
export const nearestIndex = (
|
|
164
|
+
sortedMagnets: readonly MagnetEntry[],
|
|
165
|
+
pixelX: number,
|
|
166
|
+
): number => {
|
|
167
|
+
if (sortedMagnets.length === 0) return -1;
|
|
168
|
+
|
|
169
|
+
let left = 0;
|
|
170
|
+
let right = sortedMagnets.length;
|
|
171
|
+
|
|
172
|
+
while (left < right) {
|
|
173
|
+
const mid = Math.floor((left + right) / 2);
|
|
174
|
+
if (sortedMagnets[mid].pixelX < pixelX) {
|
|
175
|
+
left = mid + 1;
|
|
176
|
+
} else {
|
|
177
|
+
right = mid;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (left === 0) return 0;
|
|
182
|
+
if (left === sortedMagnets.length) return left - 1;
|
|
183
|
+
|
|
184
|
+
const distBefore = pixelX - sortedMagnets[left - 1].pixelX;
|
|
185
|
+
const distAfter = sortedMagnets[left].pixelX - pixelX;
|
|
186
|
+
|
|
187
|
+
return distBefore <= distAfter ? left - 1 : left;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Given a resolved data index and the raw pixel position, checks whether any
|
|
192
|
+
* magnetic point is within `magnetRadius` pixels of `pixelX`. If so, returns
|
|
193
|
+
* the closest magnetic index; otherwise returns the original `resolvedIndex`.
|
|
194
|
+
*/
|
|
195
|
+
export const applyMagnetisation = ({
|
|
196
|
+
resolvedIndex,
|
|
197
|
+
pixelX,
|
|
198
|
+
sortedMagnets,
|
|
199
|
+
magnetRadius,
|
|
200
|
+
}: {
|
|
201
|
+
resolvedIndex: number;
|
|
202
|
+
pixelX: number;
|
|
203
|
+
sortedMagnets: readonly MagnetEntry[];
|
|
204
|
+
magnetRadius: number;
|
|
205
|
+
}): number => {
|
|
206
|
+
if (magnetRadius <= 0 || sortedMagnets.length === 0) return resolvedIndex;
|
|
207
|
+
|
|
208
|
+
const nearest = nearestIndex(sortedMagnets, pixelX);
|
|
209
|
+
if (
|
|
210
|
+
nearest >= 0 &&
|
|
211
|
+
Math.abs(sortedMagnets[nearest].pixelX - pixelX) <= magnetRadius
|
|
212
|
+
) {
|
|
213
|
+
return sortedMagnets[nearest].index;
|
|
214
|
+
}
|
|
215
|
+
return resolvedIndex;
|
|
216
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AxisBounds,
|
|
1
|
+
import type { AxisBounds, BaseAxisProps } from '../../Components/Axis';
|
|
2
|
+
import type { Series } from '../types';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Compute the X domain (index-based) from series data and axis config.
|
|
@@ -7,7 +8,7 @@ import type { AxisBounds, AxisConfigProps, Series } from '../types';
|
|
|
7
8
|
*/
|
|
8
9
|
export const computeXDomain = (
|
|
9
10
|
series: Series[],
|
|
10
|
-
axisConfig?: Partial<
|
|
11
|
+
axisConfig?: Partial<BaseAxisProps>,
|
|
11
12
|
): AxisBounds => {
|
|
12
13
|
const axisData = axisConfig?.data;
|
|
13
14
|
|
|
@@ -44,7 +45,7 @@ export const computeXDomain = (
|
|
|
44
45
|
*/
|
|
45
46
|
export const computeYDomain = (
|
|
46
47
|
series: Series[],
|
|
47
|
-
axisConfig?: Partial<
|
|
48
|
+
axisConfig?: Partial<BaseAxisProps>,
|
|
48
49
|
): AxisBounds => {
|
|
49
50
|
let min = 0;
|
|
50
51
|
let max = 0;
|
|
@@ -76,7 +77,7 @@ export const computeYDomain = (
|
|
|
76
77
|
*/
|
|
77
78
|
export const computeDataLength = (
|
|
78
79
|
series: Series[],
|
|
79
|
-
axisConfig?: Partial<
|
|
80
|
+
axisConfig?: Partial<BaseAxisProps>,
|
|
80
81
|
): number => {
|
|
81
82
|
if (axisConfig?.data && axisConfig.data.length > 0) {
|
|
82
83
|
return axisConfig.data.length;
|
|
@@ -86,7 +87,7 @@ export const computeDataLength = (
|
|
|
86
87
|
|
|
87
88
|
const applyDomainOverride = (
|
|
88
89
|
autoBounds: AxisBounds,
|
|
89
|
-
domainOverride?:
|
|
90
|
+
domainOverride?: BaseAxisProps['domain'],
|
|
90
91
|
): AxisBounds => {
|
|
91
92
|
if (!domainOverride) return autoBounds;
|
|
92
93
|
|
package/src/lib/utils/index.ts
CHANGED
|
@@ -53,6 +53,25 @@ describe('getNumericScale', () => {
|
|
|
53
53
|
expect(range[0]).toBe(20);
|
|
54
54
|
expect(range[1]).toBe(400);
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
it('rounds the domain outward when nice is true (default)', () => {
|
|
58
|
+
const scale = getNumericScale({
|
|
59
|
+
scaleType: 'linear',
|
|
60
|
+
domain: { min: 0, max: 3769 },
|
|
61
|
+
range: { min: 0, max: 400 },
|
|
62
|
+
});
|
|
63
|
+
expect(scale.domain()).toEqual([0, 4000]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('keeps the domain exact when nice is false', () => {
|
|
67
|
+
const scale = getNumericScale({
|
|
68
|
+
scaleType: 'linear',
|
|
69
|
+
domain: { min: 0, max: 3769 },
|
|
70
|
+
range: { min: 0, max: 400 },
|
|
71
|
+
nice: false,
|
|
72
|
+
});
|
|
73
|
+
expect(scale.domain()).toEqual([0, 3769]);
|
|
74
|
+
});
|
|
56
75
|
});
|
|
57
76
|
|
|
58
77
|
describe('getCategoricalScale', () => {
|
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
import { scaleBand, scaleLinear, scaleLog } from 'd3-scale';
|
|
2
2
|
|
|
3
|
+
import type { AxisBounds, BaseAxisProps } from '../../Components/Axis';
|
|
3
4
|
import type {
|
|
4
|
-
AxisBounds,
|
|
5
|
-
AxisConfigProps,
|
|
6
5
|
CategoricalScale,
|
|
7
6
|
ChartScaleFunction,
|
|
8
7
|
NumericScale,
|
|
9
8
|
} from '../types';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
|
-
* Creates a numeric scale
|
|
13
|
-
*
|
|
11
|
+
* Creates a numeric scale.
|
|
12
|
+
*
|
|
13
|
+
* When `nice` is `true` (default), the domain is rounded outward to clean
|
|
14
|
+
* boundaries via d3's `.nice()` (e.g. `[4, 98]` → `[0, 100]`). Set `nice` to
|
|
15
|
+
* `false` to keep the domain exactly as provided so data fills the range
|
|
16
|
+
* boundary-to-boundary.
|
|
14
17
|
*/
|
|
15
18
|
export const getNumericScale = ({
|
|
16
19
|
scaleType,
|
|
17
20
|
domain,
|
|
18
21
|
range,
|
|
22
|
+
nice = true,
|
|
19
23
|
}: {
|
|
20
24
|
scaleType: 'linear' | 'log';
|
|
21
25
|
domain: AxisBounds;
|
|
22
26
|
range: AxisBounds;
|
|
27
|
+
nice?: boolean;
|
|
23
28
|
}): NumericScale => {
|
|
24
29
|
const scale = scaleType === 'log' ? scaleLog() : scaleLinear();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
.nice()
|
|
28
|
-
|
|
30
|
+
scale.domain([domain.min, domain.max]);
|
|
31
|
+
if (nice) {
|
|
32
|
+
scale.nice();
|
|
33
|
+
}
|
|
34
|
+
return scale.range([range.min, range.max]);
|
|
29
35
|
};
|
|
30
36
|
|
|
31
37
|
export const getCategoricalScale = ({
|
|
@@ -52,7 +58,7 @@ export const getCategoricalScale = ({
|
|
|
52
58
|
* Checks if a scale type config value refers to a band (categorical) scale.
|
|
53
59
|
*/
|
|
54
60
|
export const isBandScaleType = (
|
|
55
|
-
scaleType:
|
|
61
|
+
scaleType: BaseAxisProps['scaleType'],
|
|
56
62
|
): scaleType is 'band' => scaleType === 'band';
|
|
57
63
|
|
|
58
64
|
/**
|
|
@@ -36,6 +36,24 @@ describe('getTickValues', () => {
|
|
|
36
36
|
it('should return full domain for band scales', () => {
|
|
37
37
|
expect(getTickValues(bandScale)).toEqual([0, 1, 2, 3, 4]);
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
it('should return numeric axis data verbatim (no d3-invented intermediate ticks)', () => {
|
|
41
|
+
expect(getTickValues(numericScale, undefined, [0, 2, 4])).toEqual([
|
|
42
|
+
0, 2, 4,
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return indices for string axis data', () => {
|
|
47
|
+
expect(
|
|
48
|
+
getTickValues(numericScale, undefined, ['Jan', 'Feb', 'Mar']),
|
|
49
|
+
).toEqual([0, 1, 2]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should prioritize explicit ticks over axis data', () => {
|
|
53
|
+
expect(
|
|
54
|
+
getTickValues(numericScale, [10, 20], ['Jan', 'Feb', 'Mar']),
|
|
55
|
+
).toEqual([10, 20]);
|
|
56
|
+
});
|
|
39
57
|
});
|
|
40
58
|
|
|
41
59
|
describe('getTickPosition', () => {
|
|
@@ -121,6 +139,33 @@ describe('buildTicksData', () => {
|
|
|
121
139
|
expect(result[1].label).toBe('Tue');
|
|
122
140
|
});
|
|
123
141
|
|
|
142
|
+
it('should derive ticks from numeric axis data (no intermediate ticks)', () => {
|
|
143
|
+
const result = buildTicksData(numericScale, { data: [0, 2, 4] });
|
|
144
|
+
expect(result.map((t: { value: number }) => t.value)).toEqual([0, 2, 4]);
|
|
145
|
+
expect(result.map((t: { label: string }) => t.label)).toEqual([
|
|
146
|
+
'0',
|
|
147
|
+
'2',
|
|
148
|
+
'4',
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should derive ticks from string axis data without intermediate values', () => {
|
|
153
|
+
const stringScale = getNumericScale({
|
|
154
|
+
scaleType: 'linear',
|
|
155
|
+
domain: { min: 0, max: 2 },
|
|
156
|
+
range: { min: 0, max: 500 },
|
|
157
|
+
});
|
|
158
|
+
const result = buildTicksData(stringScale, {
|
|
159
|
+
data: ['Jan', 'Feb', 'Mar'],
|
|
160
|
+
});
|
|
161
|
+
expect(result.map((t: { value: number }) => t.value)).toEqual([0, 1, 2]);
|
|
162
|
+
expect(result.map((t: { label: string }) => t.label)).toEqual([
|
|
163
|
+
'Jan',
|
|
164
|
+
'Feb',
|
|
165
|
+
'Mar',
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
|
|
124
169
|
it('should apply tick formatter', () => {
|
|
125
170
|
const formatter = (v: number | string): string => `$${v}`;
|
|
126
171
|
const result = buildTicksData(numericScale, undefined, [0, 50], formatter);
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
+
import type { BaseAxisProps } from '../../Components/Axis';
|
|
1
2
|
import { isCategoricalScale, isNumericScale } from '../scales/scales';
|
|
2
|
-
import type {
|
|
3
|
-
AxisConfigProps,
|
|
4
|
-
ChartScaleFunction,
|
|
5
|
-
DrawingArea,
|
|
6
|
-
} from '../types';
|
|
3
|
+
import type { ChartScaleFunction, DrawingArea } from '../types';
|
|
7
4
|
|
|
8
5
|
export const APPROXIMATE_TICK_COUNT = 5;
|
|
9
6
|
|
|
@@ -15,13 +12,26 @@ export type TickData = {
|
|
|
15
12
|
|
|
16
13
|
/**
|
|
17
14
|
* Resolves which numeric tick values should appear on the axis.
|
|
18
|
-
*
|
|
15
|
+
*
|
|
16
|
+
* Priority:
|
|
17
|
+
* 1. Explicit `ticks` provided by the consumer.
|
|
18
|
+
* 2. Axis `data` — when provided, ticks come from the data itself (numeric
|
|
19
|
+
* values for numeric data, indices for string data) so the rendered ticks
|
|
20
|
+
* mirror exactly what the consumer asked for, with no d3-invented
|
|
21
|
+
* intermediate values.
|
|
22
|
+
* 3. Scale-specific defaults (band domain, or `scale.ticks()` for numeric).
|
|
19
23
|
*/
|
|
20
24
|
export const getTickValues = (
|
|
21
25
|
scale: ChartScaleFunction,
|
|
22
26
|
explicitTicks?: number[],
|
|
27
|
+
axisData?: BaseAxisProps['data'],
|
|
23
28
|
): number[] => {
|
|
24
29
|
if (explicitTicks) return explicitTicks;
|
|
30
|
+
if (axisData && axisData.length > 0) {
|
|
31
|
+
return typeof axisData[0] === 'number'
|
|
32
|
+
? (axisData as number[])
|
|
33
|
+
: axisData.map((_, i) => i);
|
|
34
|
+
}
|
|
25
35
|
if (isCategoricalScale(scale)) return scale.domain();
|
|
26
36
|
if (isNumericScale(scale)) return scale.ticks(APPROXIMATE_TICK_COUNT);
|
|
27
37
|
return [];
|
|
@@ -47,7 +57,7 @@ export const getTickPosition = (
|
|
|
47
57
|
*/
|
|
48
58
|
export const getTickLabel = (
|
|
49
59
|
tick: number,
|
|
50
|
-
axisData:
|
|
60
|
+
axisData: BaseAxisProps['data'],
|
|
51
61
|
formatter?: (value: number | string) => string,
|
|
52
62
|
): string => {
|
|
53
63
|
const hasStringLabels =
|
|
@@ -71,12 +81,12 @@ export const getTickLabel = (
|
|
|
71
81
|
*/
|
|
72
82
|
export const buildTicksData = (
|
|
73
83
|
scale: ChartScaleFunction,
|
|
74
|
-
axisConfig?:
|
|
84
|
+
axisConfig?: BaseAxisProps,
|
|
75
85
|
explicitTicks?: number[],
|
|
76
86
|
formatter?: (value: number | string) => string,
|
|
77
87
|
): TickData[] => {
|
|
78
|
-
const tickValues = getTickValues(scale, explicitTicks);
|
|
79
88
|
const axisData = axisConfig?.data;
|
|
89
|
+
const tickValues = getTickValues(scale, explicitTicks, axisData);
|
|
80
90
|
|
|
81
91
|
return tickValues.map((tick) => ({
|
|
82
92
|
position: getTickPosition(scale, tick),
|
package/src/lib/utils/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ScaleBand, ScaleLinear, ScaleLogarithmic } from 'd3-scale';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type { BaseAxisProps } from '../Components/Axis';
|
|
4
4
|
|
|
5
5
|
export type ChartInset = {
|
|
6
6
|
top: number;
|
|
@@ -38,29 +38,6 @@ export type Series = {
|
|
|
38
38
|
stroke: string;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
export type AxisConfigProps = {
|
|
42
|
-
/**
|
|
43
|
-
* Scale algorithm used by this axis.
|
|
44
|
-
* @default 'linear'
|
|
45
|
-
*/
|
|
46
|
-
scaleType?: 'linear' | 'log' | 'band';
|
|
47
|
-
/**
|
|
48
|
-
* Explicit data values for band scales or category labels.
|
|
49
|
-
* For band scales, provides the discrete domain. For numeric scales, string values
|
|
50
|
-
* are used as tick labels at corresponding indices.
|
|
51
|
-
*/
|
|
52
|
-
data?: string[] | number[];
|
|
53
|
-
/**
|
|
54
|
-
* Fixed domain bounds or a function that adjusts the computed bounds.
|
|
55
|
-
* A partial object overrides only the specified bound(s).
|
|
56
|
-
* A function receives the auto-computed bounds and returns adjusted ones.
|
|
57
|
-
*
|
|
58
|
-
* The final domain is always rounded to nice boundaries via d3's `.nice()`,
|
|
59
|
-
* ensuring tick marks land on clean values (e.g. `[4, 98]` becomes `[0, 100]`).
|
|
60
|
-
*/
|
|
61
|
-
domain?: Partial<AxisBounds> | ((bounds: AxisBounds) => AxisBounds);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
41
|
export type NumericScale =
|
|
65
42
|
| ScaleLinear<number, number>
|
|
66
43
|
| ScaleLogarithmic<number, number>;
|
|
@@ -89,11 +66,11 @@ export type CartesianChartContextValue = {
|
|
|
89
66
|
/**
|
|
90
67
|
* Returns the x-axis config. Accepts an optional axis ID for future multi-axis support.
|
|
91
68
|
*/
|
|
92
|
-
getXAxisConfig: (id?: string) =>
|
|
69
|
+
getXAxisConfig: (id?: string) => BaseAxisProps | undefined;
|
|
93
70
|
/**
|
|
94
71
|
* Returns the y-axis config. Accepts an optional axis ID for future multi-axis support.
|
|
95
72
|
*/
|
|
96
|
-
getYAxisConfig: (id?: string) =>
|
|
73
|
+
getYAxisConfig: (id?: string) => BaseAxisProps | undefined;
|
|
97
74
|
/**
|
|
98
75
|
* Pixel bounds of the drawable region.
|
|
99
76
|
*/
|