アニメーション(#139) (#252)
#139 #139 #139 #139 #139 Merge branch 'feature/140' into feature/139 Merge remote-tracking branch 'origin/main' into feature/139 #140 Merge remote-tracking branch 'origin/main' into feature/140 Merge remote-tracking branch 'origin/main' into feature/140 #140 ぼちぼち Merge remote-tracking branch 'origin/main' into feature/140 #140 #140 #140 #139 アニメーション Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #252
This commit was merged in pull request #252.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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)
|
||||
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}/>)
|
||||
})
|
||||
Reference in New Issue
Block a user