@bashem/rn-charts 0.0.3 → 0.0.5
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/README.md +1 -1
- package/lib/module/skia/AreaChart/AreaChart.js +60 -51
- package/lib/module/skia/AreaChart/AreaChart.js.map +1 -1
- package/lib/module/skia/AreaChart/useAreaChart.js +18 -14
- package/lib/module/skia/AreaChart/useAreaChart.js.map +1 -1
- package/lib/module/skia/BarChart/BarChart.js +156 -69
- package/lib/module/skia/BarChart/BarChart.js.map +1 -1
- package/lib/module/skia/BarChart/useBarChart.js +53 -41
- package/lib/module/skia/BarChart/useBarChart.js.map +1 -1
- package/lib/module/skia/Common/HorizontalLabelView.js +121 -0
- package/lib/module/skia/Common/HorizontalLabelView.js.map +1 -0
- package/lib/module/skia/Common/VerticalLabelView.js +100 -0
- package/lib/module/skia/Common/VerticalLabelView.js.map +1 -0
- package/lib/module/skia/Common/useComponentLayout.js +13 -0
- package/lib/module/skia/Common/useComponentLayout.js.map +1 -0
- package/lib/module/skia/HeatMap/DateHeatMap.js +472 -0
- package/lib/module/skia/HeatMap/DateHeatMap.js.map +1 -0
- package/lib/module/skia/HeatMap/HeatMap.js +121 -33
- package/lib/module/skia/HeatMap/HeatMap.js.map +1 -1
- package/lib/module/skia/HeatMap/useHeatMap.js +132 -48
- package/lib/module/skia/HeatMap/useHeatMap.js.map +1 -1
- package/lib/module/skia/PieChart/PieChart.js +21 -17
- package/lib/module/skia/PieChart/PieChart.js.map +1 -1
- package/lib/module/skia/PieChart/usePieChart.js +241 -30
- package/lib/module/skia/PieChart/usePieChart.js.map +1 -1
- package/lib/module/skia/Popup.js +7 -5
- package/lib/module/skia/Popup.js.map +1 -1
- package/lib/module/skia/common.js +4 -41
- package/lib/module/skia/common.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/skia/AreaChart/AreaChart.d.ts +15 -3
- package/lib/typescript/src/skia/AreaChart/AreaChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/AreaChart/useAreaChart.d.ts +13 -14
- package/lib/typescript/src/skia/AreaChart/useAreaChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/BarChart/BarChart.d.ts +16 -2
- package/lib/typescript/src/skia/BarChart/BarChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/BarChart/useBarChart.d.ts +17 -5
- package/lib/typescript/src/skia/BarChart/useBarChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/Common/HorizontalLabelView.d.ts +33 -0
- package/lib/typescript/src/skia/Common/HorizontalLabelView.d.ts.map +1 -0
- package/lib/typescript/src/skia/Common/VerticalLabelView.d.ts +24 -0
- package/lib/typescript/src/skia/Common/VerticalLabelView.d.ts.map +1 -0
- package/lib/typescript/src/skia/Common/useComponentLayout.d.ts +4 -0
- package/lib/typescript/src/skia/Common/useComponentLayout.d.ts.map +1 -0
- package/lib/typescript/src/skia/HeatMap/DateHeatMap.d.ts +1 -0
- package/lib/typescript/src/skia/HeatMap/DateHeatMap.d.ts.map +1 -0
- package/lib/typescript/src/skia/HeatMap/HeatMap.d.ts +28 -12
- package/lib/typescript/src/skia/HeatMap/HeatMap.d.ts.map +1 -1
- package/lib/typescript/src/skia/HeatMap/useHeatMap.d.ts +15 -4
- package/lib/typescript/src/skia/HeatMap/useHeatMap.d.ts.map +1 -1
- package/lib/typescript/src/skia/PieChart/PieChart.d.ts +5 -1
- package/lib/typescript/src/skia/PieChart/PieChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/PieChart/usePieChart.d.ts +6 -1
- package/lib/typescript/src/skia/PieChart/usePieChart.d.ts.map +1 -1
- package/lib/typescript/src/skia/Popup.d.ts.map +1 -1
- package/lib/typescript/src/skia/common.d.ts +3 -10
- package/lib/typescript/src/skia/common.d.ts.map +1 -1
- package/package.json +11 -8
- package/src/index.tsx +6 -4
- package/src/skia/AreaChart/AreaChart.tsx +85 -62
- package/src/skia/AreaChart/useAreaChart.ts +25 -26
- package/src/skia/BarChart/BarChart.tsx +163 -95
- package/src/skia/BarChart/useBarChart.ts +55 -44
- package/src/skia/Common/HorizontalLabelView.tsx +153 -0
- package/src/skia/Common/VerticalLabelView.tsx +113 -0
- package/src/skia/Common/useComponentLayout.ts +14 -0
- package/src/skia/HeatMap/DateHeatMap.tsx +470 -0
- package/src/skia/HeatMap/HeatMap.tsx +168 -54
- package/src/skia/HeatMap/useHeatMap.ts +139 -65
- package/src/skia/PieChart/PieChart.tsx +16 -11
- package/src/skia/PieChart/usePieChart.ts +316 -66
- package/src/skia/Popup.tsx +38 -36
- package/src/skia/common.ts +8 -46
- package/lib/module/skia/Common/VerticalLabel.js +0 -73
- package/lib/module/skia/Common/VerticalLabel.js.map +0 -1
- package/lib/typescript/src/skia/Common/VerticalLabel.d.ts +0 -17
- package/lib/typescript/src/skia/Common/VerticalLabel.d.ts.map +0 -1
- package/src/skia/Common/VerticalLabel.tsx +0 -91
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
// AreaChart.tsx
|
|
2
|
-
import { Canvas, Circle, Group, Path
|
|
2
|
+
import { Canvas, Circle, Group, Path } from '@shopify/react-native-skia';
|
|
3
3
|
import { type CommonStyle } from '../common';
|
|
4
|
-
import VerticalLabel from '../Common/VerticalLabel';
|
|
5
4
|
import { View } from 'react-native';
|
|
6
|
-
import useAreaChart
|
|
5
|
+
import useAreaChart from './useAreaChart';
|
|
7
6
|
import { useState } from 'react';
|
|
8
7
|
import { lighten } from '../../util/colors';
|
|
9
8
|
import Popup, { type PopupStyle } from '../Popup';
|
|
9
|
+
import VerticalLabelView, { type VerticalLabelStyle } from "../Common/VerticalLabelView";
|
|
10
|
+
import HorizontalLabelView, { type HorizontalLabelStyle } from "../Common/HorizontalLabelView";
|
|
10
11
|
|
|
11
|
-
export interface
|
|
12
|
+
export interface AreaData {
|
|
13
|
+
values: number[];
|
|
14
|
+
label?: string;
|
|
15
|
+
color?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AreaChartStyle extends CommonStyle, VerticalLabelStyle {
|
|
12
19
|
width: number;
|
|
13
20
|
height: number;
|
|
14
21
|
showPoints?: boolean;
|
|
15
22
|
pointRadius?: number;
|
|
16
23
|
lightenPointsBy?: number;
|
|
24
|
+
strokeWidth?: number;
|
|
25
|
+
verticalLabelStyle?: VerticalLabelStyle;
|
|
26
|
+
horizontalLabelStyle?: HorizontalLabelStyle;
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
export interface AreaChartProps {
|
|
@@ -21,35 +31,40 @@ export interface AreaChartProps {
|
|
|
21
31
|
minValue?: number;
|
|
22
32
|
maxValue?: number;
|
|
23
33
|
xLabels?: string[];
|
|
34
|
+
yLabels: number[];
|
|
35
|
+
yLabelView?: (percentage: number, min: number, max: number) => JSX.Element;
|
|
36
|
+
xLabelView?: (label?: string) => JSX.Element;
|
|
24
37
|
style?: AreaChartStyle;
|
|
25
|
-
popupStyle: PopupStyle<{ rowIndex: number; colIndex: number; value: number }>;
|
|
38
|
+
popupStyle: PopupStyle<{ rowIndex: number; colIndex: number; value: number; }>;
|
|
26
39
|
}
|
|
27
40
|
|
|
28
|
-
function AreaChart(props: AreaChartProps) {
|
|
41
|
+
function AreaChart({ xLabelView, yLabelView, ...props }: AreaChartProps) {
|
|
29
42
|
const {
|
|
30
43
|
minValue,
|
|
31
44
|
maxValue,
|
|
32
45
|
canvasHeight,
|
|
33
46
|
areaCanvasHeight,
|
|
34
|
-
|
|
47
|
+
verticalLabelWidth,
|
|
48
|
+
verticalLabelStrokeWidth,
|
|
49
|
+
yLabels,
|
|
50
|
+
setVerticalLabelWidth,
|
|
51
|
+
setHorizontalLabelHeight,
|
|
35
52
|
chartWidth,
|
|
36
53
|
paths,
|
|
37
54
|
xLabelsData,
|
|
38
55
|
paddingLeft,
|
|
39
56
|
paddingTop,
|
|
40
57
|
paddingHorizontal,
|
|
41
|
-
font,
|
|
42
58
|
touchLine,
|
|
43
59
|
touchHandler,
|
|
44
60
|
} = useAreaChart(props);
|
|
45
61
|
const { style, popupStyle } = props;
|
|
46
62
|
|
|
47
63
|
const [viewOffset, setViewOffset] = useState({ x: 0, y: 0 });
|
|
48
|
-
console.log('AreaChart render', { touchLine });
|
|
49
64
|
|
|
50
65
|
return (
|
|
51
66
|
<View
|
|
52
|
-
style={[style, { flexDirection:
|
|
67
|
+
style={[style, { flexDirection: "column" }]}
|
|
53
68
|
ref={(view) => {
|
|
54
69
|
view?.measureInWindow((fx, fy) => {
|
|
55
70
|
setViewOffset((prev) => {
|
|
@@ -61,57 +76,65 @@ function AreaChart(props: AreaChartProps) {
|
|
|
61
76
|
});
|
|
62
77
|
}}
|
|
63
78
|
>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
height: canvasHeight,
|
|
79
|
-
}}
|
|
80
|
-
onTouchStart={(event) =>
|
|
81
|
-
touchHandler(event.nativeEvent.locationX, event.nativeEvent.locationY)
|
|
79
|
+
<View style={{ flexDirection: "row" }}>
|
|
80
|
+
{yLabelView &&
|
|
81
|
+
<VerticalLabelView
|
|
82
|
+
onLayout={(event) => {
|
|
83
|
+
setVerticalLabelWidth(event.nativeEvent.layout.width);
|
|
84
|
+
}}
|
|
85
|
+
labelPercentages={yLabels}
|
|
86
|
+
styles={{
|
|
87
|
+
height: areaCanvasHeight,
|
|
88
|
+
verticalLabelStyle: props.style?.verticalLabelStyle,
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{percentage => yLabelView(percentage, minValue, maxValue)}
|
|
92
|
+
</VerticalLabelView>
|
|
82
93
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
94
|
+
|
|
95
|
+
<Canvas
|
|
96
|
+
style={{
|
|
97
|
+
width: chartWidth,
|
|
98
|
+
height: areaCanvasHeight,
|
|
99
|
+
}}
|
|
100
|
+
onTouchStart={(event) =>
|
|
101
|
+
touchHandler(event.nativeEvent.locationX, event.nativeEvent.locationY)
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
{paths.map(({ path, points, color }, index) => {
|
|
105
|
+
return (
|
|
106
|
+
<Group key={index}>
|
|
107
|
+
<Path path={path} color={color} />
|
|
108
|
+
{style?.showPoints &&
|
|
109
|
+
color &&
|
|
110
|
+
points.map((points) => (
|
|
111
|
+
<Circle
|
|
112
|
+
key={`${points.x}-${points.y}`}
|
|
113
|
+
cx={points.x}
|
|
114
|
+
cy={points.y}
|
|
115
|
+
r={style?.pointRadius ?? 3}
|
|
116
|
+
color={lighten(color, style?.lightenPointsBy ?? 0.3)}
|
|
117
|
+
/>
|
|
118
|
+
))}
|
|
119
|
+
</Group>
|
|
120
|
+
);
|
|
121
|
+
})}
|
|
122
|
+
</Canvas>
|
|
123
|
+
</View>
|
|
124
|
+
{
|
|
125
|
+
xLabelView &&
|
|
126
|
+
<HorizontalLabelView
|
|
127
|
+
labels={xLabelsData.map(labelData => labelData.label)}
|
|
128
|
+
positions={xLabelsData.map(labelData => labelData.xPosition)}
|
|
129
|
+
style={{
|
|
130
|
+
left: verticalLabelWidth,
|
|
131
|
+
width: chartWidth,
|
|
132
|
+
}}
|
|
133
|
+
onLayout={(event) => setHorizontalLabelHeight(event.nativeEvent.layout.height)}
|
|
134
|
+
>
|
|
135
|
+
{label => xLabelView(label.toString())}
|
|
136
|
+
</HorizontalLabelView>
|
|
137
|
+
}
|
|
115
138
|
{touchLine && (
|
|
116
139
|
<Popup
|
|
117
140
|
popupData={touchLine.y.map((y, index) => ({
|
|
@@ -123,11 +146,11 @@ function AreaChart(props: AreaChartProps) {
|
|
|
123
146
|
value: touchLine.values[index]!,
|
|
124
147
|
},
|
|
125
148
|
}))}
|
|
126
|
-
totalWidth={chartWidth +
|
|
149
|
+
totalWidth={chartWidth + verticalLabelWidth + paddingHorizontal}
|
|
127
150
|
totalHeight={canvasHeight}
|
|
128
151
|
touchHandler={(x, y) => {
|
|
129
152
|
console.log('Popup touchHandler', x, y);
|
|
130
|
-
touchHandler(x -
|
|
153
|
+
touchHandler(x - verticalLabelWidth - paddingLeft, y - paddingTop);
|
|
131
154
|
}}
|
|
132
155
|
viewOffset={viewOffset}
|
|
133
156
|
popupStyle={popupStyle}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { Skia, type SkPath } from "@shopify/react-native-skia";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import type { AreaChartProps
|
|
3
|
+
import { getPaddings } from "../common";
|
|
4
|
+
import type { AreaChartProps } from "./AreaChart";
|
|
5
5
|
import { isDefined } from "../../util/util";
|
|
6
6
|
|
|
7
|
-
export interface AreaData {
|
|
8
|
-
values: number[];
|
|
9
|
-
label?: string;
|
|
10
|
-
color?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
7
|
interface Point {
|
|
14
8
|
x: number;
|
|
15
9
|
y: number;
|
|
@@ -23,11 +17,6 @@ export interface PathData {
|
|
|
23
17
|
label?: string;
|
|
24
18
|
}
|
|
25
19
|
|
|
26
|
-
export interface XLable {
|
|
27
|
-
label: string;
|
|
28
|
-
xPosition: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
20
|
interface TouchLine {
|
|
32
21
|
col: number;
|
|
33
22
|
x: number;
|
|
@@ -35,12 +24,18 @@ interface TouchLine {
|
|
|
35
24
|
values: number[];
|
|
36
25
|
}
|
|
37
26
|
|
|
27
|
+
interface XLabel {
|
|
28
|
+
label: string;
|
|
29
|
+
xPosition: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
38
32
|
function useAreaChart({
|
|
39
33
|
data,
|
|
40
34
|
xLabels,
|
|
41
35
|
maxValue,
|
|
42
36
|
minValue,
|
|
43
37
|
style,
|
|
38
|
+
yLabels,
|
|
44
39
|
}: AreaChartProps
|
|
45
40
|
) {
|
|
46
41
|
|
|
@@ -54,12 +49,12 @@ function useAreaChart({
|
|
|
54
49
|
} = getPaddings(style);
|
|
55
50
|
|
|
56
51
|
const canvasHeight = height - paddingVertical;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
52
|
+
const [verticalLabelWidth, setVerticalLabelWidth] = useState(style?.verticalLabelStyle?.width ?? 0);
|
|
53
|
+
const strokeWidth = style?.strokeWidth ?? 2
|
|
54
|
+
const verticalLabelStrokeWidth = style?.verticalLabelStyle?.strokeWidth ?? strokeWidth
|
|
55
|
+
const chartWidth = width - verticalLabelWidth - paddingHorizontal;
|
|
56
|
+
const [horizontalLabelHeight, setHorizontalLabelHeight] = useState(20)
|
|
57
|
+
const areaCanvasHeight = canvasHeight - horizontalLabelHeight;
|
|
63
58
|
|
|
64
59
|
const { maxValueCalculated, minValueCalculated } = useMemo(() => {
|
|
65
60
|
if (isDefined(maxValue) && isDefined(minValue)) {
|
|
@@ -97,7 +92,7 @@ function useAreaChart({
|
|
|
97
92
|
const xPos = i * stepX;
|
|
98
93
|
const yPos = Math.max(0, areaCanvasHeight - ((y - minValueCalculated) / (maxValueCalculated - minValueCalculated)) * areaCanvasHeight);
|
|
99
94
|
|
|
100
|
-
points.push({
|
|
95
|
+
points.push({x: xPos, y: yPos });
|
|
101
96
|
values.push(y);
|
|
102
97
|
p.lineTo(xPos, yPos);
|
|
103
98
|
});
|
|
@@ -117,7 +112,7 @@ function useAreaChart({
|
|
|
117
112
|
return pathData;
|
|
118
113
|
}, [data, chartWidth, maxValueCalculated, minValueCalculated]);
|
|
119
114
|
|
|
120
|
-
const xLabelsData:
|
|
115
|
+
const xLabelsData: XLabel[] = useMemo(() => {
|
|
121
116
|
if (!xLabels || xLabels.length === 0) {
|
|
122
117
|
return [];
|
|
123
118
|
}
|
|
@@ -126,7 +121,7 @@ function useAreaChart({
|
|
|
126
121
|
const labels = xLabels.map((label, i) => {
|
|
127
122
|
return {
|
|
128
123
|
label,
|
|
129
|
-
xPosition: i * stepX
|
|
124
|
+
xPosition: i * stepX,
|
|
130
125
|
};
|
|
131
126
|
});
|
|
132
127
|
return labels;
|
|
@@ -152,7 +147,7 @@ function useAreaChart({
|
|
|
152
147
|
|
|
153
148
|
setTouchLine({
|
|
154
149
|
col: xIndex,
|
|
155
|
-
x: xIndex * stepX,
|
|
150
|
+
x: xIndex * stepX + verticalLabelWidth,
|
|
156
151
|
y: yValues,
|
|
157
152
|
values
|
|
158
153
|
});
|
|
@@ -166,12 +161,16 @@ function useAreaChart({
|
|
|
166
161
|
paddingTop,
|
|
167
162
|
paddingHorizontal,
|
|
168
163
|
areaCanvasHeight,
|
|
169
|
-
|
|
164
|
+
strokeWidth,
|
|
165
|
+
verticalLabelStrokeWidth,
|
|
166
|
+
yLabels,
|
|
167
|
+
verticalLabelWidth,
|
|
168
|
+
setVerticalLabelWidth,
|
|
170
169
|
maxValue: maxValueCalculated,
|
|
171
170
|
minValue: minValueCalculated,
|
|
172
171
|
xLabelsData,
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
horizontalLabelHeight,
|
|
173
|
+
setHorizontalLabelHeight,
|
|
175
174
|
touchHandler,
|
|
176
175
|
touchLine
|
|
177
176
|
};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import {
|
|
4
4
|
GestureDetector,
|
|
5
5
|
Gesture,
|
|
6
6
|
GestureHandlerRootView,
|
|
7
|
+
ScrollView,
|
|
7
8
|
} from 'react-native-gesture-handler';
|
|
9
|
+
import { Canvas, Rect, vec, type SkHostRect, LinearGradient, Group, rect as SKRect, type Transforms3d } from '@shopify/react-native-skia';
|
|
8
10
|
|
|
9
|
-
import { Canvas, Rect, Text, vec, Line } from '@shopify/react-native-skia';
|
|
10
11
|
import { type CommonStyle } from '../common';
|
|
11
12
|
import useBarChart from './useBarChart';
|
|
12
|
-
import VerticalLabel from '../Common/VerticalLabel';
|
|
13
13
|
import Popup, { type PopupStyle } from '../Popup';
|
|
14
|
+
import VerticalLabelView, { type VerticalLabelStyle } from "../Common/VerticalLabelView";
|
|
15
|
+
import HorizontalLabelView, { type HorizontalLabelStyle } from "../Common/HorizontalLabelView";
|
|
16
|
+
import { useDerivedValue } from "react-native-reanimated";
|
|
14
17
|
|
|
15
18
|
export interface StackValue {
|
|
16
19
|
value: number;
|
|
@@ -30,22 +33,32 @@ export interface BarChartStyle extends CommonStyle {
|
|
|
30
33
|
barSpacing?: number;
|
|
31
34
|
firstBarLeadingSpacing?: number;
|
|
32
35
|
lastBarTrailingSpacing?: number;
|
|
36
|
+
verticalLabelStyle?: VerticalLabelStyle;
|
|
37
|
+
horizontalLabelStyle?: HorizontalLabelStyle;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
export interface BarChartProps {
|
|
36
41
|
data: BarData[];
|
|
37
|
-
colors?: Record<string, string>;
|
|
42
|
+
colors?: Record<string, string | string[]>;
|
|
43
|
+
yLabels: number[];
|
|
44
|
+
yLabelView?: (percentage: number, min: number, max: number) => React.JSX.Element;
|
|
45
|
+
yLabelSkiaView?: (percentage: number, yPosition: number) => React.JSX.Element | undefined;
|
|
46
|
+
xLabelView?: (label?: string) => React.JSX.Element;
|
|
47
|
+
onSelectBarView?: (stackValue: StackValue, xLabel?: string) => React.JSX.Element | undefined;
|
|
48
|
+
barSkiaView?: (rect: SkHostRect, stackValue: StackValue, xLabel?: string) => React.JSX.Element | undefined;
|
|
49
|
+
onSelectBarSkiaView?: (rect: SkHostRect, stackValue: StackValue, xLabel?: string) => React.JSX.Element | undefined;
|
|
38
50
|
maxValue?: number;
|
|
39
51
|
minValue?: number;
|
|
52
|
+
overscanRatio?: number;
|
|
40
53
|
popupStyle?: PopupStyle<StackValue>;
|
|
41
54
|
style?: BarChartStyle;
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
function BarChart(props: BarChartProps) {
|
|
57
|
+
function BarChart({ xLabelView, yLabelView, yLabelSkiaView, barSkiaView, onSelectBarSkiaView, onSelectBarView, ...props }: BarChartProps) {
|
|
45
58
|
const {
|
|
46
59
|
maxValueCalculated,
|
|
47
60
|
minValueCalculated,
|
|
48
|
-
|
|
61
|
+
yLabels,
|
|
49
62
|
canvasWidth,
|
|
50
63
|
paddingRight,
|
|
51
64
|
paddingLeft,
|
|
@@ -53,34 +66,54 @@ function BarChart(props: BarChartProps) {
|
|
|
53
66
|
paddingTop,
|
|
54
67
|
rectangles,
|
|
55
68
|
verticalLabelWidth,
|
|
69
|
+
setVerticalLabelWidth,
|
|
56
70
|
chartHeight,
|
|
57
|
-
strokeWidth,
|
|
58
71
|
tooltip,
|
|
59
|
-
|
|
60
|
-
font,
|
|
72
|
+
setBottomLabelHeight,
|
|
61
73
|
onScroll,
|
|
62
74
|
touchHandler,
|
|
63
75
|
totalHeight,
|
|
64
76
|
totalWidth,
|
|
77
|
+
horizontalStrokeWidth,
|
|
78
|
+
startX,
|
|
79
|
+
offset
|
|
65
80
|
} = useBarChart(props);
|
|
66
81
|
|
|
67
|
-
const
|
|
68
|
-
|
|
82
|
+
const panGestureRef = useRef(Gesture.Pan());
|
|
83
|
+
|
|
84
|
+
const panGesture = Gesture.Pan()
|
|
69
85
|
.onChange((event) => {
|
|
70
86
|
onScroll(-event.changeX);
|
|
87
|
+
})
|
|
88
|
+
.withRef(panGestureRef);
|
|
89
|
+
|
|
90
|
+
const tapGesture = Gesture.Tap()
|
|
91
|
+
.runOnJS(true)
|
|
92
|
+
.onStart((event) => {
|
|
93
|
+
touchHandler(event.x, event.y);
|
|
71
94
|
});
|
|
72
95
|
|
|
73
96
|
const [viewOffset, setViewOffset] = useState({ x: 0, y: 0 });
|
|
97
|
+
const canvasGestures = Gesture.Exclusive(panGesture, tapGesture);
|
|
98
|
+
const onSelectBarViewMemo = useMemo(() => {
|
|
99
|
+
if (tooltip === undefined) { return undefined; }
|
|
100
|
+
return onSelectBarView?.(tooltip.data, tooltip.xLabel);
|
|
101
|
+
}, [tooltip]);
|
|
102
|
+
const onSelectBarSkiaViewMemo = useMemo(() => {
|
|
103
|
+
if (tooltip === undefined) { return undefined; }
|
|
104
|
+
return onSelectBarSkiaView?.(tooltip.rect, tooltip.data, tooltip.xLabel);
|
|
105
|
+
}, [tooltip]);
|
|
106
|
+
const canvasGroupTranslate = useDerivedValue<Transforms3d>(() => [{ translateX: -offset.value }], []);
|
|
74
107
|
|
|
75
108
|
return (
|
|
76
109
|
<GestureHandlerRootView>
|
|
77
110
|
<View
|
|
78
111
|
style={{
|
|
79
112
|
width: totalWidth,
|
|
80
|
-
flexDirection: '
|
|
113
|
+
flexDirection: 'column',
|
|
81
114
|
backgroundColor: props.style?.backgroundColor,
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
paddingStart: paddingLeft,
|
|
116
|
+
paddingEnd: paddingRight,
|
|
84
117
|
paddingTop: paddingTop,
|
|
85
118
|
paddingBottom: paddingBottom,
|
|
86
119
|
}}
|
|
@@ -95,95 +128,130 @@ function BarChart(props: BarChartProps) {
|
|
|
95
128
|
});
|
|
96
129
|
}}
|
|
97
130
|
>
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
<View style={{ flexDirection: "row", height: chartHeight, padding: 0 }}>
|
|
132
|
+
{
|
|
133
|
+
(yLabelView || yLabelSkiaView) && (<VerticalLabelView
|
|
134
|
+
onLayout={(event) => {
|
|
135
|
+
setVerticalLabelWidth(event.nativeEvent.layout.width);
|
|
136
|
+
}}
|
|
137
|
+
labelPercentages={yLabels}
|
|
138
|
+
styles={{
|
|
139
|
+
height: chartHeight + horizontalStrokeWidth,
|
|
140
|
+
verticalLabelStyle: props.style?.verticalLabelStyle,
|
|
141
|
+
}}
|
|
142
|
+
labelSkiaView={(percentage, yPosition) => yLabelSkiaView?.(percentage, yPosition)}
|
|
143
|
+
>
|
|
144
|
+
{percentage => yLabelView?.(percentage, minValueCalculated, maxValueCalculated)}
|
|
145
|
+
</VerticalLabelView>)
|
|
146
|
+
}
|
|
147
|
+
<ScrollView horizontal={true} simultaneousHandlers={panGestureRef} style={{ padding: 0 }}>
|
|
148
|
+
<GestureDetector gesture={canvasGestures}>
|
|
149
|
+
<Canvas
|
|
150
|
+
style={{
|
|
151
|
+
width: canvasWidth,
|
|
152
|
+
height: chartHeight,
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
<Group transform={canvasGroupTranslate}>
|
|
156
|
+
{rectangles.map((bar, xIndex) => {
|
|
157
|
+
if (bar.bars.length === 0) return null;
|
|
158
|
+
return (
|
|
159
|
+
<Group key={xIndex}>
|
|
160
|
+
{bar.bars.map(({ rect, stackValue }, yIndex) => {
|
|
161
|
+
let skiaView = barSkiaView?.(SKRect(rect.x, rect.y, rect.width, rect.height), stackValue, bar.label);
|
|
162
|
+
if (skiaView !== undefined) { return <Group key={xIndex + "-" + yIndex}>{skiaView}</Group>; }
|
|
163
|
+
let currentData = props.data[xIndex]!.values[yIndex]!;
|
|
164
|
+
let color =
|
|
165
|
+
props?.colors?.[currentData.id ?? currentData.label];
|
|
166
|
+
return (
|
|
167
|
+
<Rect
|
|
168
|
+
key={xIndex + '-' + yIndex}
|
|
169
|
+
x={rect.x}
|
|
170
|
+
y={rect.y}
|
|
171
|
+
width={rect.width}
|
|
172
|
+
height={rect.height}
|
|
173
|
+
color={Array.isArray(color) ? undefined : color}
|
|
174
|
+
>
|
|
175
|
+
{Array.isArray(color) && (
|
|
176
|
+
<LinearGradient
|
|
177
|
+
start={vec(rect.x, rect.y)}
|
|
178
|
+
end={vec(rect.x + rect.width, rect.y + rect.height)}
|
|
179
|
+
colors={color}
|
|
180
|
+
/>
|
|
181
|
+
)}
|
|
182
|
+
</Rect>
|
|
183
|
+
);
|
|
184
|
+
})}
|
|
185
|
+
</Group>
|
|
186
|
+
);
|
|
187
|
+
})}
|
|
188
|
+
</Group>
|
|
189
|
+
{onSelectBarSkiaViewMemo}
|
|
190
|
+
</Canvas>
|
|
191
|
+
</GestureDetector>
|
|
192
|
+
</ScrollView>
|
|
193
|
+
</View>
|
|
194
|
+
{
|
|
195
|
+
xLabelView &&
|
|
196
|
+
<HorizontalLabelView
|
|
197
|
+
labels={rectangles.map(bar => bar.label)}
|
|
198
|
+
positions={rectangles.map(bar => bar.x)}
|
|
199
|
+
transform={canvasGroupTranslate}
|
|
200
|
+
xOffset={offset}
|
|
110
201
|
style={{
|
|
202
|
+
left: verticalLabelWidth,
|
|
111
203
|
width: canvasWidth,
|
|
112
|
-
|
|
113
|
-
paddingRight: 50,
|
|
114
|
-
backgroundColor: 'red',
|
|
204
|
+
horizontalLabelStyle: props.style?.horizontalLabelStyle
|
|
115
205
|
}}
|
|
116
|
-
|
|
117
|
-
touchHandler(
|
|
118
|
-
event.nativeEvent.locationX,
|
|
119
|
-
event.nativeEvent.locationY
|
|
120
|
-
)
|
|
121
|
-
}
|
|
206
|
+
onLayout={(event) => setBottomLabelHeight(event.nativeEvent.layout.height)}
|
|
122
207
|
>
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
p2={vec(canvasWidth, chartHeight)}
|
|
127
|
-
color="white"
|
|
128
|
-
strokeWidth={strokeWidth}
|
|
129
|
-
/>
|
|
130
|
-
|
|
131
|
-
{/* Bars */}
|
|
132
|
-
|
|
133
|
-
{rectangles.map((bar, xIndex) => {
|
|
134
|
-
if (bar.bars.length === 0) return null;
|
|
135
|
-
return (
|
|
136
|
-
<Fragment key={xIndex}>
|
|
137
|
-
<Text
|
|
138
|
-
x={bar.bars[0]!.x}
|
|
139
|
-
y={
|
|
140
|
-
chartHeight +
|
|
141
|
-
font.getSize() +
|
|
142
|
-
(bottomLabelHeight - font.getSize()) / 2
|
|
143
|
-
}
|
|
144
|
-
text={bar.label ?? ''}
|
|
145
|
-
color="white"
|
|
146
|
-
font={font}
|
|
147
|
-
/>
|
|
148
|
-
{bar.bars.map((item, yIndex) => {
|
|
149
|
-
let currentData = props.data[xIndex]!.values[yIndex]!;
|
|
150
|
-
let color =
|
|
151
|
-
props?.colors?.[currentData.id ?? currentData.label] ||
|
|
152
|
-
'#4A90E2';
|
|
153
|
-
return (
|
|
154
|
-
<Rect
|
|
155
|
-
key={xIndex + '-' + yIndex}
|
|
156
|
-
x={item.x}
|
|
157
|
-
y={item.y}
|
|
158
|
-
width={item.width}
|
|
159
|
-
height={item.height}
|
|
160
|
-
color={color}
|
|
161
|
-
/>
|
|
162
|
-
);
|
|
163
|
-
})}
|
|
164
|
-
</Fragment>
|
|
165
|
-
);
|
|
166
|
-
})}
|
|
167
|
-
</Canvas>
|
|
168
|
-
</GestureDetector>
|
|
208
|
+
{(_index, data) => xLabelView(data)}
|
|
209
|
+
</HorizontalLabelView>
|
|
210
|
+
}
|
|
169
211
|
{tooltip && (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
212
|
+
<>
|
|
213
|
+
{onSelectBarViewMemo &&
|
|
214
|
+
<View
|
|
215
|
+
style={{
|
|
216
|
+
position: "absolute",
|
|
217
|
+
top: tooltip.rect.y + paddingTop,
|
|
218
|
+
left: verticalLabelWidth + paddingLeft + Math.max(0, tooltip.rect.x),
|
|
219
|
+
width: tooltip.rect.width,
|
|
220
|
+
height: tooltip.rect.height,
|
|
221
|
+
overflow: "hidden",
|
|
222
|
+
}}>
|
|
223
|
+
<View
|
|
224
|
+
style={{
|
|
225
|
+
position: "relative",
|
|
226
|
+
top: 0,
|
|
227
|
+
left: -Math.max(0, -tooltip.rect.x),
|
|
228
|
+
width: tooltip.rect.width,
|
|
229
|
+
height: tooltip.rect.height,
|
|
230
|
+
}}
|
|
231
|
+
>
|
|
232
|
+
{onSelectBarViewMemo}
|
|
233
|
+
</View>
|
|
234
|
+
</View>
|
|
181
235
|
}
|
|
182
|
-
|
|
183
|
-
|
|
236
|
+
<Popup
|
|
237
|
+
popupData={{
|
|
238
|
+
x: tooltip.centerX,
|
|
239
|
+
y: tooltip.centerY,
|
|
240
|
+
data: tooltip.data,
|
|
241
|
+
}}
|
|
242
|
+
popupStyle={props.popupStyle}
|
|
243
|
+
totalWidth={totalWidth}
|
|
244
|
+
totalHeight={totalHeight}
|
|
245
|
+
touchHandler={(x, y) => {
|
|
246
|
+
touchHandler(x - verticalLabelWidth - paddingLeft, y);
|
|
247
|
+
}}
|
|
248
|
+
onTouchOutside={() => touchHandler(-1, -1)}
|
|
249
|
+
viewOffset={viewOffset}
|
|
250
|
+
/>
|
|
251
|
+
</>
|
|
184
252
|
)}
|
|
185
253
|
</View>
|
|
186
|
-
</GestureHandlerRootView>
|
|
254
|
+
</GestureHandlerRootView >
|
|
187
255
|
);
|
|
188
256
|
}
|
|
189
257
|
|