@@ -67,6 +67,7 @@ export default function ProofSearchCanvas({
6767 const rafRef = useRef ( null ) ;
6868 const timeRef = useRef ( 0 ) ;
6969 const themeRef = useRef ( 'light' ) ;
70+ const tapRef = useRef ( { time : 0 , id : null , x : 0 , y : 0 } ) ;
7071
7172 useEffect ( ( ) => {
7273 nodesRef . current . clear ( ) ;
@@ -392,6 +393,7 @@ export default function ProofSearchCanvas({
392393 const canvas = canvasRef . current ;
393394 if ( ! canvas ) return undefined ;
394395 const handleMove = ( ev ) => {
396+ if ( ev . pointerType === 'touch' ) return ;
395397 const { w, h} = sizeRef . current ;
396398 const rect = canvas . getBoundingClientRect ( ) ;
397399 const { zoom, panX, panY} = cameraRef . current ;
@@ -448,6 +450,7 @@ export default function ProofSearchCanvas({
448450 canvas . style . cursor = 'default' ;
449451 } ;
450452 const handleDown = ( ev ) => {
453+ if ( ev . pointerType === 'touch' ) return ;
451454 const rect = canvas . getBoundingClientRect ( ) ;
452455 const { zoom, panX, panY} = cameraRef . current ;
453456 const x = ( ev . clientX - rect . left - panX ) / zoom ;
@@ -487,7 +490,8 @@ export default function ProofSearchCanvas({
487490 canvas . style . cursor = 'grabbing' ;
488491 }
489492 } ;
490- const handleUp = ( ) => {
493+ const handleUp = ( ev ) => {
494+ if ( ev ?. pointerType === 'touch' ) return ;
491495 dragRef . current = null ;
492496 canvas . style . cursor = 'grab' ;
493497 } ;
@@ -522,10 +526,10 @@ export default function ProofSearchCanvas({
522526 canvas . addEventListener ( 'dblclick' , handleDbl ) ;
523527 const handleTouchStart = ( ev ) => {
524528 if ( ev . touches . length === 0 ) return ;
525- ev . preventDefault ( ) ;
526529 const rect = canvas . getBoundingClientRect ( ) ;
527530 const { zoom, panX, panY} = cameraRef . current ;
528531 if ( ev . touches . length >= 2 ) {
532+ ev . preventDefault ( ) ;
529533 const t1 = ev . touches [ 0 ] ;
530534 const t2 = ev . touches [ 1 ] ;
531535 const cX = ( t1 . clientX + t2 . clientX ) / 2 - rect . left ;
@@ -564,14 +568,16 @@ export default function ProofSearchCanvas({
564568 }
565569 } ) ;
566570 if ( chosen ) {
571+ ev . preventDefault ( ) ;
567572 dragRef . current = {
568573 type : 'node' ,
569574 id : chosen . id ,
570575 offsetX : chosen . x - x ,
571576 offsetY : chosen . y - y ,
572577 startX : x ,
573578 startY : y ,
574- dragging : true ,
579+ startTime : performance . now ( ) ,
580+ dragging : false ,
575581 } ;
576582 chosen . vx = 0 ;
577583 chosen . vy = 0 ;
@@ -581,11 +587,11 @@ export default function ProofSearchCanvas({
581587 } ;
582588 const handleTouchMove = ( ev ) => {
583589 if ( ev . touches . length === 0 ) return ;
584- ev . preventDefault ( ) ;
585590 const rect = canvas . getBoundingClientRect ( ) ;
586591 const state = dragRef . current ;
587592 const { zoom, panX, panY} = cameraRef . current ;
588593 if ( ev . touches . length >= 2 ) {
594+ ev . preventDefault ( ) ;
589595 const t1 = ev . touches [ 0 ] ;
590596 const t2 = ev . touches [ 1 ] ;
591597 const cX = ( t1 . clientX + t2 . clientX ) / 2 - rect . left ;
@@ -617,21 +623,46 @@ export default function ProofSearchCanvas({
617623 }
618624
619625 if ( state && state . type === 'node' ) {
626+ ev . preventDefault ( ) ;
620627 const t = ev . touches [ 0 ] ;
621628 const x = ( t . clientX - rect . left - panX ) / zoom ;
622629 const y = ( t . clientY - rect . top - panY ) / zoom ;
623630 const node = nodesRef . current . get ( state . id ) ;
624631 if ( node ) {
625- node . x = x + state . offsetX ;
626- node . y = y + state . offsetY ;
627- node . vx = 0 ;
628- node . vy = 0 ;
632+ const dx = x - state . startX ;
633+ const dy = y - state . startY ;
634+ const distance = Math . hypot ( dx , dy ) ;
635+ if ( ! state . dragging && distance > 4 ) {
636+ state . dragging = true ;
637+ }
638+ if ( state . dragging ) {
639+ node . x = x + state . offsetX ;
640+ node . y = y + state . offsetY ;
641+ node . vx = 0 ;
642+ node . vy = 0 ;
643+ }
629644 }
630645 }
631646 } ;
632647 const handleTouchEnd = ( ev ) => {
633- ev . preventDefault ( ) ;
634648 if ( ev . touches . length >= 2 ) return ;
649+ const state = dragRef . current ;
650+ if ( state && state . type === 'node' && ! state . dragging ) {
651+ const now = performance . now ( ) ;
652+ const tapDuration = state . startTime ? now - state . startTime : 0 ;
653+ if ( tapDuration < 250 ) {
654+ const last = tapRef . current ;
655+ if ( last . id === state . id && now - last . time < 320 ) {
656+ const node = nodesRef . current . get ( state . id ) ;
657+ if ( node && node . status === 'passive' && onSelect ) {
658+ onSelect ( node . id ) ;
659+ }
660+ tapRef . current = { time : 0 , id : null , x : 0 , y : 0 } ;
661+ } else {
662+ tapRef . current = { time : now , id : state . id , x : 0 , y : 0 } ;
663+ }
664+ }
665+ }
635666 dragRef . current = null ;
636667 canvas . style . cursor = 'grab' ;
637668 } ;
@@ -675,7 +706,7 @@ export default function ProofSearchCanvas({
675706 return (
676707 < canvas
677708 ref = { canvasRef }
678- style = { { width : '100%' , height : '100%' , display : 'block' , touchAction : 'none ' } }
709+ style = { { width : '100%' , height : '100%' , display : 'block' , touchAction : 'pan-y ' } }
679710 />
680711 ) ;
681712}
0 commit comments