2adff3966a
#297 #297 #297 #297 #297 Merge remote-tracking branch 'origin/main' into feature/297 #297 #297 #297 #297 #297 #297 #297 #297 #297 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #299
109 lines
2.5 KiB
TypeScript
109 lines
2.5 KiB
TypeScript
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}/>)
|
|
})
|