canvu-react 0.4.18 → 0.4.19

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 CHANGED
@@ -4008,6 +4008,7 @@ function nativeFallbackToolCursorPoint(size) {
4008
4008
  }
4009
4009
 
4010
4010
  // src/native/native-vector-interactions.ts
4011
+ var NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX = 24;
4011
4012
  function supportsNativeResizeHandles(item) {
4012
4013
  const k = item?.toolKind;
4013
4014
  if (k === "rect" || k === "ellipse" || k === "architectural-cloud" || k === "line" || k === "arrow" || k === "image" || k === "text") {
@@ -4029,7 +4030,7 @@ function hitTestNativeSelectionHandle({
4029
4030
  if (!supportsNativeResizeHandles(selectedItem)) return null;
4030
4031
  const bounds = normalizeRect(selectedItem.bounds);
4031
4032
  const rotation = selectedItem.rotation ?? 0;
4032
- const handleRadiusWorld = 6 / Math.max(zoom, 1e-9);
4033
+ const handleRadiusWorld = NATIVE_SELECTION_HANDLE_HIT_RADIUS_PX / Math.max(zoom, 1e-9);
4033
4034
  const rotateOffsetWorld = 24 / Math.max(zoom, 1e-9);
4034
4035
  if (hitTestRotateHandle(
4035
4036
  bounds,
@@ -4336,12 +4337,6 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4336
4337
  react.useEffect(() => {
4337
4338
  showFallbackToolCursor(toolId);
4338
4339
  }, [showFallbackToolCursor, toolId]);
4339
- const handlePointerMove = react.useCallback(
4340
- (event) => {
4341
- updateToolCursorPoint(screenPointFromPointerEvent(event));
4342
- },
4343
- [updateToolCursorPoint]
4344
- );
4345
4340
  const selectedItems = react.useMemo(
4346
4341
  () => items.filter((it) => selectedIds.includes(it.id)),
4347
4342
  [items, selectedIds]
@@ -4354,6 +4349,153 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4354
4349
  const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
4355
4350
  const lastPinchDist = react.useRef(null);
4356
4351
  const lastPanPoint = react.useRef(null);
4352
+ const applyDragMoveAtScreenPoint = react.useCallback(
4353
+ (point, pagePoint) => {
4354
+ const cam = cameraRef.current;
4355
+ if (!cam) return;
4356
+ updateToolCursorPoint(point);
4357
+ const { worldX, worldY } = screenToWorld(point.x, point.y);
4358
+ const st = dragStateRef.current;
4359
+ if (st.kind === "pan") {
4360
+ const current = pagePoint ?? point;
4361
+ if (lastPanPoint.current) {
4362
+ const dx = current.x - lastPanPoint.current.x;
4363
+ const dy = current.y - lastPanPoint.current.y;
4364
+ cam.x += dx;
4365
+ cam.y += dy;
4366
+ requestRender();
4367
+ }
4368
+ lastPanPoint.current = current;
4369
+ return;
4370
+ }
4371
+ lastPanPoint.current = null;
4372
+ if (st.kind === "draw") {
4373
+ const pts = st.points;
4374
+ const last = pts[pts.length - 1];
4375
+ const dx = worldX - (last?.x ?? worldX);
4376
+ const dy = worldY - (last?.y ?? worldY);
4377
+ const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
4378
+ if (shouldAppendPoint) {
4379
+ pts.push({ x: worldX, y: worldY });
4380
+ }
4381
+ if (st.tool === "laser") {
4382
+ if (shouldAppendPoint) {
4383
+ setLaserTrail((prev) => [
4384
+ ...prev,
4385
+ { x: worldX, y: worldY, t: Date.now() }
4386
+ ]);
4387
+ }
4388
+ return;
4389
+ }
4390
+ setPlacementPreview({
4391
+ kind: "stroke",
4392
+ tool: st.tool,
4393
+ points: [...pts],
4394
+ style: { ...strokeStyleRef.current }
4395
+ });
4396
+ return;
4397
+ }
4398
+ if (st.kind === "move") {
4399
+ const dx = worldX - st.startWorld.x;
4400
+ const dy = worldY - st.startWorld.y;
4401
+ const change = onItemsChangeRef.current;
4402
+ if (!change) return;
4403
+ const nextList = itemsRef.current.map((it) => {
4404
+ const snap = st.snapshots[it.id];
4405
+ if (!snap) return it;
4406
+ return {
4407
+ ...snap,
4408
+ x: snap.x + dx,
4409
+ y: snap.y + dy,
4410
+ bounds: {
4411
+ ...snap.bounds,
4412
+ x: snap.bounds.x + dx,
4413
+ y: snap.bounds.y + dy
4414
+ }
4415
+ };
4416
+ });
4417
+ change(nextList);
4418
+ return;
4419
+ }
4420
+ if (st.kind === "rotate") {
4421
+ const change = onItemsChangeRef.current;
4422
+ if (!change) return;
4423
+ const angle = Math.atan2(worldY - st.pivotWorld.y, worldX - st.pivotWorld.x);
4424
+ const next = applyRotationFromPointer(
4425
+ st.snapshot,
4426
+ st.startRotation,
4427
+ st.startPointerAngleRad,
4428
+ angle
4429
+ );
4430
+ change(itemsRef.current.map((item) => item.id === st.id ? next : item));
4431
+ return;
4432
+ }
4433
+ if (st.kind === "resize") {
4434
+ const change = onItemsChangeRef.current;
4435
+ if (!change) return;
4436
+ const next = resizeItemByHandle(st.snapshot, st.start, st.handle, {
4437
+ x: worldX,
4438
+ y: worldY
4439
+ });
4440
+ change(itemsRef.current.map((item) => item.id === st.id ? next : item));
4441
+ return;
4442
+ }
4443
+ if (st.kind === "marquee") {
4444
+ const a = st.startWorld;
4445
+ const b = { x: worldX, y: worldY };
4446
+ const rect = {
4447
+ x: Math.min(a.x, b.x),
4448
+ y: Math.min(a.y, b.y),
4449
+ width: Math.abs(b.x - a.x),
4450
+ height: Math.abs(b.y - a.y)
4451
+ };
4452
+ setPlacementPreview({ kind: "marquee", rect });
4453
+ return;
4454
+ }
4455
+ if (st.kind === "erase") {
4456
+ setEraserTrail((prev) => [...prev, { x: worldX, y: worldY, t: Date.now() }]);
4457
+ const toErase = collectEraserTargetsAtWorldPoint(
4458
+ itemsRef.current,
4459
+ worldX,
4460
+ worldY,
4461
+ { lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
4462
+ );
4463
+ for (const id of toErase) {
4464
+ eraserPreviewIdSetRef.current.add(id);
4465
+ }
4466
+ setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
4467
+ return;
4468
+ }
4469
+ if (st.kind === "place") {
4470
+ setPlacementPreview(
4471
+ placementPreviewForTool(st.tool, st.startWorld, {
4472
+ x: worldX,
4473
+ y: worldY
4474
+ })
4475
+ );
4476
+ return;
4477
+ }
4478
+ if (st.kind === "custom-place") {
4479
+ setPlacementPreview({
4480
+ kind: "rect",
4481
+ rect: rectFromCorners(st.startWorld, { x: worldX, y: worldY })
4482
+ });
4483
+ return;
4484
+ }
4485
+ },
4486
+ [requestRender, screenToWorld, updateToolCursorPoint]
4487
+ );
4488
+ const handlePointerMove = react.useCallback(
4489
+ (event) => {
4490
+ const point = screenPointFromPointerEvent(event);
4491
+ if (dragStateRef.current.kind !== "idle") {
4492
+ applyDragMoveAtScreenPoint(point, point);
4493
+ return;
4494
+ }
4495
+ updateToolCursorPoint(point);
4496
+ },
4497
+ [applyDragMoveAtScreenPoint, updateToolCursorPoint]
4498
+ );
4357
4499
  const panResponder = react.useMemo(
4358
4500
  () => reactNative.PanResponder.create({
4359
4501
  onStartShouldSetPanResponder: () => true,
@@ -4580,145 +4722,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4580
4722
  return;
4581
4723
  }
4582
4724
  lastPinchDist.current = null;
4583
- updateToolCursorPoint({ x: sx, y: sy });
4584
- const { worldX, worldY } = screenToWorld(sx, sy);
4585
- const st = dragStateRef.current;
4586
- if (st.kind === "pan") {
4587
- const current = { x: pageX, y: pageY };
4588
- if (lastPanPoint.current) {
4589
- const dx = current.x - lastPanPoint.current.x;
4590
- const dy = current.y - lastPanPoint.current.y;
4591
- cam.x += dx;
4592
- cam.y += dy;
4593
- requestRender();
4594
- }
4595
- lastPanPoint.current = current;
4596
- return;
4597
- }
4598
- lastPanPoint.current = null;
4599
- if (st.kind === "draw") {
4600
- const pts = st.points;
4601
- const last = pts[pts.length - 1];
4602
- const dx = worldX - (last?.x ?? worldX);
4603
- const dy = worldY - (last?.y ?? worldY);
4604
- const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
4605
- if (shouldAppendPoint) {
4606
- pts.push({ x: worldX, y: worldY });
4607
- }
4608
- if (st.tool === "laser") {
4609
- if (shouldAppendPoint) {
4610
- setLaserTrail((prev) => [
4611
- ...prev,
4612
- { x: worldX, y: worldY, t: Date.now() }
4613
- ]);
4614
- }
4615
- return;
4616
- }
4617
- setPlacementPreview({
4618
- kind: "stroke",
4619
- tool: st.tool,
4620
- points: [...pts],
4621
- style: { ...strokeStyleRef.current }
4622
- });
4623
- return;
4624
- }
4625
- if (st.kind === "move") {
4626
- const dx = worldX - st.startWorld.x;
4627
- const dy = worldY - st.startWorld.y;
4628
- const change = onItemsChangeRef.current;
4629
- if (!change) return;
4630
- const nextList = itemsRef.current.map((it) => {
4631
- const snap = st.snapshots[it.id];
4632
- if (!snap) return it;
4633
- return {
4634
- ...snap,
4635
- x: snap.x + dx,
4636
- y: snap.y + dy,
4637
- bounds: {
4638
- ...snap.bounds,
4639
- x: snap.bounds.x + dx,
4640
- y: snap.bounds.y + dy
4641
- }
4642
- };
4643
- });
4644
- change(nextList);
4645
- return;
4646
- }
4647
- if (st.kind === "rotate") {
4648
- const change = onItemsChangeRef.current;
4649
- if (!change) return;
4650
- const angle = Math.atan2(
4651
- worldY - st.pivotWorld.y,
4652
- worldX - st.pivotWorld.x
4653
- );
4654
- const next = applyRotationFromPointer(
4655
- st.snapshot,
4656
- st.startRotation,
4657
- st.startPointerAngleRad,
4658
- angle
4659
- );
4660
- change(
4661
- itemsRef.current.map((item) => item.id === st.id ? next : item)
4662
- );
4663
- return;
4664
- }
4665
- if (st.kind === "resize") {
4666
- const change = onItemsChangeRef.current;
4667
- if (!change) return;
4668
- const next = resizeItemByHandle(st.snapshot, st.start, st.handle, {
4669
- x: worldX,
4670
- y: worldY
4671
- });
4672
- change(
4673
- itemsRef.current.map((item) => item.id === st.id ? next : item)
4674
- );
4675
- return;
4676
- }
4677
- if (st.kind === "marquee") {
4678
- const a = st.startWorld;
4679
- const b = { x: worldX, y: worldY };
4680
- const rect = {
4681
- x: Math.min(a.x, b.x),
4682
- y: Math.min(a.y, b.y),
4683
- width: Math.abs(b.x - a.x),
4684
- height: Math.abs(b.y - a.y)
4685
- };
4686
- setPlacementPreview({ kind: "marquee", rect });
4687
- return;
4688
- }
4689
- if (st.kind === "erase") {
4690
- setEraserTrail((prev) => [
4691
- ...prev,
4692
- { x: worldX, y: worldY, t: Date.now() }
4693
- ]);
4694
- const toErase = collectEraserTargetsAtWorldPoint(
4695
- itemsRef.current,
4696
- worldX,
4697
- worldY,
4698
- { lineHitWorld: 10 / cam.zoom, ignoreLocked: true }
4699
- );
4700
- for (const id of toErase) {
4701
- eraserPreviewIdSetRef.current.add(id);
4702
- }
4703
- setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
4704
- return;
4705
- }
4706
- if (st.kind === "place") {
4707
- setPlacementPreview(
4708
- placementPreviewForTool(st.tool, st.startWorld, {
4709
- x: worldX,
4710
- y: worldY
4711
- })
4712
- );
4713
- return;
4714
- }
4715
- if (st.kind === "custom-place") {
4716
- setPlacementPreview({
4717
- kind: "rect",
4718
- rect: rectFromCorners(st.startWorld, { x: worldX, y: worldY })
4719
- });
4720
- return;
4721
- }
4725
+ applyDragMoveAtScreenPoint({ x: sx, y: sy }, { x: pageX, y: pageY });
4722
4726
  },
4723
4727
  onPanResponderRelease: (evt) => {
4724
4728
  lastPinchDist.current = null;
@@ -4952,6 +4956,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4952
4956
  }
4953
4957
  }),
4954
4958
  [
4959
+ applyDragMoveAtScreenPoint,
4955
4960
  screenToWorld,
4956
4961
  requestRender,
4957
4962
  requestSelectToolAfterUse,