| @@ -1,4 +1,4 @@ | |||||
| import { AnimatePresence, LayoutGroup } from 'framer-motion' | |||||
| import { AnimatePresence, LayoutGroup, motion } from 'framer-motion' | |||||
| import { useEffect, useState } from 'react' | import { useEffect, useState } from 'react' | ||||
| import { BrowserRouter, | import { BrowserRouter, | ||||
| Navigate, | Navigate, | ||||
| @@ -46,38 +46,36 @@ const RouteTransitionWrapper = ({ user, setUser }: { | |||||
| const location = useLocation () | const location = useLocation () | ||||
| return ( | return ( | ||||
| <LayoutGroup id="gallery-shared"> | |||||
| <AnimatePresence mode="wait"> | |||||
| <Routes location={location}> | |||||
| <Route path="/" element={<Navigate to="/posts" replace/>}/> | |||||
| <Route path="/posts" element={<PostListPage/>}/> | |||||
| <Route path="/posts/new" element={<PostNewPage user={user}/>}/> | |||||
| <Route path="/posts/search" element={<PostSearchPage/>}/> | |||||
| <Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/> | |||||
| <Route path="/posts/changes" element={<PostHistoryPage/>}/> | |||||
| <Route path="/tags" element={<TagListPage/>}/> | |||||
| <Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/> | |||||
| <Route path="/theatres/:id" element={<TheatreDetailPage/>}/> | |||||
| <Route path="/materials" element={<MaterialBasePage/>}> | |||||
| <Route index element={<MaterialListPage/>}/> | |||||
| <Route path="new" element={<MaterialNewPage/>}/> | |||||
| <Route path=":id" element ={<MaterialDetailPage/>}/> | |||||
| </Route> | |||||
| {/* <Route path="/materials/search" element={<MaterialSearchPage/>}/> */} | |||||
| <Route path="/wiki" element={<WikiSearchPage/>}/> | |||||
| <Route path="/wiki/:title" element={<WikiDetailPage/>}/> | |||||
| <Route path="/wiki/new" element={<WikiNewPage user={user}/>}/> | |||||
| <Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/> | |||||
| <Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/> | |||||
| <Route path="/wiki/changes" element={<WikiHistoryPage/>}/> | |||||
| <Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/> | |||||
| <Route path="/settings" element={<Navigate to="/users/settings" replace/>}/> | |||||
| <Route path="/tos" element={<TOSPage/>}/> | |||||
| <Route path="/more" element={<MorePage/>}/> | |||||
| <Route path="*" element={<NotFound/>}/> | |||||
| </Routes> | |||||
| </AnimatePresence> | |||||
| </LayoutGroup>) | |||||
| <AnimatePresence mode="wait"> | |||||
| <Routes location={location}> | |||||
| <Route path="/" element={<Navigate to="/posts" replace/>}/> | |||||
| <Route path="/posts" element={<PostListPage/>}/> | |||||
| <Route path="/posts/new" element={<PostNewPage user={user}/>}/> | |||||
| <Route path="/posts/search" element={<PostSearchPage/>}/> | |||||
| <Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/> | |||||
| <Route path="/posts/changes" element={<PostHistoryPage/>}/> | |||||
| <Route path="/tags" element={<TagListPage/>}/> | |||||
| <Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/> | |||||
| <Route path="/theatres/:id" element={<TheatreDetailPage/>}/> | |||||
| <Route path="/materials" element={<MaterialBasePage/>}> | |||||
| <Route index element={<MaterialListPage/>}/> | |||||
| <Route path="new" element={<MaterialNewPage/>}/> | |||||
| <Route path=":id" element ={<MaterialDetailPage/>}/> | |||||
| </Route> | |||||
| {/* <Route path="/materials/search" element={<MaterialSearchPage/>}/> */} | |||||
| <Route path="/wiki" element={<WikiSearchPage/>}/> | |||||
| <Route path="/wiki/:title" element={<WikiDetailPage/>}/> | |||||
| <Route path="/wiki/new" element={<WikiNewPage user={user}/>}/> | |||||
| <Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/> | |||||
| <Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/> | |||||
| <Route path="/wiki/changes" element={<WikiHistoryPage/>}/> | |||||
| <Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/> | |||||
| <Route path="/settings" element={<Navigate to="/users/settings" replace/>}/> | |||||
| <Route path="/tos" element={<TOSPage/>}/> | |||||
| <Route path="/more" element={<MorePage/>}/> | |||||
| <Route path="*" element={<NotFound/>}/> | |||||
| </Routes> | |||||
| </AnimatePresence>) | |||||
| } | } | ||||
| @@ -135,10 +133,15 @@ export default (() => { | |||||
| <> | <> | ||||
| <RouteBlockerOverlay/> | <RouteBlockerOverlay/> | ||||
| <BrowserRouter> | <BrowserRouter> | ||||
| <div className="flex flex-col h-dvh w-full"> | |||||
| <TopNav user={user}/> | |||||
| <RouteTransitionWrapper user={user} setUser={setUser}/> | |||||
| </div> | |||||
| <LayoutGroup> | |||||
| <motion.div | |||||
| layout="position" | |||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| className="flex flex-col h-dvh w-full overflow-y-hidden"> | |||||
| <TopNav user={user}/> | |||||
| <RouteTransitionWrapper user={user} setUser={setUser}/> | |||||
| </motion.div> | |||||
| </LayoutGroup> | |||||
| <Toaster/> | <Toaster/> | ||||
| </BrowserRouter> | </BrowserRouter> | ||||
| </>) | </>) | ||||
| @@ -56,7 +56,7 @@ export default (({ posts, onClick }: Props) => { | |||||
| cardRef.current.style.zIndex = '' | cardRef.current.style.zIndex = '' | ||||
| cardRef.current.style.position = '' | cardRef.current.style.position = '' | ||||
| }} | }} | ||||
| transition={{ type: 'spring', stiffness: 500, damping: 40, mass: .5 }}> | |||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }}> | |||||
| <img src={post.thumbnail || post.thumbnailBase || undefined} | <img src={post.thumbnail || post.thumbnailBase || undefined} | ||||
| alt={post.title || post.url} | alt={post.title || post.url} | ||||
| title={post.title || post.url || undefined} | title={post.title || post.url || undefined} | ||||
| @@ -65,7 +65,9 @@ export default (({ posts, onClick }: Props) => { | |||||
| {CATEGORIES.flatMap (cat => cat in tags ? ( | {CATEGORIES.flatMap (cat => cat in tags ? ( | ||||
| tags[cat].map (tag => ( | tags[cat].map (tag => ( | ||||
| <li key={tag.id} className="mb-1"> | <li key={tag.id} className="mb-1"> | ||||
| <motion.div layoutId={`tag-${ tag.id }`}> | |||||
| <motion.div | |||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| layoutId={`tag-${ tag.id }`}> | |||||
| <TagLink tag={tag} onClick={onClick}/> | <TagLink tag={tag} onClick={onClick}/> | ||||
| </motion.div> | </motion.div> | ||||
| </li>))) : [])} | </li>))) : [])} | ||||
| @@ -201,17 +201,17 @@ export default (({ user }: Props) => { | |||||
| </PrefetchLink> | </PrefetchLink> | ||||
| </motion.div>))} | </motion.div>))} | ||||
| <PrefetchLink | <PrefetchLink | ||||
| to="#" | |||||
| to="/more" | |||||
| ref={(el: (HTMLAnchorElement | null)) => { | ref={(el: (HTMLAnchorElement | null)) => { | ||||
| itemsRef.current[visibleMenu.length] = el | itemsRef.current[visibleMenu.length] = el | ||||
| }} | }} | ||||
| onClick={e => e.preventDefault ()} | |||||
| onClick={() => setMoreVsbl (false)} | |||||
| onMouseEnter={() => { | onMouseEnter={() => { | ||||
| setMoreVsbl (true) | setMoreVsbl (true) | ||||
| measure (-1) | measure (-1) | ||||
| }} | }} | ||||
| className={cn ('relative z-10 flex h-full items-center px-5', | className={cn ('relative z-10 flex h-full items-center px-5', | ||||
| (openItemIdx < 0) && 'font-bold')}> | |||||
| (openItemIdx < 0 || moreVsbl) && 'font-bold')}> | |||||
| その他 » | その他 » | ||||
| </PrefetchLink> | </PrefetchLink> | ||||
| </div> | </div> | ||||
| @@ -280,6 +280,7 @@ export default (({ user }: Props) => { | |||||
| : ( | : ( | ||||
| <motion.div | <motion.div | ||||
| key={`l-${ i }-${ j }`} | key={`l-${ i }-${ j }`} | ||||
| transition={{ duration: .2, ease: 'easeOut' }} | |||||
| {...((visibleMenu[activeIdx]?.name | {...((visibleMenu[activeIdx]?.name | ||||
| === item.name) | === item.name) | ||||
| ? { layoutId: `submenu-${ item.name }-${ j }` } | ? { layoutId: `submenu-${ item.name }-${ j }` } | ||||
| @@ -289,6 +290,7 @@ export default (({ user }: Props) => { | |||||
| <PrefetchLink | <PrefetchLink | ||||
| to={subItem.to} | to={subItem.to} | ||||
| target={subItem.to.slice (0, 2) === '//' ? '_blank' : undefined} | target={subItem.to.slice (0, 2) === '//' ? '_blank' : undefined} | ||||
| onClick={() => setMoreVsbl (false)} | |||||
| className="h-full flex items-center px-3"> | className="h-full flex items-center px-3"> | ||||
| {subItem.name} | {subItem.name} | ||||
| </PrefetchLink> | </PrefetchLink> | ||||
| @@ -317,12 +319,14 @@ export default (({ user }: Props) => { | |||||
| ? ( | ? ( | ||||
| <motion.div | <motion.div | ||||
| key={`c-${ i }`} | key={`c-${ i }`} | ||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}> | layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}> | ||||
| {item.component} | {item.component} | ||||
| </motion.div>) | </motion.div>) | ||||
| : ( | : ( | ||||
| <motion.div | <motion.div | ||||
| key={`l-${ i }`} | key={`l-${ i }`} | ||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}> | layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}> | ||||
| <PrefetchLink | <PrefetchLink | ||||
| to={item.to} | to={item.to} | ||||
| @@ -1,3 +1,5 @@ | |||||
| import { motion } from 'framer-motion' | |||||
| import { cn } from '@/lib/utils' | import { cn } from '@/lib/utils' | ||||
| import type { FC, ReactNode } from 'react' | import type { FC, ReactNode } from 'react' | ||||
| @@ -8,6 +10,9 @@ type Props = { | |||||
| export default (({ children, className }: Props) => ( | export default (({ children, className }: Props) => ( | ||||
| <main className={cn ('flex-1 overflow-y-auto p-4', className)}> | |||||
| <motion.main | |||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| className={cn ('flex-1 overflow-y-auto p-4', className)} | |||||
| layout="position"> | |||||
| {children} | {children} | ||||
| </main>)) satisfies FC<Props> | |||||
| </motion.main>)) satisfies FC<Props> | |||||
| @@ -1,3 +1,4 @@ | |||||
| import { motion } from 'framer-motion' | |||||
| import { Helmet } from 'react-helmet-async' | import { Helmet } from 'react-helmet-async' | ||||
| import type { FC, ReactNode } from 'react' | import type { FC, ReactNode } from 'react' | ||||
| @@ -6,7 +7,10 @@ type Props = { children: ReactNode } | |||||
| export default (({ children }: Props) => ( | export default (({ children }: Props) => ( | ||||
| <div className="p-4 w-full md:w-64 md:h-full md:overflow-y-auto sidebar"> | |||||
| <motion.div | |||||
| layout="position" | |||||
| transition={{ layout: { duration: .2, ease: 'easeOut' } }} | |||||
| className="p-4 w-full md:w-64 md:h-full md:overflow-y-auto sidebar"> | |||||
| <Helmet> | <Helmet> | ||||
| <style> | <style> | ||||
| {` | {` | ||||
| @@ -23,4 +27,4 @@ export default (({ children }: Props) => ( | |||||
| </Helmet> | </Helmet> | ||||
| {children} | {children} | ||||
| </div>)) satisfies FC<Props> | |||||
| </motion.div>)) satisfies FC<Props> | |||||