@react-native-ohos/victory-native-xl 41.17.5 → 41.18.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @react-native-ohos/victory-native-xl
2
2
 
3
- 本项目基于 [victory-native-xl@v41.17.4](https://github.com/FormidableLabs/victory-native-xl/tree/victory-native%4041.17.4)
3
+ 本项目基于 [victory-native-xl@v41.20.2](https://github.com/FormidableLabs/victory-native-xl/tree/victory-native%4041.20.2)
4
4
 
5
5
  ## 文档地址 / Documentation URL
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-ohos/victory-native-xl",
3
- "version": "41.17.5",
3
+ "version": "41.18.0-beta.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,9 +24,11 @@
24
24
  "dependencies": {
25
25
  "d3-scale": "^4.0.2",
26
26
  "d3-shape": "^3.2.0",
27
+ "d3-zoom": "^3.0.0",
27
28
  "its-fine": "^1.2.5",
28
29
  "react-fast-compare": "^3.2.2",
29
- "react-native-reanimated": "3.18.0"
30
+ "react-native-reanimated": "3.18.0",
31
+ "victory-native": "41.20.2"
30
32
  },
31
33
  "peerDependencies": {
32
34
  "@react-native-ohos/react-native-skia": "file:packages/react-native-ohos-react-native-skia-1.3.7.tgz",
@@ -34,7 +36,8 @@
34
36
  "@react-native-ohos/react-native-reanimated": "file:packages/react-native-ohos-react-native-reanimated-3.18.0.tgz",
35
37
  "@shopify/react-native-skia": "^1.3.7",
36
38
  "react": "18.3.1",
37
- "react-native": "0.77.1"
39
+ "react-native": "0.77.1",
40
+ "victory-native": "41.20.2"
38
41
  },
39
42
  "devDependencies": {
40
43
  "@types/d3-scale": "^4.0.3",
@@ -1,16 +1,17 @@
1
1
  import * as React from "react";
2
- import { type LayoutChangeEvent, Button, View } from "react-native";
3
- import { Canvas, Group, rect, Path } from "@shopify/react-native-skia";
4
- import { useSharedValue } from "react-native-reanimated";
2
+ import { View, type LayoutChangeEvent } from "react-native";
3
+ import { Canvas, Group, type CanvasRef } from "@shopify/react-native-skia";
4
+ import { useSharedValue, useAnimatedReaction, runOnJS } from "react-native-reanimated";
5
5
  import {
6
6
  type ComposedGesture,
7
7
  Gesture,
8
- GestureDetector,
9
8
  GestureHandlerRootView,
10
9
  type TouchData,
11
10
  } from "react-native-gesture-handler";
12
11
  import { type MutableRefObject } from "react";
13
- import type { ScaleLinear } from "d3-scale";
12
+ import { ZoomTransform } from "d3-zoom";
13
+ import type { ScaleLinear, ScaleLogarithmic } from "d3-scale";
14
+ import isEqual from "react-fast-compare";
14
15
  import type {
15
16
  AxisProps,
16
17
  CartesianChartRenderArg,
@@ -19,23 +20,21 @@ import type {
19
20
  SidedNumber,
20
21
  TransformedData,
21
22
  ChartBounds,
22
- Viewport,
23
- ChartPressPanConfig,
24
23
  YAxisInputProps,
25
- FrameInputProps,
26
24
  XAxisInputProps,
27
- GestureHandlerConfig
25
+ FrameInputProps,
26
+ ChartPressPanConfig,
27
+ Viewport,
28
+ GestureHandlerConfig,
28
29
  } from "victory-native/src/types";
29
30
  import { transformInputData } from "victory-native/src/cartesian/utils/transformInputData";
30
31
  import { findClosestPoint } from "victory-native/src/utils/findClosestPoint";
31
32
  import { valueFromSidedNumber } from "victory-native/src/utils/valueFromSidedNumber";
32
- import {
33
- CartesianAxis,
34
- CartesianAxisDefaultProps,
35
- } from "victory-native/src/cartesian/components/CartesianAxis";
36
33
  import { asNumber } from "victory-native/src/utils/asNumber";
37
- import type { ChartPressState,
38
- ChartPressStateInit, } from "victory-native/src/cartesian/hooks/useChartPressState";
34
+ import type {
35
+ ChartPressState,
36
+ ChartPressStateInit,
37
+ } from "victory-native/src/cartesian/hooks/useChartPressState";
39
38
  import { useFunctionRef } from "victory-native/src/hooks/useFunctionRef";
40
39
  import { CartesianChartProvider } from "victory-native/src/cartesian/contexts/CartesianChartContext";
41
40
  import { XAxis } from "victory-native/src/cartesian/components/XAxis";
@@ -54,12 +53,11 @@ import {
54
53
  useCartesianTransformContext,
55
54
  } from "victory-native/src/cartesian/contexts/CartesianTransformContext";
56
55
  import { downsampleTicks } from "victory-native/src/utils/tickHelpers";
57
- import { GestureHandler } from "victory-native/src/shared/GestureHandler";
56
+ import { GestureHandler } from "../shared/GestureHandler";
58
57
  import { boundsToClip } from "victory-native/src/utils/boundsToClip";
59
58
  import { normalizeYAxisTicks } from "victory-native/src/utils/normalizeYAxisTicks";
60
59
  import { createFallbackChartState } from "victory-native/src/cartesian/utils/createFallbackChartState";
61
- import { ZoomTransform } from "d3-zoom";
62
- import isEqual from "react-fast-compare";
60
+
63
61
  export type CartesianActionsHandle<T = undefined> =
64
62
  T extends ChartPressState<infer S>
65
63
  ? S extends ChartPressStateInit
@@ -69,6 +67,11 @@ export type CartesianActionsHandle<T = undefined> =
69
67
  : never
70
68
  : never;
71
69
 
70
+ export type CartesianChartRef<T = undefined> = {
71
+ canvas: CanvasRef | null;
72
+ actions: CartesianActionsHandle<T>;
73
+ };
74
+
72
75
  type CartesianChartProps<
73
76
  RawData extends Record<string, unknown>,
74
77
  XK extends keyof InputFields<RawData>,
@@ -93,7 +96,6 @@ type CartesianChartProps<
93
96
  args: CartesianChartRenderArg<RawData, YK>,
94
97
  ) => React.ReactNode;
95
98
  axisOptions?: Partial<Omit<AxisProps<RawData, XK, YK>, "xScale" | "yScale">>;
96
-
97
99
  onChartBoundsChange?: (bounds: ChartBounds) => void;
98
100
  onScaleChange?: (
99
101
  xScale: ScaleLinear<number, number>,
@@ -111,6 +113,8 @@ type CartesianChartProps<
111
113
  pan?: PanTransformGestureConfig;
112
114
  pinch?: PinchTransformGestureConfig;
113
115
  };
116
+ /** Optional hook for ScrollView coordination (e.g. disable scroll on pan/pinch). */
117
+ onTransformInteractionChange?: (active: boolean) => void;
114
118
  customGestures?: ComposedGesture;
115
119
  actionsRef?: MutableRefObject<CartesianActionsHandle<
116
120
  | ChartPressState<{
@@ -119,16 +123,30 @@ type CartesianChartProps<
119
123
  }>
120
124
  | undefined
121
125
  > | null>;
126
+ ref?: React.Ref<
127
+ CartesianChartRef<
128
+ | ChartPressState<{
129
+ x: InputFields<RawData>[XK];
130
+ y: Record<YK, number>;
131
+ }>
132
+ | undefined
133
+ >
134
+ >;
122
135
  };
123
136
 
124
137
  export function CartesianChart<
125
138
  RawData extends Record<string, unknown>,
126
139
  XK extends keyof InputFields<RawData>,
127
140
  YK extends keyof NumericalFields<RawData>,
128
- >({ transformState, children, ...rest }: CartesianChartProps<RawData, XK, YK>) {
141
+ >({
142
+ transformState,
143
+ children,
144
+ ref,
145
+ ...rest
146
+ }: CartesianChartProps<RawData, XK, YK>) {
129
147
  return (
130
148
  <CartesianTransformProvider transformState={transformState}>
131
- <CartesianChartContent {...{ ...rest, transformState }}>
149
+ <CartesianChartContent {...{ ...rest, transformState }} ref={ref}>
132
150
  {children}
133
151
  </CartesianChartContent>
134
152
  </CartesianTransformProvider>
@@ -160,18 +178,43 @@ function CartesianChartContent<
160
178
  frame,
161
179
  transformState,
162
180
  transformConfig,
181
+ onTransformInteractionChange,
163
182
  customGestures,
164
183
  actionsRef,
165
184
  viewport,
185
+ ref,
166
186
  }: CartesianChartProps<RawData, XK, YK>) {
187
+ const onTransformInteractionChangeRef = useFunctionRef(
188
+ onTransformInteractionChange,
189
+ );
190
+
191
+ useAnimatedReaction(
192
+ () =>
193
+ transformState
194
+ ? transformState.panActive.value || transformState.zoomActive.value
195
+ : false,
196
+ (active, prev) => {
197
+ if (
198
+ !onTransformInteractionChangeRef.current ||
199
+ prev === null ||
200
+ active === prev
201
+ ) {
202
+ return;
203
+ }
204
+ runOnJS(onTransformInteractionChangeRef.current)(active);
205
+ },
206
+ );
207
+
167
208
  const [size, setSize] = React.useState({ width: 0, height: 0 });
168
209
  const chartBoundsRef = React.useRef<ChartBounds | undefined>(undefined);
169
- const xScaleRef = React.useRef<ScaleLinear<number, number> | undefined>(
170
- undefined,
171
- );
210
+ const xScaleRef = React.useRef<
211
+ ScaleLogarithmic<number, number> | ScaleLinear<number, number> | undefined
212
+ >(undefined);
213
+
172
214
  const yScaleRef = React.useRef<ScaleLinear<number, number> | undefined>(
173
215
  undefined,
174
216
  );
217
+ const canvasRef = React.useRef<CanvasRef | null>(null);
175
218
  const [hasMeasuredLayoutSize, setHasMeasuredLayoutSize] =
176
219
  React.useState(false);
177
220
  const onLayout = React.useCallback(
@@ -241,6 +284,7 @@ function CartesianChartContent<
241
284
  yAxes: normalizedAxisProps.yAxes,
242
285
  viewport,
243
286
  labelRotate: normalizedAxisProps.xAxis.labelRotate,
287
+ axisScales: axisOptions?.axisScales,
244
288
  });
245
289
 
246
290
  const primaryYAxis = yAxes[0];
@@ -298,7 +342,10 @@ function CartesianChartContent<
298
342
  * Take a "press value" and an x-value and update the shared values accordingly.
299
343
  */
300
344
  const handleTouch = (
301
- v: ChartPressState<{ x: InputFields<RawData>[XK]; y: Record<YK, number> }>,
345
+ v: ChartPressState<{
346
+ x: InputFields<RawData>[XK];
347
+ y: Record<YK, number>;
348
+ }>,
302
349
  x: number,
303
350
  y: number,
304
351
  ) => {
@@ -308,7 +355,6 @@ function CartesianChartContent<
308
355
  if (typeof idx !== "number") return;
309
356
 
310
357
  const isInYs = (yk: string): yk is YK & string => yKeys.includes(yk as YK);
311
-
312
358
  // begin stacked bar handling:
313
359
  // store the heights of each bar segment
314
360
  const barHeights: number[] = [];
@@ -363,6 +409,18 @@ function CartesianChartContent<
363
409
  lastIdx.value = idx;
364
410
  };
365
411
 
412
+ React.useImperativeHandle(
413
+ ref,
414
+ () => ({
415
+ canvas: canvasRef.current,
416
+ actions: {
417
+ handleTouch,
418
+ },
419
+ }),
420
+ // eslint-disable-next-line react-hooks/exhaustive-deps
421
+ [canvasRef],
422
+ );
423
+
366
424
  if (actionsRef) {
367
425
  actionsRef.current = {
368
426
  handleTouch,
@@ -410,7 +468,16 @@ function CartesianChartContent<
410
468
  touchMap.value[touch.id] = i;
411
469
 
412
470
  v.isActive.value = true;
413
- handleTouch(v, touch.x, touch.y);
471
+
472
+ // [3] and [7] are the X and Y translation components of the 4x4 transformation matrix, respectively.
473
+ const scrolledX = transformState?.matrix.value?.[3] || 0;
474
+ const scrolledY = transformState?.matrix.value?.[7] || 0;
475
+
476
+ handleTouch(
477
+ v,
478
+ touch.absoluteX - scrolledX,
479
+ touch.absoluteY - scrolledY,
480
+ );
414
481
  } else {
415
482
  gestureState.value.bootstrap.push([v, touch]);
416
483
  }
@@ -429,7 +496,15 @@ function CartesianChartContent<
429
496
  touchMap.value[touch.id] = i;
430
497
 
431
498
  v.isActive.value = true;
432
- handleTouch(v, touch.x, touch.y);
499
+
500
+ const scrolledX = transformState?.matrix.value?.[3] || 0;
501
+ const scrolledY = transformState?.matrix.value?.[7] || 0;
502
+
503
+ handleTouch(
504
+ v,
505
+ touch.absoluteX - scrolledX,
506
+ touch.absoluteY - scrolledY,
507
+ );
433
508
  }
434
509
  })
435
510
  /**
@@ -454,7 +529,15 @@ function CartesianChartContent<
454
529
 
455
530
  if (!v || !touch) continue;
456
531
  if (!v.isActive.value) v.isActive.value = true;
457
- handleTouch(v, touch.x, touch.y);
532
+
533
+ const scrolledX = transformState?.matrix.value?.[3] || 0;
534
+ const scrolledY = transformState?.matrix.value?.[7] || 0;
535
+
536
+ handleTouch(
537
+ v,
538
+ touch.absoluteX - scrolledX,
539
+ touch.absoluteY - scrolledY,
540
+ );
458
541
  }
459
542
  })
460
543
  /**
@@ -650,7 +733,7 @@ function CartesianChartContent<
650
733
 
651
734
  // Body of the chart.
652
735
  const body = (
653
- <Canvas style={{ flex: 1 }} onLayout={onLayout}>
736
+ <Canvas ref={canvasRef} style={{ flex: 1 }}>
654
737
  {YAxisComponents}
655
738
  {XAxisComponents}
656
739
  {FrameComponent}
@@ -690,20 +773,23 @@ function CartesianChartContent<
690
773
  }
691
774
 
692
775
  return (
693
- <GestureHandlerRootView style={{ flex: 1, overflow: "hidden" }}>
694
- {body}
695
- <GestureHandler
696
- config={gestureHandlerConfig}
697
- gesture={composed}
698
- transformState={transformState}
699
- dimensions={{
700
- x: Math.min(xScale.range()[0]!, 0),
701
- y: Math.min(primaryYScale.range()[0]!, 0),
702
- width: xScale.range()[1]! - Math.min(xScale.range()[0]!, 0),
703
- height:
704
- primaryYScale.range()[1]! - Math.min(primaryYScale.range()[0]!, 0),
705
- }}
706
- />
776
+ <GestureHandlerRootView style={{ flex: 1}}>
777
+ <View style={{ flex: 1, overflow: "hidden" }} onLayout={onLayout}>
778
+ {body}
779
+ <GestureHandler
780
+ config={gestureHandlerConfig}
781
+ gesture={composed}
782
+ transformState={transformState}
783
+ dimensions={{
784
+ x: Math.min(xScale.range()[0]!, 0),
785
+ y: Math.min(primaryYScale.range()[0]!, 0),
786
+ width: xScale.range()[1]! - Math.min(xScale.range()[0]!, 0),
787
+ height:
788
+ primaryYScale.range()[1]! -
789
+ Math.min(primaryYScale.range()[0]!, 0),
790
+ }}
791
+ />
792
+ </View>
707
793
  </GestureHandlerRootView>
708
794
  );
709
795
  }
package/src/index.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  /**
2
2
  * Cartesian chart exports (including useful types)
3
3
  */
4
- export { CartesianChart } from "./cartesian/CartesianChart";
4
+ export {
5
+ CartesianChart,
6
+ type CartesianActionsHandle,
7
+ type CartesianChartRef,
8
+ } from "./cartesian/CartesianChart";
5
9
  export { PolarChart } from "./polar/PolarChart";
6
10
 
7
-
8
11
  export { type CurveType } from "victory-native";
9
12
  export { type RoundedCorners } from "victory-native";
13
+ export { type AxisScaleType, type AxisScales } from "victory-native/src/types";
10
14
 
11
15
  export { useAnimatedPath } from "victory-native";
12
16
  export { AnimatedPath } from "victory-native";
@@ -52,9 +52,11 @@ const PolarChartBase = (
52
52
  const ctx = usePolarChartContext();
53
53
  const ContextBridge = useContextBridge();
54
54
  return (
55
- <View style={[styles.baseContainer, containerStyle]}>
55
+ <View
56
+ style={[styles.baseContainer, containerStyle]}
57
+ onLayout={onLayout}
58
+ >
56
59
  <Canvas
57
- onLayout={onLayout}
58
60
  style={StyleSheet.flatten([
59
61
  styles.canvasContainer,
60
62
  hasMeasuredLayoutSize ? { width, height } : null,
@@ -0,0 +1,45 @@
1
+ import {
2
+ type ComposedGesture,
3
+ GestureDetector,
4
+ type GestureType,
5
+ } from "react-native-gesture-handler";
6
+ import { type SkRect } from "@shopify/react-native-skia";
7
+ import Animated, { useAnimatedStyle } from "react-native-reanimated";
8
+ import * as React from "react";
9
+ import { type ChartTransformState } from "victory-native/src/cartesian/hooks/useChartTransformState";
10
+ import type { GestureHandlerConfig } from "victory-native/src/types";
11
+
12
+ type GestureHandlerProps = {
13
+ gesture: ComposedGesture | GestureType;
14
+ dimensions?: SkRect;
15
+ transformState?: ChartTransformState;
16
+ debug?: boolean;
17
+ config?: GestureHandlerConfig;
18
+ };
19
+
20
+ /**
21
+ * OHOS: Keep the gesture overlay fixed to the chart container instead of
22
+ * following transformState.matrix. Upstream 41.20.2 moves the overlay with
23
+ * matrix transforms, which breaks pan/pinch on HarmonyOS RNGH (e.g. Y pan
24
+ * requires a horizontal move first). Aligns with upstream main PR #664.
25
+ */
26
+ export const GestureHandler = ({
27
+ gesture,
28
+ debug = false,
29
+ config,
30
+ }: GestureHandlerProps) => {
31
+ const style = useAnimatedStyle(() => ({
32
+ position: "absolute",
33
+ top: 0,
34
+ left: 0,
35
+ right: 0,
36
+ bottom: 0,
37
+ backgroundColor: debug ? "rgba(100, 200, 255, 0.4)" : "transparent",
38
+ }));
39
+
40
+ return (
41
+ <GestureDetector {...config} gesture={gesture}>
42
+ <Animated.View style={style} />
43
+ </GestureDetector>
44
+ );
45
+ };