import { useQueryClient } from '@tanstack/react-query' import { forwardRef, useMemo } from 'react' import { flushSync } from 'react-dom' import { createPath, useNavigate } from 'react-router-dom' import { useOverlayStore } from '@/components/RouteBlockerOverlay' import { prefetchForURL } from '@/lib/prefetchers' import { cn } from '@/lib/utils' import type { AnchorHTMLAttributes, MouseEvent, TouchEvent } from 'react' import type { To } from 'react-router-dom' type Props = AnchorHTMLAttributes & { to: To state?: Record replace?: boolean className?: string cancelOnError?: boolean } export default forwardRef (({ to, replace, className, state, onMouseEnter, onTouchStart, onClick, cancelOnError = false, ...rest }, ref) => { if ('onClick' in rest) delete rest['onClick'] const navigate = useNavigate () const qc = useQueryClient () const url = useMemo (() => { const path = (typeof to === 'string') ? to : createPath (to) return (new URL (path, location.origin)).toString () }, [to]) const setOverlay = useOverlayStore (s => s.setActive) const doPrefetch = async () => { try { await prefetchForURL (qc, url) return true } catch (e) { console.error ('データ取得エラー', e) return false } } const handleMouseEnter = async (ev: MouseEvent) => { onMouseEnter?.(ev) await doPrefetch () } const handleTouchStart = async (ev: TouchEvent) => { onTouchStart?.(ev) await doPrefetch () } const handleClick = async (ev: MouseEvent) => { try { onClick?.(ev) if (ev.defaultPrevented || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey || (rest.target && rest.target !== '_self')) return ev.preventDefault () flushSync (() => { setOverlay (true) }) const ok = await doPrefetch () flushSync (() => { setOverlay (false) }) if (!(ok) && cancelOnError) return navigate (to, { replace, ...(state && { state }) }) } catch (ex) { console.log (ex) ev.preventDefault () } } return ( ) })