canvu-react 0.4.52 → 0.4.54
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 +682 -151
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +19 -0
- package/dist/native.d.ts +19 -0
- package/dist/native.js +682 -151
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.js
CHANGED
|
@@ -1193,6 +1193,9 @@ function cloneVectorSceneItemWithNewId(item) {
|
|
|
1193
1193
|
}
|
|
1194
1194
|
return next;
|
|
1195
1195
|
}
|
|
1196
|
+
function cloneVectorSceneItemsWithNewIds(items) {
|
|
1197
|
+
return items.map(cloneVectorSceneItemWithNewId);
|
|
1198
|
+
}
|
|
1196
1199
|
|
|
1197
1200
|
// src/scene/managed-images.ts
|
|
1198
1201
|
var MANAGED_KEY = "managed";
|
|
@@ -1256,8 +1259,68 @@ function rotateManagedImage(items, id) {
|
|
|
1256
1259
|
function deleteManagedImage(items, id) {
|
|
1257
1260
|
return restackManagedImages(items.filter((i) => i.id !== id));
|
|
1258
1261
|
}
|
|
1262
|
+
function reorderManagedImages(items, orderedManagedIds) {
|
|
1263
|
+
const managedSlots = [];
|
|
1264
|
+
for (let i = 0; i < items.length; i++) {
|
|
1265
|
+
const item = items[i];
|
|
1266
|
+
if (item && isManagedImage(item)) managedSlots.push(i);
|
|
1267
|
+
}
|
|
1268
|
+
if (managedSlots.length !== orderedManagedIds.length) {
|
|
1269
|
+
return [...items];
|
|
1270
|
+
}
|
|
1271
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
1272
|
+
const next = [...items];
|
|
1273
|
+
managedSlots.forEach((slot, k) => {
|
|
1274
|
+
const orderedId = orderedManagedIds[k];
|
|
1275
|
+
if (orderedId === void 0) return;
|
|
1276
|
+
const replacement = byId.get(orderedId);
|
|
1277
|
+
if (replacement) next[slot] = replacement;
|
|
1278
|
+
});
|
|
1279
|
+
return restackManagedImages(next);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/native/native-images-menu-reorder.ts
|
|
1283
|
+
function moveNativeManagedImageId(managedIds, fromIndex, toIndex) {
|
|
1284
|
+
const next = [...managedIds];
|
|
1285
|
+
if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= next.length || toIndex >= next.length) {
|
|
1286
|
+
return next;
|
|
1287
|
+
}
|
|
1288
|
+
const [item] = next.splice(fromIndex, 1);
|
|
1289
|
+
if (item === void 0) return next;
|
|
1290
|
+
next.splice(toIndex, 0, item);
|
|
1291
|
+
return next;
|
|
1292
|
+
}
|
|
1293
|
+
function getNativeImagesMenuReorderIndex({
|
|
1294
|
+
activeId,
|
|
1295
|
+
managedIds,
|
|
1296
|
+
rowLayouts,
|
|
1297
|
+
dragDeltaY
|
|
1298
|
+
}) {
|
|
1299
|
+
const currentIndex = managedIds.indexOf(activeId);
|
|
1300
|
+
if (currentIndex < 0) return -1;
|
|
1301
|
+
const layoutById = new Map(rowLayouts.map((layout) => [layout.id, layout]));
|
|
1302
|
+
const activeLayout = layoutById.get(activeId);
|
|
1303
|
+
if (!activeLayout) return currentIndex;
|
|
1304
|
+
const activeCenterY = activeLayout.y + activeLayout.height / 2 + dragDeltaY;
|
|
1305
|
+
let nextIndex = currentIndex;
|
|
1306
|
+
let closestDistance = Infinity;
|
|
1307
|
+
for (let index = 0; index < managedIds.length; index++) {
|
|
1308
|
+
const id = managedIds[index];
|
|
1309
|
+
if (id === void 0) continue;
|
|
1310
|
+
const layout = layoutById.get(id);
|
|
1311
|
+
if (!layout) continue;
|
|
1312
|
+
const centerY = layout.y + layout.height / 2;
|
|
1313
|
+
const distance = Math.abs(activeCenterY - centerY);
|
|
1314
|
+
if (distance < closestDistance) {
|
|
1315
|
+
closestDistance = distance;
|
|
1316
|
+
nextIndex = index;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return nextIndex;
|
|
1320
|
+
}
|
|
1259
1321
|
var defaultLabels = {
|
|
1260
1322
|
title: "Images",
|
|
1323
|
+
dragHandle: "Drag to reorder",
|
|
1261
1324
|
focus: "Focus on canvas",
|
|
1262
1325
|
duplicate: "Duplicate",
|
|
1263
1326
|
rotate: "Rotate",
|
|
@@ -1329,61 +1392,110 @@ function NativeImagesMenuRow({
|
|
|
1329
1392
|
item,
|
|
1330
1393
|
items,
|
|
1331
1394
|
labels,
|
|
1395
|
+
isDragging,
|
|
1396
|
+
isDropTarget,
|
|
1397
|
+
dragDeltaY,
|
|
1332
1398
|
onItemsChange,
|
|
1333
1399
|
onFocusItem,
|
|
1400
|
+
onDragStart,
|
|
1401
|
+
onDragMove,
|
|
1402
|
+
onDragEnd,
|
|
1403
|
+
onRowLayout,
|
|
1334
1404
|
getImageUri,
|
|
1335
1405
|
renderThumbnail,
|
|
1336
1406
|
rowStyle,
|
|
1337
1407
|
actionButtonStyle
|
|
1338
1408
|
}) {
|
|
1339
1409
|
const imageUri = getImageUri(item);
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1410
|
+
const panResponder = useMemo(
|
|
1411
|
+
() => PanResponder.create({
|
|
1412
|
+
onStartShouldSetPanResponder: () => true,
|
|
1413
|
+
onMoveShouldSetPanResponder: () => true,
|
|
1414
|
+
onPanResponderGrant: () => onDragStart(item.id),
|
|
1415
|
+
onPanResponderMove: (_event, gestureState) => onDragMove(item.id, gestureState.dy),
|
|
1416
|
+
onPanResponderRelease: (_event, gestureState) => onDragEnd(item.id, gestureState.dy),
|
|
1417
|
+
onPanResponderTerminate: (_event, gestureState) => onDragEnd(item.id, gestureState.dy),
|
|
1418
|
+
onPanResponderTerminationRequest: () => false,
|
|
1419
|
+
onShouldBlockNativeResponder: () => true
|
|
1420
|
+
}),
|
|
1421
|
+
[item.id, onDragEnd, onDragMove, onDragStart]
|
|
1422
|
+
);
|
|
1423
|
+
const onLayout = useCallback(
|
|
1424
|
+
(event) => {
|
|
1425
|
+
const { height, y } = event.nativeEvent.layout;
|
|
1426
|
+
onRowLayout({ id: item.id, y, height });
|
|
1427
|
+
},
|
|
1428
|
+
[item.id, onRowLayout]
|
|
1429
|
+
);
|
|
1430
|
+
return /* @__PURE__ */ jsxs(
|
|
1431
|
+
View,
|
|
1432
|
+
{
|
|
1433
|
+
onLayout,
|
|
1434
|
+
style: [
|
|
1435
|
+
styles.row,
|
|
1436
|
+
rowStyle,
|
|
1437
|
+
isDropTarget && styles.dropTargetRow,
|
|
1438
|
+
isDragging && styles.draggingRow,
|
|
1439
|
+
isDragging && { transform: [{ translateY: dragDeltaY }] }
|
|
1440
|
+
],
|
|
1441
|
+
children: [
|
|
1442
|
+
/* @__PURE__ */ jsx(
|
|
1443
|
+
View,
|
|
1444
|
+
{
|
|
1445
|
+
accessibilityRole: "button",
|
|
1446
|
+
accessibilityLabel: labels.dragHandle,
|
|
1447
|
+
style: [styles.handle, isDragging && styles.handleDragging],
|
|
1448
|
+
...panResponder.panHandlers,
|
|
1449
|
+
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "grip", color: "#94a3b8" })
|
|
1450
|
+
}
|
|
1451
|
+
),
|
|
1452
|
+
/* @__PURE__ */ jsx(
|
|
1453
|
+
Pressable,
|
|
1454
|
+
{
|
|
1455
|
+
accessibilityRole: "button",
|
|
1456
|
+
accessibilityLabel: labels.focus,
|
|
1457
|
+
disabled: !onFocusItem,
|
|
1458
|
+
onPress: () => onFocusItem?.(item),
|
|
1459
|
+
style: styles.thumbnailButton,
|
|
1460
|
+
children: /* @__PURE__ */ jsxs(View, { style: styles.thumbnailBox, children: [
|
|
1461
|
+
renderThumbnail ? renderThumbnail(item) : null,
|
|
1462
|
+
!renderThumbnail && imageUri ? /* @__PURE__ */ jsx(Image$1, { source: { uri: imageUri }, style: styles.thumbnailImage }) : null
|
|
1463
|
+
] })
|
|
1464
|
+
}
|
|
1465
|
+
),
|
|
1466
|
+
/* @__PURE__ */ jsxs(View, { style: styles.actionsColumn, children: [
|
|
1467
|
+
/* @__PURE__ */ jsx(
|
|
1468
|
+
NativeImagesMenuAction,
|
|
1469
|
+
{
|
|
1470
|
+
label: labels.duplicate,
|
|
1471
|
+
onPress: () => onItemsChange(copyManagedImage(items, item.id)),
|
|
1472
|
+
style: actionButtonStyle,
|
|
1473
|
+
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "copy" })
|
|
1474
|
+
}
|
|
1475
|
+
),
|
|
1476
|
+
/* @__PURE__ */ jsx(
|
|
1477
|
+
NativeImagesMenuAction,
|
|
1478
|
+
{
|
|
1479
|
+
label: labels.rotate,
|
|
1480
|
+
onPress: () => onItemsChange(rotateManagedImage(items, item.id)),
|
|
1481
|
+
style: actionButtonStyle,
|
|
1482
|
+
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "rotate" })
|
|
1483
|
+
}
|
|
1484
|
+
),
|
|
1485
|
+
/* @__PURE__ */ jsx(
|
|
1486
|
+
NativeImagesMenuAction,
|
|
1487
|
+
{
|
|
1488
|
+
label: labels.delete,
|
|
1489
|
+
danger: true,
|
|
1490
|
+
onPress: () => onItemsChange(deleteManagedImage(items, item.id)),
|
|
1491
|
+
style: actionButtonStyle,
|
|
1492
|
+
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "trash", color: "#b91c1c" })
|
|
1493
|
+
}
|
|
1494
|
+
)
|
|
1353
1495
|
] })
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
/* @__PURE__ */ jsx(
|
|
1358
|
-
NativeImagesMenuAction,
|
|
1359
|
-
{
|
|
1360
|
-
label: labels.duplicate,
|
|
1361
|
-
onPress: () => onItemsChange(copyManagedImage(items, item.id)),
|
|
1362
|
-
style: actionButtonStyle,
|
|
1363
|
-
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "copy" })
|
|
1364
|
-
}
|
|
1365
|
-
),
|
|
1366
|
-
/* @__PURE__ */ jsx(
|
|
1367
|
-
NativeImagesMenuAction,
|
|
1368
|
-
{
|
|
1369
|
-
label: labels.rotate,
|
|
1370
|
-
onPress: () => onItemsChange(rotateManagedImage(items, item.id)),
|
|
1371
|
-
style: actionButtonStyle,
|
|
1372
|
-
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "rotate" })
|
|
1373
|
-
}
|
|
1374
|
-
),
|
|
1375
|
-
/* @__PURE__ */ jsx(
|
|
1376
|
-
NativeImagesMenuAction,
|
|
1377
|
-
{
|
|
1378
|
-
label: labels.delete,
|
|
1379
|
-
danger: true,
|
|
1380
|
-
onPress: () => onItemsChange(deleteManagedImage(items, item.id)),
|
|
1381
|
-
style: actionButtonStyle,
|
|
1382
|
-
children: /* @__PURE__ */ jsx(NativeImagesMenuIcon, { name: "trash", color: "#b91c1c" })
|
|
1383
|
-
}
|
|
1384
|
-
)
|
|
1385
|
-
] })
|
|
1386
|
-
] });
|
|
1496
|
+
]
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1387
1499
|
}
|
|
1388
1500
|
function NativeImagesMenu({
|
|
1389
1501
|
items,
|
|
@@ -1400,7 +1512,69 @@ function NativeImagesMenu({
|
|
|
1400
1512
|
renderThumbnail
|
|
1401
1513
|
}) {
|
|
1402
1514
|
const managed = useMemo(() => items.filter(isManagedImage), [items]);
|
|
1515
|
+
const managedIds = useMemo(() => managed.map((item) => item.id), [managed]);
|
|
1403
1516
|
const [collapsed, setCollapsed] = useState(!defaultOpen);
|
|
1517
|
+
const [dragState, setDragState] = useState(null);
|
|
1518
|
+
const rowLayoutsRef = useRef(/* @__PURE__ */ new Map());
|
|
1519
|
+
const getRowLayouts = useCallback(
|
|
1520
|
+
() => managedIds.map((id) => rowLayoutsRef.current.get(id)).filter((layout) => layout != null),
|
|
1521
|
+
[managedIds]
|
|
1522
|
+
);
|
|
1523
|
+
const handleRowLayout = useCallback((layout) => {
|
|
1524
|
+
rowLayoutsRef.current.set(layout.id, layout);
|
|
1525
|
+
}, []);
|
|
1526
|
+
const handleDragStart = useCallback(
|
|
1527
|
+
(id) => {
|
|
1528
|
+
const fromIndex = managedIds.indexOf(id);
|
|
1529
|
+
if (fromIndex < 0) return;
|
|
1530
|
+
setDragState({
|
|
1531
|
+
activeId: id,
|
|
1532
|
+
fromIndex,
|
|
1533
|
+
currentIndex: fromIndex,
|
|
1534
|
+
dragDeltaY: 0
|
|
1535
|
+
});
|
|
1536
|
+
},
|
|
1537
|
+
[managedIds]
|
|
1538
|
+
);
|
|
1539
|
+
const handleDragMove = useCallback(
|
|
1540
|
+
(id, dragDeltaY) => {
|
|
1541
|
+
setDragState((current) => {
|
|
1542
|
+
if (!current || current.activeId !== id) return current;
|
|
1543
|
+
const currentIndex = getNativeImagesMenuReorderIndex({
|
|
1544
|
+
activeId: id,
|
|
1545
|
+
managedIds,
|
|
1546
|
+
rowLayouts: getRowLayouts(),
|
|
1547
|
+
dragDeltaY
|
|
1548
|
+
});
|
|
1549
|
+
return {
|
|
1550
|
+
...current,
|
|
1551
|
+
currentIndex: currentIndex >= 0 ? currentIndex : current.fromIndex,
|
|
1552
|
+
dragDeltaY
|
|
1553
|
+
};
|
|
1554
|
+
});
|
|
1555
|
+
},
|
|
1556
|
+
[getRowLayouts, managedIds]
|
|
1557
|
+
);
|
|
1558
|
+
const handleDragEnd = useCallback(
|
|
1559
|
+
(id, dragDeltaY) => {
|
|
1560
|
+
const fromIndex = managedIds.indexOf(id);
|
|
1561
|
+
const currentIndex = getNativeImagesMenuReorderIndex({
|
|
1562
|
+
activeId: id,
|
|
1563
|
+
managedIds,
|
|
1564
|
+
rowLayouts: getRowLayouts(),
|
|
1565
|
+
dragDeltaY
|
|
1566
|
+
});
|
|
1567
|
+
setDragState(null);
|
|
1568
|
+
if (fromIndex < 0 || currentIndex < 0 || fromIndex === currentIndex) return;
|
|
1569
|
+
const orderedIds = moveNativeManagedImageId(
|
|
1570
|
+
managedIds,
|
|
1571
|
+
fromIndex,
|
|
1572
|
+
currentIndex
|
|
1573
|
+
);
|
|
1574
|
+
onItemsChange(reorderManagedImages(items, orderedIds));
|
|
1575
|
+
},
|
|
1576
|
+
[getRowLayouts, items, managedIds, onItemsChange]
|
|
1577
|
+
);
|
|
1404
1578
|
if (managed.length === 0) return null;
|
|
1405
1579
|
const resolvedLabels = resolveLabels(labels);
|
|
1406
1580
|
if (collapsed) {
|
|
@@ -1445,15 +1619,23 @@ function NativeImagesMenu({
|
|
|
1445
1619
|
{
|
|
1446
1620
|
style: styles.listScroll,
|
|
1447
1621
|
contentContainerStyle: styles.listContent,
|
|
1622
|
+
scrollEnabled: dragState == null,
|
|
1448
1623
|
showsVerticalScrollIndicator: false,
|
|
1449
|
-
children: managed.map((item) => /* @__PURE__ */ jsx(
|
|
1624
|
+
children: managed.map((item, index) => /* @__PURE__ */ jsx(
|
|
1450
1625
|
NativeImagesMenuRow,
|
|
1451
1626
|
{
|
|
1452
1627
|
item,
|
|
1453
1628
|
items,
|
|
1454
1629
|
labels: resolvedLabels,
|
|
1630
|
+
isDragging: dragState?.activeId === item.id,
|
|
1631
|
+
isDropTarget: dragState != null && dragState.activeId !== item.id && dragState.currentIndex === index,
|
|
1632
|
+
dragDeltaY: dragState?.activeId === item.id ? dragState.dragDeltaY : 0,
|
|
1455
1633
|
onItemsChange,
|
|
1456
1634
|
onFocusItem,
|
|
1635
|
+
onDragStart: handleDragStart,
|
|
1636
|
+
onDragMove: handleDragMove,
|
|
1637
|
+
onDragEnd: handleDragEnd,
|
|
1638
|
+
onRowLayout: handleRowLayout,
|
|
1457
1639
|
getImageUri,
|
|
1458
1640
|
renderThumbnail,
|
|
1459
1641
|
rowStyle,
|
|
@@ -1513,6 +1695,19 @@ var styles = StyleSheet.create({
|
|
|
1513
1695
|
dangerActionButton: {
|
|
1514
1696
|
backgroundColor: "#fef2f2"
|
|
1515
1697
|
},
|
|
1698
|
+
draggingRow: {
|
|
1699
|
+
backgroundColor: "#eef2f7",
|
|
1700
|
+
elevation: 4,
|
|
1701
|
+
opacity: 0.85,
|
|
1702
|
+
zIndex: 1
|
|
1703
|
+
},
|
|
1704
|
+
dropTargetRow: {
|
|
1705
|
+
backgroundColor: "#f8fafc"
|
|
1706
|
+
},
|
|
1707
|
+
handleDragging: {
|
|
1708
|
+
backgroundColor: "#e2e8f0",
|
|
1709
|
+
borderRadius: 6
|
|
1710
|
+
},
|
|
1516
1711
|
handle: {
|
|
1517
1712
|
alignItems: "center",
|
|
1518
1713
|
height: 128,
|
|
@@ -3072,22 +3267,6 @@ function resolveNativeStrokePreviewStyle(tool, previewStrokeStyle) {
|
|
|
3072
3267
|
};
|
|
3073
3268
|
}
|
|
3074
3269
|
|
|
3075
|
-
// src/native/native-stroke-preview.ts
|
|
3076
|
-
function buildNativeStrokePreviewPath(points) {
|
|
3077
|
-
if (points.length < 2) return null;
|
|
3078
|
-
const d = smoothFreehandPointsToPathD(points);
|
|
3079
|
-
return d || null;
|
|
3080
|
-
}
|
|
3081
|
-
function buildNativeFreehandStrokePreviewPayload(points, style, tool) {
|
|
3082
|
-
if (tool === "laser") return null;
|
|
3083
|
-
return computeFreehandSvgPayload(
|
|
3084
|
-
points.map((point) => ({ x: point.x, y: point.y })),
|
|
3085
|
-
style,
|
|
3086
|
-
tool,
|
|
3087
|
-
true
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
3270
|
// src/native/native-vector-interactions.ts
|
|
3092
3271
|
var NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX = 24;
|
|
3093
3272
|
function nativeItemPlacementBounds(item) {
|
|
@@ -3155,6 +3334,26 @@ function pointInNativeSelectedItemBounds(item, worldPoint) {
|
|
|
3155
3334
|
);
|
|
3156
3335
|
return local.x >= 0 && local.x <= bounds.width && local.y >= 0 && local.y <= bounds.height;
|
|
3157
3336
|
}
|
|
3337
|
+
function resolveNativeSelectionContextMenuRequest({
|
|
3338
|
+
interactive,
|
|
3339
|
+
toolId,
|
|
3340
|
+
selectedItems,
|
|
3341
|
+
worldPoint,
|
|
3342
|
+
screenPoint
|
|
3343
|
+
}) {
|
|
3344
|
+
if (!interactive || toolId !== "select") return null;
|
|
3345
|
+
const editableSelectedItems = selectedItems.filter((item) => !item.locked);
|
|
3346
|
+
if (editableSelectedItems.length === 0) return null;
|
|
3347
|
+
const pressedSelectedItem = editableSelectedItems.some(
|
|
3348
|
+
(item) => pointInNativeSelectedItemBounds(item, worldPoint)
|
|
3349
|
+
);
|
|
3350
|
+
if (!pressedSelectedItem) return null;
|
|
3351
|
+
return {
|
|
3352
|
+
itemIds: editableSelectedItems.map((item) => item.id),
|
|
3353
|
+
x: screenPoint.x,
|
|
3354
|
+
y: screenPoint.y
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3158
3357
|
function resolveNativeCustomPlacement(toolId, customPlacement, customPlacements) {
|
|
3159
3358
|
if (customPlacement?.toolId === toolId) return customPlacement;
|
|
3160
3359
|
return customPlacements?.find((placement) => placement.toolId === toolId) ?? null;
|
|
@@ -3459,90 +3658,58 @@ function NativeInteractionOverlay({
|
|
|
3459
3658
|
p.tool,
|
|
3460
3659
|
p.style ?? previewStrokeStyle
|
|
3461
3660
|
);
|
|
3462
|
-
const
|
|
3463
|
-
|
|
3464
|
-
|
|
3661
|
+
const payload = computeFreehandSvgPayload(
|
|
3662
|
+
p.points,
|
|
3663
|
+
style,
|
|
3664
|
+
isLaser ? "draw" : p.tool,
|
|
3665
|
+
p.points.length === 2
|
|
3465
3666
|
);
|
|
3466
|
-
if (!
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3667
|
+
if (!payload) return null;
|
|
3668
|
+
if (payload.kind === "circle") {
|
|
3669
|
+
return /* @__PURE__ */ jsx(
|
|
3670
|
+
Circle,
|
|
3671
|
+
{
|
|
3672
|
+
cx: payload.cx,
|
|
3673
|
+
cy: payload.cy,
|
|
3674
|
+
r: payload.r,
|
|
3675
|
+
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
3676
|
+
style: "fill",
|
|
3677
|
+
antiAlias: true
|
|
3678
|
+
}
|
|
3471
3679
|
);
|
|
3472
|
-
if (payload?.kind === "circle") {
|
|
3473
|
-
return /* @__PURE__ */ jsx(
|
|
3474
|
-
Circle,
|
|
3475
|
-
{
|
|
3476
|
-
cx: payload.cx,
|
|
3477
|
-
cy: payload.cy,
|
|
3478
|
-
r: payload.r,
|
|
3479
|
-
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
3480
|
-
style: "fill",
|
|
3481
|
-
antiAlias: true
|
|
3482
|
-
}
|
|
3483
|
-
);
|
|
3484
|
-
}
|
|
3485
|
-
if (payload?.kind === "fillPath") {
|
|
3486
|
-
return /* @__PURE__ */ jsx(
|
|
3487
|
-
Path,
|
|
3488
|
-
{
|
|
3489
|
-
path: payload.d,
|
|
3490
|
-
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
3491
|
-
style: "fill",
|
|
3492
|
-
fillType: "winding",
|
|
3493
|
-
antiAlias: true
|
|
3494
|
-
}
|
|
3495
|
-
);
|
|
3496
|
-
}
|
|
3497
|
-
if (payload?.kind === "strokePath") {
|
|
3498
|
-
const intervals = dashIntervalsFromStrokeDasharray3(
|
|
3499
|
-
payload.strokeDasharray
|
|
3500
|
-
);
|
|
3501
|
-
return /* @__PURE__ */ jsx(
|
|
3502
|
-
Path,
|
|
3503
|
-
{
|
|
3504
|
-
path: payload.d,
|
|
3505
|
-
color: colorWithOpacity(payload.stroke, payload.strokeOpacity),
|
|
3506
|
-
style: "stroke",
|
|
3507
|
-
strokeWidth: payload.strokeWidth,
|
|
3508
|
-
strokeCap: "round",
|
|
3509
|
-
strokeJoin: "round",
|
|
3510
|
-
antiAlias: true,
|
|
3511
|
-
children: intervals ? /* @__PURE__ */ jsx(DashPathEffect, { intervals }) : null
|
|
3512
|
-
}
|
|
3513
|
-
);
|
|
3514
|
-
}
|
|
3515
|
-
return null;
|
|
3516
3680
|
}
|
|
3517
|
-
if (
|
|
3518
|
-
const point = p.points[0];
|
|
3519
|
-
if (!point) return null;
|
|
3681
|
+
if (payload.kind === "fillPath") {
|
|
3520
3682
|
return /* @__PURE__ */ jsx(
|
|
3521
|
-
|
|
3683
|
+
Path,
|
|
3522
3684
|
{
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
r: Math.max(0.5, style.strokeWidth / 2),
|
|
3526
|
-
color: strokeColor,
|
|
3685
|
+
path: payload.d,
|
|
3686
|
+
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
3527
3687
|
style: "fill",
|
|
3688
|
+
fillType: "winding",
|
|
3528
3689
|
antiAlias: true
|
|
3529
3690
|
}
|
|
3530
3691
|
);
|
|
3531
3692
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3693
|
+
if (payload.kind === "strokePath") {
|
|
3694
|
+
const intervals = dashIntervalsFromStrokeDasharray3(payload.strokeDasharray);
|
|
3695
|
+
return /* @__PURE__ */ jsx(
|
|
3696
|
+
Path,
|
|
3697
|
+
{
|
|
3698
|
+
path: payload.d,
|
|
3699
|
+
color: colorWithOpacity(
|
|
3700
|
+
payload.stroke,
|
|
3701
|
+
isLaser ? 0.85 : payload.strokeOpacity
|
|
3702
|
+
),
|
|
3703
|
+
style: "stroke",
|
|
3704
|
+
strokeWidth: payload.strokeWidth,
|
|
3705
|
+
strokeCap: "round",
|
|
3706
|
+
strokeJoin: "round",
|
|
3707
|
+
antiAlias: true,
|
|
3708
|
+
children: intervals ? /* @__PURE__ */ jsx(DashPathEffect, { intervals }) : null
|
|
3709
|
+
}
|
|
3710
|
+
);
|
|
3711
|
+
}
|
|
3712
|
+
return null;
|
|
3546
3713
|
}
|
|
3547
3714
|
return null;
|
|
3548
3715
|
}, [placementPreview, previewStrokeStyle, overlayStrokeWorld, marqueeDashWorld]);
|
|
@@ -5106,6 +5273,18 @@ function applyRotationFromPointer(item, startRotation, startPointerAngleRad, poi
|
|
|
5106
5273
|
}
|
|
5107
5274
|
return { ...item, rotation: startRotation + delta };
|
|
5108
5275
|
}
|
|
5276
|
+
function moveItemByDelta(item, dx, dy) {
|
|
5277
|
+
return {
|
|
5278
|
+
...item,
|
|
5279
|
+
x: item.x + dx,
|
|
5280
|
+
y: item.y + dy,
|
|
5281
|
+
bounds: {
|
|
5282
|
+
...item.bounds,
|
|
5283
|
+
x: item.bounds.x + dx,
|
|
5284
|
+
y: item.bounds.y + dy
|
|
5285
|
+
}
|
|
5286
|
+
};
|
|
5287
|
+
}
|
|
5109
5288
|
function resizeItemByHandle(item, start, handle, currentWorld) {
|
|
5110
5289
|
const sb = normalizeRect(start.bounds);
|
|
5111
5290
|
if (!itemAllowsResizeHandle(item, handle)) {
|
|
@@ -5330,6 +5509,39 @@ function hitTestNativeRemotePresence(peers, camera, point) {
|
|
|
5330
5509
|
return null;
|
|
5331
5510
|
}
|
|
5332
5511
|
|
|
5512
|
+
// src/native/native-shape-clipboard.ts
|
|
5513
|
+
var NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD = 24;
|
|
5514
|
+
function copyNativeSelectedShapeItems(items, selectedIds) {
|
|
5515
|
+
if (selectedIds.length === 0) return null;
|
|
5516
|
+
const copies = selectedIds.map((id) => items.find((item) => item.id === id)).filter((item) => item != null);
|
|
5517
|
+
if (copies.length === 0) return null;
|
|
5518
|
+
return copies.map((item) => JSON.parse(JSON.stringify(item)));
|
|
5519
|
+
}
|
|
5520
|
+
function pasteNativeShapeClipboard(input) {
|
|
5521
|
+
if (input.clipboard.length === 0) return null;
|
|
5522
|
+
const clones = cloneVectorSceneItemsWithNewIds(input.clipboard);
|
|
5523
|
+
const moved = clones.map(
|
|
5524
|
+
(item) => moveItemByDelta(
|
|
5525
|
+
item,
|
|
5526
|
+
NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD,
|
|
5527
|
+
NATIVE_SHAPE_CLIPBOARD_PASTE_OFFSET_WORLD
|
|
5528
|
+
)
|
|
5529
|
+
);
|
|
5530
|
+
if (moved.length === 0) return null;
|
|
5531
|
+
return {
|
|
5532
|
+
items: [...input.items, ...moved],
|
|
5533
|
+
selectedIds: moved.map((item) => item.id)
|
|
5534
|
+
};
|
|
5535
|
+
}
|
|
5536
|
+
function duplicateNativeSelectedShapes(input) {
|
|
5537
|
+
const clipboard = copyNativeSelectedShapeItems(input.items, input.selectedIds);
|
|
5538
|
+
if (!clipboard) return null;
|
|
5539
|
+
return pasteNativeShapeClipboard({
|
|
5540
|
+
items: input.items,
|
|
5541
|
+
clipboard
|
|
5542
|
+
});
|
|
5543
|
+
}
|
|
5544
|
+
|
|
5333
5545
|
// src/native/native-transient-items.ts
|
|
5334
5546
|
function moveNativeTransientItems({
|
|
5335
5547
|
items,
|
|
@@ -5378,6 +5590,11 @@ var NATIVE_VIEWPORT_OVERLAY_Z_INDEX = 40;
|
|
|
5378
5590
|
var NATIVE_VIEWPORT_OVERLAY_ELEVATION = 40;
|
|
5379
5591
|
var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_Z_INDEX = 20;
|
|
5380
5592
|
var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_ELEVATION = 20;
|
|
5593
|
+
var LONG_PRESS_CONTEXT_MENU_MS = 520;
|
|
5594
|
+
var LONG_PRESS_CONTEXT_MENU_CANCEL_PX = 10;
|
|
5595
|
+
var NATIVE_CONTEXT_MENU_WIDTH = 304;
|
|
5596
|
+
var NATIVE_CONTEXT_MENU_HEIGHT = 52;
|
|
5597
|
+
var NATIVE_CONTEXT_MENU_MARGIN = 12;
|
|
5381
5598
|
function isPlacementTool(toolId) {
|
|
5382
5599
|
return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
|
|
5383
5600
|
}
|
|
@@ -5450,6 +5667,102 @@ function fitCameraToWorldRect(camera, viewportW, viewportH, worldRect, padding)
|
|
|
5450
5667
|
camera.x = viewportW / 2 - cx * z;
|
|
5451
5668
|
camera.y = viewportH / 2 - cy * z;
|
|
5452
5669
|
}
|
|
5670
|
+
function clampNativeContextMenuState(menu, size) {
|
|
5671
|
+
return {
|
|
5672
|
+
...menu,
|
|
5673
|
+
x: Math.max(
|
|
5674
|
+
NATIVE_CONTEXT_MENU_MARGIN,
|
|
5675
|
+
Math.min(
|
|
5676
|
+
menu.x,
|
|
5677
|
+
Math.max(NATIVE_CONTEXT_MENU_MARGIN, size.width - NATIVE_CONTEXT_MENU_WIDTH)
|
|
5678
|
+
)
|
|
5679
|
+
),
|
|
5680
|
+
y: Math.max(
|
|
5681
|
+
NATIVE_CONTEXT_MENU_MARGIN,
|
|
5682
|
+
Math.min(
|
|
5683
|
+
menu.y - NATIVE_CONTEXT_MENU_HEIGHT - NATIVE_CONTEXT_MENU_MARGIN,
|
|
5684
|
+
Math.max(
|
|
5685
|
+
NATIVE_CONTEXT_MENU_MARGIN,
|
|
5686
|
+
size.height - NATIVE_CONTEXT_MENU_HEIGHT - NATIVE_CONTEXT_MENU_MARGIN
|
|
5687
|
+
)
|
|
5688
|
+
)
|
|
5689
|
+
)
|
|
5690
|
+
};
|
|
5691
|
+
}
|
|
5692
|
+
function NativeSelectionContextMenu({
|
|
5693
|
+
x,
|
|
5694
|
+
y,
|
|
5695
|
+
canPaste,
|
|
5696
|
+
onCopy,
|
|
5697
|
+
onPaste,
|
|
5698
|
+
onDuplicate,
|
|
5699
|
+
onDelete
|
|
5700
|
+
}) {
|
|
5701
|
+
return /* @__PURE__ */ jsxs(
|
|
5702
|
+
View,
|
|
5703
|
+
{
|
|
5704
|
+
style: [
|
|
5705
|
+
styles4.nativeSelectionContextMenu,
|
|
5706
|
+
{
|
|
5707
|
+
left: x,
|
|
5708
|
+
top: y
|
|
5709
|
+
}
|
|
5710
|
+
],
|
|
5711
|
+
children: [
|
|
5712
|
+
/* @__PURE__ */ jsx(NativeSelectionContextMenuButton, { label: "Copy", onPress: onCopy }),
|
|
5713
|
+
/* @__PURE__ */ jsx(
|
|
5714
|
+
NativeSelectionContextMenuButton,
|
|
5715
|
+
{
|
|
5716
|
+
label: "Paste",
|
|
5717
|
+
onPress: onPaste,
|
|
5718
|
+
disabled: !canPaste
|
|
5719
|
+
}
|
|
5720
|
+
),
|
|
5721
|
+
/* @__PURE__ */ jsx(NativeSelectionContextMenuButton, { label: "Duplicate", onPress: onDuplicate }),
|
|
5722
|
+
/* @__PURE__ */ jsx(
|
|
5723
|
+
NativeSelectionContextMenuButton,
|
|
5724
|
+
{
|
|
5725
|
+
label: "Delete",
|
|
5726
|
+
onPress: onDelete,
|
|
5727
|
+
destructive: true
|
|
5728
|
+
}
|
|
5729
|
+
)
|
|
5730
|
+
]
|
|
5731
|
+
}
|
|
5732
|
+
);
|
|
5733
|
+
}
|
|
5734
|
+
function NativeSelectionContextMenuButton({
|
|
5735
|
+
label,
|
|
5736
|
+
onPress,
|
|
5737
|
+
disabled = false,
|
|
5738
|
+
destructive = false
|
|
5739
|
+
}) {
|
|
5740
|
+
return /* @__PURE__ */ jsx(
|
|
5741
|
+
Pressable,
|
|
5742
|
+
{
|
|
5743
|
+
accessibilityRole: "button",
|
|
5744
|
+
accessibilityState: { disabled },
|
|
5745
|
+
disabled,
|
|
5746
|
+
onPress,
|
|
5747
|
+
style: ({ pressed }) => [
|
|
5748
|
+
styles4.nativeSelectionContextMenuButton,
|
|
5749
|
+
pressed && !disabled ? styles4.nativeSelectionContextMenuButtonPressed : void 0,
|
|
5750
|
+
disabled ? styles4.nativeSelectionContextMenuButtonDisabled : void 0
|
|
5751
|
+
],
|
|
5752
|
+
children: /* @__PURE__ */ jsx(
|
|
5753
|
+
Text,
|
|
5754
|
+
{
|
|
5755
|
+
style: [
|
|
5756
|
+
styles4.nativeSelectionContextMenuButtonText,
|
|
5757
|
+
destructive ? styles4.nativeSelectionContextMenuDeleteText : void 0,
|
|
5758
|
+
disabled ? styles4.nativeSelectionContextMenuButtonTextDisabled : void 0
|
|
5759
|
+
],
|
|
5760
|
+
children: label
|
|
5761
|
+
}
|
|
5762
|
+
)
|
|
5763
|
+
}
|
|
5764
|
+
);
|
|
5765
|
+
}
|
|
5453
5766
|
var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
5454
5767
|
items,
|
|
5455
5768
|
selectedIds = [],
|
|
@@ -5516,6 +5829,16 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5516
5829
|
selectedIdsRef.current = selectedIds;
|
|
5517
5830
|
const remotePresenceRef = useRef(remotePresence);
|
|
5518
5831
|
remotePresenceRef.current = remotePresence;
|
|
5832
|
+
const shapeClipboardRef = useRef(null);
|
|
5833
|
+
const [selectionContextMenu, setSelectionContextMenu] = useState(null);
|
|
5834
|
+
const selectionContextMenuRef = useRef(
|
|
5835
|
+
null
|
|
5836
|
+
);
|
|
5837
|
+
selectionContextMenuRef.current = selectionContextMenu;
|
|
5838
|
+
const contextMenuLongPressTimerRef = useRef(
|
|
5839
|
+
null
|
|
5840
|
+
);
|
|
5841
|
+
const contextMenuLongPressStartRef = useRef(null);
|
|
5519
5842
|
const dragStateRef = useRef({ kind: "idle" });
|
|
5520
5843
|
useEffect(() => {
|
|
5521
5844
|
const committedItems = items;
|
|
@@ -5533,6 +5856,16 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5533
5856
|
transientItemsRef.current = null;
|
|
5534
5857
|
setTransientItems(null);
|
|
5535
5858
|
}, []);
|
|
5859
|
+
const clearNativeContextMenuLongPress = useCallback(() => {
|
|
5860
|
+
if (contextMenuLongPressTimerRef.current) {
|
|
5861
|
+
clearTimeout(contextMenuLongPressTimerRef.current);
|
|
5862
|
+
contextMenuLongPressTimerRef.current = null;
|
|
5863
|
+
}
|
|
5864
|
+
contextMenuLongPressStartRef.current = null;
|
|
5865
|
+
}, []);
|
|
5866
|
+
const closeNativeSelectionContextMenu = useCallback(() => {
|
|
5867
|
+
setSelectionContextMenu(null);
|
|
5868
|
+
}, []);
|
|
5536
5869
|
const commitTransientItemsPreview = useCallback(() => {
|
|
5537
5870
|
const nextItems = transientItemsRef.current;
|
|
5538
5871
|
const change = onItemsChangeRef.current;
|
|
@@ -5565,6 +5898,9 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5565
5898
|
if (laserClearTimerRef.current) {
|
|
5566
5899
|
clearTimeout(laserClearTimerRef.current);
|
|
5567
5900
|
}
|
|
5901
|
+
if (contextMenuLongPressTimerRef.current) {
|
|
5902
|
+
clearTimeout(contextMenuLongPressTimerRef.current);
|
|
5903
|
+
}
|
|
5568
5904
|
},
|
|
5569
5905
|
[]
|
|
5570
5906
|
);
|
|
@@ -5649,6 +5985,10 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5649
5985
|
}
|
|
5650
5986
|
const camera = cameraRef.current;
|
|
5651
5987
|
const [cameraTick, setCameraTick] = useState(0);
|
|
5988
|
+
const selectedItems = useMemo(
|
|
5989
|
+
() => activeItems.filter((item) => selectedIds.includes(item.id)),
|
|
5990
|
+
[activeItems, selectedIds]
|
|
5991
|
+
);
|
|
5652
5992
|
const screenToWorld = useCallback(
|
|
5653
5993
|
(sx, sy) => {
|
|
5654
5994
|
const cam = cameraRef.current;
|
|
@@ -5657,6 +5997,59 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5657
5997
|
},
|
|
5658
5998
|
[]
|
|
5659
5999
|
);
|
|
6000
|
+
const startNativeContextMenuLongPress = useCallback(
|
|
6001
|
+
(point) => {
|
|
6002
|
+
clearNativeContextMenuLongPress();
|
|
6003
|
+
if (toolIdRef.current !== "select") return;
|
|
6004
|
+
const { worldX, worldY } = screenToWorld(point.x, point.y);
|
|
6005
|
+
const request = resolveNativeSelectionContextMenuRequest({
|
|
6006
|
+
interactive,
|
|
6007
|
+
toolId: toolIdRef.current,
|
|
6008
|
+
selectedItems,
|
|
6009
|
+
worldPoint: { x: worldX, y: worldY },
|
|
6010
|
+
screenPoint: point
|
|
6011
|
+
});
|
|
6012
|
+
if (!request) return;
|
|
6013
|
+
contextMenuLongPressStartRef.current = point;
|
|
6014
|
+
contextMenuLongPressTimerRef.current = setTimeout(() => {
|
|
6015
|
+
contextMenuLongPressTimerRef.current = null;
|
|
6016
|
+
contextMenuLongPressStartRef.current = null;
|
|
6017
|
+
dragStateRef.current = { kind: "idle" };
|
|
6018
|
+
lastPanPoint.current = null;
|
|
6019
|
+
lastPinchDist.current = null;
|
|
6020
|
+
clearTransientItemsPreview();
|
|
6021
|
+
setRealtimePlacementPreview(null);
|
|
6022
|
+
setSelectionContextMenu(
|
|
6023
|
+
clampNativeContextMenuState(
|
|
6024
|
+
{
|
|
6025
|
+
...request,
|
|
6026
|
+
canPaste: shapeClipboardRef.current !== null
|
|
6027
|
+
},
|
|
6028
|
+
size
|
|
6029
|
+
)
|
|
6030
|
+
);
|
|
6031
|
+
}, LONG_PRESS_CONTEXT_MENU_MS);
|
|
6032
|
+
},
|
|
6033
|
+
[
|
|
6034
|
+
clearNativeContextMenuLongPress,
|
|
6035
|
+
clearTransientItemsPreview,
|
|
6036
|
+
interactive,
|
|
6037
|
+
screenToWorld,
|
|
6038
|
+
selectedItems,
|
|
6039
|
+
setRealtimePlacementPreview,
|
|
6040
|
+
size
|
|
6041
|
+
]
|
|
6042
|
+
);
|
|
6043
|
+
const cancelNativeContextMenuLongPressAfterMove = useCallback(
|
|
6044
|
+
(point) => {
|
|
6045
|
+
const start = contextMenuLongPressStartRef.current;
|
|
6046
|
+
if (!start) return;
|
|
6047
|
+
if (Math.hypot(point.x - start.x, point.y - start.y) > LONG_PRESS_CONTEXT_MENU_CANCEL_PX) {
|
|
6048
|
+
clearNativeContextMenuLongPress();
|
|
6049
|
+
}
|
|
6050
|
+
},
|
|
6051
|
+
[clearNativeContextMenuLongPress]
|
|
6052
|
+
);
|
|
5660
6053
|
const notifyWorldPointerMove = useCallback(
|
|
5661
6054
|
(point) => {
|
|
5662
6055
|
const { worldX, worldY } = screenToWorld(point.x, point.y);
|
|
@@ -5679,10 +6072,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5679
6072
|
}, []);
|
|
5680
6073
|
const hideToolCursor = useCallback(() => {
|
|
5681
6074
|
}, []);
|
|
5682
|
-
const selectedItems = useMemo(
|
|
5683
|
-
() => activeItems.filter((item) => selectedIds.includes(item.id)),
|
|
5684
|
-
[activeItems, selectedIds]
|
|
5685
|
-
);
|
|
5686
6075
|
const selectedStyleInspectorState = useMemo(() => {
|
|
5687
6076
|
const styleableItems = selectedItems.filter(
|
|
5688
6077
|
(item) => !item.locked && getNativeStyleInspectorToolId(item) !== null
|
|
@@ -5727,10 +6116,77 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5727
6116
|
},
|
|
5728
6117
|
[patchCurrentStrokeStyle]
|
|
5729
6118
|
);
|
|
6119
|
+
const copySelectedShapes = useCallback(() => {
|
|
6120
|
+
if (!interactive) return false;
|
|
6121
|
+
const clipboard = copyNativeSelectedShapeItems(
|
|
6122
|
+
itemsRef.current,
|
|
6123
|
+
selectedIdsRef.current
|
|
6124
|
+
);
|
|
6125
|
+
if (!clipboard) return false;
|
|
6126
|
+
shapeClipboardRef.current = clipboard;
|
|
6127
|
+
return true;
|
|
6128
|
+
}, [interactive]);
|
|
6129
|
+
const pasteCopiedShapes = useCallback(() => {
|
|
6130
|
+
if (!interactive) return false;
|
|
6131
|
+
const change = onItemsChangeRef.current;
|
|
6132
|
+
const clipboard = shapeClipboardRef.current;
|
|
6133
|
+
if (!change || !clipboard) return false;
|
|
6134
|
+
const pasted = pasteNativeShapeClipboard({
|
|
6135
|
+
items: itemsRef.current,
|
|
6136
|
+
clipboard
|
|
6137
|
+
});
|
|
6138
|
+
if (!pasted) return false;
|
|
6139
|
+
change(pasted.items);
|
|
6140
|
+
onSelectionChangeRef.current?.(pasted.selectedIds);
|
|
6141
|
+
return true;
|
|
6142
|
+
}, [interactive]);
|
|
6143
|
+
const duplicateSelectedShapes = useCallback(() => {
|
|
6144
|
+
if (!interactive) return false;
|
|
6145
|
+
const change = onItemsChangeRef.current;
|
|
6146
|
+
if (!change) return false;
|
|
6147
|
+
const duplicated = duplicateNativeSelectedShapes({
|
|
6148
|
+
items: itemsRef.current,
|
|
6149
|
+
selectedIds: selectedIdsRef.current
|
|
6150
|
+
});
|
|
6151
|
+
if (!duplicated) return false;
|
|
6152
|
+
change(duplicated.items);
|
|
6153
|
+
onSelectionChangeRef.current?.(duplicated.selectedIds);
|
|
6154
|
+
return true;
|
|
6155
|
+
}, [interactive]);
|
|
6156
|
+
const handleCopySelectedShapesFromMenu = useCallback(() => {
|
|
6157
|
+
copySelectedShapes();
|
|
6158
|
+
closeNativeSelectionContextMenu();
|
|
6159
|
+
}, [closeNativeSelectionContextMenu, copySelectedShapes]);
|
|
6160
|
+
const handlePasteCopiedShapesFromMenu = useCallback(() => {
|
|
6161
|
+
pasteCopiedShapes();
|
|
6162
|
+
closeNativeSelectionContextMenu();
|
|
6163
|
+
}, [closeNativeSelectionContextMenu, pasteCopiedShapes]);
|
|
6164
|
+
const handleDuplicateSelectedShapesFromMenu = useCallback(() => {
|
|
6165
|
+
duplicateSelectedShapes();
|
|
6166
|
+
closeNativeSelectionContextMenu();
|
|
6167
|
+
}, [closeNativeSelectionContextMenu, duplicateSelectedShapes]);
|
|
6168
|
+
const handleDeleteSelectedShapes = useCallback(() => {
|
|
6169
|
+
const menu = selectionContextMenuRef.current;
|
|
6170
|
+
const ids = menu?.itemIds ?? selectedIdsRef.current;
|
|
6171
|
+
if (ids.length === 0) return false;
|
|
6172
|
+
const change = onItemsChangeRef.current;
|
|
6173
|
+
if (!change) return false;
|
|
6174
|
+
const idSet = new Set(ids);
|
|
6175
|
+
const nextItems = itemsRef.current.filter((item) => !idSet.has(item.id));
|
|
6176
|
+
if (nextItems.length === itemsRef.current.length) return false;
|
|
6177
|
+
change(nextItems);
|
|
6178
|
+
onSelectionChangeRef.current?.(
|
|
6179
|
+
selectedIdsRef.current.filter((id) => !idSet.has(id))
|
|
6180
|
+
);
|
|
6181
|
+
closeNativeSelectionContextMenu();
|
|
6182
|
+
return true;
|
|
6183
|
+
}, [closeNativeSelectionContextMenu]);
|
|
5730
6184
|
const lastPinchDist = useRef(null);
|
|
5731
6185
|
const lastPanPoint = useRef(null);
|
|
5732
6186
|
const beginDragAtScreenPoint = useCallback(
|
|
5733
6187
|
(point) => {
|
|
6188
|
+
closeNativeSelectionContextMenu();
|
|
6189
|
+
startNativeContextMenuLongPress(point);
|
|
5734
6190
|
lastPinchDist.current = null;
|
|
5735
6191
|
lastPanPoint.current = null;
|
|
5736
6192
|
const sx = point.x;
|
|
@@ -5944,15 +6400,18 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5944
6400
|
dragStateRef.current = { kind: "pan" };
|
|
5945
6401
|
},
|
|
5946
6402
|
[
|
|
6403
|
+
closeNativeSelectionContextMenu,
|
|
5947
6404
|
interactive,
|
|
5948
6405
|
requestSelectToolAfterUse,
|
|
5949
6406
|
screenToWorld,
|
|
5950
6407
|
setRealtimePlacementPreview,
|
|
6408
|
+
startNativeContextMenuLongPress,
|
|
5951
6409
|
updateToolCursorPoint
|
|
5952
6410
|
]
|
|
5953
6411
|
);
|
|
5954
6412
|
const applyDragMoveAtScreenPoint = useCallback(
|
|
5955
6413
|
(point, pagePoint) => {
|
|
6414
|
+
cancelNativeContextMenuLongPressAfterMove(point);
|
|
5956
6415
|
const cam = cameraRef.current;
|
|
5957
6416
|
if (!cam) return;
|
|
5958
6417
|
updateToolCursorPoint(point);
|
|
@@ -5978,19 +6437,22 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5978
6437
|
const dx = worldX - (last?.x ?? worldX);
|
|
5979
6438
|
const dy = worldY - (last?.y ?? worldY);
|
|
5980
6439
|
const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
|
|
5981
|
-
if (
|
|
5982
|
-
|
|
6440
|
+
if (shouldAppendPoint) {
|
|
6441
|
+
pts.push({ x: worldX, y: worldY });
|
|
6442
|
+
}
|
|
5983
6443
|
if (st.tool === "laser") {
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
6444
|
+
if (shouldAppendPoint) {
|
|
6445
|
+
setLaserTrail((prev) => [
|
|
6446
|
+
...prev,
|
|
6447
|
+
{ x: worldX, y: worldY, t: Date.now() }
|
|
6448
|
+
]);
|
|
6449
|
+
}
|
|
5988
6450
|
return;
|
|
5989
6451
|
}
|
|
5990
6452
|
setRealtimePlacementPreview({
|
|
5991
6453
|
kind: "stroke",
|
|
5992
6454
|
tool: st.tool,
|
|
5993
|
-
points: pts
|
|
6455
|
+
points: [...pts],
|
|
5994
6456
|
style: { ...strokeStyleRef.current }
|
|
5995
6457
|
});
|
|
5996
6458
|
return;
|
|
@@ -6088,6 +6550,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6088
6550
|
}
|
|
6089
6551
|
},
|
|
6090
6552
|
[
|
|
6553
|
+
cancelNativeContextMenuLongPressAfterMove,
|
|
6091
6554
|
requestRender,
|
|
6092
6555
|
screenToWorld,
|
|
6093
6556
|
setRealtimePlacementPreview,
|
|
@@ -6097,6 +6560,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6097
6560
|
);
|
|
6098
6561
|
const finishDragAtScreenPoint = useCallback(
|
|
6099
6562
|
(point) => {
|
|
6563
|
+
clearNativeContextMenuLongPress();
|
|
6100
6564
|
lastPinchDist.current = null;
|
|
6101
6565
|
lastPanPoint.current = null;
|
|
6102
6566
|
updateToolCursorPoint(point);
|
|
@@ -6344,6 +6808,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6344
6808
|
dragStateRef.current = { kind: "idle" };
|
|
6345
6809
|
},
|
|
6346
6810
|
[
|
|
6811
|
+
clearNativeContextMenuLongPress,
|
|
6347
6812
|
requestSelectToolAfterNativeLinkUse,
|
|
6348
6813
|
requestSelectToolAfterUse,
|
|
6349
6814
|
screenToWorld,
|
|
@@ -6387,6 +6852,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6387
6852
|
const sx = evt.nativeEvent.locationX;
|
|
6388
6853
|
const sy = evt.nativeEvent.locationY;
|
|
6389
6854
|
if (touches && touches.length >= 2) {
|
|
6855
|
+
clearNativeContextMenuLongPress();
|
|
6390
6856
|
hideToolCursor();
|
|
6391
6857
|
notifyWorldPointerLeave();
|
|
6392
6858
|
dragStateRef.current = { kind: "pan" };
|
|
@@ -6403,6 +6869,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6403
6869
|
const pageX = evt.nativeEvent.pageX;
|
|
6404
6870
|
const pageY = evt.nativeEvent.pageY;
|
|
6405
6871
|
if (touches && touches.length >= 2) {
|
|
6872
|
+
clearNativeContextMenuLongPress();
|
|
6406
6873
|
hideToolCursor();
|
|
6407
6874
|
notifyWorldPointerLeave();
|
|
6408
6875
|
const t0 = touches[0];
|
|
@@ -6433,6 +6900,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6433
6900
|
});
|
|
6434
6901
|
},
|
|
6435
6902
|
onPanResponderTerminate: () => {
|
|
6903
|
+
clearNativeContextMenuLongPress();
|
|
6436
6904
|
lastPinchDist.current = null;
|
|
6437
6905
|
lastPanPoint.current = null;
|
|
6438
6906
|
hideToolCursor();
|
|
@@ -6449,6 +6917,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6449
6917
|
[
|
|
6450
6918
|
applyDragMoveAtScreenPoint,
|
|
6451
6919
|
beginDragAtScreenPoint,
|
|
6920
|
+
clearNativeContextMenuLongPress,
|
|
6452
6921
|
finishDragAtScreenPoint,
|
|
6453
6922
|
requestRender,
|
|
6454
6923
|
hideToolCursor,
|
|
@@ -6474,9 +6943,18 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6474
6943
|
options?.padding ?? 0.08
|
|
6475
6944
|
);
|
|
6476
6945
|
requestRender();
|
|
6477
|
-
}
|
|
6946
|
+
},
|
|
6947
|
+
copySelectedShapes,
|
|
6948
|
+
pasteCopiedShapes,
|
|
6949
|
+
duplicateSelectedShapes
|
|
6478
6950
|
}),
|
|
6479
|
-
[
|
|
6951
|
+
[
|
|
6952
|
+
copySelectedShapes,
|
|
6953
|
+
duplicateSelectedShapes,
|
|
6954
|
+
pasteCopiedShapes,
|
|
6955
|
+
requestRender,
|
|
6956
|
+
size
|
|
6957
|
+
]
|
|
6480
6958
|
);
|
|
6481
6959
|
const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
|
|
6482
6960
|
const styleInspectorToolId = selectedStyleInspectorState?.toolId ?? activeStyleToolId;
|
|
@@ -6594,7 +7072,17 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6594
7072
|
pointerEvents: "box-none",
|
|
6595
7073
|
children: toolbar
|
|
6596
7074
|
}
|
|
6597
|
-
)
|
|
7075
|
+
),
|
|
7076
|
+
interactive && selectionContextMenu ? /* @__PURE__ */ jsx(
|
|
7077
|
+
NativeSelectionContextMenu,
|
|
7078
|
+
{
|
|
7079
|
+
...selectionContextMenu,
|
|
7080
|
+
onCopy: handleCopySelectedShapesFromMenu,
|
|
7081
|
+
onPaste: handlePasteCopiedShapesFromMenu,
|
|
7082
|
+
onDuplicate: handleDuplicateSelectedShapesFromMenu,
|
|
7083
|
+
onDelete: handleDeleteSelectedShapes
|
|
7084
|
+
}
|
|
7085
|
+
) : null
|
|
6598
7086
|
] }),
|
|
6599
7087
|
/* @__PURE__ */ jsx(
|
|
6600
7088
|
Modal,
|
|
@@ -6657,6 +7145,49 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
6657
7145
|
] });
|
|
6658
7146
|
});
|
|
6659
7147
|
var styles4 = StyleSheet.create({
|
|
7148
|
+
nativeSelectionContextMenu: {
|
|
7149
|
+
position: "absolute",
|
|
7150
|
+
width: NATIVE_CONTEXT_MENU_WIDTH,
|
|
7151
|
+
minHeight: NATIVE_CONTEXT_MENU_HEIGHT,
|
|
7152
|
+
flexDirection: "row",
|
|
7153
|
+
alignItems: "center",
|
|
7154
|
+
justifyContent: "space-between",
|
|
7155
|
+
padding: 4,
|
|
7156
|
+
borderRadius: 14,
|
|
7157
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
7158
|
+
borderColor: "#d4d4d8",
|
|
7159
|
+
backgroundColor: "#ffffff",
|
|
7160
|
+
shadowColor: "#000000",
|
|
7161
|
+
shadowOpacity: 0.16,
|
|
7162
|
+
shadowRadius: 18,
|
|
7163
|
+
shadowOffset: { width: 0, height: 8 },
|
|
7164
|
+
elevation: NATIVE_VIEWPORT_OVERLAY_ELEVATION + 4,
|
|
7165
|
+
zIndex: NATIVE_VIEWPORT_OVERLAY_Z_INDEX + 4
|
|
7166
|
+
},
|
|
7167
|
+
nativeSelectionContextMenuButton: {
|
|
7168
|
+
minHeight: 42,
|
|
7169
|
+
alignItems: "center",
|
|
7170
|
+
justifyContent: "center",
|
|
7171
|
+
borderRadius: 10,
|
|
7172
|
+
paddingHorizontal: 10
|
|
7173
|
+
},
|
|
7174
|
+
nativeSelectionContextMenuButtonPressed: {
|
|
7175
|
+
backgroundColor: "#f4f4f5"
|
|
7176
|
+
},
|
|
7177
|
+
nativeSelectionContextMenuButtonDisabled: {
|
|
7178
|
+
opacity: 0.45
|
|
7179
|
+
},
|
|
7180
|
+
nativeSelectionContextMenuButtonText: {
|
|
7181
|
+
color: "#18181b",
|
|
7182
|
+
fontSize: 14,
|
|
7183
|
+
fontWeight: "700"
|
|
7184
|
+
},
|
|
7185
|
+
nativeSelectionContextMenuButtonTextDisabled: {
|
|
7186
|
+
color: "#71717a"
|
|
7187
|
+
},
|
|
7188
|
+
nativeSelectionContextMenuDeleteText: {
|
|
7189
|
+
color: "#dc2626"
|
|
7190
|
+
},
|
|
6660
7191
|
nativeLinkDialogBackdrop: {
|
|
6661
7192
|
flex: 1,
|
|
6662
7193
|
alignItems: "center",
|