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 { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings' 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 behaviourSettings = useClientBehaviourSettings () const linkPreloadMode = behaviourSettings.linkPreload ?? 'intent' const path = useMemo ( () => typeof to === 'string' ? to : createPath (to), [to], ) const url = useMemo (() => { return (new URL (path, window.location.origin)).toString () }, [path]) 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) if (ev.defaultPrevented || linkPreloadMode !== 'intent') return await doPrefetch () } const handleTouchStart = async (ev: TouchEvent) => { onTouchStart?.(ev) if (ev.defaultPrevented || linkPreloadMode !== 'intent') return 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 ( ) })