canvu-react 0.4.15 → 0.4.16

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/native.d.cts CHANGED
@@ -12,6 +12,16 @@ type TimedTrailPoint = {
12
12
  readonly t: number;
13
13
  };
14
14
 
15
+ type NativeVectorToolCursorKind = "crosshair" | "draw" | "marker" | "eraser" | "text";
16
+ type NativeVectorToolCursor = {
17
+ readonly kind: NativeVectorToolCursorKind;
18
+ readonly size: number;
19
+ readonly hotspot: {
20
+ readonly x: number;
21
+ readonly y: number;
22
+ };
23
+ };
24
+
15
25
  type PlacementPreview = {
16
26
  readonly kind: "rect" | "ellipse" | "architectural-cloud";
17
27
  readonly rect: {
@@ -58,8 +68,15 @@ type NativeInteractionOverlayProps = {
58
68
  readonly laserTrail?: readonly TimedTrailPoint[];
59
69
  readonly eraserPreviewItems?: readonly VectorSceneItem[];
60
70
  readonly previewStrokeStyle?: StrokeStyle;
71
+ readonly toolCursor?: {
72
+ readonly cursor: NativeVectorToolCursor;
73
+ readonly point: {
74
+ readonly x: number;
75
+ readonly y: number;
76
+ };
77
+ } | null;
61
78
  };
62
- declare function NativeInteractionOverlay({ camera, width, height, selectedItems, showResizeHandles, placementPreview, eraserTrail, laserTrail, eraserPreviewItems, previewStrokeStyle, }: NativeInteractionOverlayProps): react_jsx_runtime.JSX.Element | null;
79
+ declare function NativeInteractionOverlay({ camera, width, height, selectedItems, showResizeHandles, placementPreview, eraserTrail, laserTrail, eraserPreviewItems, previewStrokeStyle, toolCursor, }: NativeInteractionOverlayProps): react_jsx_runtime.JSX.Element | null;
63
80
 
64
81
  type NativeSceneRendererProps = {
65
82
  readonly items: readonly VectorSceneItem[];
package/dist/native.d.ts CHANGED
@@ -12,6 +12,16 @@ type TimedTrailPoint = {
12
12
  readonly t: number;
13
13
  };
14
14
 
15
+ type NativeVectorToolCursorKind = "crosshair" | "draw" | "marker" | "eraser" | "text";
16
+ type NativeVectorToolCursor = {
17
+ readonly kind: NativeVectorToolCursorKind;
18
+ readonly size: number;
19
+ readonly hotspot: {
20
+ readonly x: number;
21
+ readonly y: number;
22
+ };
23
+ };
24
+
15
25
  type PlacementPreview = {
16
26
  readonly kind: "rect" | "ellipse" | "architectural-cloud";
17
27
  readonly rect: {
@@ -58,8 +68,15 @@ type NativeInteractionOverlayProps = {
58
68
  readonly laserTrail?: readonly TimedTrailPoint[];
59
69
  readonly eraserPreviewItems?: readonly VectorSceneItem[];
60
70
  readonly previewStrokeStyle?: StrokeStyle;
71
+ readonly toolCursor?: {
72
+ readonly cursor: NativeVectorToolCursor;
73
+ readonly point: {
74
+ readonly x: number;
75
+ readonly y: number;
76
+ };
77
+ } | null;
61
78
  };
62
- declare function NativeInteractionOverlay({ camera, width, height, selectedItems, showResizeHandles, placementPreview, eraserTrail, laserTrail, eraserPreviewItems, previewStrokeStyle, }: NativeInteractionOverlayProps): react_jsx_runtime.JSX.Element | null;
79
+ declare function NativeInteractionOverlay({ camera, width, height, selectedItems, showResizeHandles, placementPreview, eraserTrail, laserTrail, eraserPreviewItems, previewStrokeStyle, toolCursor, }: NativeInteractionOverlayProps): react_jsx_runtime.JSX.Element | null;
63
80
 
64
81
  type NativeSceneRendererProps = {
65
82
  readonly items: readonly VectorSceneItem[];
package/dist/native.js CHANGED
@@ -1816,6 +1816,178 @@ function NativeShapeRenderer({ item }) {
1816
1816
  }
1817
1817
  return null;
1818
1818
  }
1819
+ var CURSOR_STROKE = "#18181b";
1820
+ var CURSOR_STROKE_WIDTH = 2;
1821
+ var CROSSHAIR_STROKE_WIDTH = 1.5;
1822
+ function NativeDrawCursor() {
1823
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1824
+ /* @__PURE__ */ jsx(
1825
+ Path,
1826
+ {
1827
+ path: "M13 21h8",
1828
+ color: CURSOR_STROKE,
1829
+ style: "stroke",
1830
+ strokeWidth: CURSOR_STROKE_WIDTH,
1831
+ strokeCap: "round",
1832
+ strokeJoin: "round",
1833
+ antiAlias: true
1834
+ }
1835
+ ),
1836
+ /* @__PURE__ */ jsx(
1837
+ Path,
1838
+ {
1839
+ path: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
1840
+ color: CURSOR_STROKE,
1841
+ style: "stroke",
1842
+ strokeWidth: CURSOR_STROKE_WIDTH,
1843
+ strokeCap: "round",
1844
+ strokeJoin: "round",
1845
+ antiAlias: true
1846
+ }
1847
+ )
1848
+ ] });
1849
+ }
1850
+ function NativeMarkerCursor() {
1851
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1852
+ /* @__PURE__ */ jsx(
1853
+ Path,
1854
+ {
1855
+ path: "m9 11-6 6v3h9l3-3",
1856
+ color: CURSOR_STROKE,
1857
+ style: "stroke",
1858
+ strokeWidth: CURSOR_STROKE_WIDTH,
1859
+ strokeCap: "round",
1860
+ strokeJoin: "round",
1861
+ antiAlias: true
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsx(
1865
+ Path,
1866
+ {
1867
+ path: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4",
1868
+ color: CURSOR_STROKE,
1869
+ style: "stroke",
1870
+ strokeWidth: CURSOR_STROKE_WIDTH,
1871
+ strokeCap: "round",
1872
+ strokeJoin: "round",
1873
+ antiAlias: true
1874
+ }
1875
+ )
1876
+ ] });
1877
+ }
1878
+ function NativeEraserCursor() {
1879
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1880
+ /* @__PURE__ */ jsx(
1881
+ Path,
1882
+ {
1883
+ path: "M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21",
1884
+ color: CURSOR_STROKE,
1885
+ style: "stroke",
1886
+ strokeWidth: CURSOR_STROKE_WIDTH,
1887
+ strokeCap: "round",
1888
+ strokeJoin: "round",
1889
+ antiAlias: true
1890
+ }
1891
+ ),
1892
+ /* @__PURE__ */ jsx(
1893
+ Path,
1894
+ {
1895
+ path: "m5.082 11.09 8.828 8.828",
1896
+ color: CURSOR_STROKE,
1897
+ style: "stroke",
1898
+ strokeWidth: CURSOR_STROKE_WIDTH,
1899
+ strokeCap: "round",
1900
+ strokeJoin: "round",
1901
+ antiAlias: true
1902
+ }
1903
+ )
1904
+ ] });
1905
+ }
1906
+ function NativeCrosshairCursor() {
1907
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1908
+ /* @__PURE__ */ jsx(
1909
+ Line,
1910
+ {
1911
+ p1: vec(12, 3),
1912
+ p2: vec(12, 21),
1913
+ color: CURSOR_STROKE,
1914
+ style: "stroke",
1915
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
1916
+ strokeCap: "round",
1917
+ antiAlias: true
1918
+ }
1919
+ ),
1920
+ /* @__PURE__ */ jsx(
1921
+ Line,
1922
+ {
1923
+ p1: vec(3, 12),
1924
+ p2: vec(21, 12),
1925
+ color: CURSOR_STROKE,
1926
+ style: "stroke",
1927
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
1928
+ strokeCap: "round",
1929
+ antiAlias: true
1930
+ }
1931
+ )
1932
+ ] });
1933
+ }
1934
+ function NativeTextCursor() {
1935
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1936
+ /* @__PURE__ */ jsx(
1937
+ Line,
1938
+ {
1939
+ p1: vec(12, 4),
1940
+ p2: vec(12, 20),
1941
+ color: CURSOR_STROKE,
1942
+ style: "stroke",
1943
+ strokeWidth: CURSOR_STROKE_WIDTH,
1944
+ strokeCap: "round",
1945
+ antiAlias: true
1946
+ }
1947
+ ),
1948
+ /* @__PURE__ */ jsx(
1949
+ Line,
1950
+ {
1951
+ p1: vec(8, 4),
1952
+ p2: vec(16, 4),
1953
+ color: CURSOR_STROKE,
1954
+ style: "stroke",
1955
+ strokeWidth: CURSOR_STROKE_WIDTH,
1956
+ strokeCap: "round",
1957
+ antiAlias: true
1958
+ }
1959
+ ),
1960
+ /* @__PURE__ */ jsx(
1961
+ Line,
1962
+ {
1963
+ p1: vec(8, 20),
1964
+ p2: vec(16, 20),
1965
+ color: CURSOR_STROKE,
1966
+ style: "stroke",
1967
+ strokeWidth: CURSOR_STROKE_WIDTH,
1968
+ strokeCap: "round",
1969
+ antiAlias: true
1970
+ }
1971
+ )
1972
+ ] });
1973
+ }
1974
+ function NativeToolCursorGlyph({
1975
+ cursor
1976
+ }) {
1977
+ if (cursor.kind === "draw") return /* @__PURE__ */ jsx(NativeDrawCursor, {});
1978
+ if (cursor.kind === "marker") return /* @__PURE__ */ jsx(NativeMarkerCursor, {});
1979
+ if (cursor.kind === "eraser") return /* @__PURE__ */ jsx(NativeEraserCursor, {});
1980
+ if (cursor.kind === "text") return /* @__PURE__ */ jsx(NativeTextCursor, {});
1981
+ return /* @__PURE__ */ jsx(NativeCrosshairCursor, {});
1982
+ }
1983
+ function NativeToolCursorRenderer({
1984
+ cursor,
1985
+ point
1986
+ }) {
1987
+ const x = point.x - cursor.hotspot.x;
1988
+ const y = point.y - cursor.hotspot.y;
1989
+ return /* @__PURE__ */ jsx(Group, { transform: [{ translateX: x }, { translateY: y }], children: /* @__PURE__ */ jsx(NativeToolCursorGlyph, { cursor }) });
1990
+ }
1819
1991
 
1820
1992
  // src/native/native-overlay-style.ts
1821
1993
  var ERASER_TINT = "#cbd5e1";
@@ -1872,7 +2044,8 @@ function NativeInteractionOverlay({
1872
2044
  eraserTrail,
1873
2045
  laserTrail,
1874
2046
  eraserPreviewItems = [],
1875
- previewStrokeStyle
2047
+ previewStrokeStyle,
2048
+ toolCursor
1876
2049
  }) {
1877
2050
  const z = camera.zoom;
1878
2051
  const camTransform = skiaCameraTransform(z, camera.x, camera.y);
@@ -2249,7 +2422,7 @@ function NativeInteractionOverlay({
2249
2422
  ] });
2250
2423
  }, [laserTrail, z]);
2251
2424
  if (width <= 0 || height <= 0) return null;
2252
- return /* @__PURE__ */ jsx(
2425
+ return /* @__PURE__ */ jsxs(
2253
2426
  Canvas,
2254
2427
  {
2255
2428
  style: {
@@ -2260,13 +2433,22 @@ function NativeInteractionOverlay({
2260
2433
  height
2261
2434
  },
2262
2435
  pointerEvents: "none",
2263
- children: /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2264
- previewElements,
2265
- laserTrailElements,
2266
- eraserTrailElements,
2267
- eraserPreviewElements,
2268
- selectionElements
2269
- ] })
2436
+ children: [
2437
+ /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2438
+ previewElements,
2439
+ laserTrailElements,
2440
+ eraserTrailElements,
2441
+ eraserPreviewElements,
2442
+ selectionElements
2443
+ ] }),
2444
+ toolCursor ? /* @__PURE__ */ jsx(
2445
+ NativeToolCursorRenderer,
2446
+ {
2447
+ cursor: toolCursor.cursor,
2448
+ point: toolCursor.point
2449
+ }
2450
+ ) : null
2451
+ ]
2270
2452
  }
2271
2453
  );
2272
2454
  }
@@ -3398,6 +3580,32 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
3398
3580
  }
3399
3581
  return ids;
3400
3582
  }
3583
+
3584
+ // src/native/native-tool-cursors.ts
3585
+ var ICON_SIZE = 24;
3586
+ var CENTER_HOTSPOT = { x: 12, y: 12 };
3587
+ function nativeCursorForVectorToolId(toolId) {
3588
+ switch (toolId) {
3589
+ case "rect":
3590
+ case "ellipse":
3591
+ case "architectural-cloud":
3592
+ case "line":
3593
+ case "arrow":
3594
+ case "laser":
3595
+ case "image":
3596
+ return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3597
+ case "draw":
3598
+ return { kind: "draw", size: ICON_SIZE, hotspot: { x: 4, y: 18 } };
3599
+ case "marker":
3600
+ return { kind: "marker", size: ICON_SIZE, hotspot: { x: 11, y: 14 } };
3601
+ case "eraser":
3602
+ return { kind: "eraser", size: ICON_SIZE, hotspot: { x: 12, y: 14 } };
3603
+ case "text":
3604
+ return { kind: "text", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3605
+ default:
3606
+ return null;
3607
+ }
3608
+ }
3401
3609
  var MIN_PLACE_SIZE = 8;
3402
3610
  var MIN_ARROW_DRAG_PX = 8;
3403
3611
  var TAP_PX = 20;
@@ -3458,6 +3666,12 @@ function collectIdsInRect(items, marquee) {
3458
3666
  }
3459
3667
  return out;
3460
3668
  }
3669
+ function screenPointFromPointerEvent(event) {
3670
+ const nativeEvent = event.nativeEvent;
3671
+ const x = Number.isFinite(nativeEvent.offsetX) ? nativeEvent.offsetX : nativeEvent.x;
3672
+ const y = Number.isFinite(nativeEvent.offsetY) ? nativeEvent.offsetY : nativeEvent.y;
3673
+ return { x, y };
3674
+ }
3461
3675
  function fitCameraToWorldRect(camera, viewportW, viewportH, worldRect, padding) {
3462
3676
  const r = normalizeRect(worldRect);
3463
3677
  if (r.width <= 0 || r.height <= 0 || viewportW <= 0 || viewportH <= 0) return;
@@ -3512,6 +3726,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3512
3726
  );
3513
3727
  const [eraserTrail, setEraserTrail] = useState([]);
3514
3728
  const [laserTrail, setLaserTrail] = useState([]);
3729
+ const [toolCursorPoint, setToolCursorPoint] = useState(null);
3515
3730
  const laserClearTimerRef = useRef(null);
3516
3731
  const strokeStyleRef = useRef({ ...DEFAULT_STROKE_STYLE });
3517
3732
  const markerStrokeStyleRef = useRef({ ...MARKER_TOOL_STYLE });
@@ -3610,6 +3825,23 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3610
3825
  const { width, height } = e.nativeEvent.layout;
3611
3826
  setSize({ width, height });
3612
3827
  }, []);
3828
+ const updateToolCursorPoint = useCallback(
3829
+ (point) => {
3830
+ if (!interactive) return;
3831
+ if (!nativeCursorForVectorToolId(toolIdRef.current)) return;
3832
+ setToolCursorPoint(point);
3833
+ },
3834
+ [interactive]
3835
+ );
3836
+ const hideToolCursor = useCallback(() => {
3837
+ setToolCursorPoint(null);
3838
+ }, []);
3839
+ const handlePointerMove = useCallback(
3840
+ (event) => {
3841
+ updateToolCursorPoint(screenPointFromPointerEvent(event));
3842
+ },
3843
+ [updateToolCursorPoint]
3844
+ );
3613
3845
  const selectedItems = useMemo(
3614
3846
  () => items.filter((it) => selectedIds.includes(it.id)),
3615
3847
  [items, selectedIds]
@@ -3633,9 +3865,11 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3633
3865
  const sx = evt.nativeEvent.locationX;
3634
3866
  const sy = evt.nativeEvent.locationY;
3635
3867
  if (touches && touches.length >= 2) {
3868
+ hideToolCursor();
3636
3869
  dragStateRef.current = { kind: "pan" };
3637
3870
  return;
3638
3871
  }
3872
+ updateToolCursorPoint({ x: sx, y: sy });
3639
3873
  if (!interactive) {
3640
3874
  dragStateRef.current = { kind: "pan" };
3641
3875
  return;
@@ -3761,6 +3995,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3761
3995
  const pageX = evt.nativeEvent.pageX;
3762
3996
  const pageY = evt.nativeEvent.pageY;
3763
3997
  if (touches && touches.length >= 2) {
3998
+ hideToolCursor();
3764
3999
  const t0 = touches[0];
3765
4000
  const t1 = touches[1];
3766
4001
  if (t0 && t1) {
@@ -3780,6 +4015,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3780
4015
  return;
3781
4016
  }
3782
4017
  lastPinchDist.current = null;
4018
+ updateToolCursorPoint({ x: sx, y: sy });
3783
4019
  const { worldX, worldY } = screenToWorld(sx, sy);
3784
4020
  const st = dragStateRef.current;
3785
4021
  if (st.kind === "pan") {
@@ -3885,6 +4121,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3885
4121
  onPanResponderRelease: (evt) => {
3886
4122
  lastPinchDist.current = null;
3887
4123
  lastPanPoint.current = null;
4124
+ hideToolCursor();
3888
4125
  const st = dragStateRef.current;
3889
4126
  if (st.kind === "draw") {
3890
4127
  dragStateRef.current = { kind: "idle" };
@@ -4068,6 +4305,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4068
4305
  onPanResponderTerminate: () => {
4069
4306
  lastPinchDist.current = null;
4070
4307
  lastPanPoint.current = null;
4308
+ hideToolCursor();
4071
4309
  dragStateRef.current = { kind: "idle" };
4072
4310
  setPlacementPreview(null);
4073
4311
  setLaserTrail([]);
@@ -4076,7 +4314,14 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4076
4314
  eraserPreviewIdSetRef.current.clear();
4077
4315
  }
4078
4316
  }),
4079
- [screenToWorld, requestRender, requestSelectToolAfterUse, interactive]
4317
+ [
4318
+ screenToWorld,
4319
+ requestRender,
4320
+ requestSelectToolAfterUse,
4321
+ interactive,
4322
+ updateToolCursorPoint,
4323
+ hideToolCursor
4324
+ ]
4080
4325
  );
4081
4326
  useImperativeHandle(
4082
4327
  ref,
@@ -4100,11 +4345,16 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4100
4345
  [requestRender, size]
4101
4346
  );
4102
4347
  const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
4348
+ const activeToolCursor = nativeCursorForVectorToolId(toolId);
4349
+ const toolCursor = activeToolCursor && toolCursorPoint ? { cursor: activeToolCursor, point: toolCursorPoint } : null;
4103
4350
  return /* @__PURE__ */ jsx(
4104
4351
  View,
4105
4352
  {
4106
4353
  style: { flex: 1, overflow: "hidden" },
4107
4354
  onLayout,
4355
+ onPointerMove: handlePointerMove,
4356
+ onPointerEnter: handlePointerMove,
4357
+ onPointerLeave: hideToolCursor,
4108
4358
  ...panResponder.panHandlers,
4109
4359
  children: size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4110
4360
  /* @__PURE__ */ jsx(
@@ -4130,7 +4380,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
4130
4380
  eraserPreviewItems: items.filter(
4131
4381
  (it) => eraserPreviewIds.includes(it.id)
4132
4382
  ),
4133
- previewStrokeStyle: strokeStyleState
4383
+ previewStrokeStyle: strokeStyleState,
4384
+ toolCursor
4134
4385
  }
4135
4386
  ),
4136
4387
  interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsx(