canvu-react 0.4.19 → 0.4.21
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.cjs +432 -684
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +1 -19
- package/dist/native.d.ts +1 -19
- package/dist/native.js +432 -684
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.cjs
CHANGED
|
@@ -2004,178 +2004,6 @@ function NativeShapeRenderer({ item }) {
|
|
|
2004
2004
|
}
|
|
2005
2005
|
return null;
|
|
2006
2006
|
}
|
|
2007
|
-
var CURSOR_STROKE = "#18181b";
|
|
2008
|
-
var CURSOR_STROKE_WIDTH = 2;
|
|
2009
|
-
var CROSSHAIR_STROKE_WIDTH = 1.5;
|
|
2010
|
-
function NativeDrawCursor() {
|
|
2011
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2012
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2013
|
-
reactNativeSkia.Path,
|
|
2014
|
-
{
|
|
2015
|
-
path: "M13 21h8",
|
|
2016
|
-
color: CURSOR_STROKE,
|
|
2017
|
-
style: "stroke",
|
|
2018
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2019
|
-
strokeCap: "round",
|
|
2020
|
-
strokeJoin: "round",
|
|
2021
|
-
antiAlias: true
|
|
2022
|
-
}
|
|
2023
|
-
),
|
|
2024
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2025
|
-
reactNativeSkia.Path,
|
|
2026
|
-
{
|
|
2027
|
-
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",
|
|
2028
|
-
color: CURSOR_STROKE,
|
|
2029
|
-
style: "stroke",
|
|
2030
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2031
|
-
strokeCap: "round",
|
|
2032
|
-
strokeJoin: "round",
|
|
2033
|
-
antiAlias: true
|
|
2034
|
-
}
|
|
2035
|
-
)
|
|
2036
|
-
] });
|
|
2037
|
-
}
|
|
2038
|
-
function NativeMarkerCursor() {
|
|
2039
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2040
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2041
|
-
reactNativeSkia.Path,
|
|
2042
|
-
{
|
|
2043
|
-
path: "m9 11-6 6v3h9l3-3",
|
|
2044
|
-
color: CURSOR_STROKE,
|
|
2045
|
-
style: "stroke",
|
|
2046
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2047
|
-
strokeCap: "round",
|
|
2048
|
-
strokeJoin: "round",
|
|
2049
|
-
antiAlias: true
|
|
2050
|
-
}
|
|
2051
|
-
),
|
|
2052
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2053
|
-
reactNativeSkia.Path,
|
|
2054
|
-
{
|
|
2055
|
-
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",
|
|
2056
|
-
color: CURSOR_STROKE,
|
|
2057
|
-
style: "stroke",
|
|
2058
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2059
|
-
strokeCap: "round",
|
|
2060
|
-
strokeJoin: "round",
|
|
2061
|
-
antiAlias: true
|
|
2062
|
-
}
|
|
2063
|
-
)
|
|
2064
|
-
] });
|
|
2065
|
-
}
|
|
2066
|
-
function NativeEraserCursor() {
|
|
2067
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2068
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2069
|
-
reactNativeSkia.Path,
|
|
2070
|
-
{
|
|
2071
|
-
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",
|
|
2072
|
-
color: CURSOR_STROKE,
|
|
2073
|
-
style: "stroke",
|
|
2074
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2075
|
-
strokeCap: "round",
|
|
2076
|
-
strokeJoin: "round",
|
|
2077
|
-
antiAlias: true
|
|
2078
|
-
}
|
|
2079
|
-
),
|
|
2080
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2081
|
-
reactNativeSkia.Path,
|
|
2082
|
-
{
|
|
2083
|
-
path: "m5.082 11.09 8.828 8.828",
|
|
2084
|
-
color: CURSOR_STROKE,
|
|
2085
|
-
style: "stroke",
|
|
2086
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2087
|
-
strokeCap: "round",
|
|
2088
|
-
strokeJoin: "round",
|
|
2089
|
-
antiAlias: true
|
|
2090
|
-
}
|
|
2091
|
-
)
|
|
2092
|
-
] });
|
|
2093
|
-
}
|
|
2094
|
-
function NativeCrosshairCursor() {
|
|
2095
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2096
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2097
|
-
reactNativeSkia.Line,
|
|
2098
|
-
{
|
|
2099
|
-
p1: reactNativeSkia.vec(12, 3),
|
|
2100
|
-
p2: reactNativeSkia.vec(12, 21),
|
|
2101
|
-
color: CURSOR_STROKE,
|
|
2102
|
-
style: "stroke",
|
|
2103
|
-
strokeWidth: CROSSHAIR_STROKE_WIDTH,
|
|
2104
|
-
strokeCap: "round",
|
|
2105
|
-
antiAlias: true
|
|
2106
|
-
}
|
|
2107
|
-
),
|
|
2108
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2109
|
-
reactNativeSkia.Line,
|
|
2110
|
-
{
|
|
2111
|
-
p1: reactNativeSkia.vec(3, 12),
|
|
2112
|
-
p2: reactNativeSkia.vec(21, 12),
|
|
2113
|
-
color: CURSOR_STROKE,
|
|
2114
|
-
style: "stroke",
|
|
2115
|
-
strokeWidth: CROSSHAIR_STROKE_WIDTH,
|
|
2116
|
-
strokeCap: "round",
|
|
2117
|
-
antiAlias: true
|
|
2118
|
-
}
|
|
2119
|
-
)
|
|
2120
|
-
] });
|
|
2121
|
-
}
|
|
2122
|
-
function NativeTextCursor() {
|
|
2123
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2124
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2125
|
-
reactNativeSkia.Line,
|
|
2126
|
-
{
|
|
2127
|
-
p1: reactNativeSkia.vec(12, 4),
|
|
2128
|
-
p2: reactNativeSkia.vec(12, 20),
|
|
2129
|
-
color: CURSOR_STROKE,
|
|
2130
|
-
style: "stroke",
|
|
2131
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2132
|
-
strokeCap: "round",
|
|
2133
|
-
antiAlias: true
|
|
2134
|
-
}
|
|
2135
|
-
),
|
|
2136
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2137
|
-
reactNativeSkia.Line,
|
|
2138
|
-
{
|
|
2139
|
-
p1: reactNativeSkia.vec(8, 4),
|
|
2140
|
-
p2: reactNativeSkia.vec(16, 4),
|
|
2141
|
-
color: CURSOR_STROKE,
|
|
2142
|
-
style: "stroke",
|
|
2143
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2144
|
-
strokeCap: "round",
|
|
2145
|
-
antiAlias: true
|
|
2146
|
-
}
|
|
2147
|
-
),
|
|
2148
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2149
|
-
reactNativeSkia.Line,
|
|
2150
|
-
{
|
|
2151
|
-
p1: reactNativeSkia.vec(8, 20),
|
|
2152
|
-
p2: reactNativeSkia.vec(16, 20),
|
|
2153
|
-
color: CURSOR_STROKE,
|
|
2154
|
-
style: "stroke",
|
|
2155
|
-
strokeWidth: CURSOR_STROKE_WIDTH,
|
|
2156
|
-
strokeCap: "round",
|
|
2157
|
-
antiAlias: true
|
|
2158
|
-
}
|
|
2159
|
-
)
|
|
2160
|
-
] });
|
|
2161
|
-
}
|
|
2162
|
-
function NativeToolCursorGlyph({
|
|
2163
|
-
cursor
|
|
2164
|
-
}) {
|
|
2165
|
-
if (cursor.kind === "draw") return /* @__PURE__ */ jsxRuntime.jsx(NativeDrawCursor, {});
|
|
2166
|
-
if (cursor.kind === "marker") return /* @__PURE__ */ jsxRuntime.jsx(NativeMarkerCursor, {});
|
|
2167
|
-
if (cursor.kind === "eraser") return /* @__PURE__ */ jsxRuntime.jsx(NativeEraserCursor, {});
|
|
2168
|
-
if (cursor.kind === "text") return /* @__PURE__ */ jsxRuntime.jsx(NativeTextCursor, {});
|
|
2169
|
-
return /* @__PURE__ */ jsxRuntime.jsx(NativeCrosshairCursor, {});
|
|
2170
|
-
}
|
|
2171
|
-
function NativeToolCursorRenderer({
|
|
2172
|
-
cursor,
|
|
2173
|
-
point
|
|
2174
|
-
}) {
|
|
2175
|
-
const x = point.x - cursor.hotspot.x;
|
|
2176
|
-
const y = point.y - cursor.hotspot.y;
|
|
2177
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { transform: [{ translateX: x }, { translateY: y }], children: /* @__PURE__ */ jsxRuntime.jsx(NativeToolCursorGlyph, { cursor }) });
|
|
2178
|
-
}
|
|
2179
2007
|
|
|
2180
2008
|
// src/native/native-overlay-style.ts
|
|
2181
2009
|
var ERASER_TINT = "#cbd5e1";
|
|
@@ -2232,8 +2060,7 @@ function NativeInteractionOverlay({
|
|
|
2232
2060
|
eraserTrail,
|
|
2233
2061
|
laserTrail,
|
|
2234
2062
|
eraserPreviewItems = [],
|
|
2235
|
-
previewStrokeStyle
|
|
2236
|
-
toolCursor
|
|
2063
|
+
previewStrokeStyle
|
|
2237
2064
|
}) {
|
|
2238
2065
|
const z = camera.zoom;
|
|
2239
2066
|
const camTransform = skiaCameraTransform(z, camera.x, camera.y);
|
|
@@ -2610,7 +2437,7 @@ function NativeInteractionOverlay({
|
|
|
2610
2437
|
] });
|
|
2611
2438
|
}, [laserTrail, z]);
|
|
2612
2439
|
if (width <= 0 || height <= 0) return null;
|
|
2613
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
2440
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2614
2441
|
reactNativeSkia.Canvas,
|
|
2615
2442
|
{
|
|
2616
2443
|
style: {
|
|
@@ -2621,22 +2448,13 @@ function NativeInteractionOverlay({
|
|
|
2621
2448
|
height
|
|
2622
2449
|
},
|
|
2623
2450
|
pointerEvents: "none",
|
|
2624
|
-
children: [
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
] }),
|
|
2632
|
-
toolCursor ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2633
|
-
NativeToolCursorRenderer,
|
|
2634
|
-
{
|
|
2635
|
-
cursor: toolCursor.cursor,
|
|
2636
|
-
point: toolCursor.point
|
|
2637
|
-
}
|
|
2638
|
-
) : null
|
|
2639
|
-
]
|
|
2451
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { transform: camTransform, children: [
|
|
2452
|
+
previewElements,
|
|
2453
|
+
laserTrailElements,
|
|
2454
|
+
eraserTrailElements,
|
|
2455
|
+
eraserPreviewElements,
|
|
2456
|
+
selectionElements
|
|
2457
|
+
] })
|
|
2640
2458
|
}
|
|
2641
2459
|
);
|
|
2642
2460
|
}
|
|
@@ -3974,39 +3792,6 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
|
|
|
3974
3792
|
return { ...item, x: nb.x, y: nb.y, bounds: nb };
|
|
3975
3793
|
}
|
|
3976
3794
|
|
|
3977
|
-
// src/native/native-tool-cursors.ts
|
|
3978
|
-
var ICON_SIZE = 24;
|
|
3979
|
-
var CENTER_HOTSPOT = { x: 12, y: 12 };
|
|
3980
|
-
function nativeCursorForVectorToolId(toolId) {
|
|
3981
|
-
switch (toolId) {
|
|
3982
|
-
case "rect":
|
|
3983
|
-
case "ellipse":
|
|
3984
|
-
case "architectural-cloud":
|
|
3985
|
-
case "line":
|
|
3986
|
-
case "arrow":
|
|
3987
|
-
case "laser":
|
|
3988
|
-
case "image":
|
|
3989
|
-
return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
|
|
3990
|
-
case "draw":
|
|
3991
|
-
return { kind: "draw", size: ICON_SIZE, hotspot: { x: 4, y: 18 } };
|
|
3992
|
-
case "marker":
|
|
3993
|
-
return { kind: "marker", size: ICON_SIZE, hotspot: { x: 11, y: 14 } };
|
|
3994
|
-
case "eraser":
|
|
3995
|
-
return { kind: "eraser", size: ICON_SIZE, hotspot: { x: 12, y: 14 } };
|
|
3996
|
-
case "text":
|
|
3997
|
-
return { kind: "text", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
|
|
3998
|
-
default:
|
|
3999
|
-
return null;
|
|
4000
|
-
}
|
|
4001
|
-
}
|
|
4002
|
-
function nativeCrosshairToolCursor() {
|
|
4003
|
-
return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
|
|
4004
|
-
}
|
|
4005
|
-
function nativeFallbackToolCursorPoint(size) {
|
|
4006
|
-
if (size.width <= 0 || size.height <= 0) return null;
|
|
4007
|
-
return { x: size.width / 2, y: size.height / 2 };
|
|
4008
|
-
}
|
|
4009
|
-
|
|
4010
3795
|
// src/native/native-vector-interactions.ts
|
|
4011
3796
|
var NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX = 24;
|
|
4012
3797
|
function supportsNativeResizeHandles(item) {
|
|
@@ -4151,7 +3936,6 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4151
3936
|
onCameraChange,
|
|
4152
3937
|
customPlacement,
|
|
4153
3938
|
customPlacements = [],
|
|
4154
|
-
customCrosshairToolIds = [],
|
|
4155
3939
|
toolbar,
|
|
4156
3940
|
showStyleInspector = false,
|
|
4157
3941
|
styleInspectorPlacement = "bottom"
|
|
@@ -4174,8 +3958,6 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4174
3958
|
customPlacementRef.current = customPlacement;
|
|
4175
3959
|
const customPlacementsRef = react.useRef(customPlacements);
|
|
4176
3960
|
customPlacementsRef.current = customPlacements;
|
|
4177
|
-
const customCrosshairToolIdsRef = react.useRef(customCrosshairToolIds);
|
|
4178
|
-
customCrosshairToolIdsRef.current = customCrosshairToolIds;
|
|
4179
3961
|
const onSelectionChangeRef = react.useRef(onSelectionChange);
|
|
4180
3962
|
onSelectionChangeRef.current = onSelectionChange;
|
|
4181
3963
|
const itemsRef = react.useRef(items);
|
|
@@ -4188,7 +3970,6 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4188
3970
|
);
|
|
4189
3971
|
const [eraserTrail, setEraserTrail] = react.useState([]);
|
|
4190
3972
|
const [laserTrail, setLaserTrail] = react.useState([]);
|
|
4191
|
-
const [toolCursorPoint, setToolCursorPoint] = react.useState(null);
|
|
4192
3973
|
const laserClearTimerRef = react.useRef(null);
|
|
4193
3974
|
const strokeStyleRef = react.useRef({ ...DEFAULT_STROKE_STYLE });
|
|
4194
3975
|
const markerStrokeStyleRef = react.useRef({ ...MARKER_TOOL_STYLE });
|
|
@@ -4283,60 +4064,14 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4283
4064
|
setCameraTick((n) => n + 1);
|
|
4284
4065
|
onCameraChangeRef.current?.();
|
|
4285
4066
|
}, []);
|
|
4286
|
-
const
|
|
4287
|
-
const
|
|
4288
|
-
|
|
4289
|
-
|
|
4067
|
+
const onLayout = react.useCallback((e) => {
|
|
4068
|
+
const { width, height } = e.nativeEvent.layout;
|
|
4069
|
+
setSize({ width, height });
|
|
4070
|
+
}, []);
|
|
4071
|
+
const updateToolCursorPoint = react.useCallback((_point) => {
|
|
4290
4072
|
}, []);
|
|
4291
|
-
const onLayout = react.useCallback(
|
|
4292
|
-
(e) => {
|
|
4293
|
-
const { width, height } = e.nativeEvent.layout;
|
|
4294
|
-
const nextSize = { width, height };
|
|
4295
|
-
setSize(nextSize);
|
|
4296
|
-
if (!interactive) {
|
|
4297
|
-
setToolCursorPoint(null);
|
|
4298
|
-
return;
|
|
4299
|
-
}
|
|
4300
|
-
if (!cursorForToolId(toolIdRef.current)) {
|
|
4301
|
-
setToolCursorPoint(null);
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
setToolCursorPoint(
|
|
4305
|
-
(current) => current ?? nativeFallbackToolCursorPoint(nextSize)
|
|
4306
|
-
);
|
|
4307
|
-
},
|
|
4308
|
-
[cursorForToolId, interactive]
|
|
4309
|
-
);
|
|
4310
|
-
const updateToolCursorPoint = react.useCallback(
|
|
4311
|
-
(point) => {
|
|
4312
|
-
if (!interactive) return;
|
|
4313
|
-
if (!cursorForToolId(toolIdRef.current)) return;
|
|
4314
|
-
setToolCursorPoint(point);
|
|
4315
|
-
},
|
|
4316
|
-
[cursorForToolId, interactive]
|
|
4317
|
-
);
|
|
4318
4073
|
const hideToolCursor = react.useCallback(() => {
|
|
4319
|
-
setToolCursorPoint(null);
|
|
4320
4074
|
}, []);
|
|
4321
|
-
const showFallbackToolCursor = react.useCallback(
|
|
4322
|
-
(nextToolId) => {
|
|
4323
|
-
if (!interactive) {
|
|
4324
|
-
setToolCursorPoint(null);
|
|
4325
|
-
return;
|
|
4326
|
-
}
|
|
4327
|
-
if (!cursorForToolId(nextToolId)) {
|
|
4328
|
-
setToolCursorPoint(null);
|
|
4329
|
-
return;
|
|
4330
|
-
}
|
|
4331
|
-
setToolCursorPoint(
|
|
4332
|
-
(current) => current ?? nativeFallbackToolCursorPoint(size)
|
|
4333
|
-
);
|
|
4334
|
-
},
|
|
4335
|
-
[cursorForToolId, interactive, size]
|
|
4336
|
-
);
|
|
4337
|
-
react.useEffect(() => {
|
|
4338
|
-
showFallbackToolCursor(toolId);
|
|
4339
|
-
}, [showFallbackToolCursor, toolId]);
|
|
4340
4075
|
const selectedItems = react.useMemo(
|
|
4341
4076
|
() => items.filter((it) => selectedIds.includes(it.id)),
|
|
4342
4077
|
[items, selectedIds]
|
|
@@ -4349,6 +4084,196 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4349
4084
|
const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
|
|
4350
4085
|
const lastPinchDist = react.useRef(null);
|
|
4351
4086
|
const lastPanPoint = react.useRef(null);
|
|
4087
|
+
const beginDragAtScreenPoint = react.useCallback(
|
|
4088
|
+
(point) => {
|
|
4089
|
+
lastPinchDist.current = null;
|
|
4090
|
+
lastPanPoint.current = null;
|
|
4091
|
+
const sx = point.x;
|
|
4092
|
+
const sy = point.y;
|
|
4093
|
+
updateToolCursorPoint(point);
|
|
4094
|
+
if (!interactive) {
|
|
4095
|
+
dragStateRef.current = { kind: "pan" };
|
|
4096
|
+
return;
|
|
4097
|
+
}
|
|
4098
|
+
const tool = toolIdRef.current;
|
|
4099
|
+
const cam = cameraRef.current;
|
|
4100
|
+
if (!cam) return;
|
|
4101
|
+
const { worldX, worldY } = screenToWorld(sx, sy);
|
|
4102
|
+
if (tool === "hand") {
|
|
4103
|
+
dragStateRef.current = { kind: "pan" };
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
4106
|
+
if (tool === "select") {
|
|
4107
|
+
const currentSelectedIds = selectedIdsRef.current;
|
|
4108
|
+
const selectedItem = currentSelectedIds.length === 1 ? itemsRef.current.find((item) => item.id === currentSelectedIds[0]) : void 0;
|
|
4109
|
+
const selectionHandle = hitTestNativeSelectionHandle({
|
|
4110
|
+
selectedItem,
|
|
4111
|
+
selectedCount: currentSelectedIds.length,
|
|
4112
|
+
worldPoint: { x: worldX, y: worldY },
|
|
4113
|
+
zoom: cam.zoom
|
|
4114
|
+
});
|
|
4115
|
+
if (selectionHandle && selectedItem) {
|
|
4116
|
+
if (selectionHandle.kind === "rotate") {
|
|
4117
|
+
const rotationStart = nativeRotationDragStart({
|
|
4118
|
+
item: selectedItem,
|
|
4119
|
+
worldPoint: { x: worldX, y: worldY }
|
|
4120
|
+
});
|
|
4121
|
+
dragStateRef.current = {
|
|
4122
|
+
kind: "rotate",
|
|
4123
|
+
id: selectedItem.id,
|
|
4124
|
+
snapshot: selectedItem,
|
|
4125
|
+
...rotationStart
|
|
4126
|
+
};
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
dragStateRef.current = {
|
|
4130
|
+
kind: "resize",
|
|
4131
|
+
id: selectedItem.id,
|
|
4132
|
+
handle: selectionHandle.handle,
|
|
4133
|
+
snapshot: selectedItem,
|
|
4134
|
+
start: {
|
|
4135
|
+
bounds: selectedItem.bounds,
|
|
4136
|
+
line: selectedItem.line
|
|
4137
|
+
}
|
|
4138
|
+
};
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
const hit = hitTestWorldPoint(itemsRef.current, worldX, worldY, {
|
|
4142
|
+
lineHitWorld: 10 / cam.zoom,
|
|
4143
|
+
ignoreLocked: true
|
|
4144
|
+
});
|
|
4145
|
+
if (hit) {
|
|
4146
|
+
const cur = selectedIdsRef.current;
|
|
4147
|
+
const ids = cur.includes(hit.id) ? [...cur] : [hit.id];
|
|
4148
|
+
const snapshots = {};
|
|
4149
|
+
for (const id of ids) {
|
|
4150
|
+
const it = itemsRef.current.find((i) => i.id === id);
|
|
4151
|
+
if (it) snapshots[id] = it;
|
|
4152
|
+
}
|
|
4153
|
+
dragStateRef.current = {
|
|
4154
|
+
kind: "move",
|
|
4155
|
+
ids,
|
|
4156
|
+
snapshots,
|
|
4157
|
+
startWorld: { x: worldX, y: worldY }
|
|
4158
|
+
};
|
|
4159
|
+
if (!cur.includes(hit.id)) {
|
|
4160
|
+
onSelectionChangeRef.current?.([hit.id]);
|
|
4161
|
+
}
|
|
4162
|
+
} else {
|
|
4163
|
+
onSelectionChangeRef.current?.([]);
|
|
4164
|
+
dragStateRef.current = {
|
|
4165
|
+
kind: "marquee",
|
|
4166
|
+
startWorld: { x: worldX, y: worldY }
|
|
4167
|
+
};
|
|
4168
|
+
setPlacementPreview({
|
|
4169
|
+
kind: "marquee",
|
|
4170
|
+
rect: { x: worldX, y: worldY, width: 0, height: 0 }
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
4175
|
+
if (tool === "draw" || tool === "marker" || tool === "laser") {
|
|
4176
|
+
dragStateRef.current = {
|
|
4177
|
+
kind: "draw",
|
|
4178
|
+
tool,
|
|
4179
|
+
points: [{ x: worldX, y: worldY }]
|
|
4180
|
+
};
|
|
4181
|
+
if (tool === "laser") {
|
|
4182
|
+
if (laserClearTimerRef.current) {
|
|
4183
|
+
clearTimeout(laserClearTimerRef.current);
|
|
4184
|
+
laserClearTimerRef.current = null;
|
|
4185
|
+
}
|
|
4186
|
+
setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
4187
|
+
} else {
|
|
4188
|
+
setPlacementPreview({
|
|
4189
|
+
kind: "stroke",
|
|
4190
|
+
tool,
|
|
4191
|
+
points: [{ x: worldX, y: worldY }],
|
|
4192
|
+
style: { ...strokeStyleRef.current }
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
if (tool === "eraser") {
|
|
4198
|
+
dragStateRef.current = { kind: "erase" };
|
|
4199
|
+
eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
|
|
4200
|
+
setEraserPreviewIds([]);
|
|
4201
|
+
setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
4202
|
+
const toErase = collectEraserTargetsAtWorldPoint(
|
|
4203
|
+
itemsRef.current,
|
|
4204
|
+
worldX,
|
|
4205
|
+
worldY,
|
|
4206
|
+
{ lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
|
|
4207
|
+
);
|
|
4208
|
+
for (const id of toErase) {
|
|
4209
|
+
eraserPreviewIdSetRef.current.add(id);
|
|
4210
|
+
}
|
|
4211
|
+
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
if (isPlacementTool(tool)) {
|
|
4215
|
+
dragStateRef.current = {
|
|
4216
|
+
kind: "place",
|
|
4217
|
+
tool,
|
|
4218
|
+
startWorld: { x: worldX, y: worldY },
|
|
4219
|
+
startScreen: { x: sx, y: sy }
|
|
4220
|
+
};
|
|
4221
|
+
setPlacementPreview(
|
|
4222
|
+
placementPreviewForTool(
|
|
4223
|
+
tool,
|
|
4224
|
+
{ x: worldX, y: worldY },
|
|
4225
|
+
{
|
|
4226
|
+
x: worldX,
|
|
4227
|
+
y: worldY
|
|
4228
|
+
}
|
|
4229
|
+
)
|
|
4230
|
+
);
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
const customPlacement2 = resolveNativeCustomPlacement(
|
|
4234
|
+
tool,
|
|
4235
|
+
customPlacementRef.current,
|
|
4236
|
+
customPlacementsRef.current
|
|
4237
|
+
);
|
|
4238
|
+
if (customPlacement2) {
|
|
4239
|
+
dragStateRef.current = {
|
|
4240
|
+
kind: "custom-place",
|
|
4241
|
+
tool,
|
|
4242
|
+
placement: customPlacement2,
|
|
4243
|
+
startWorld: { x: worldX, y: worldY },
|
|
4244
|
+
startScreen: { x: sx, y: sy }
|
|
4245
|
+
};
|
|
4246
|
+
setPlacementPreview({
|
|
4247
|
+
kind: "rect",
|
|
4248
|
+
rect: { x: worldX, y: worldY, width: 0, height: 0 }
|
|
4249
|
+
});
|
|
4250
|
+
return;
|
|
4251
|
+
}
|
|
4252
|
+
if (tool === "note" || tool === "text") {
|
|
4253
|
+
dragStateRef.current = {
|
|
4254
|
+
kind: "tap",
|
|
4255
|
+
tool,
|
|
4256
|
+
startWorld: { x: worldX, y: worldY },
|
|
4257
|
+
startScreen: { x: sx, y: sy }
|
|
4258
|
+
};
|
|
4259
|
+
return;
|
|
4260
|
+
}
|
|
4261
|
+
const handleWorldPointerDown = onWorldPointerDownRef.current;
|
|
4262
|
+
if (handleWorldPointerDown) {
|
|
4263
|
+
handleWorldPointerDown({
|
|
4264
|
+
toolId: tool,
|
|
4265
|
+
worldX,
|
|
4266
|
+
worldY,
|
|
4267
|
+
screenX: sx,
|
|
4268
|
+
screenY: sy
|
|
4269
|
+
});
|
|
4270
|
+
requestSelectToolAfterUse();
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
dragStateRef.current = { kind: "pan" };
|
|
4274
|
+
},
|
|
4275
|
+
[interactive, requestSelectToolAfterUse, screenToWorld, updateToolCursorPoint]
|
|
4276
|
+
);
|
|
4352
4277
|
const applyDragMoveAtScreenPoint = react.useCallback(
|
|
4353
4278
|
(point, pagePoint) => {
|
|
4354
4279
|
const cam = cameraRef.current;
|
|
@@ -4485,6 +4410,220 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4485
4410
|
},
|
|
4486
4411
|
[requestRender, screenToWorld, updateToolCursorPoint]
|
|
4487
4412
|
);
|
|
4413
|
+
const finishDragAtScreenPoint = react.useCallback(
|
|
4414
|
+
(point) => {
|
|
4415
|
+
lastPinchDist.current = null;
|
|
4416
|
+
lastPanPoint.current = null;
|
|
4417
|
+
updateToolCursorPoint(point);
|
|
4418
|
+
const st = dragStateRef.current;
|
|
4419
|
+
if (st.kind === "draw") {
|
|
4420
|
+
dragStateRef.current = { kind: "idle" };
|
|
4421
|
+
setPlacementPreview(null);
|
|
4422
|
+
if (st.tool === "laser") {
|
|
4423
|
+
if (laserClearTimerRef.current) {
|
|
4424
|
+
clearTimeout(laserClearTimerRef.current);
|
|
4425
|
+
}
|
|
4426
|
+
laserClearTimerRef.current = setTimeout(() => {
|
|
4427
|
+
setLaserTrail([]);
|
|
4428
|
+
laserClearTimerRef.current = null;
|
|
4429
|
+
}, 650);
|
|
4430
|
+
requestSelectToolAfterUse();
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
if (st.points.length < 1) return;
|
|
4434
|
+
const change = onItemsChangeRef.current;
|
|
4435
|
+
if (!change) return;
|
|
4436
|
+
const id = createShapeId();
|
|
4437
|
+
const item = createFreehandStrokeItem(
|
|
4438
|
+
id,
|
|
4439
|
+
st.points,
|
|
4440
|
+
st.tool,
|
|
4441
|
+
strokeStyleRef.current
|
|
4442
|
+
);
|
|
4443
|
+
if (item) {
|
|
4444
|
+
change([...itemsRef.current, item]);
|
|
4445
|
+
}
|
|
4446
|
+
requestSelectToolAfterUse();
|
|
4447
|
+
return;
|
|
4448
|
+
}
|
|
4449
|
+
if (st.kind === "move") {
|
|
4450
|
+
dragStateRef.current = { kind: "idle" };
|
|
4451
|
+
return;
|
|
4452
|
+
}
|
|
4453
|
+
if (st.kind === "resize" || st.kind === "rotate") {
|
|
4454
|
+
dragStateRef.current = { kind: "idle" };
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4457
|
+
if (st.kind === "marquee") {
|
|
4458
|
+
dragStateRef.current = { kind: "idle" };
|
|
4459
|
+
setPlacementPreview(null);
|
|
4460
|
+
const cam = cameraRef.current;
|
|
4461
|
+
if (!cam) return;
|
|
4462
|
+
const { worldX, worldY } = screenToWorld(point.x, point.y);
|
|
4463
|
+
const a = st.startWorld;
|
|
4464
|
+
if (typeof worldX !== "number" || typeof worldY !== "number") return;
|
|
4465
|
+
const raw = {
|
|
4466
|
+
x: Math.min(a.x, worldX),
|
|
4467
|
+
y: Math.min(a.y, worldY),
|
|
4468
|
+
width: Math.abs(worldX - a.x),
|
|
4469
|
+
height: Math.abs(worldY - a.y)
|
|
4470
|
+
};
|
|
4471
|
+
const picked = collectIdsInRect(itemsRef.current, raw);
|
|
4472
|
+
onSelectionChangeRef.current?.(picked);
|
|
4473
|
+
return;
|
|
4474
|
+
}
|
|
4475
|
+
if (st.kind === "erase") {
|
|
4476
|
+
const change = onItemsChangeRef.current;
|
|
4477
|
+
if (change && eraserPreviewIdSetRef.current.size > 0) {
|
|
4478
|
+
const idSet = new Set(eraserPreviewIdSetRef.current);
|
|
4479
|
+
change(itemsRef.current.filter((i) => !idSet.has(i.id)));
|
|
4480
|
+
}
|
|
4481
|
+
eraserPreviewIdSetRef.current.clear();
|
|
4482
|
+
setEraserPreviewIds([]);
|
|
4483
|
+
setEraserTrail([]);
|
|
4484
|
+
dragStateRef.current = { kind: "idle" };
|
|
4485
|
+
requestSelectToolAfterUse();
|
|
4486
|
+
return;
|
|
4487
|
+
}
|
|
4488
|
+
if (st.kind === "place") {
|
|
4489
|
+
dragStateRef.current = { kind: "idle" };
|
|
4490
|
+
setPlacementPreview(null);
|
|
4491
|
+
const change = onItemsChangeRef.current;
|
|
4492
|
+
if (!change) return;
|
|
4493
|
+
const { worldX, worldY } = screenToWorld(point.x, point.y);
|
|
4494
|
+
const a = st.startWorld;
|
|
4495
|
+
const b = { x: worldX, y: worldY };
|
|
4496
|
+
const screenDx = point.x - st.startScreen.x;
|
|
4497
|
+
const screenDy = point.y - st.startScreen.y;
|
|
4498
|
+
if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
|
|
4499
|
+
return;
|
|
4500
|
+
}
|
|
4501
|
+
let raw = rectFromCorners(a, b);
|
|
4502
|
+
let br = normalizeRect(raw);
|
|
4503
|
+
let lineStart = a;
|
|
4504
|
+
let lineEnd = b;
|
|
4505
|
+
if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
|
|
4506
|
+
const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
4507
|
+
const defaults = defaultPlacementWorld(st.tool, center);
|
|
4508
|
+
raw = defaults.raw;
|
|
4509
|
+
br = normalizeRect(raw);
|
|
4510
|
+
if (defaults.lineWorld) {
|
|
4511
|
+
const [defaultStart, defaultEnd] = defaults.lineWorld;
|
|
4512
|
+
lineStart = defaultStart;
|
|
4513
|
+
lineEnd = defaultEnd;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
const id = createShapeId();
|
|
4517
|
+
const style = strokeStyleRef.current;
|
|
4518
|
+
if (st.tool === "rect") {
|
|
4519
|
+
change([...itemsRef.current, createRectangleItem(id, raw, style)]);
|
|
4520
|
+
onSelectionChangeRef.current?.([id]);
|
|
4521
|
+
requestSelectToolAfterUse();
|
|
4522
|
+
return;
|
|
4523
|
+
}
|
|
4524
|
+
if (st.tool === "ellipse") {
|
|
4525
|
+
change([...itemsRef.current, createEllipseItem(id, raw, style)]);
|
|
4526
|
+
onSelectionChangeRef.current?.([id]);
|
|
4527
|
+
requestSelectToolAfterUse();
|
|
4528
|
+
return;
|
|
4529
|
+
}
|
|
4530
|
+
if (st.tool === "architectural-cloud") {
|
|
4531
|
+
change([
|
|
4532
|
+
...itemsRef.current,
|
|
4533
|
+
createArchitecturalCloudItem(id, raw, style)
|
|
4534
|
+
]);
|
|
4535
|
+
onSelectionChangeRef.current?.([id]);
|
|
4536
|
+
requestSelectToolAfterUse();
|
|
4537
|
+
return;
|
|
4538
|
+
}
|
|
4539
|
+
const line = lineEndpointsToLocal(br, lineStart, lineEnd);
|
|
4540
|
+
change([...itemsRef.current, createLineItem(id, br, line, st.tool, style)]);
|
|
4541
|
+
onSelectionChangeRef.current?.([id]);
|
|
4542
|
+
requestSelectToolAfterUse();
|
|
4543
|
+
return;
|
|
4544
|
+
}
|
|
4545
|
+
if (st.kind === "custom-place") {
|
|
4546
|
+
dragStateRef.current = { kind: "idle" };
|
|
4547
|
+
setPlacementPreview(null);
|
|
4548
|
+
const change = onItemsChangeRef.current;
|
|
4549
|
+
if (!change) return;
|
|
4550
|
+
const { worldX, worldY } = screenToWorld(point.x, point.y);
|
|
4551
|
+
const center = {
|
|
4552
|
+
x: (st.startWorld.x + worldX) / 2,
|
|
4553
|
+
y: (st.startWorld.y + worldY) / 2
|
|
4554
|
+
};
|
|
4555
|
+
const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
|
|
4556
|
+
const normalized = normalizeRect(raw);
|
|
4557
|
+
const bounds = normalized.width < MIN_PLACE_SIZE || normalized.height < MIN_PLACE_SIZE ? {
|
|
4558
|
+
x: center.x - 60,
|
|
4559
|
+
y: center.y - 40,
|
|
4560
|
+
width: 120,
|
|
4561
|
+
height: 80
|
|
4562
|
+
} : normalized;
|
|
4563
|
+
const id = createShapeId();
|
|
4564
|
+
const item = st.placement.createItem({ id, bounds });
|
|
4565
|
+
change([...itemsRef.current, item]);
|
|
4566
|
+
onSelectionChangeRef.current?.([id]);
|
|
4567
|
+
requestSelectToolAfterUse();
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4570
|
+
if (st.kind === "tap") {
|
|
4571
|
+
dragStateRef.current = { kind: "idle" };
|
|
4572
|
+
const screenDx = point.x - st.startScreen.x;
|
|
4573
|
+
const screenDy = point.y - st.startScreen.y;
|
|
4574
|
+
if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
|
|
4575
|
+
const change = onItemsChangeRef.current;
|
|
4576
|
+
if (!change) return;
|
|
4577
|
+
if (st.tool === "text") {
|
|
4578
|
+
const id = createShapeId();
|
|
4579
|
+
const item = createTextItem(
|
|
4580
|
+
id,
|
|
4581
|
+
{
|
|
4582
|
+
x: st.startWorld.x - 4,
|
|
4583
|
+
y: st.startWorld.y - 18,
|
|
4584
|
+
width: 160,
|
|
4585
|
+
height: 26
|
|
4586
|
+
},
|
|
4587
|
+
"Text",
|
|
4588
|
+
strokeStyleRef.current,
|
|
4589
|
+
18
|
|
4590
|
+
);
|
|
4591
|
+
change([...itemsRef.current, item]);
|
|
4592
|
+
onSelectionChangeRef.current?.([id]);
|
|
4593
|
+
requestSelectToolAfterUse();
|
|
4594
|
+
}
|
|
4595
|
+
if (st.tool === "note") {
|
|
4596
|
+
const id = createShapeId();
|
|
4597
|
+
const note = {
|
|
4598
|
+
id,
|
|
4599
|
+
x: st.startWorld.x - 70,
|
|
4600
|
+
y: st.startWorld.y - 40,
|
|
4601
|
+
bounds: {
|
|
4602
|
+
x: st.startWorld.x - 70,
|
|
4603
|
+
y: st.startWorld.y - 40,
|
|
4604
|
+
width: 140,
|
|
4605
|
+
height: 80
|
|
4606
|
+
},
|
|
4607
|
+
childrenSvg: `<rect width="140" height="80" rx="8" fill="#fef08a" stroke="#facc15" stroke-width="1" /><text x="10" y="22" fill="#1f2937" font-size="12" font-family="system-ui">Nota</text>`,
|
|
4608
|
+
toolKind: "custom"
|
|
4609
|
+
};
|
|
4610
|
+
change([...itemsRef.current, note]);
|
|
4611
|
+
onSelectionChangeRef.current?.([id]);
|
|
4612
|
+
requestSelectToolAfterUse();
|
|
4613
|
+
}
|
|
4614
|
+
return;
|
|
4615
|
+
}
|
|
4616
|
+
dragStateRef.current = { kind: "idle" };
|
|
4617
|
+
},
|
|
4618
|
+
[requestSelectToolAfterUse, screenToWorld, updateToolCursorPoint]
|
|
4619
|
+
);
|
|
4620
|
+
const handlePointerDown = react.useCallback(
|
|
4621
|
+
(event) => {
|
|
4622
|
+
const point = screenPointFromPointerEvent(event);
|
|
4623
|
+
beginDragAtScreenPoint(point);
|
|
4624
|
+
},
|
|
4625
|
+
[beginDragAtScreenPoint]
|
|
4626
|
+
);
|
|
4488
4627
|
const handlePointerMove = react.useCallback(
|
|
4489
4628
|
(event) => {
|
|
4490
4629
|
const point = screenPointFromPointerEvent(event);
|
|
@@ -4496,13 +4635,18 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4496
4635
|
},
|
|
4497
4636
|
[applyDragMoveAtScreenPoint, updateToolCursorPoint]
|
|
4498
4637
|
);
|
|
4638
|
+
const handlePointerUp = react.useCallback(
|
|
4639
|
+
(event) => {
|
|
4640
|
+
const point = screenPointFromPointerEvent(event);
|
|
4641
|
+
finishDragAtScreenPoint(point);
|
|
4642
|
+
},
|
|
4643
|
+
[finishDragAtScreenPoint]
|
|
4644
|
+
);
|
|
4499
4645
|
const panResponder = react.useMemo(
|
|
4500
4646
|
() => reactNative.PanResponder.create({
|
|
4501
4647
|
onStartShouldSetPanResponder: () => true,
|
|
4502
4648
|
onMoveShouldSetPanResponder: () => true,
|
|
4503
4649
|
onPanResponderGrant: (evt) => {
|
|
4504
|
-
lastPinchDist.current = null;
|
|
4505
|
-
lastPanPoint.current = null;
|
|
4506
4650
|
const touches = evt.nativeEvent.touches;
|
|
4507
4651
|
const sx = evt.nativeEvent.locationX;
|
|
4508
4652
|
const sy = evt.nativeEvent.locationY;
|
|
@@ -4511,187 +4655,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4511
4655
|
dragStateRef.current = { kind: "pan" };
|
|
4512
4656
|
return;
|
|
4513
4657
|
}
|
|
4514
|
-
|
|
4515
|
-
if (!interactive) {
|
|
4516
|
-
dragStateRef.current = { kind: "pan" };
|
|
4517
|
-
return;
|
|
4518
|
-
}
|
|
4519
|
-
const tool = toolIdRef.current;
|
|
4520
|
-
const cam = cameraRef.current;
|
|
4521
|
-
if (!cam) return;
|
|
4522
|
-
const { worldX, worldY } = screenToWorld(sx, sy);
|
|
4523
|
-
if (tool === "hand") {
|
|
4524
|
-
dragStateRef.current = { kind: "pan" };
|
|
4525
|
-
return;
|
|
4526
|
-
}
|
|
4527
|
-
if (tool === "select") {
|
|
4528
|
-
const currentSelectedIds = selectedIdsRef.current;
|
|
4529
|
-
const selectedItem = currentSelectedIds.length === 1 ? itemsRef.current.find((item) => item.id === currentSelectedIds[0]) : void 0;
|
|
4530
|
-
const selectionHandle = hitTestNativeSelectionHandle({
|
|
4531
|
-
selectedItem,
|
|
4532
|
-
selectedCount: currentSelectedIds.length,
|
|
4533
|
-
worldPoint: { x: worldX, y: worldY },
|
|
4534
|
-
zoom: cam.zoom
|
|
4535
|
-
});
|
|
4536
|
-
if (selectionHandle && selectedItem) {
|
|
4537
|
-
if (selectionHandle.kind === "rotate") {
|
|
4538
|
-
const rotationStart = nativeRotationDragStart({
|
|
4539
|
-
item: selectedItem,
|
|
4540
|
-
worldPoint: { x: worldX, y: worldY }
|
|
4541
|
-
});
|
|
4542
|
-
dragStateRef.current = {
|
|
4543
|
-
kind: "rotate",
|
|
4544
|
-
id: selectedItem.id,
|
|
4545
|
-
snapshot: selectedItem,
|
|
4546
|
-
...rotationStart
|
|
4547
|
-
};
|
|
4548
|
-
return;
|
|
4549
|
-
}
|
|
4550
|
-
dragStateRef.current = {
|
|
4551
|
-
kind: "resize",
|
|
4552
|
-
id: selectedItem.id,
|
|
4553
|
-
handle: selectionHandle.handle,
|
|
4554
|
-
snapshot: selectedItem,
|
|
4555
|
-
start: {
|
|
4556
|
-
bounds: selectedItem.bounds,
|
|
4557
|
-
line: selectedItem.line
|
|
4558
|
-
}
|
|
4559
|
-
};
|
|
4560
|
-
return;
|
|
4561
|
-
}
|
|
4562
|
-
const hit = hitTestWorldPoint(itemsRef.current, worldX, worldY, {
|
|
4563
|
-
lineHitWorld: 10 / cam.zoom,
|
|
4564
|
-
ignoreLocked: true
|
|
4565
|
-
});
|
|
4566
|
-
if (hit) {
|
|
4567
|
-
const cur = selectedIdsRef.current;
|
|
4568
|
-
const ids = cur.includes(hit.id) ? [...cur] : [hit.id];
|
|
4569
|
-
const snapshots = {};
|
|
4570
|
-
for (const id of ids) {
|
|
4571
|
-
const it = itemsRef.current.find((i) => i.id === id);
|
|
4572
|
-
if (it) snapshots[id] = it;
|
|
4573
|
-
}
|
|
4574
|
-
dragStateRef.current = {
|
|
4575
|
-
kind: "move",
|
|
4576
|
-
ids,
|
|
4577
|
-
snapshots,
|
|
4578
|
-
startWorld: { x: worldX, y: worldY }
|
|
4579
|
-
};
|
|
4580
|
-
if (!cur.includes(hit.id)) {
|
|
4581
|
-
onSelectionChangeRef.current?.([hit.id]);
|
|
4582
|
-
}
|
|
4583
|
-
} else {
|
|
4584
|
-
onSelectionChangeRef.current?.([]);
|
|
4585
|
-
dragStateRef.current = {
|
|
4586
|
-
kind: "marquee",
|
|
4587
|
-
startWorld: { x: worldX, y: worldY }
|
|
4588
|
-
};
|
|
4589
|
-
setPlacementPreview({
|
|
4590
|
-
kind: "marquee",
|
|
4591
|
-
rect: { x: worldX, y: worldY, width: 0, height: 0 }
|
|
4592
|
-
});
|
|
4593
|
-
}
|
|
4594
|
-
return;
|
|
4595
|
-
}
|
|
4596
|
-
if (tool === "draw" || tool === "marker" || tool === "laser") {
|
|
4597
|
-
dragStateRef.current = {
|
|
4598
|
-
kind: "draw",
|
|
4599
|
-
tool,
|
|
4600
|
-
points: [{ x: worldX, y: worldY }]
|
|
4601
|
-
};
|
|
4602
|
-
if (tool === "laser") {
|
|
4603
|
-
if (laserClearTimerRef.current) {
|
|
4604
|
-
clearTimeout(laserClearTimerRef.current);
|
|
4605
|
-
laserClearTimerRef.current = null;
|
|
4606
|
-
}
|
|
4607
|
-
setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
4608
|
-
} else {
|
|
4609
|
-
setPlacementPreview({
|
|
4610
|
-
kind: "stroke",
|
|
4611
|
-
tool,
|
|
4612
|
-
points: [{ x: worldX, y: worldY }],
|
|
4613
|
-
style: { ...strokeStyleRef.current }
|
|
4614
|
-
});
|
|
4615
|
-
}
|
|
4616
|
-
return;
|
|
4617
|
-
}
|
|
4618
|
-
if (tool === "eraser") {
|
|
4619
|
-
dragStateRef.current = { kind: "erase" };
|
|
4620
|
-
eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
|
|
4621
|
-
setEraserPreviewIds([]);
|
|
4622
|
-
setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
4623
|
-
const toErase = collectEraserTargetsAtWorldPoint(
|
|
4624
|
-
itemsRef.current,
|
|
4625
|
-
worldX,
|
|
4626
|
-
worldY,
|
|
4627
|
-
{ lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
|
|
4628
|
-
);
|
|
4629
|
-
for (const id of toErase) {
|
|
4630
|
-
eraserPreviewIdSetRef.current.add(id);
|
|
4631
|
-
}
|
|
4632
|
-
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
4633
|
-
return;
|
|
4634
|
-
}
|
|
4635
|
-
if (isPlacementTool(tool)) {
|
|
4636
|
-
dragStateRef.current = {
|
|
4637
|
-
kind: "place",
|
|
4638
|
-
tool,
|
|
4639
|
-
startWorld: { x: worldX, y: worldY },
|
|
4640
|
-
startScreen: { x: sx, y: sy }
|
|
4641
|
-
};
|
|
4642
|
-
setPlacementPreview(
|
|
4643
|
-
placementPreviewForTool(
|
|
4644
|
-
tool,
|
|
4645
|
-
{ x: worldX, y: worldY },
|
|
4646
|
-
{
|
|
4647
|
-
x: worldX,
|
|
4648
|
-
y: worldY
|
|
4649
|
-
}
|
|
4650
|
-
)
|
|
4651
|
-
);
|
|
4652
|
-
return;
|
|
4653
|
-
}
|
|
4654
|
-
const customPlacement2 = resolveNativeCustomPlacement(
|
|
4655
|
-
tool,
|
|
4656
|
-
customPlacementRef.current,
|
|
4657
|
-
customPlacementsRef.current
|
|
4658
|
-
);
|
|
4659
|
-
if (customPlacement2) {
|
|
4660
|
-
dragStateRef.current = {
|
|
4661
|
-
kind: "custom-place",
|
|
4662
|
-
tool,
|
|
4663
|
-
placement: customPlacement2,
|
|
4664
|
-
startWorld: { x: worldX, y: worldY },
|
|
4665
|
-
startScreen: { x: sx, y: sy }
|
|
4666
|
-
};
|
|
4667
|
-
setPlacementPreview({
|
|
4668
|
-
kind: "rect",
|
|
4669
|
-
rect: { x: worldX, y: worldY, width: 0, height: 0 }
|
|
4670
|
-
});
|
|
4671
|
-
return;
|
|
4672
|
-
}
|
|
4673
|
-
if (tool === "note" || tool === "text") {
|
|
4674
|
-
dragStateRef.current = {
|
|
4675
|
-
kind: "tap",
|
|
4676
|
-
tool,
|
|
4677
|
-
startWorld: { x: worldX, y: worldY },
|
|
4678
|
-
startScreen: { x: sx, y: sy }
|
|
4679
|
-
};
|
|
4680
|
-
return;
|
|
4681
|
-
}
|
|
4682
|
-
const handleWorldPointerDown = onWorldPointerDownRef.current;
|
|
4683
|
-
if (handleWorldPointerDown) {
|
|
4684
|
-
handleWorldPointerDown({
|
|
4685
|
-
toolId: tool,
|
|
4686
|
-
worldX,
|
|
4687
|
-
worldY,
|
|
4688
|
-
screenX: sx,
|
|
4689
|
-
screenY: sy
|
|
4690
|
-
});
|
|
4691
|
-
requestSelectToolAfterUse();
|
|
4692
|
-
return;
|
|
4693
|
-
}
|
|
4694
|
-
dragStateRef.current = { kind: "pan" };
|
|
4658
|
+
beginDragAtScreenPoint({ x: sx, y: sy });
|
|
4695
4659
|
},
|
|
4696
4660
|
onPanResponderMove: (evt) => {
|
|
4697
4661
|
const cam = cameraRef.current;
|
|
@@ -4725,223 +4689,10 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4725
4689
|
applyDragMoveAtScreenPoint({ x: sx, y: sy }, { x: pageX, y: pageY });
|
|
4726
4690
|
},
|
|
4727
4691
|
onPanResponderRelease: (evt) => {
|
|
4728
|
-
|
|
4729
|
-
lastPanPoint.current = null;
|
|
4730
|
-
updateToolCursorPoint({
|
|
4692
|
+
finishDragAtScreenPoint({
|
|
4731
4693
|
x: evt.nativeEvent.locationX,
|
|
4732
4694
|
y: evt.nativeEvent.locationY
|
|
4733
4695
|
});
|
|
4734
|
-
const st = dragStateRef.current;
|
|
4735
|
-
if (st.kind === "draw") {
|
|
4736
|
-
dragStateRef.current = { kind: "idle" };
|
|
4737
|
-
setPlacementPreview(null);
|
|
4738
|
-
if (st.tool === "laser") {
|
|
4739
|
-
if (laserClearTimerRef.current) {
|
|
4740
|
-
clearTimeout(laserClearTimerRef.current);
|
|
4741
|
-
}
|
|
4742
|
-
laserClearTimerRef.current = setTimeout(() => {
|
|
4743
|
-
setLaserTrail([]);
|
|
4744
|
-
laserClearTimerRef.current = null;
|
|
4745
|
-
}, 650);
|
|
4746
|
-
requestSelectToolAfterUse();
|
|
4747
|
-
return;
|
|
4748
|
-
}
|
|
4749
|
-
if (st.points.length < 1) return;
|
|
4750
|
-
const change = onItemsChangeRef.current;
|
|
4751
|
-
if (!change) return;
|
|
4752
|
-
const id = createShapeId();
|
|
4753
|
-
const item = createFreehandStrokeItem(
|
|
4754
|
-
id,
|
|
4755
|
-
st.points,
|
|
4756
|
-
st.tool,
|
|
4757
|
-
strokeStyleRef.current
|
|
4758
|
-
);
|
|
4759
|
-
if (item) {
|
|
4760
|
-
change([...itemsRef.current, item]);
|
|
4761
|
-
}
|
|
4762
|
-
requestSelectToolAfterUse();
|
|
4763
|
-
return;
|
|
4764
|
-
}
|
|
4765
|
-
if (st.kind === "move") {
|
|
4766
|
-
dragStateRef.current = { kind: "idle" };
|
|
4767
|
-
return;
|
|
4768
|
-
}
|
|
4769
|
-
if (st.kind === "resize" || st.kind === "rotate") {
|
|
4770
|
-
dragStateRef.current = { kind: "idle" };
|
|
4771
|
-
return;
|
|
4772
|
-
}
|
|
4773
|
-
if (st.kind === "marquee") {
|
|
4774
|
-
dragStateRef.current = { kind: "idle" };
|
|
4775
|
-
setPlacementPreview(null);
|
|
4776
|
-
const cam = cameraRef.current;
|
|
4777
|
-
if (!cam) return;
|
|
4778
|
-
const { worldX, worldY } = screenToWorld(
|
|
4779
|
-
evt.nativeEvent.locationX,
|
|
4780
|
-
evt.nativeEvent.locationY
|
|
4781
|
-
);
|
|
4782
|
-
const a = st.startWorld;
|
|
4783
|
-
if (typeof worldX !== "number" || typeof worldY !== "number") return;
|
|
4784
|
-
const raw = {
|
|
4785
|
-
x: Math.min(a.x, worldX),
|
|
4786
|
-
y: Math.min(a.y, worldY),
|
|
4787
|
-
width: Math.abs(worldX - a.x),
|
|
4788
|
-
height: Math.abs(worldY - a.y)
|
|
4789
|
-
};
|
|
4790
|
-
const picked = collectIdsInRect(itemsRef.current, raw);
|
|
4791
|
-
onSelectionChangeRef.current?.(picked);
|
|
4792
|
-
return;
|
|
4793
|
-
}
|
|
4794
|
-
if (st.kind === "erase") {
|
|
4795
|
-
const change = onItemsChangeRef.current;
|
|
4796
|
-
if (change && eraserPreviewIdSetRef.current.size > 0) {
|
|
4797
|
-
const idSet = new Set(eraserPreviewIdSetRef.current);
|
|
4798
|
-
change(itemsRef.current.filter((i) => !idSet.has(i.id)));
|
|
4799
|
-
}
|
|
4800
|
-
eraserPreviewIdSetRef.current.clear();
|
|
4801
|
-
setEraserPreviewIds([]);
|
|
4802
|
-
setEraserTrail([]);
|
|
4803
|
-
dragStateRef.current = { kind: "idle" };
|
|
4804
|
-
requestSelectToolAfterUse();
|
|
4805
|
-
return;
|
|
4806
|
-
}
|
|
4807
|
-
if (st.kind === "place") {
|
|
4808
|
-
dragStateRef.current = { kind: "idle" };
|
|
4809
|
-
setPlacementPreview(null);
|
|
4810
|
-
const change = onItemsChangeRef.current;
|
|
4811
|
-
if (!change) return;
|
|
4812
|
-
const { worldX, worldY } = screenToWorld(
|
|
4813
|
-
evt.nativeEvent.locationX,
|
|
4814
|
-
evt.nativeEvent.locationY
|
|
4815
|
-
);
|
|
4816
|
-
const a = st.startWorld;
|
|
4817
|
-
const b = { x: worldX, y: worldY };
|
|
4818
|
-
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
4819
|
-
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
4820
|
-
if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
|
|
4821
|
-
return;
|
|
4822
|
-
}
|
|
4823
|
-
let raw = rectFromCorners(a, b);
|
|
4824
|
-
let br = normalizeRect(raw);
|
|
4825
|
-
let lineStart = a;
|
|
4826
|
-
let lineEnd = b;
|
|
4827
|
-
if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
|
|
4828
|
-
const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
4829
|
-
const defaults = defaultPlacementWorld(st.tool, center);
|
|
4830
|
-
raw = defaults.raw;
|
|
4831
|
-
br = normalizeRect(raw);
|
|
4832
|
-
if (defaults.lineWorld) {
|
|
4833
|
-
const [defaultStart, defaultEnd] = defaults.lineWorld;
|
|
4834
|
-
lineStart = defaultStart;
|
|
4835
|
-
lineEnd = defaultEnd;
|
|
4836
|
-
}
|
|
4837
|
-
}
|
|
4838
|
-
const id = createShapeId();
|
|
4839
|
-
const style = strokeStyleRef.current;
|
|
4840
|
-
if (st.tool === "rect") {
|
|
4841
|
-
change([...itemsRef.current, createRectangleItem(id, raw, style)]);
|
|
4842
|
-
onSelectionChangeRef.current?.([id]);
|
|
4843
|
-
requestSelectToolAfterUse();
|
|
4844
|
-
return;
|
|
4845
|
-
}
|
|
4846
|
-
if (st.tool === "ellipse") {
|
|
4847
|
-
change([...itemsRef.current, createEllipseItem(id, raw, style)]);
|
|
4848
|
-
onSelectionChangeRef.current?.([id]);
|
|
4849
|
-
requestSelectToolAfterUse();
|
|
4850
|
-
return;
|
|
4851
|
-
}
|
|
4852
|
-
if (st.tool === "architectural-cloud") {
|
|
4853
|
-
change([
|
|
4854
|
-
...itemsRef.current,
|
|
4855
|
-
createArchitecturalCloudItem(id, raw, style)
|
|
4856
|
-
]);
|
|
4857
|
-
onSelectionChangeRef.current?.([id]);
|
|
4858
|
-
requestSelectToolAfterUse();
|
|
4859
|
-
return;
|
|
4860
|
-
}
|
|
4861
|
-
const line = lineEndpointsToLocal(br, lineStart, lineEnd);
|
|
4862
|
-
change([
|
|
4863
|
-
...itemsRef.current,
|
|
4864
|
-
createLineItem(id, br, line, st.tool, style)
|
|
4865
|
-
]);
|
|
4866
|
-
onSelectionChangeRef.current?.([id]);
|
|
4867
|
-
requestSelectToolAfterUse();
|
|
4868
|
-
return;
|
|
4869
|
-
}
|
|
4870
|
-
if (st.kind === "custom-place") {
|
|
4871
|
-
dragStateRef.current = { kind: "idle" };
|
|
4872
|
-
setPlacementPreview(null);
|
|
4873
|
-
const change = onItemsChangeRef.current;
|
|
4874
|
-
if (!change) return;
|
|
4875
|
-
const { worldX, worldY } = screenToWorld(
|
|
4876
|
-
evt.nativeEvent.locationX,
|
|
4877
|
-
evt.nativeEvent.locationY
|
|
4878
|
-
);
|
|
4879
|
-
const center = {
|
|
4880
|
-
x: (st.startWorld.x + worldX) / 2,
|
|
4881
|
-
y: (st.startWorld.y + worldY) / 2
|
|
4882
|
-
};
|
|
4883
|
-
const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
|
|
4884
|
-
const normalized = normalizeRect(raw);
|
|
4885
|
-
const bounds = normalized.width < MIN_PLACE_SIZE || normalized.height < MIN_PLACE_SIZE ? {
|
|
4886
|
-
x: center.x - 60,
|
|
4887
|
-
y: center.y - 40,
|
|
4888
|
-
width: 120,
|
|
4889
|
-
height: 80
|
|
4890
|
-
} : normalized;
|
|
4891
|
-
const id = createShapeId();
|
|
4892
|
-
const item = st.placement.createItem({ id, bounds });
|
|
4893
|
-
change([...itemsRef.current, item]);
|
|
4894
|
-
onSelectionChangeRef.current?.([id]);
|
|
4895
|
-
requestSelectToolAfterUse();
|
|
4896
|
-
return;
|
|
4897
|
-
}
|
|
4898
|
-
if (st.kind === "tap") {
|
|
4899
|
-
dragStateRef.current = { kind: "idle" };
|
|
4900
|
-
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
4901
|
-
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
4902
|
-
if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
|
|
4903
|
-
const change = onItemsChangeRef.current;
|
|
4904
|
-
if (!change) return;
|
|
4905
|
-
if (st.tool === "text") {
|
|
4906
|
-
const id = createShapeId();
|
|
4907
|
-
const item = createTextItem(
|
|
4908
|
-
id,
|
|
4909
|
-
{
|
|
4910
|
-
x: st.startWorld.x - 4,
|
|
4911
|
-
y: st.startWorld.y - 18,
|
|
4912
|
-
width: 160,
|
|
4913
|
-
height: 26
|
|
4914
|
-
},
|
|
4915
|
-
"Text",
|
|
4916
|
-
strokeStyleRef.current,
|
|
4917
|
-
18
|
|
4918
|
-
);
|
|
4919
|
-
change([...itemsRef.current, item]);
|
|
4920
|
-
onSelectionChangeRef.current?.([id]);
|
|
4921
|
-
requestSelectToolAfterUse();
|
|
4922
|
-
}
|
|
4923
|
-
if (st.tool === "note") {
|
|
4924
|
-
const id = createShapeId();
|
|
4925
|
-
const note = {
|
|
4926
|
-
id,
|
|
4927
|
-
x: st.startWorld.x - 70,
|
|
4928
|
-
y: st.startWorld.y - 40,
|
|
4929
|
-
bounds: {
|
|
4930
|
-
x: st.startWorld.x - 70,
|
|
4931
|
-
y: st.startWorld.y - 40,
|
|
4932
|
-
width: 140,
|
|
4933
|
-
height: 80
|
|
4934
|
-
},
|
|
4935
|
-
childrenSvg: `<rect width="140" height="80" rx="8" fill="#fef08a" stroke="#facc15" stroke-width="1" /><text x="10" y="22" fill="#1f2937" font-size="12" font-family="system-ui">Nota</text>`,
|
|
4936
|
-
toolKind: "custom"
|
|
4937
|
-
};
|
|
4938
|
-
change([...itemsRef.current, note]);
|
|
4939
|
-
onSelectionChangeRef.current?.([id]);
|
|
4940
|
-
requestSelectToolAfterUse();
|
|
4941
|
-
}
|
|
4942
|
-
return;
|
|
4943
|
-
}
|
|
4944
|
-
dragStateRef.current = { kind: "idle" };
|
|
4945
4696
|
},
|
|
4946
4697
|
onPanResponderTerminate: () => {
|
|
4947
4698
|
lastPinchDist.current = null;
|
|
@@ -4957,11 +4708,9 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4957
4708
|
}),
|
|
4958
4709
|
[
|
|
4959
4710
|
applyDragMoveAtScreenPoint,
|
|
4960
|
-
|
|
4711
|
+
beginDragAtScreenPoint,
|
|
4712
|
+
finishDragAtScreenPoint,
|
|
4961
4713
|
requestRender,
|
|
4962
|
-
requestSelectToolAfterUse,
|
|
4963
|
-
interactive,
|
|
4964
|
-
updateToolCursorPoint,
|
|
4965
4714
|
hideToolCursor
|
|
4966
4715
|
]
|
|
4967
4716
|
);
|
|
@@ -4987,14 +4736,14 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
4987
4736
|
[requestRender, size]
|
|
4988
4737
|
);
|
|
4989
4738
|
const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
|
|
4990
|
-
const activeToolCursor = cursorForToolId(toolId);
|
|
4991
|
-
const toolCursor = activeToolCursor && toolCursorPoint ? { cursor: activeToolCursor, point: toolCursorPoint } : null;
|
|
4992
4739
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4993
4740
|
reactNative.View,
|
|
4994
4741
|
{
|
|
4995
4742
|
style: { flex: 1, overflow: "hidden" },
|
|
4996
4743
|
onLayout,
|
|
4744
|
+
onPointerDown: handlePointerDown,
|
|
4997
4745
|
onPointerMove: handlePointerMove,
|
|
4746
|
+
onPointerUp: handlePointerUp,
|
|
4998
4747
|
onPointerEnter: handlePointerMove,
|
|
4999
4748
|
onPointerLeave: hideToolCursor,
|
|
5000
4749
|
...panResponder.panHandlers,
|
|
@@ -5022,8 +4771,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
5022
4771
|
eraserPreviewItems: items.filter(
|
|
5023
4772
|
(it) => eraserPreviewIds.includes(it.id)
|
|
5024
4773
|
),
|
|
5025
|
-
previewStrokeStyle: strokeStyleState
|
|
5026
|
-
toolCursor
|
|
4774
|
+
previewStrokeStyle: strokeStyleState
|
|
5027
4775
|
}
|
|
5028
4776
|
),
|
|
5029
4777
|
interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
|