UNPKG

circuit-bricks

Version:

A modular, Lego-style SVG circuit component system for React (ALPHA - Not for production use)

1 lines 19.4 kB
{"version":3,"file":"touchUtils-DFo5SzxM.mjs","names":["defaultTouchConfig: TouchConfig","p1: Point","p2: Point","touch1: Touch","touch2: Touch","touch: Touch","onGesture: (gesture: TouchEventData) => void","config: Partial<TouchConfig>","event: TouchEvent","enabled: boolean","e: TouchEvent","e: Event","query: string","event: MediaQueryListEvent"],"sources":["../../src/utils/touchUtils.ts"],"sourcesContent":["/**\n * Touch and Mobile Utilities\n *\n * Utilities for handling touch interactions and mobile-specific functionality\n * in Circuit-Bricks. Provides enhanced touch gesture support for circuit\n * manipulation on mobile devices.\n */\n\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport { Point } from '../types';\nimport { isBrowser } from './ssrUtils';\n\n/**\n * Touch gesture types\n */\nexport type TouchGesture = 'tap' | 'double-tap' | 'long-press' | 'pan' | 'pinch' | 'swipe';\n\n/**\n * Touch event data\n */\nexport interface TouchEventData {\n type: TouchGesture;\n startPoint: Point;\n currentPoint: Point;\n deltaX: number;\n deltaY: number;\n distance: number;\n scale?: number;\n velocity?: Point;\n duration: number;\n}\n\n/**\n * Touch gesture configuration\n */\nexport interface TouchConfig {\n tapThreshold: number; // Maximum movement for tap (px)\n longPressDelay: number; // Long press delay (ms)\n doubleTapDelay: number; // Double tap max delay (ms)\n swipeThreshold: number; // Minimum distance for swipe (px)\n swipeVelocityThreshold: number; // Minimum velocity for swipe (px/ms)\n pinchThreshold: number; // Minimum scale change for pinch\n preventDefaultOnTouch: boolean;\n}\n\n/**\n * Default touch configuration\n */\nconst defaultTouchConfig: TouchConfig = {\n tapThreshold: 10,\n longPressDelay: 500,\n doubleTapDelay: 300,\n swipeThreshold: 50,\n swipeVelocityThreshold: 0.5,\n pinchThreshold: 0.1,\n preventDefaultOnTouch: true\n};\n\n/**\n * Touch state interface\n */\ninterface TouchState {\n isActive: boolean;\n startTime: number;\n startPoint: Point;\n currentPoint: Point;\n lastTapTime: number;\n touches: Touch[];\n initialDistance?: number;\n initialScale: number;\n}\n\n/**\n * Calculate distance between two points\n */\nconst getDistance = (p1: Point, p2: Point): number => {\n const dx = p2.x - p1.x;\n const dy = p2.y - p1.y;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\n/**\n * Calculate distance between two touches\n */\nconst getTouchDistance = (touch1: Touch, touch2: Touch): number => {\n return getDistance(\n { x: touch1.clientX, y: touch1.clientY },\n { x: touch2.clientX, y: touch2.clientY }\n );\n};\n\n/**\n * Get touch point from touch event\n */\nconst getTouchPoint = (touch: Touch): Point => ({\n x: touch.clientX,\n y: touch.clientY\n});\n\n/**\n * Hook for handling touch gestures\n */\nexport const useTouchGestures = (\n onGesture: (gesture: TouchEventData) => void,\n config: Partial<TouchConfig> = {}\n) => {\n const touchConfig = { ...defaultTouchConfig, ...config };\n const stateRef = useRef<TouchState>({\n isActive: false,\n startTime: 0,\n startPoint: { x: 0, y: 0 },\n currentPoint: { x: 0, y: 0 },\n lastTapTime: 0,\n touches: [],\n initialScale: 1\n });\n const longPressTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);\n\n const clearLongPressTimer = useCallback(() => {\n if (longPressTimerRef.current) {\n clearTimeout(longPressTimerRef.current);\n longPressTimerRef.current = undefined;\n }\n }, []);\n\n const handleTouchStart = useCallback((event: TouchEvent) => {\n if (touchConfig.preventDefaultOnTouch) {\n event.preventDefault();\n }\n\n const touch = event.touches[0];\n const currentTime = Date.now();\n const touchPoint = getTouchPoint(touch);\n\n stateRef.current = {\n isActive: true,\n startTime: currentTime,\n startPoint: touchPoint,\n currentPoint: touchPoint,\n lastTapTime: stateRef.current.lastTapTime,\n touches: Array.from(event.touches),\n initialScale: 1\n };\n\n // Set up long press detection\n longPressTimerRef.current = setTimeout(() => {\n if (stateRef.current.isActive) {\n const distance = getDistance(stateRef.current.startPoint, stateRef.current.currentPoint);\n if (distance < touchConfig.tapThreshold) {\n onGesture({\n type: 'long-press',\n startPoint: stateRef.current.startPoint,\n currentPoint: stateRef.current.currentPoint,\n deltaX: 0,\n deltaY: 0,\n distance: 0,\n duration: currentTime - stateRef.current.startTime\n });\n }\n }\n }, touchConfig.longPressDelay);\n\n // Handle pinch start\n if (event.touches.length === 2) {\n stateRef.current.initialDistance = getTouchDistance(event.touches[0], event.touches[1]);\n }\n }, [onGesture, touchConfig]);\n\n const handleTouchMove = useCallback((event: TouchEvent) => {\n if (!stateRef.current.isActive) return;\n\n if (touchConfig.preventDefaultOnTouch) {\n event.preventDefault();\n }\n\n const touch = event.touches[0];\n const currentPoint = getTouchPoint(touch);\n const currentTime = Date.now();\n\n stateRef.current.currentPoint = currentPoint;\n stateRef.current.touches = Array.from(event.touches);\n\n const deltaX = currentPoint.x - stateRef.current.startPoint.x;\n const deltaY = currentPoint.y - stateRef.current.startPoint.y;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const duration = currentTime - stateRef.current.startTime;\n\n // Clear long press if moved too much\n if (distance > touchConfig.tapThreshold) {\n clearLongPressTimer();\n }\n\n // Handle pinch gesture\n if (event.touches.length === 2 && stateRef.current.initialDistance) {\n const currentDistance = getTouchDistance(event.touches[0], event.touches[1]);\n const scale = currentDistance / stateRef.current.initialDistance;\n\n if (Math.abs(scale - stateRef.current.initialScale) > touchConfig.pinchThreshold) {\n onGesture({\n type: 'pinch',\n startPoint: stateRef.current.startPoint,\n currentPoint,\n deltaX,\n deltaY,\n distance,\n scale,\n duration\n });\n stateRef.current.initialScale = scale;\n }\n }\n // Handle pan gesture\n else if (event.touches.length === 1 && distance > touchConfig.tapThreshold) {\n const velocity = {\n x: deltaX / Math.max(duration, 1),\n y: deltaY / Math.max(duration, 1)\n };\n\n onGesture({\n type: 'pan',\n startPoint: stateRef.current.startPoint,\n currentPoint,\n deltaX,\n deltaY,\n distance,\n velocity,\n duration\n });\n }\n }, [onGesture, touchConfig, clearLongPressTimer]);\n\n const handleTouchEnd = useCallback((event: TouchEvent) => {\n if (!stateRef.current.isActive) return;\n\n if (touchConfig.preventDefaultOnTouch) {\n event.preventDefault();\n }\n\n clearLongPressTimer();\n\n const currentTime = Date.now();\n const deltaX = stateRef.current.currentPoint.x - stateRef.current.startPoint.x;\n const deltaY = stateRef.current.currentPoint.y - stateRef.current.startPoint.y;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const duration = currentTime - stateRef.current.startTime;\n\n // Handle tap gestures\n if (distance < touchConfig.tapThreshold && duration < touchConfig.longPressDelay) {\n const timeSinceLastTap = currentTime - stateRef.current.lastTapTime;\n\n if (timeSinceLastTap < touchConfig.doubleTapDelay) {\n // Double tap\n onGesture({\n type: 'double-tap',\n startPoint: stateRef.current.startPoint,\n currentPoint: stateRef.current.currentPoint,\n deltaX: 0,\n deltaY: 0,\n distance: 0,\n duration\n });\n stateRef.current.lastTapTime = 0; // Reset to prevent triple tap\n } else {\n // Single tap\n onGesture({\n type: 'tap',\n startPoint: stateRef.current.startPoint,\n currentPoint: stateRef.current.currentPoint,\n deltaX: 0,\n deltaY: 0,\n distance: 0,\n duration\n });\n stateRef.current.lastTapTime = currentTime;\n }\n }\n // Handle swipe gestures\n else if (distance > touchConfig.swipeThreshold) {\n const velocity = {\n x: deltaX / Math.max(duration, 1),\n y: deltaY / Math.max(duration, 1)\n };\n const velocityMagnitude = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y);\n\n if (velocityMagnitude > touchConfig.swipeVelocityThreshold) {\n onGesture({\n type: 'swipe',\n startPoint: stateRef.current.startPoint,\n currentPoint: stateRef.current.currentPoint,\n deltaX,\n deltaY,\n distance,\n velocity,\n duration\n });\n }\n }\n\n stateRef.current.isActive = false;\n }, [onGesture, touchConfig, clearLongPressTimer]);\n\n const touchHandlers = {\n onTouchStart: handleTouchStart,\n onTouchMove: handleTouchMove,\n onTouchEnd: handleTouchEnd,\n onTouchCancel: handleTouchEnd\n };\n\n return touchHandlers;\n};\n\n/**\n * Hook for detecting device orientation\n */\nexport const useDeviceOrientation = () => {\n const [orientation, setOrientation] = useState<'portrait' | 'landscape'>('portrait');\n\n useEffect(() => {\n if (!isBrowser()) return;\n\n const updateOrientation = () => {\n setOrientation(window.innerHeight > window.innerWidth ? 'portrait' : 'landscape');\n };\n\n updateOrientation();\n window.addEventListener('resize', updateOrientation);\n window.addEventListener('orientationchange', updateOrientation);\n\n return () => {\n window.removeEventListener('resize', updateOrientation);\n window.removeEventListener('orientationchange', updateOrientation);\n };\n }, []);\n\n return orientation;\n};\n\n/**\n * Hook for safe area insets (for devices with notches)\n */\nexport const useSafeAreaInsets = () => {\n const [insets, setInsets] = useState({\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n });\n\n useEffect(() => {\n if (!isBrowser()) return;\n\n const updateInsets = () => {\n const computedStyle = getComputedStyle(document.documentElement);\n setInsets({\n top: parseInt(computedStyle.getPropertyValue('env(safe-area-inset-top)') || '0'),\n right: parseInt(computedStyle.getPropertyValue('env(safe-area-inset-right)') || '0'),\n bottom: parseInt(computedStyle.getPropertyValue('env(safe-area-inset-bottom)') || '0'),\n left: parseInt(computedStyle.getPropertyValue('env(safe-area-inset-left)') || '0')\n });\n };\n\n updateInsets();\n window.addEventListener('resize', updateInsets);\n\n return () => {\n window.removeEventListener('resize', updateInsets);\n };\n }, []);\n\n return insets;\n};\n\n/**\n * Prevent zoom on double tap (iOS Safari)\n */\nexport const usePreventZoom = (enabled: boolean = true) => {\n useEffect(() => {\n if (!isBrowser() || !enabled) return;\n\n const preventDefault = (e: TouchEvent) => {\n if (e.touches.length > 1) {\n e.preventDefault();\n }\n };\n\n const preventZoom = (e: Event) => {\n e.preventDefault();\n };\n\n document.addEventListener('touchstart', preventDefault, { passive: false });\n document.addEventListener('gesturestart', preventZoom, { passive: false });\n\n return () => {\n document.removeEventListener('touchstart', preventDefault);\n document.removeEventListener('gesturestart', preventZoom);\n };\n }, [enabled]);\n};\n\n/**\n * SSR-safe user agent detection (internal)\n */\nconst useUserAgentInternal = () => {\n const [userAgent, setUserAgent] = useState('');\n\n useEffect(() => {\n if (isBrowser()) {\n setUserAgent(navigator.userAgent);\n }\n }, []);\n\n return userAgent;\n};\n\n/**\n * SSR-safe media query hook (internal)\n */\nconst useMediaQueryInternal = (query: string): boolean => {\n const [matches, setMatches] = useState(false);\n\n useEffect(() => {\n if (!isBrowser()) return;\n\n const mediaQuery = window.matchMedia(query);\n setMatches(mediaQuery.matches);\n\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n mediaQuery.addEventListener('change', handler);\n\n return () => {\n mediaQuery.removeEventListener('change', handler);\n };\n }, [query]);\n\n return matches;\n};\n\n/**\n * Detect mobile devices\n */\nexport const useIsMobileTouch = (): boolean => {\n const userAgent = useUserAgentInternal();\n const isSmallScreen = useMediaQueryInternal('(max-width: 768px)');\n\n const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);\n\n return isMobileUA || isSmallScreen;\n};\n\n/**\n * Detect touch support\n */\nexport const useHasTouchSupportTouch = (): boolean => {\n const [hasTouch, setHasTouch] = useState(false);\n\n useEffect(() => {\n if (isBrowser()) {\n setHasTouch(\n 'ontouchstart' in window ||\n navigator.maxTouchPoints > 0 ||\n // @ts-ignore - for older browsers\n navigator.msMaxTouchPoints > 0\n );\n }\n }, []);\n\n return hasTouch;\n};\n"],"mappings":"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"}