|
- 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<HTMLAnchorElement> & {
- to: To
- state?: Record<string, string>
- replace?: boolean
- className?: string
- cancelOnError?: boolean }
-
-
- export default forwardRef<HTMLAnchorElement, Props> (({
- 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<HTMLAnchorElement>) => {
- onMouseEnter?.(ev)
- await doPrefetch ()
- }
-
- const handleTouchStart = async (ev: TouchEvent<HTMLAnchorElement>) => {
- onTouchStart?.(ev)
- await doPrefetch ()
- }
-
- const handleClick = async (ev: MouseEvent<HTMLAnchorElement>) => {
- 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 (
- <a ref={ref}
- href={typeof to === 'string' ? to : createPath (to)}
- onMouseEnter={handleMouseEnter}
- onTouchStart={handleTouchStart}
- onClick={handleClick}
- className={cn ('cursor-pointer', className)}
- {...rest}/>)
- })
|