アニメーション(#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:
2026-02-05 23:25:27 +09:00
parent f3cd108b2e
commit 797e67ac37
22 changed files with 810 additions and 311 deletions
+107
View File
@@ -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}/>)
})