@ledgerhq/lumen-ui-rnative-visualization 0.1.18 → 0.1.20
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/XAxis/XAxis.js +2 -1
- package/dist/module/lib/Components/Axis/XAxis/XAxis.js.map +1 -1
- package/dist/module/lib/Components/Axis/XAxis/XAxis.test.js +14 -0
- package/dist/module/lib/Components/Axis/XAxis/XAxis.test.js.map +1 -1
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js +2 -1
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js.map +1 -1
- package/dist/module/lib/Components/Axis/YAxis/YAxis.test.js +14 -0
- package/dist/module/lib/Components/Axis/YAxis/YAxis.test.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js +2 -2
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/{RevealClip/RevealClipDefs.js → RevealAnimation/RevealAnimationProvider.js} +12 -11
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/RevealAnimationProvider.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/{RevealClip/RevealClipDefs.test.js → RevealAnimation/RevealAnimationProvider.test.js} +51 -12
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/RevealAnimationProvider.test.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/context.js +35 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/context.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/index.js +5 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/types.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/utils.js +64 -0
- package/dist/module/lib/Components/CartesianChart/RevealAnimation/utils.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/utils.js +1 -1
- package/dist/module/lib/Components/CartesianChart/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/Line.js +6 -4
- package/dist/module/lib/Components/Line/Line.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.js +19 -9
- package/dist/module/lib/Components/Line/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.test.js +22 -2
- package/dist/module/lib/Components/Line/utils.test.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.js +23 -8
- package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.test.js +69 -0
- package/dist/module/lib/Components/LineChart/LineChart.test.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChartEmptyState/LineChartEmptyState.js +2 -2
- package/dist/module/lib/Components/LineChart/LineChartEmptyState/LineChartEmptyState.js.map +1 -1
- package/dist/module/lib/Components/Point/Point.js +6 -4
- package/dist/module/lib/Components/Point/Point.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js +8 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.test.js +79 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.test.js.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts +6 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/XAxis/XAxis.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Axis/XAxis/XAxis.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/YAxis/YAxis.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Axis/YAxis/YAxis.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/RevealAnimationProvider.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/RevealAnimationProvider.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/context.d.ts +22 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/context.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/{RevealClip → RevealAnimation}/types.d.ts +14 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/utils.d.ts +33 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealAnimation/utils.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/Line.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Line/Line.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/types.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Line/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/utils.d.ts +8 -4
- 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 +13 -1
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/types.d.ts +7 -0
- package/dist/typescript/src/lib/utils/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/lib/Components/Axis/Axis.types.ts +6 -0
- package/src/lib/Components/Axis/XAxis/XAxis.test.tsx +12 -0
- package/src/lib/Components/Axis/XAxis/XAxis.tsx +16 -14
- package/src/lib/Components/Axis/YAxis/YAxis.test.tsx +12 -0
- package/src/lib/Components/Axis/YAxis/YAxis.tsx +16 -14
- package/src/lib/Components/CartesianChart/CartesianChart.tsx +3 -3
- package/src/lib/Components/CartesianChart/{RevealClip/RevealClipDefs.test.tsx → RevealAnimation/RevealAnimationProvider.test.tsx} +60 -17
- package/src/lib/Components/CartesianChart/{RevealClip/RevealClipDefs.tsx → RevealAnimation/RevealAnimationProvider.tsx} +10 -11
- package/src/lib/Components/CartesianChart/RevealAnimation/context.ts +39 -0
- package/src/lib/Components/CartesianChart/RevealAnimation/index.ts +2 -0
- package/src/lib/Components/CartesianChart/{RevealClip → RevealAnimation}/types.ts +15 -1
- package/src/lib/Components/CartesianChart/RevealAnimation/utils.ts +87 -0
- package/src/lib/Components/CartesianChart/utils.ts +2 -1
- package/src/lib/Components/Line/Line.tsx +13 -4
- package/src/lib/Components/Line/types.ts +7 -0
- package/src/lib/Components/Line/utils.test.ts +36 -2
- package/src/lib/Components/Line/utils.ts +24 -9
- package/src/lib/Components/LineChart/LineChart.test.tsx +71 -0
- package/src/lib/Components/LineChart/LineChart.tsx +21 -3
- package/src/lib/Components/LineChart/LineChartEmptyState/LineChartEmptyState.tsx +2 -2
- package/src/lib/Components/LineChart/types.ts +15 -1
- package/src/lib/Components/Point/Point.tsx +7 -4
- package/src/lib/Components/Scrubber/ScrubberProvider.test.tsx +83 -1
- package/src/lib/Components/Scrubber/ScrubberProvider.tsx +15 -1
- package/src/lib/utils/types.ts +7 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.test.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/RevealClip/context.js +0 -8
- package/dist/module/lib/Components/CartesianChart/RevealClip/context.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/RevealClip/index.js +0 -5
- package/dist/module/lib/Components/CartesianChart/RevealClip/index.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/RevealClip/types.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/hooks/useDataFingerprint.js +0 -13
- package/dist/module/lib/Components/CartesianChart/hooks/useDataFingerprint.js.map +0 -1
- package/dist/module/lib/Components/CartesianChart/hooks/useRevealClipAnimation.js +0 -27
- package/dist/module/lib/Components/CartesianChart/hooks/useRevealClipAnimation.js.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.d.ts +0 -3
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.d.ts.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/context.d.ts +0 -7
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/context.d.ts.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/index.d.ts +0 -3
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/index.d.ts.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/types.d.ts.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/hooks/useDataFingerprint.d.ts +0 -9
- package/dist/typescript/src/lib/Components/CartesianChart/hooks/useDataFingerprint.d.ts.map +0 -1
- package/dist/typescript/src/lib/Components/CartesianChart/hooks/useRevealClipAnimation.d.ts +0 -14
- package/dist/typescript/src/lib/Components/CartesianChart/hooks/useRevealClipAnimation.d.ts.map +0 -1
- package/src/lib/Components/CartesianChart/RevealClip/context.ts +0 -13
- package/src/lib/Components/CartesianChart/RevealClip/index.ts +0 -2
- package/src/lib/Components/CartesianChart/hooks/useDataFingerprint.ts +0 -16
- package/src/lib/Components/CartesianChart/hooks/useRevealClipAnimation.ts +0 -43
- /package/dist/module/lib/Components/CartesianChart/{RevealClip → RevealAnimation}/types.js +0 -0
|
@@ -17,6 +17,7 @@ export const YAxis = ({
|
|
|
17
17
|
showGrid = false,
|
|
18
18
|
showLine = false,
|
|
19
19
|
showTickMark = false,
|
|
20
|
+
showLabels = true,
|
|
20
21
|
gridLineStyle = 'dashed',
|
|
21
22
|
ticks: ticksProp,
|
|
22
23
|
tickLabelFormatter,
|
|
@@ -91,20 +92,21 @@ export const YAxis = ({
|
|
|
91
92
|
/>
|
|
92
93
|
))}
|
|
93
94
|
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
{showLabels &&
|
|
96
|
+
ticksData.map((tick, i) => (
|
|
97
|
+
<SvgText
|
|
98
|
+
key={`label-${tick.value}-${i}`}
|
|
99
|
+
x={labelX}
|
|
100
|
+
y={tick.position}
|
|
101
|
+
dy={labelDy}
|
|
102
|
+
textAnchor={position === 'start' ? 'end' : 'start'}
|
|
103
|
+
fill={textFill}
|
|
104
|
+
fontSize={theme.typographies.body4.fontSize}
|
|
105
|
+
fontFamily={theme.fontFamilies.sans}
|
|
106
|
+
>
|
|
107
|
+
{tick.label}
|
|
108
|
+
</SvgText>
|
|
109
|
+
))}
|
|
108
110
|
</G>
|
|
109
111
|
);
|
|
110
112
|
};
|
|
@@ -5,7 +5,7 @@ import { Svg } from 'react-native-svg';
|
|
|
5
5
|
import { MagneticPointsProvider } from '../Point/pointContext';
|
|
6
6
|
import { ScrubberProvider } from '../Scrubber/ScrubberProvider';
|
|
7
7
|
import { CartesianChartProvider, useBuildChartContext } from './context';
|
|
8
|
-
import {
|
|
8
|
+
import { RevealAnimationProvider } from './RevealAnimation';
|
|
9
9
|
import type { CartesianChartProps } from './types';
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_HEIGHT,
|
|
@@ -102,13 +102,13 @@ export function CartesianChart({
|
|
|
102
102
|
height={svgHeight}
|
|
103
103
|
style={{ overflow: 'visible' }}
|
|
104
104
|
>
|
|
105
|
-
<
|
|
105
|
+
<RevealAnimationProvider
|
|
106
106
|
drawingArea={contextValue.drawingArea}
|
|
107
107
|
series={series}
|
|
108
108
|
animate={animate}
|
|
109
109
|
>
|
|
110
110
|
{children}
|
|
111
|
-
</
|
|
111
|
+
</RevealAnimationProvider>
|
|
112
112
|
</Svg>
|
|
113
113
|
</ScrubberProvider>
|
|
114
114
|
</MagneticPointsProvider>
|
|
@@ -7,8 +7,8 @@ import { Svg } from 'react-native-svg';
|
|
|
7
7
|
import type { DrawingArea, Series } from '../../../utils/types';
|
|
8
8
|
import { OVERFLOW_BUFFER } from '../utils';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { usePathReveal, usePointReveal } from './context';
|
|
11
|
+
import { RevealAnimationProvider } from './RevealAnimationProvider';
|
|
12
12
|
|
|
13
13
|
const drawingArea: DrawingArea = {
|
|
14
14
|
x: 10,
|
|
@@ -20,10 +20,15 @@ const drawingArea: DrawingArea = {
|
|
|
20
20
|
const series: Series[] = [{ id: 'a', stroke: '#000', data: [1, 2, 3] }];
|
|
21
21
|
|
|
22
22
|
function ClipConsumer() {
|
|
23
|
-
const clipPath =
|
|
23
|
+
const clipPath = usePathReveal();
|
|
24
24
|
return <Svg testID='consumer' clipPath={clipPath} />;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function PointConsumer() {
|
|
28
|
+
const pointOpacity = usePointReveal();
|
|
29
|
+
return <Svg testID='point-consumer' opacity={pointOpacity ? 1 : 0} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
const renderInSvg = (ui: React.ReactElement) =>
|
|
28
33
|
render(
|
|
29
34
|
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
@@ -31,16 +36,16 @@ const renderInSvg = (ui: React.ReactElement) =>
|
|
|
31
36
|
</ThemeProvider>,
|
|
32
37
|
);
|
|
33
38
|
|
|
34
|
-
describe('
|
|
39
|
+
describe('RevealAnimationProvider', () => {
|
|
35
40
|
it('renders children', () => {
|
|
36
41
|
const { getByTestId } = renderInSvg(
|
|
37
|
-
<
|
|
42
|
+
<RevealAnimationProvider
|
|
38
43
|
drawingArea={drawingArea}
|
|
39
44
|
series={series}
|
|
40
45
|
transitions={{ enter: { duration: 0.5 } }}
|
|
41
46
|
>
|
|
42
47
|
<Svg testID='child' />
|
|
43
|
-
</
|
|
48
|
+
</RevealAnimationProvider>,
|
|
44
49
|
);
|
|
45
50
|
|
|
46
51
|
expect(getByTestId('child')).toBeTruthy();
|
|
@@ -48,13 +53,13 @@ describe('RevealClipDefs', () => {
|
|
|
48
53
|
|
|
49
54
|
it('renders a ClipPath with a Rect when animate is true', () => {
|
|
50
55
|
const { UNSAFE_root } = renderInSvg(
|
|
51
|
-
<
|
|
56
|
+
<RevealAnimationProvider
|
|
52
57
|
drawingArea={drawingArea}
|
|
53
58
|
series={series}
|
|
54
59
|
transitions={{ enter: { duration: 0.5 } }}
|
|
55
60
|
>
|
|
56
61
|
<Svg testID='child' />
|
|
57
|
-
</
|
|
62
|
+
</RevealAnimationProvider>,
|
|
58
63
|
);
|
|
59
64
|
|
|
60
65
|
const clipPaths = UNSAFE_root.findAllByType('ClipPath' as any);
|
|
@@ -71,26 +76,45 @@ describe('RevealClipDefs', () => {
|
|
|
71
76
|
);
|
|
72
77
|
});
|
|
73
78
|
|
|
74
|
-
it('provides clipPathAttr to consumers via
|
|
79
|
+
it('provides clipPathAttr to consumers via usePathReveal', () => {
|
|
75
80
|
const { UNSAFE_root } = renderInSvg(
|
|
76
|
-
<
|
|
81
|
+
<RevealAnimationProvider
|
|
77
82
|
drawingArea={drawingArea}
|
|
78
83
|
series={series}
|
|
79
84
|
transitions={{ enter: { duration: 0.5 } }}
|
|
80
85
|
>
|
|
81
86
|
<ClipConsumer />
|
|
82
|
-
</
|
|
87
|
+
</RevealAnimationProvider>,
|
|
83
88
|
);
|
|
84
89
|
|
|
85
90
|
const consumer = UNSAFE_root.findByProps({ testID: 'consumer' });
|
|
86
91
|
expect(consumer.props.clipPath).toMatch(/^url\(#/);
|
|
87
92
|
});
|
|
88
93
|
|
|
94
|
+
it('provides a point opacity shared value via usePointReveal', () => {
|
|
95
|
+
const { UNSAFE_root } = renderInSvg(
|
|
96
|
+
<RevealAnimationProvider
|
|
97
|
+
drawingArea={drawingArea}
|
|
98
|
+
series={series}
|
|
99
|
+
transitions={{ enter: { duration: 0.5 } }}
|
|
100
|
+
>
|
|
101
|
+
<PointConsumer />
|
|
102
|
+
</RevealAnimationProvider>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const consumer = UNSAFE_root.findByProps({ testID: 'point-consumer' });
|
|
106
|
+
expect(consumer.props.opacity).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
89
109
|
it('skips ClipPath when animate is false', () => {
|
|
90
110
|
const { UNSAFE_root, getByTestId } = renderInSvg(
|
|
91
|
-
<
|
|
111
|
+
<RevealAnimationProvider
|
|
112
|
+
drawingArea={drawingArea}
|
|
113
|
+
series={series}
|
|
114
|
+
animate={false}
|
|
115
|
+
>
|
|
92
116
|
<Svg testID='child' />
|
|
93
|
-
</
|
|
117
|
+
</RevealAnimationProvider>,
|
|
94
118
|
);
|
|
95
119
|
|
|
96
120
|
expect(getByTestId('child')).toBeTruthy();
|
|
@@ -101,24 +125,43 @@ describe('RevealClipDefs', () => {
|
|
|
101
125
|
|
|
102
126
|
it('does not provide clipPathAttr when animate is false', () => {
|
|
103
127
|
const { UNSAFE_root } = renderInSvg(
|
|
104
|
-
<
|
|
128
|
+
<RevealAnimationProvider
|
|
129
|
+
drawingArea={drawingArea}
|
|
130
|
+
series={series}
|
|
131
|
+
animate={false}
|
|
132
|
+
>
|
|
105
133
|
<ClipConsumer />
|
|
106
|
-
</
|
|
134
|
+
</RevealAnimationProvider>,
|
|
107
135
|
);
|
|
108
136
|
|
|
109
137
|
const consumer = UNSAFE_root.findByProps({ testID: 'consumer' });
|
|
110
138
|
expect(consumer.props.clipPath).toBeUndefined();
|
|
111
139
|
});
|
|
112
140
|
|
|
141
|
+
it('does not provide a point opacity value when animate is false', () => {
|
|
142
|
+
const { UNSAFE_root } = renderInSvg(
|
|
143
|
+
<RevealAnimationProvider
|
|
144
|
+
drawingArea={drawingArea}
|
|
145
|
+
series={series}
|
|
146
|
+
animate={false}
|
|
147
|
+
>
|
|
148
|
+
<PointConsumer />
|
|
149
|
+
</RevealAnimationProvider>,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const consumer = UNSAFE_root.findByProps({ testID: 'point-consumer' });
|
|
153
|
+
expect(consumer.props.opacity).toBe(0);
|
|
154
|
+
});
|
|
155
|
+
|
|
113
156
|
it('renders without crashing when transitions.enter is not defined', () => {
|
|
114
157
|
const { UNSAFE_root, getByTestId } = renderInSvg(
|
|
115
|
-
<
|
|
158
|
+
<RevealAnimationProvider
|
|
116
159
|
drawingArea={drawingArea}
|
|
117
160
|
series={series}
|
|
118
161
|
transitions={{}}
|
|
119
162
|
>
|
|
120
163
|
<Svg testID='child' />
|
|
121
|
-
</
|
|
164
|
+
</RevealAnimationProvider>,
|
|
122
165
|
);
|
|
123
166
|
|
|
124
167
|
expect(getByTestId('child')).toBeTruthy();
|
|
@@ -4,24 +4,23 @@ import { useMemo } from 'react';
|
|
|
4
4
|
import Animated from 'react-native-reanimated';
|
|
5
5
|
import { ClipPath, Defs, Rect } from 'react-native-svg';
|
|
6
6
|
|
|
7
|
-
import { useDataFingerprint } from '../hooks/useDataFingerprint';
|
|
8
|
-
import { useRevealClipAnimation } from '../hooks/useRevealClipAnimation';
|
|
9
7
|
import { OVERFLOW_BUFFER } from '../utils';
|
|
10
8
|
|
|
11
|
-
import {
|
|
12
|
-
import type {
|
|
9
|
+
import { RevealAnimationContext } from './context';
|
|
10
|
+
import type { RevealAnimationProps } from './types';
|
|
11
|
+
import { useDataFingerprint, useRevealAnimation } from './utils';
|
|
13
12
|
|
|
14
13
|
const DEFAULT_DURATION_IN_SECONDS = 0.8;
|
|
15
14
|
|
|
16
15
|
const AnimatedRect = Animated.createAnimatedComponent(Rect);
|
|
17
16
|
|
|
18
|
-
export function
|
|
17
|
+
export function RevealAnimationProvider({
|
|
19
18
|
children,
|
|
20
19
|
drawingArea,
|
|
21
20
|
series,
|
|
22
21
|
animate = true,
|
|
23
22
|
transitions,
|
|
24
|
-
}:
|
|
23
|
+
}: RevealAnimationProps) {
|
|
25
24
|
/**
|
|
26
25
|
* Disable reveal animation on Android devices due to issues with SVG clip-path animation support.
|
|
27
26
|
* On Android, the animation does not render correctly, so we opt out for a consistent user experience.
|
|
@@ -31,15 +30,15 @@ export function RevealClipDefs({
|
|
|
31
30
|
(transitions?.enter?.duration ?? DEFAULT_DURATION_IN_SECONDS) * 1000;
|
|
32
31
|
|
|
33
32
|
const dataFingerprint = useDataFingerprint(series);
|
|
34
|
-
const { clipId, animatedRectProps } =
|
|
33
|
+
const { clipId, animatedRectProps, pointOpacity } = useRevealAnimation({
|
|
35
34
|
durationMs,
|
|
36
35
|
drawingArea,
|
|
37
36
|
dataFingerprint,
|
|
38
37
|
});
|
|
39
38
|
|
|
40
39
|
const contextValue = useMemo(
|
|
41
|
-
() => ({ clipPathAttr: `url(#${clipId})
|
|
42
|
-
[clipId],
|
|
40
|
+
() => ({ clipPathAttr: `url(#${clipId})`, pointOpacity }),
|
|
41
|
+
[clipId, pointOpacity],
|
|
43
42
|
);
|
|
44
43
|
|
|
45
44
|
if (isDisabled) {
|
|
@@ -47,7 +46,7 @@ export function RevealClipDefs({
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
return (
|
|
50
|
-
<
|
|
49
|
+
<RevealAnimationContext.Provider value={contextValue}>
|
|
51
50
|
<Defs>
|
|
52
51
|
<ClipPath id={clipId}>
|
|
53
52
|
<AnimatedRect
|
|
@@ -61,6 +60,6 @@ export function RevealClipDefs({
|
|
|
61
60
|
</ClipPath>
|
|
62
61
|
</Defs>
|
|
63
62
|
{children}
|
|
64
|
-
</
|
|
63
|
+
</RevealAnimationContext.Provider>
|
|
65
64
|
);
|
|
66
65
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { useAnimatedProps } from 'react-native-reanimated';
|
|
4
|
+
|
|
5
|
+
import type { RevealAnimationContextValue } from './types';
|
|
6
|
+
|
|
7
|
+
export const RevealAnimationContext = createContext<
|
|
8
|
+
RevealAnimationContextValue | undefined
|
|
9
|
+
>(undefined);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Clip-path attribute for path-based consumers (e.g. `Line`).
|
|
13
|
+
* Returns `undefined` when the reveal animation is disabled.
|
|
14
|
+
*/
|
|
15
|
+
export const usePathReveal = (): string | undefined => {
|
|
16
|
+
return useContext(RevealAnimationContext)?.clipPathAttr;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Raw shared opacity value for the point/accessory fade. Returns `undefined`
|
|
21
|
+
* when the reveal animation is disabled. Prefer {@link useRevealFadeProps} in
|
|
22
|
+
* components; this is the low-level primitive for custom needs.
|
|
23
|
+
*/
|
|
24
|
+
export const usePointReveal = (): SharedValue<number> | undefined => {
|
|
25
|
+
return useContext(RevealAnimationContext)?.pointOpacity;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Ready-to-use animated props for accessory components (e.g. `Point`) that
|
|
30
|
+
* fade in with the reveal instead of being clipped. Spread directly onto any
|
|
31
|
+
* animated SVG element via `animatedProps`
|
|
32
|
+
*/
|
|
33
|
+
export const useRevealFadeProps = (): ReturnType<typeof useAnimatedProps> => {
|
|
34
|
+
const pointOpacity = usePointReveal();
|
|
35
|
+
return useAnimatedProps(
|
|
36
|
+
() => ({ opacity: pointOpacity?.value ?? 1 }),
|
|
37
|
+
[pointOpacity],
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
2
3
|
|
|
3
4
|
import type { DrawingArea, Series } from '../../../utils/types';
|
|
4
5
|
|
|
@@ -14,7 +15,7 @@ export type EnterTransitionConfig = {
|
|
|
14
15
|
easing?: string;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
export type
|
|
18
|
+
export type RevealAnimationProps = {
|
|
18
19
|
children: ReactNode;
|
|
19
20
|
/**
|
|
20
21
|
* The drawing area whose bounds define the clip rectangle.
|
|
@@ -36,3 +37,16 @@ export type RevealClipDefsProps = {
|
|
|
36
37
|
*/
|
|
37
38
|
series: Series[];
|
|
38
39
|
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Value shared by the reveal animation provider.
|
|
43
|
+
*
|
|
44
|
+
* - `clipPathAttr` drives the left-to-right clip wipe consumed by path-based
|
|
45
|
+
* components (e.g. `Line`, `LineChartEmptyState`).
|
|
46
|
+
* - `pointOpacity` is the shared opacity value for accessory components (e.g.
|
|
47
|
+
* `Point`) that must not be clipped, so they fade in after the wipe instead.
|
|
48
|
+
*/
|
|
49
|
+
export type RevealAnimationContextValue = {
|
|
50
|
+
clipPathAttr: string;
|
|
51
|
+
pointOpacity: SharedValue<number>;
|
|
52
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useEffect, useId, useMemo, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
useAnimatedProps,
|
|
4
|
+
useSharedValue,
|
|
5
|
+
withDelay,
|
|
6
|
+
withTiming,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
|
|
9
|
+
import type { DrawingArea, Series } from '../../../utils/types';
|
|
10
|
+
import { OVERFLOW_BUFFER } from '../utils';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Duration in ms of an individual point's opacity fade-in.
|
|
14
|
+
*/
|
|
15
|
+
const POINT_FADE_DURATION_IN_MS = 200;
|
|
16
|
+
const POINT_FADE_IN_AFTER_CLIP_IN_MS = -100;
|
|
17
|
+
|
|
18
|
+
type RevealAnimationConfig = {
|
|
19
|
+
durationMs: number;
|
|
20
|
+
drawingArea: DrawingArea;
|
|
21
|
+
dataFingerprint: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type RevealAnimationResult = {
|
|
25
|
+
clipId: string;
|
|
26
|
+
animatedRectProps: ReturnType<typeof useAnimatedProps>;
|
|
27
|
+
pointOpacity: ReturnType<typeof useSharedValue<number>>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Drives the two reveal animations:
|
|
32
|
+
* - the left-to-right clip-path wipe (animated `Rect` width, including the
|
|
33
|
+
* {@link OVERFLOW_BUFFER} so edge content is revealed too), and
|
|
34
|
+
* - the point fade-in, delayed so points appear as the wipe finishes.
|
|
35
|
+
*
|
|
36
|
+
* On a data change the shared values are reset to `0` (hidden) *synchronously
|
|
37
|
+
* during render* — before the new line/points are committed and painted — so
|
|
38
|
+
* they never paint once at the previous cycle's end values (a visible flash).
|
|
39
|
+
* The effect then animates them back in.
|
|
40
|
+
*/
|
|
41
|
+
export const useRevealAnimation = ({
|
|
42
|
+
durationMs,
|
|
43
|
+
drawingArea,
|
|
44
|
+
dataFingerprint,
|
|
45
|
+
}: RevealAnimationConfig): RevealAnimationResult => {
|
|
46
|
+
const clipId = useId();
|
|
47
|
+
const clipWidth = useSharedValue(0);
|
|
48
|
+
const pointOpacity = useSharedValue(0);
|
|
49
|
+
|
|
50
|
+
const previousFingerprint = useRef<string | null>(null);
|
|
51
|
+
if (previousFingerprint.current !== dataFingerprint) {
|
|
52
|
+
previousFingerprint.current = dataFingerprint;
|
|
53
|
+
clipWidth.value = 0;
|
|
54
|
+
pointOpacity.value = 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
clipWidth.value = withTiming(
|
|
59
|
+
drawingArea.width + OVERFLOW_BUFFER.left + OVERFLOW_BUFFER.right,
|
|
60
|
+
{ duration: durationMs },
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
pointOpacity.value = withDelay(
|
|
64
|
+
Math.max(durationMs + POINT_FADE_IN_AFTER_CLIP_IN_MS, 0),
|
|
65
|
+
withTiming(1, { duration: POINT_FADE_DURATION_IN_MS }),
|
|
66
|
+
);
|
|
67
|
+
}, [drawingArea.width, durationMs, clipWidth, pointOpacity, dataFingerprint]);
|
|
68
|
+
|
|
69
|
+
const animatedRectProps = useAnimatedProps(() => ({
|
|
70
|
+
width: clipWidth.value,
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
return { clipId, animatedRectProps, pointOpacity };
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Stable signature of every series' data points, memoized on the `series`
|
|
78
|
+
* reference. Used to key the reveal provider so the reveal animation only
|
|
79
|
+
* replays when the underlying data actually changes — not on unrelated
|
|
80
|
+
* re-renders such as scrubbing or hover.
|
|
81
|
+
*/
|
|
82
|
+
export const useDataFingerprint = (series: Series[]): string => {
|
|
83
|
+
return useMemo(
|
|
84
|
+
() => series.map((s) => s.data?.join(',') ?? '').join('|'),
|
|
85
|
+
[series],
|
|
86
|
+
);
|
|
87
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChartInset } from '../../utils/types';
|
|
2
2
|
import type { CartesianChartProps } from './types';
|
|
3
3
|
|
|
4
|
-
export const DEFAULT_HEIGHT =
|
|
4
|
+
export const DEFAULT_HEIGHT = 228;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal buffer added around the drawing area so SVG content (labels, points,
|
|
@@ -30,6 +30,7 @@ export const OVERFLOW_OFFSET = {
|
|
|
30
30
|
top: -OVERFLOW_BUFFER.top,
|
|
31
31
|
left: -OVERFLOW_BUFFER.left,
|
|
32
32
|
};
|
|
33
|
+
|
|
33
34
|
export const ZERO_PADDING: ChartInset = {
|
|
34
35
|
top: 0,
|
|
35
36
|
right: 0,
|
|
@@ -4,7 +4,7 @@ import { Defs, G, LinearGradient, Path, Stop } from 'react-native-svg';
|
|
|
4
4
|
|
|
5
5
|
import { isNumericScale } from '../../utils/scales/scales';
|
|
6
6
|
import { useCartesianChartContext } from '../CartesianChart/context';
|
|
7
|
-
import {
|
|
7
|
+
import { usePathReveal } from '../CartesianChart/RevealAnimation';
|
|
8
8
|
|
|
9
9
|
import { LINE_AREA_GRADIENT_OPACITY, LINE_STROKE_WIDTH } from './constants';
|
|
10
10
|
import type { LineProps } from './types';
|
|
@@ -16,10 +16,11 @@ export const Line = ({
|
|
|
16
16
|
showArea = false,
|
|
17
17
|
areaType: _areaType = 'gradient',
|
|
18
18
|
curve,
|
|
19
|
+
connectNulls,
|
|
19
20
|
}: LineProps) => {
|
|
20
21
|
const { getXScale, getYScale, getXAxisConfig, drawingArea, seriesMap } =
|
|
21
22
|
useCartesianChartContext();
|
|
22
|
-
const clipPath =
|
|
23
|
+
const clipPath = usePathReveal();
|
|
23
24
|
|
|
24
25
|
const xScale = getXScale();
|
|
25
26
|
const yScale = getYScale();
|
|
@@ -31,13 +32,21 @@ export const Line = ({
|
|
|
31
32
|
const resolvedStroke =
|
|
32
33
|
stroke ?? seriesData?.stroke ?? theme.colors.border.muted;
|
|
33
34
|
const resolvedCurve = curve ?? seriesData?.curve;
|
|
35
|
+
const resolvedConnectNulls =
|
|
36
|
+
connectNulls ?? seriesData?.connectNulls ?? false;
|
|
34
37
|
|
|
35
38
|
const points = useMemo(
|
|
36
39
|
() =>
|
|
37
40
|
seriesData?.data && xScale && yScale && isNumericScale(yScale)
|
|
38
|
-
? toScaledPoints(
|
|
41
|
+
? toScaledPoints(
|
|
42
|
+
seriesData.data,
|
|
43
|
+
xScale,
|
|
44
|
+
yScale,
|
|
45
|
+
xAxisConfig?.data,
|
|
46
|
+
resolvedConnectNulls,
|
|
47
|
+
)
|
|
39
48
|
: null,
|
|
40
|
-
[seriesData, xScale, yScale, xAxisConfig],
|
|
49
|
+
[seriesData, xScale, yScale, xAxisConfig, resolvedConnectNulls],
|
|
41
50
|
);
|
|
42
51
|
|
|
43
52
|
const linePath = useMemo(
|
|
@@ -30,4 +30,11 @@ export type LineProps = {
|
|
|
30
30
|
* @default 'bump'
|
|
31
31
|
*/
|
|
32
32
|
curve?: CurveType;
|
|
33
|
+
/**
|
|
34
|
+
* When true, skips null values and draws a continuous line across gaps.
|
|
35
|
+
* When false, null values create gaps in the line.
|
|
36
|
+
* When omitted, falls back to the `connectNulls` defined on the series.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
connectNulls?: boolean;
|
|
33
40
|
};
|
|
@@ -44,17 +44,29 @@ describe('toScaledPoints', () => {
|
|
|
44
44
|
expect(points![points!.length - 1][0]).toBe(800);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
it('
|
|
47
|
+
it('keeps null values as holes by default so the line breaks', () => {
|
|
48
48
|
const data: (number | null)[] = [10, null, 30, 40, 50, 60, 70, 80, 90];
|
|
49
49
|
const xData = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
|
50
50
|
|
|
51
51
|
const points = toScaledPoints(data, xScale, yScale, xData);
|
|
52
52
|
|
|
53
53
|
expect(points).not.toBeNull();
|
|
54
|
-
expect(points).toHaveLength(
|
|
54
|
+
expect(points).toHaveLength(8);
|
|
55
|
+
expect(points![1][1]).toBeNull();
|
|
55
56
|
expect(points![points!.length - 1][0]).toBe((800 / 7) * 7);
|
|
56
57
|
});
|
|
57
58
|
|
|
59
|
+
it('skips null values when connectNulls is true', () => {
|
|
60
|
+
const data: (number | null)[] = [10, null, 30, 40, 50, 60, 70, 80, 90];
|
|
61
|
+
const xData = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
|
62
|
+
|
|
63
|
+
const points = toScaledPoints(data, xScale, yScale, xData, true);
|
|
64
|
+
|
|
65
|
+
expect(points).not.toBeNull();
|
|
66
|
+
expect(points).toHaveLength(7);
|
|
67
|
+
expect(points!.every((p) => p[1] !== null)).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
58
70
|
it('uses numeric xData values as the x input', () => {
|
|
59
71
|
const data = [10, 20, 30];
|
|
60
72
|
const customX = [0, 3.5, 7];
|
|
@@ -127,4 +139,26 @@ describe('buildLinePath', () => {
|
|
|
127
139
|
|
|
128
140
|
expect(path).toBe(buildLinePath(points));
|
|
129
141
|
});
|
|
142
|
+
|
|
143
|
+
it('breaks the path into segments at null holes', () => {
|
|
144
|
+
const gapped: [number, number | null][] = [
|
|
145
|
+
[0, 100],
|
|
146
|
+
[50, 80],
|
|
147
|
+
[100, null],
|
|
148
|
+
[150, 60],
|
|
149
|
+
[200, 40],
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const path = buildLinePath(gapped, 'linear');
|
|
153
|
+
|
|
154
|
+
expect(path).not.toBeNull();
|
|
155
|
+
expect(path!.match(/M/g)).toHaveLength(2);
|
|
156
|
+
expect(path).toBe('M0,100L50,80M150,60L200,40');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('draws a single continuous segment when there are no holes', () => {
|
|
160
|
+
const path = buildLinePath(points, 'linear');
|
|
161
|
+
|
|
162
|
+
expect(path!.match(/M/g)).toHaveLength(1);
|
|
163
|
+
});
|
|
130
164
|
});
|