Merge remote-tracking branch 'origin/main' into feature/140

このコミットが含まれているのは:
2026-02-06 02:54:03 +09:00
コミット 874559dc6c
12個のファイルの変更215行の追加86行の削除
+60 -16
ファイルの表示
@@ -1,4 +1,9 @@
import { motion } from 'framer-motion'
import { useRef } from 'react'
import { useLocation } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import { useSharedTransitionStore } from '@/stores/sharedTransitionStore'
import type { FC, MouseEvent } from 'react'
@@ -8,19 +13,58 @@ type Props = { posts: Post[]
onClick?: (event: MouseEvent<HTMLElement>) => void }
export default (({ posts, onClick }: Props) => (
<div className="flex flex-wrap gap-6 p-4">
{posts.map ((post, i) => (
<PrefetchLink
to={`/posts/${ post.id }`}
key={post.id}
className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg"
onClick={onClick}>
<img src={post.thumbnail || post.thumbnailBase || undefined}
alt={post.title || post.url}
title={post.title || post.url || undefined}
loading={i < 12 ? 'eager' : 'lazy'}
decoding="async"
className="object-cover w-full h-full"/>
</PrefetchLink>))}
</div>)) satisfies FC<Props>
export default (({ posts, onClick }: Props) => {
const location = useLocation ()
const setForLocationKey = useSharedTransitionStore (s => s.setForLocationKey)
const cardRef = useRef<HTMLDivElement> (null)
return (
<div className="flex flex-wrap gap-6 p-4">
{posts.map ((post, i) => {
const sharedId = `page-${ post.id }`
const layoutId = sharedId
return (
<PrefetchLink
to={`/posts/${ post.id }`}
key={post.id}
className="w-40 h-40"
state={{ sharedId }}
onClick={e => {
setForLocationKey (location.key, sharedId)
onClick?.(e)
}}>
<motion.div
ref={cardRef}
layoutId={layoutId}
className="w-full h-full overflow-hidden rounded-xl shadow
transform-gpu will-change-transform"
whileHover={{ scale: 1.02 }}
onLayoutAnimationStart={() => {
if (!(cardRef.current))
return
cardRef.current.style.position = 'relative'
cardRef.current.style.zIndex = '9999'
}}
onLayoutAnimationComplete={() => {
if (!(cardRef.current))
return
cardRef.current.style.zIndex = ''
cardRef.current.style.position = ''
}}
transition={{ type: 'spring', stiffness: 500, damping: 40, mass: .5 }}>
<img src={post.thumbnail || post.thumbnailBase || undefined}
alt={post.title || post.url}
title={post.title || post.url || undefined}
loading={i < 12 ? 'eager' : 'lazy'}
decoding="async"
className="object-cover w-full h-full"/>
</motion.div>
</PrefetchLink>)
})}
</div>)
}) satisfies FC<Props>
+32 -14
ファイルの表示
@@ -1,5 +1,6 @@
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'
@@ -11,6 +12,7 @@ import type { To } from 'react-router-dom'
type Props = AnchorHTMLAttributes<HTMLAnchorElement> & {
to: To
state?: Record<string, string>
replace?: boolean
className?: string
cancelOnError?: boolean }
@@ -20,11 +22,15 @@ 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 (() => {
@@ -57,25 +63,37 @@ export default forwardRef<HTMLAnchorElement, Props> (({
}
const handleClick = async (ev: MouseEvent<HTMLAnchorElement>) => {
onClick?.(ev)
try
{
onClick?.(ev)
if (ev.defaultPrevented
|| ev.metaKey
|| ev.ctrlKey
|| ev.shiftKey
|| ev.altKey)
return
if (ev.defaultPrevented
|| ev.metaKey
|| ev.ctrlKey
|| ev.shiftKey
|| ev.altKey)
return
ev.preventDefault ()
ev.preventDefault ()
setOverlay (true)
const ok = await doPrefetch ()
setOverlay (false)
flushSync (() => {
setOverlay (true)
})
const ok = await doPrefetch ()
flushSync (() => {
setOverlay (false)
})
if (!(ok) && cancelOnError)
return
if (!(ok) && cancelOnError)
return
navigate (to, { replace })
navigate (to, { replace, ...(state && { state }) })
}
catch (ex)
{
console.log (ex)
ev.preventDefault ()
}
}
return (
+7 -3
ファイルの表示
@@ -138,9 +138,13 @@ export default (({ user }: Props) => {
<nav className="px-3 flex justify-between items-center w-full min-h-[48px]
bg-yellow-200 dark:bg-red-975 md:bg-yellow-50">
<div className="flex items-center gap-2 h-full">
<PrefetchLink to="/"
className="mx-4 text-xl font-bold text-pink-600 hover:text-pink-400
dark:text-pink-300 dark:hover:text-pink-100">
<PrefetchLink
to="/posts"
className="mx-4 text-xl font-bold text-pink-600 hover:text-pink-400
dark:text-pink-300 dark:hover:text-pink-100"
onClick={() => {
scroll (0, 0)
}}>
</PrefetchLink>
+9 -5
ファイルの表示
@@ -1,9 +1,13 @@
import React from 'react'
import { cn } from '@/lib/utils'
type Props = { children: React.ReactNode }
import type { FC, ReactNode } from 'react'
type Props = {
children: ReactNode
className?: string }
export default ({ children }: Props) => (
<main className="flex-1 overflow-y-auto p-4">
export default (({ children, className }: Props) => (
<main className={cn ('flex-1 overflow-y-auto p-4', className)}>
{children}
</main>)
</main>)) satisfies FC<Props>