@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,18 +1,175 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import type { PieChartProps,
|
|
3
|
-
import { rect, Skia } from "@shopify/react-native-skia";
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import type { PieChartProps, PopupData } from "./PieChart";
|
|
3
|
+
import { rect, Skia, type SkPath } from "@shopify/react-native-skia";
|
|
4
4
|
import { getRandomRGBColor } from "../common";
|
|
5
5
|
|
|
6
|
-
function
|
|
6
|
+
function degreesToRadians(degrees: number): number {
|
|
7
7
|
return (degrees * Math.PI) / 180;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function ypoint(angle: number, radius: number, cy: number): number {
|
|
11
|
-
return cy - radius * Math.sin(
|
|
11
|
+
return cy - radius * Math.sin(degreesToRadians(angle));
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function xpoint(angle: number, radius: number, cx: number): number {
|
|
15
|
-
return cx - radius * Math.cos(
|
|
15
|
+
return cx - radius * Math.cos(degreesToRadians(angle));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Point = { x: number; y: number; };
|
|
19
|
+
|
|
20
|
+
function euclideanDistance(dx: number, dy: number) {
|
|
21
|
+
return Math.hypot(dx, dy);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getProportionPoint(
|
|
25
|
+
point: Point,
|
|
26
|
+
segment: number,
|
|
27
|
+
length: number,
|
|
28
|
+
dx: number,
|
|
29
|
+
dy: number
|
|
30
|
+
): Point {
|
|
31
|
+
const factor = segment / length;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
x: point.x - dx * factor,
|
|
35
|
+
y: point.y - dy * factor,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function angle(cx: number, cy: number, x: number, y: number) {
|
|
40
|
+
return (Math.atan2(y - cy, x - cx) * (180 / Math.PI) + 360) % 360;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// https://stackoverflow.com/a/24780108/13509919
|
|
44
|
+
function drawRoundedCorner(
|
|
45
|
+
path: SkPath,
|
|
46
|
+
angularPoint: Point,
|
|
47
|
+
p1: Point,
|
|
48
|
+
p2: Point,
|
|
49
|
+
radius: number
|
|
50
|
+
) {
|
|
51
|
+
// Vector 1
|
|
52
|
+
const dx1 = angularPoint.x - p1.x;
|
|
53
|
+
const dy1 = angularPoint.y - p1.y;
|
|
54
|
+
|
|
55
|
+
// Vector 2
|
|
56
|
+
const dx2 = angularPoint.x - p2.x;
|
|
57
|
+
const dy2 = angularPoint.y - p2.y;
|
|
58
|
+
|
|
59
|
+
// Angle / 2
|
|
60
|
+
let angleHalf =
|
|
61
|
+
(Math.atan2(dy1, dx1) - Math.atan2(dy2, dx2)) / 2;
|
|
62
|
+
|
|
63
|
+
const tan = Math.abs(Math.tan(angleHalf));
|
|
64
|
+
if (tan === 0) return;
|
|
65
|
+
|
|
66
|
+
let segment = radius / tan;
|
|
67
|
+
|
|
68
|
+
// Lengths
|
|
69
|
+
const length1 = euclideanDistance(dx1, dy1);
|
|
70
|
+
const length2 = euclideanDistance(dx2, dy2);
|
|
71
|
+
|
|
72
|
+
const length = Math.min(length1, length2);
|
|
73
|
+
|
|
74
|
+
// Clamp
|
|
75
|
+
if (segment > length) {
|
|
76
|
+
segment = length;
|
|
77
|
+
radius = length * tan;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Trimmed points
|
|
81
|
+
const p1Cross = getProportionPoint(
|
|
82
|
+
angularPoint,
|
|
83
|
+
segment,
|
|
84
|
+
length1,
|
|
85
|
+
dx1,
|
|
86
|
+
dy1
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const p2Cross = getProportionPoint(
|
|
90
|
+
angularPoint,
|
|
91
|
+
segment,
|
|
92
|
+
length2,
|
|
93
|
+
dx2,
|
|
94
|
+
dy2
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Circle center
|
|
98
|
+
const dx = angularPoint.x * 2 - p1Cross.x - p2Cross.x;
|
|
99
|
+
const dy = angularPoint.y * 2 - p1Cross.y - p2Cross.y;
|
|
100
|
+
|
|
101
|
+
const L = euclideanDistance(dx, dy);
|
|
102
|
+
const d = Math.hypot(segment, radius);
|
|
103
|
+
|
|
104
|
+
const circlePoint = getProportionPoint(
|
|
105
|
+
angularPoint,
|
|
106
|
+
d,
|
|
107
|
+
L,
|
|
108
|
+
dx,
|
|
109
|
+
dy
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Angles (degrees for Skia)
|
|
113
|
+
let startAngle = angle(
|
|
114
|
+
circlePoint.x,
|
|
115
|
+
circlePoint.y,
|
|
116
|
+
p1Cross.x,
|
|
117
|
+
p1Cross.y
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
let endAngle = angle(
|
|
121
|
+
circlePoint.x,
|
|
122
|
+
circlePoint.y,
|
|
123
|
+
p2Cross.x,
|
|
124
|
+
p2Cross.y
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
let sweepAngle = endAngle - startAngle;
|
|
128
|
+
|
|
129
|
+
// Normalize
|
|
130
|
+
if (sweepAngle < 0) {
|
|
131
|
+
const temp = startAngle;
|
|
132
|
+
startAngle = endAngle;
|
|
133
|
+
sweepAngle = temp - endAngle;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (sweepAngle > 180) {
|
|
137
|
+
sweepAngle = 180 - sweepAngle;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
path.moveTo(p1Cross.x, p1Cross.y);
|
|
141
|
+
|
|
142
|
+
// arc
|
|
143
|
+
path.addArc(
|
|
144
|
+
rect(
|
|
145
|
+
circlePoint.x - radius,
|
|
146
|
+
circlePoint.y - radius,
|
|
147
|
+
radius * 2,
|
|
148
|
+
radius * 2
|
|
149
|
+
),
|
|
150
|
+
startAngle,
|
|
151
|
+
sweepAngle
|
|
152
|
+
);
|
|
153
|
+
path.lineTo(p1Cross.x, p1Cross.y);
|
|
154
|
+
path.close();
|
|
155
|
+
|
|
156
|
+
drawLines(path, [
|
|
157
|
+
{ x: p1.x, y: p1.y },
|
|
158
|
+
{ x: p1Cross.x, y: p1Cross.y },
|
|
159
|
+
{ x: p2Cross.x, y: p2Cross.y },
|
|
160
|
+
{ x: p2.x, y: p2.y },
|
|
161
|
+
{ x: p1.x, y: p1.y },
|
|
162
|
+
]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function drawLines(path: SkPath, points: Point[]) {
|
|
166
|
+
if (points.length === 0) return;
|
|
167
|
+
|
|
168
|
+
path.moveTo(points[0]!.x, points[0]!.y);
|
|
169
|
+
for (let i = 1; i < points.length; i++) {
|
|
170
|
+
path.lineTo(points[i]!.x, points[i]!.y);
|
|
171
|
+
}
|
|
172
|
+
path.close();
|
|
16
173
|
}
|
|
17
174
|
|
|
18
175
|
function getCircularPoints(
|
|
@@ -30,6 +187,9 @@ function getCircularPoints(
|
|
|
30
187
|
return [x1, y1, x2, y2];
|
|
31
188
|
}
|
|
32
189
|
|
|
190
|
+
// FIXME: Doesn't work innerradius is set to 0, need to investigate why. If it is set to a very small number, it works fine. COPILOT: This is a known issue in Skia when the path has self-intersection. Issue also occurs when rounding is set and innerRadius is small.
|
|
191
|
+
// QUICKFIX: If innerRadius is 0, we can set it to a very small number like 0.001 to avoid the issue.
|
|
192
|
+
// TODO: Verify the rounding of corners throughly
|
|
33
193
|
export function usePieChart({
|
|
34
194
|
slices,
|
|
35
195
|
style,
|
|
@@ -40,64 +200,151 @@ export function usePieChart({
|
|
|
40
200
|
|
|
41
201
|
const radius = style.radius ?? 150;
|
|
42
202
|
const diameter = radius * 2;
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
sweepAngle
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
203
|
+
const width = diameter + 30;
|
|
204
|
+
const height = width;
|
|
205
|
+
const innerRadius = Math.max(0.0001, style.innerRadius ?? 100);
|
|
206
|
+
const interSliceGap = style.interSliceGap ?? 20;
|
|
207
|
+
const cx = width / 2;
|
|
208
|
+
const cy = height / 2;
|
|
209
|
+
|
|
210
|
+
const { paths, total } = useMemo(() => {
|
|
211
|
+
const total = slices.reduce((sum, slice) => sum + slice.value, 0);
|
|
212
|
+
let startAngle = style.startAngle ?? 0;
|
|
213
|
+
|
|
214
|
+
const paths = slices.map(({ value, color, radius: givenRoundRadius = 0 }, index) => {
|
|
215
|
+
const sweepAngleTobeAdded = (value / total) * 360;
|
|
216
|
+
let currentStartAngle = startAngle;
|
|
217
|
+
let sweepAngle = sweepAngleTobeAdded;
|
|
218
|
+
startAngle += sweepAngleTobeAdded;
|
|
219
|
+
|
|
220
|
+
const outerCircleLength = 2 * Math.PI * radius;
|
|
221
|
+
const innerCircleLength = 2 * Math.PI * innerRadius;
|
|
222
|
+
const roundRadius = Math.min(givenRoundRadius, (radius - innerRadius) / 2, innerCircleLength / 2);
|
|
223
|
+
|
|
224
|
+
let angleReductionForGap = !interSliceGap ? 0 : (interSliceGap / outerCircleLength) * 360;
|
|
225
|
+
let angleReductionForRounding = (roundRadius / outerCircleLength) * 360 * 2;
|
|
226
|
+
let angleReductionForInnerCircle = (roundRadius / innerCircleLength) * 360 * 2;
|
|
227
|
+
|
|
228
|
+
currentStartAngle += angleReductionForGap / 2;
|
|
229
|
+
sweepAngle -= angleReductionForGap;
|
|
230
|
+
|
|
231
|
+
let innerCircleStartAngle = currentStartAngle;
|
|
232
|
+
let innerCircleSweepAngle = sweepAngle;
|
|
233
|
+
|
|
234
|
+
const boundaryCircleStartingAngle = currentStartAngle + angleReductionForRounding / 2;
|
|
235
|
+
const boundaryCircleSweepAngle = sweepAngle - angleReductionForRounding;
|
|
236
|
+
|
|
237
|
+
const innerCircleBoundaryStartingAngle = innerCircleStartAngle + angleReductionForInnerCircle / 2;
|
|
238
|
+
const innerCircleBoundarySweepAngle = innerCircleSweepAngle - angleReductionForInnerCircle;
|
|
239
|
+
// Outer Circle
|
|
240
|
+
let [x1, y1, x2, y2] = getCircularPoints(
|
|
241
|
+
boundaryCircleStartingAngle,
|
|
242
|
+
radius,
|
|
243
|
+
boundaryCircleSweepAngle,
|
|
244
|
+
cx,
|
|
245
|
+
cy
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
let [cx1, cy1, cx2, cy2] = getCircularPoints(
|
|
249
|
+
currentStartAngle,
|
|
250
|
+
radius,
|
|
251
|
+
sweepAngle,
|
|
252
|
+
cx,
|
|
253
|
+
cy
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
let [rx1, ry1, rx2, ry2] = getCircularPoints(
|
|
257
|
+
currentStartAngle,
|
|
258
|
+
radius - roundRadius,
|
|
259
|
+
sweepAngle,
|
|
260
|
+
cx,
|
|
261
|
+
cy
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// Inner circle
|
|
265
|
+
let [ix1, iy1, ix2, iy2] = getCircularPoints(
|
|
266
|
+
innerCircleBoundaryStartingAngle,
|
|
267
|
+
innerRadius,
|
|
268
|
+
innerCircleBoundarySweepAngle,
|
|
269
|
+
cx,
|
|
270
|
+
cy
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
let [cix1, ciy1, cix2, ciy2] = getCircularPoints(
|
|
274
|
+
innerCircleStartAngle,
|
|
275
|
+
innerRadius,
|
|
276
|
+
innerCircleSweepAngle,
|
|
277
|
+
cx,
|
|
278
|
+
cy
|
|
279
|
+
);
|
|
280
|
+
let [rix1, riy1, rix2, riy2] = getCircularPoints(
|
|
281
|
+
innerCircleStartAngle,
|
|
282
|
+
innerRadius + roundRadius,
|
|
283
|
+
innerCircleSweepAngle,
|
|
284
|
+
cx,
|
|
285
|
+
cy
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
let path = Skia.Path.Make();
|
|
289
|
+
drawRoundedCorner(path, { x: cx2, y: cy2 }, { x: x2, y: y2 }, { x: rx2, y: ry2 }, roundRadius);
|
|
290
|
+
drawRoundedCorner(path, { x: cix2, y: ciy2 }, { x: rix2, y: riy2 }, { x: ix2, y: iy2 }, roundRadius);
|
|
291
|
+
drawLines(path, [
|
|
292
|
+
{ x: x2, y: y2 },
|
|
293
|
+
{ x: rx2, y: ry2 },
|
|
294
|
+
{ x: rix2, y: riy2 },
|
|
295
|
+
{ x: ix2, y: iy2 },
|
|
296
|
+
{ x: x2, y: y2 },
|
|
297
|
+
]);
|
|
298
|
+
|
|
299
|
+
path.moveTo(x1, y1);
|
|
300
|
+
path.addArc(
|
|
301
|
+
rect(
|
|
302
|
+
cx - radius,
|
|
303
|
+
cy - radius,
|
|
304
|
+
radius * 2,
|
|
305
|
+
radius * 2
|
|
306
|
+
),
|
|
307
|
+
boundaryCircleStartingAngle + 180,
|
|
308
|
+
boundaryCircleSweepAngle
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
path.lineTo(ix2, iy2);
|
|
312
|
+
|
|
313
|
+
path.addArc(
|
|
314
|
+
rect(
|
|
315
|
+
cx - innerRadius,
|
|
316
|
+
cy - innerRadius,
|
|
317
|
+
innerRadius * 2,
|
|
318
|
+
innerRadius * 2
|
|
319
|
+
),
|
|
320
|
+
innerCircleBoundaryStartingAngle + 180 + innerCircleBoundarySweepAngle,
|
|
321
|
+
-innerCircleBoundarySweepAngle
|
|
322
|
+
);
|
|
323
|
+
path.lineTo(x1, y1);
|
|
324
|
+
path.close();
|
|
325
|
+
|
|
326
|
+
drawRoundedCorner(path, { x: cix1, y: ciy1 }, { x: ix1, y: iy1 }, { x: rix1, y: riy1 }, roundRadius);
|
|
327
|
+
drawRoundedCorner(path, { x: cx1, y: cy1 }, { x: rx1, y: ry1 }, { x: x1, y: y1 }, roundRadius);
|
|
328
|
+
drawLines(path, [
|
|
329
|
+
{ x: ix1, y: iy1 },
|
|
330
|
+
{ x: rix1, y: riy1 },
|
|
331
|
+
{ x: rx1, y: ry1 },
|
|
332
|
+
{ x: x1, y: y1 },
|
|
333
|
+
{ x: ix1, y: iy1 },
|
|
334
|
+
]);
|
|
335
|
+
|
|
336
|
+
return { path, color: color ?? getRandomRGBColor() };
|
|
337
|
+
});
|
|
338
|
+
return { paths, total };
|
|
339
|
+
}, [
|
|
340
|
+
slices,
|
|
341
|
+
radius,
|
|
342
|
+
innerRadius,
|
|
343
|
+
interSliceGap,
|
|
344
|
+
cx,
|
|
345
|
+
cy,
|
|
346
|
+
style.startAngle
|
|
347
|
+
]);
|
|
101
348
|
|
|
102
349
|
const touchHandler = (locationX: number, locationY: number) => {
|
|
103
350
|
if (!onSliceTouch || locationX < 0 || locationY < 0 || locationX >= diameter || locationY >= diameter) {
|
|
@@ -114,8 +361,6 @@ export function usePieChart({
|
|
|
114
361
|
|
|
115
362
|
let lastAngle = (slice.value / total) * 360;
|
|
116
363
|
if (path.contains(locationX, locationY)) {
|
|
117
|
-
const label = slice.label || 'Slice';
|
|
118
|
-
|
|
119
364
|
const outerX = xpoint(angles + lastAngle / 2, radius, cx);
|
|
120
365
|
const innerX = xpoint(angles + lastAngle / 2, innerRadius, cx);
|
|
121
366
|
|
|
@@ -145,12 +390,17 @@ export function usePieChart({
|
|
|
145
390
|
setPopupData(undefined);
|
|
146
391
|
}
|
|
147
392
|
};
|
|
393
|
+
|
|
148
394
|
return {
|
|
149
395
|
paths,
|
|
396
|
+
height,
|
|
397
|
+
width,
|
|
150
398
|
diameter,
|
|
151
399
|
innerRadius,
|
|
152
400
|
radius,
|
|
153
401
|
popupData,
|
|
154
402
|
touchHandler,
|
|
403
|
+
cx,
|
|
404
|
+
cy
|
|
155
405
|
};
|
|
156
406
|
}
|
package/src/skia/Popup.tsx
CHANGED
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import { Modal, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
export interface PopupStyle<T> {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
renderPopup?: (data: T) => React.ReactNode;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
interface PopupData<T> {
|
|
@@ -19,7 +19,7 @@ interface PopupProps<T> {
|
|
|
19
19
|
totalHeight: number;
|
|
20
20
|
touchHandler?: (x: number, y: number) => void;
|
|
21
21
|
onTouchOutside?: () => void;
|
|
22
|
-
|
|
22
|
+
popupStyle?: PopupStyle<T>;
|
|
23
23
|
viewOffset: {
|
|
24
24
|
x: number;
|
|
25
25
|
y: number;
|
|
@@ -63,13 +63,13 @@ export default function Popup<T>({
|
|
|
63
63
|
>
|
|
64
64
|
{popupData && !Array.isArray(popupData) && (
|
|
65
65
|
<View
|
|
66
|
-
style={
|
|
66
|
+
style={
|
|
67
67
|
{
|
|
68
68
|
position: 'absolute',
|
|
69
69
|
left: Math.max(
|
|
70
70
|
0,
|
|
71
71
|
Math.min(popupData.x, totalWidth - (popupStyle?.width ?? 0)) +
|
|
72
|
-
|
|
72
|
+
viewOffset.x
|
|
73
73
|
),
|
|
74
74
|
top: Math.max(
|
|
75
75
|
0,
|
|
@@ -78,8 +78,10 @@ export default function Popup<T>({
|
|
|
78
78
|
totalHeight - (popupStyle?.height ?? 0)
|
|
79
79
|
) + viewOffset.y
|
|
80
80
|
),
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
width: popupStyle.width,
|
|
82
|
+
height: popupStyle.height
|
|
83
|
+
}
|
|
84
|
+
}
|
|
83
85
|
onTouchStart={(e) => e.stopPropagation()}
|
|
84
86
|
>
|
|
85
87
|
{popupStyle?.renderPopup(popupData.data)}
|
|
@@ -88,34 +90,34 @@ export default function Popup<T>({
|
|
|
88
90
|
|
|
89
91
|
{popupData &&
|
|
90
92
|
Array.isArray(popupData) &&
|
|
91
|
-
popupData.map((popupItem, index) =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
93
|
+
popupData.map((popupItem, index) =>
|
|
94
|
+
(
|
|
95
|
+
<View
|
|
96
|
+
key={index}
|
|
97
|
+
style={
|
|
98
|
+
{
|
|
99
|
+
position: 'absolute',
|
|
100
|
+
left: Math.max(
|
|
101
|
+
0,
|
|
102
|
+
Math.min(
|
|
103
|
+
popupItem.x,
|
|
104
|
+
totalWidth - (popupStyle?.width ?? 0)
|
|
105
|
+
) + viewOffset.x
|
|
106
|
+
),
|
|
107
|
+
top: Math.max(
|
|
108
|
+
0,
|
|
109
|
+
Math.min(
|
|
110
|
+
popupItem.y,
|
|
111
|
+
totalHeight - (popupStyle?.height ?? 0)
|
|
112
|
+
) + viewOffset.y
|
|
113
|
+
),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
onTouchStart={(e) => e.stopPropagation()}
|
|
117
|
+
>
|
|
118
|
+
{popupStyle?.renderPopup?.(popupItem.data)}
|
|
119
|
+
</View>
|
|
120
|
+
)
|
|
119
121
|
)}
|
|
120
122
|
</View>
|
|
121
123
|
</Modal>
|
package/src/skia/common.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Platform } from "react-native";
|
|
1
|
+
import { I18nManager } from "react-native";
|
|
3
2
|
|
|
4
3
|
export interface CommonStyle {
|
|
5
4
|
padding?: number;
|
|
@@ -11,21 +10,15 @@ export interface CommonStyle {
|
|
|
11
10
|
paddingRight?: number;
|
|
12
11
|
backgroundColor?: string;
|
|
13
12
|
disableRTL?: boolean;
|
|
14
|
-
font?: SkFont;
|
|
15
|
-
fontSize?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getCommonStyleFont(style?: CommonStyle) {
|
|
19
|
-
const fontSize = style?.font?.getSize() ?? style?.fontSize ?? 12;
|
|
20
|
-
const font = style?.font ?? getFont(fontSize);
|
|
21
|
-
return { font, fontSize };
|
|
22
13
|
}
|
|
23
14
|
|
|
24
15
|
export function getPaddings(style?: CommonStyle) {
|
|
25
16
|
const paddingTop = style?.paddingTop ?? style?.padding ?? 0;
|
|
26
17
|
const paddingBottom = style?.paddingBottom ?? style?.padding ?? 0;
|
|
27
|
-
const
|
|
28
|
-
|
|
18
|
+
const isRTL = I18nManager.isRTL && !(style?.disableRTL ?? false)
|
|
19
|
+
|
|
20
|
+
const paddingLeft = style?.paddingLeft ?? (isRTL ? style?.paddingEnd : style?.paddingStart) ?? style?.padding ?? 0;
|
|
21
|
+
const paddingRight = style?.paddingRight ?? (isRTL ? style?.paddingStart : style?.paddingEnd) ?? style?.padding ?? 0;
|
|
29
22
|
const paddingHorizontal = paddingLeft + paddingRight;
|
|
30
23
|
const paddingVertical = paddingTop + paddingBottom;
|
|
31
24
|
|
|
@@ -46,37 +39,6 @@ export function getRandomRGBColor() {
|
|
|
46
39
|
return `rgb(${r},${g},${b})`;
|
|
47
40
|
};
|
|
48
41
|
|
|
49
|
-
export
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
default: ["sans-serif", "serif"],
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
export function getFont(size: number = 14): SkFont {
|
|
56
|
-
const fontFamily = systemFontFamilies[0];
|
|
57
|
-
// console.log("Available system font families:", listFontFamilies());
|
|
58
|
-
// console.log("Using font family:", fontFamily, systemFontFamilies);
|
|
59
|
-
const font = matchFont({
|
|
60
|
-
fontFamily,
|
|
61
|
-
fontSize: size,
|
|
62
|
-
fontStyle: "normal",
|
|
63
|
-
fontWeight: "normal",
|
|
64
|
-
});
|
|
65
|
-
return font;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const font = getFont();
|
|
69
|
-
|
|
70
|
-
function createParagraph(text: string): SkParagraph {
|
|
71
|
-
const paragraphStyle = {
|
|
72
|
-
textAlign: TextAlign.Center,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const fontStyle: SkTextStyle = {
|
|
76
|
-
fontFamilies: systemFontFamilies,
|
|
77
|
-
fontSize: 14,
|
|
78
|
-
};
|
|
79
|
-
const paragraph = Skia.ParagraphBuilder.Make(paragraphStyle).pushStyle(fontStyle).addText(text).pop().build();
|
|
80
|
-
|
|
81
|
-
return paragraph;
|
|
82
|
-
}
|
|
42
|
+
export interface HandleOutSideTouch {
|
|
43
|
+
touchedOutside: () => void;
|
|
44
|
+
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { Canvas, Line, Text, Skia } from '@shopify/react-native-skia';
|
|
4
|
-
import { getFont } from "../common.js";
|
|
5
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
|
-
function VerticalLabel({
|
|
7
|
-
minValue,
|
|
8
|
-
maxValue,
|
|
9
|
-
labelCount,
|
|
10
|
-
styles
|
|
11
|
-
}) {
|
|
12
|
-
const {
|
|
13
|
-
width,
|
|
14
|
-
height,
|
|
15
|
-
strokeWidth = 2,
|
|
16
|
-
strokeColor = 'white',
|
|
17
|
-
textColor = 'white',
|
|
18
|
-
paddingTop = 0,
|
|
19
|
-
paddingRight = 0,
|
|
20
|
-
paddingBottom = 0,
|
|
21
|
-
paddingLeft = 0,
|
|
22
|
-
fontSize = 12,
|
|
23
|
-
backgroundColor
|
|
24
|
-
} = styles;
|
|
25
|
-
|
|
26
|
-
// Generate evenly spaced values
|
|
27
|
-
const stepValue = labelCount > 1 ? (maxValue - minValue) / (labelCount - 1) : 0;
|
|
28
|
-
const labels = Array.from({
|
|
29
|
-
length: labelCount
|
|
30
|
-
}, (_, i) => minValue + i * stepValue);
|
|
31
|
-
const font = getFont(fontSize);
|
|
32
|
-
const usableHeight = height - paddingTop - paddingBottom - fontSize;
|
|
33
|
-
const stepY = labelCount > 1 ? usableHeight / (labelCount - 1) : 0;
|
|
34
|
-
|
|
35
|
-
// Precompute text paint
|
|
36
|
-
const paint = Skia.Paint();
|
|
37
|
-
paint.setColor(Skia.Color(textColor));
|
|
38
|
-
return /*#__PURE__*/_jsxs(Canvas, {
|
|
39
|
-
style: {
|
|
40
|
-
width,
|
|
41
|
-
height,
|
|
42
|
-
backgroundColor
|
|
43
|
-
},
|
|
44
|
-
children: [/*#__PURE__*/_jsx(Line, {
|
|
45
|
-
p1: {
|
|
46
|
-
x: width - paddingRight - strokeWidth / 2,
|
|
47
|
-
y: paddingTop
|
|
48
|
-
},
|
|
49
|
-
p2: {
|
|
50
|
-
x: width - paddingRight - strokeWidth / 2,
|
|
51
|
-
y: height - paddingBottom
|
|
52
|
-
},
|
|
53
|
-
color: strokeColor,
|
|
54
|
-
strokeWidth: strokeWidth
|
|
55
|
-
}), labels.map((label, i) => {
|
|
56
|
-
const y = height - paddingBottom - stepY * i;
|
|
57
|
-
const text = label.toFixed(0);
|
|
58
|
-
|
|
59
|
-
// measure text width for right-align
|
|
60
|
-
const textWidth = font.measureText(text).width;
|
|
61
|
-
const x = width - paddingRight - strokeWidth - 4 - textWidth;
|
|
62
|
-
return /*#__PURE__*/_jsx(Text, {
|
|
63
|
-
x: x,
|
|
64
|
-
y: y,
|
|
65
|
-
text: text,
|
|
66
|
-
font: font,
|
|
67
|
-
color: textColor
|
|
68
|
-
}, i);
|
|
69
|
-
})]
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
export default VerticalLabel;
|
|
73
|
-
//# sourceMappingURL=VerticalLabel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["Canvas","Line","Text","Skia","getFont","jsx","_jsx","jsxs","_jsxs","VerticalLabel","minValue","maxValue","labelCount","styles","width","height","strokeWidth","strokeColor","textColor","paddingTop","paddingRight","paddingBottom","paddingLeft","fontSize","backgroundColor","stepValue","labels","Array","from","length","_","i","font","usableHeight","stepY","paint","Paint","setColor","Color","style","children","p1","x","y","p2","color","map","label","text","toFixed","textWidth","measureText"],"sourceRoot":"../../../../src","sources":["skia/Common/VerticalLabel.tsx"],"mappings":";;AAAA,SAASA,MAAM,EAAEC,IAAI,EAAEC,IAAI,EAAEC,IAAI,QAAQ,4BAA4B;AACrE,SAASC,OAAO,QAA0B,cAAW;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAiBtD,SAASC,aAAaA,CAAC;EACrBC,QAAQ;EACRC,QAAQ;EACRC,UAAU;EACVC;AACkB,CAAC,EAAE;EACrB,MAAM;IACJC,KAAK;IACLC,MAAM;IACNC,WAAW,GAAG,CAAC;IACfC,WAAW,GAAG,OAAO;IACrBC,SAAS,GAAG,OAAO;IACnBC,UAAU,GAAG,CAAC;IACdC,YAAY,GAAG,CAAC;IAChBC,aAAa,GAAG,CAAC;IACjBC,WAAW,GAAG,CAAC;IACfC,QAAQ,GAAG,EAAE;IACbC;EACF,CAAC,GAAGX,MAAM;;EAEV;EACA,MAAMY,SAAS,GACbb,UAAU,GAAG,CAAC,GAAG,CAACD,QAAQ,GAAGD,QAAQ,KAAKE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC;EAC/D,MAAMc,MAAM,GAAGC,KAAK,CAACC,IAAI,CACvB;IAAEC,MAAM,EAAEjB;EAAW,CAAC,EACtB,CAACkB,CAAC,EAAEC,CAAC,KAAKrB,QAAQ,GAAGqB,CAAC,GAAGN,SAC3B,CAAC;EAED,MAAMO,IAAI,GAAG5B,OAAO,CAACmB,QAAQ,CAAC;EAE9B,MAAMU,YAAY,GAAGlB,MAAM,GAAGI,UAAU,GAAGE,aAAa,GAAGE,QAAQ;EACnE,MAAMW,KAAK,GAAGtB,UAAU,GAAG,CAAC,GAAGqB,YAAY,IAAIrB,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC;;EAElE;EACA,MAAMuB,KAAK,GAAGhC,IAAI,CAACiC,KAAK,CAAC,CAAC;EAC1BD,KAAK,CAACE,QAAQ,CAAClC,IAAI,CAACmC,KAAK,CAACpB,SAAS,CAAC,CAAC;EAErC,oBACEV,KAAA,CAACR,MAAM;IAACuC,KAAK,EAAE;MAAEzB,KAAK;MAAEC,MAAM;MAAES;IAAgB,CAAE;IAAAgB,QAAA,gBAChDlC,IAAA,CAACL,IAAI;MACHwC,EAAE,EAAE;QAAEC,CAAC,EAAE5B,KAAK,GAAGM,YAAY,GAAGJ,WAAW,GAAG,CAAC;QAAE2B,CAAC,EAAExB;MAAW,CAAE;MACjEyB,EAAE,EAAE;QACFF,CAAC,EAAE5B,KAAK,GAAGM,YAAY,GAAGJ,WAAW,GAAG,CAAC;QACzC2B,CAAC,EAAE5B,MAAM,GAAGM;MACd,CAAE;MACFwB,KAAK,EAAE5B,WAAY;MACnBD,WAAW,EAAEA;IAAY,CAC1B,CAAC,EAEDU,MAAM,CAACoB,GAAG,CAAC,CAACC,KAAK,EAAEhB,CAAC,KAAK;MACxB,MAAMY,CAAC,GAAG5B,MAAM,GAAGM,aAAa,GAAGa,KAAK,GAAGH,CAAC;MAC5C,MAAMiB,IAAI,GAAGD,KAAK,CAACE,OAAO,CAAC,CAAC,CAAC;;MAE7B;MACA,MAAMC,SAAS,GAAGlB,IAAI,CAACmB,WAAW,CAACH,IAAI,CAAC,CAAClC,KAAK;MAC9C,MAAM4B,CAAC,GAAG5B,KAAK,GAAGM,YAAY,GAAGJ,WAAW,GAAG,CAAC,GAAGkC,SAAS;MAE5D,oBACE5C,IAAA,CAACJ,IAAI;QAEHwC,CAAC,EAAEA,CAAE;QACLC,CAAC,EAAEA,CAAE;QACLK,IAAI,EAAEA,IAAK;QACXhB,IAAI,EAAEA,IAAK;QACXa,KAAK,EAAE3B;MAAU,GALZa,CAMN,CAAC;IAEN,CAAC,CAAC;EAAA,CACI,CAAC;AAEb;AAEA,eAAetB,aAAa","ignoreList":[]}
|