{"version":3,"file":"useViewportPosition.mjs","sources":["../../../../../src/lib/layouts/context-aware/hooks/useViewportPosition.ts"],"sourcesContent":["/**\n * @fileoverview useViewportPosition Hook\n *\n * Provides element position information relative to the viewport.\n *\n * @module layouts/context-aware/hooks/useViewportPosition\n * @author Agent 4 - PhD Context Systems Expert\n * @version 1.0.0\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type {\n  UseViewportPositionReturn,\n  ViewportInfo,\n  ViewportPosition,\n  VisibilityState,\n} from '../types';\nimport { useDOMContextValue } from '../DOMContextProvider';\nimport {\n  getDistanceFromViewportCenter,\n  getViewportTracker,\n  type VisibilityChangeCallback,\n} from '../viewport-awareness';\n\n// ============================================================================\n// useViewportPosition Hook\n// ============================================================================\n\n/**\n * Hook to track an element's position relative to the viewport.\n *\n * @remarks\n * This hook provides comprehensive information about an element's\n * position in the viewport, including visibility state, intersection\n * ratio, and distance from viewport edges.\n *\n * @returns Viewport position information and utilities\n *\n * @example\n * ```tsx\n * function MyComponent() {\n *   const {\n *     position,\n *     isVisible,\n *     isFullyVisible,\n *     visibility,\n *     ref,\n *     scrollIntoView,\n *   } = useViewportPosition();\n *\n *   return (\n *     <div ref={ref}>\n *       {isVisible && <span>I'm visible!</span>}\n *       {!isFullyVisible && (\n *         <button onClick={() => scrollIntoView()}>\n *           Scroll into view\n *         </button>\n *       )}\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useViewportPosition(): UseViewportPositionReturn {\n  const elementRef = useRef<HTMLElement>(null);\n  const unobserveRef = useRef<(() => void) | null>(null);\n\n  const [position, setPosition] = useState<ViewportPosition | null>(null);\n\n  const context = useDOMContextValue();\n  const { isSSR } = context;\n\n  /**\n   * Handles visibility changes.\n   */\n  const handleVisibilityChange = useCallback<VisibilityChangeCallback>((_element, newPosition) => {\n    setPosition(newPosition);\n  }, []);\n\n  // Set up visibility observation\n  useEffect(() => {\n    if (isSSR || !elementRef.current) {\n      return;\n    }\n\n    const tracker = getViewportTracker();\n    unobserveRef.current = tracker.observeVisibility(elementRef.current, handleVisibilityChange, {\n      thresholds: [0, 0.25, 0.5, 0.75, 1],\n      trackPosition: true,\n    });\n\n    return () => {\n      if (unobserveRef.current) {\n        unobserveRef.current();\n        unobserveRef.current = null;\n      }\n    };\n  }, [isSSR, handleVisibilityChange]);\n\n  /**\n   * Scrolls the element into view.\n   */\n  const scrollIntoView = useCallback((options?: ScrollIntoViewOptions) => {\n    if (elementRef.current) {\n      elementRef.current.scrollIntoView(options ?? { behavior: 'smooth', block: 'center' });\n    }\n  }, []);\n\n  // Compute derived values\n  const isVisible = position?.visibility === 'visible' || position?.visibility === 'partial';\n  const isFullyVisible = position?.visibility === 'visible';\n  const visibility = position?.visibility ?? 'unknown';\n\n  return {\n    position,\n    isVisible,\n    isFullyVisible,\n    visibility,\n    ref: elementRef,\n    scrollIntoView,\n  };\n}\n\n// ============================================================================\n// Specialized Viewport Hooks\n// ============================================================================\n\n/**\n * Hook to track visibility state only (optimized for performance).\n *\n * @returns Visibility state\n *\n * @example\n * ```tsx\n * function LazyImage({ src }: { src: string }) {\n *   const { isVisible, ref } = useVisibility();\n *\n *   return (\n *     <div ref={ref}>\n *       {isVisible ? <img src={src} /> : <Placeholder />}\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useVisibility(): {\n  isVisible: boolean;\n  isFullyVisible: boolean;\n  visibility: VisibilityState;\n  ref: { readonly current: HTMLElement | null };\n} {\n  const { isVisible, isFullyVisible, visibility, ref } = useViewportPosition();\n  return { isVisible, isFullyVisible, visibility, ref };\n}\n\n/**\n * Hook to get intersection ratio with the viewport.\n *\n * @returns Intersection ratio (0-1) and ref\n *\n * @example\n * ```tsx\n * function AnimatedElement() {\n *   const { ratio, ref } = useIntersectionRatio();\n *\n *   return (\n *     <div\n *       ref={ref}\n *       style={{ opacity: ratio, transform: `scale(${0.5 + ratio * 0.5})` }}\n *     >\n *       Animated content\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useIntersectionRatio(): {\n  ratio: number;\n  ref: { readonly current: HTMLElement | null };\n} {\n  const { position, ref } = useViewportPosition();\n  const ratio = position?.intersectionRatio ?? 0;\n  return { ratio, ref };\n}\n\n/**\n * Hook to track distance from viewport edges.\n *\n * @returns Distance object and ref\n *\n * @example\n * ```tsx\n * function DistanceDisplay() {\n *   const { distance, ref } = useDistanceFromViewport();\n *\n *   return (\n *     <div ref={ref}>\n *       Top: {distance?.top}px, Bottom: {distance?.bottom}px\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useDistanceFromViewport(): {\n  distance: ViewportPosition['distanceFromViewport'] | null;\n  ref: { readonly current: HTMLElement | null };\n} {\n  const { position, ref } = useViewportPosition();\n  const distance = position?.distanceFromViewport ?? null;\n  return { distance, ref };\n}\n\n/**\n * Hook to track distance from viewport center.\n *\n * @returns Distance from center and ref\n *\n * @example\n * ```tsx\n * function CenterFocusedElement() {\n *   const { distanceFromCenter, ref } = useDistanceFromCenter();\n *\n *   // Scale based on proximity to center\n *   const scale = Math.max(0.5, 1 - (distanceFromCenter / 500) * 0.5);\n *\n *   return (\n *     <div ref={ref} style={{ transform: `scale(${scale})` }}>\n *       Focus me!\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useDistanceFromCenter(): {\n  distanceFromCenter: number;\n  ref: { readonly current: HTMLElement | null };\n} {\n  const elementRef = useRef<HTMLElement | null>(null);\n  const context = useDOMContextValue();\n  const [distanceFromCenter, setDistanceFromCenter] = useState(Infinity);\n\n  useEffect(() => {\n    if (context.isSSR || !elementRef.current) {\n      return;\n    }\n\n    const updateDistance = (): void => {\n      if (elementRef.current) {\n        setDistanceFromCenter(getDistanceFromViewportCenter(elementRef.current));\n      }\n    };\n\n    updateDistance();\n\n    // Update on scroll/resize\n    const tracker = getViewportTracker();\n\n    return tracker.onViewportChange(updateDistance);\n  }, [context.isSSR, context.lastUpdated]);\n\n  return { distanceFromCenter, ref: elementRef };\n}\n\n// ============================================================================\n// Viewport Info Hooks\n// ============================================================================\n\n/**\n * Hook to get current viewport information.\n *\n * @returns Viewport info\n *\n * @example\n * ```tsx\n * function ViewportDisplay() {\n *   const viewport = useViewport();\n *\n *   return (\n *     <div>\n *       {viewport.width}x{viewport.height}\n *       {viewport.orientation === 'portrait' && ' (Portrait)'}\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useViewport(): ViewportInfo {\n  const context = useDOMContextValue();\n  return context.viewport;\n}\n\n/**\n * Hook to get viewport dimensions.\n *\n * @returns Viewport width and height\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n *   const { width, height } = useViewportDimensions();\n *\n *   if (width < 768) {\n *     return <MobileLayout />;\n *   }\n *\n *   return <DesktopLayout />;\n * }\n * ```\n */\nexport function useViewportDimensions(): { width: number; height: number } {\n  const viewport = useViewport();\n  return useMemo(\n    () => ({ width: viewport.width, height: viewport.height }),\n    [viewport.width, viewport.height]\n  );\n}\n\n/**\n * Hook to get viewport scroll position.\n *\n * @returns Scroll x and y positions\n *\n * @example\n * ```tsx\n * function ScrollProgress() {\n *   const { scrollY } = useViewportScroll();\n *   const viewport = useViewport();\n *\n *   const progress = scrollY / (viewport.scrollHeight - viewport.height);\n *\n *   return <ProgressBar value={progress} />;\n * }\n * ```\n */\nexport function useViewportScroll(): { scrollX: number; scrollY: number } {\n  const viewport = useViewport();\n  return useMemo(\n    () => ({ scrollX: viewport.scrollX, scrollY: viewport.scrollY }),\n    [viewport.scrollX, viewport.scrollY]\n  );\n}\n\n/**\n * Hook to check if on a touch device.\n *\n * @returns Whether device supports touch\n *\n * @example\n * ```tsx\n * function InteractiveElement() {\n *   const isTouch = useIsTouch();\n *\n *   return (\n *     <div\n *       onMouseEnter={!isTouch ? handleHover : undefined}\n *       onTouchStart={isTouch ? handleTouch : undefined}\n *     >\n *       Interact with me\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useIsTouch(): boolean {\n  const viewport = useViewport();\n  return viewport.isTouch;\n}\n\n/**\n * Hook to get device orientation.\n *\n * @returns Current orientation\n *\n * @example\n * ```tsx\n * function OrientationAware() {\n *   const orientation = useOrientation();\n *\n *   return (\n *     <div className={`layout-${orientation}`}>\n *       Current: {orientation}\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useOrientation(): 'portrait' | 'landscape' {\n  const viewport = useViewport();\n  return viewport.orientation;\n}\n\n/**\n * Hook to get safe area insets.\n *\n * @returns Safe area insets\n *\n * @example\n * ```tsx\n * function SafeContainer({ children }) {\n *   const safeArea = useSafeAreaInsets();\n *\n *   return (\n *     <div\n *       style={{\n *         paddingTop: safeArea.top,\n *         paddingBottom: safeArea.bottom,\n *         paddingLeft: safeArea.left,\n *         paddingRight: safeArea.right,\n *       }}\n *     >\n *       {children}\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useSafeAreaInsets(): ViewportInfo['safeAreaInsets'] {\n  const viewport = useViewport();\n  return viewport.safeAreaInsets;\n}\n"],"names":["useViewportPosition","elementRef","useRef","unobserveRef","position","setPosition","useState","context","useDOMContextValue","isSSR","handleVisibilityChange","useCallback","_element","newPosition","useEffect","tracker","getViewportTracker","scrollIntoView","options","isVisible","isFullyVisible","visibility","useVisibility","ref","useIntersectionRatio","useDistanceFromViewport","useDistanceFromCenter","distanceFromCenter","setDistanceFromCenter","updateDistance","getDistanceFromViewportCenter","useViewport","useViewportDimensions","viewport","useMemo","useViewportScroll","useIsTouch","useOrientation","useSafeAreaInsets"],"mappings":"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"}