|
|
|
@@ -14,11 +14,65 @@ import { fetchWikiPage } from '@/lib/wiki'
|
|
|
|
|
|
|
|
|
|
import type { FC, MouseEvent } from 'react'
|
|
|
|
|
|
|
|
|
|
import type { Menu, User } from '@/types'
|
|
|
|
|
import type { Menu, MenuVisibleItem, Tag, User } from '@/types'
|
|
|
|
|
|
|
|
|
|
type Props = { user: User | null }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
|
|
|
|
tag?: Tag | null
|
|
|
|
|
wikiId: number | null
|
|
|
|
|
user: User | null,
|
|
|
|
|
pathName: string }): Menu => {
|
|
|
|
|
const postCount = tag?.postCount ?? 0
|
|
|
|
|
|
|
|
|
|
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (pathName) && wikiId)
|
|
|
|
|
const wikiTitle = pathName.split ('/')[2] ?? ''
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
{ name: '広場', to: '/posts', subMenu: [
|
|
|
|
|
{ name: '一覧', to: '/posts' },
|
|
|
|
|
{ name: '検索', to: '/posts/search' },
|
|
|
|
|
{ name: '追加', to: '/posts/new' },
|
|
|
|
|
{ name: '履歴', to: '/posts/changes' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
|
|
|
|
{ name: 'タグ', to: '/tags', subMenu: [
|
|
|
|
|
{ name: 'マスタ', to: '/tags' },
|
|
|
|
|
{ name: '別名タグ', to: '/tags/aliases', visible: false },
|
|
|
|
|
{ name: '上位タグ', to: '/tags/implications', visible: false },
|
|
|
|
|
{ name: 'ニコニコ連携', to: '/tags/nico' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] },
|
|
|
|
|
{ name: '素材', to: '/materials', visible: false, subMenu: [
|
|
|
|
|
{ name: '一覧', to: '/materials' },
|
|
|
|
|
{ name: '検索', to: '/materials/search', visible: false },
|
|
|
|
|
{ name: '追加', to: '/materials/new' },
|
|
|
|
|
{ name: '履歴', to: '/materials/changes', visible: false },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:素材集' }] },
|
|
|
|
|
{ name: '上映会', to: '/theatres/1', base: '/theatres', subMenu: [
|
|
|
|
|
{ name: <>第 1 会場</>, to: '/theatres/1' },
|
|
|
|
|
{ name: 'CyTube', to: '//cytube.mm428.net/r/deernijika' },
|
|
|
|
|
{ name: <>ニジカ放送局第 1 チャンネル</>,
|
|
|
|
|
to: '//www.youtube.com/watch?v=DCU3hL4Uu6A' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:上映会' }] },
|
|
|
|
|
{ name: 'Wiki', to: '/wiki/ヘルプ:ホーム', base: '/wiki', subMenu: [
|
|
|
|
|
{ name: '検索', to: '/wiki' },
|
|
|
|
|
{ name: '新規', to: '/wiki/new' },
|
|
|
|
|
{ name: '全体履歴', to: '/wiki/changes' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' },
|
|
|
|
|
{ component: <Separator/>, visible: wikiPageFlg },
|
|
|
|
|
{ name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`,
|
|
|
|
|
visible: wikiPageFlg },
|
|
|
|
|
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
|
|
|
|
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] },
|
|
|
|
|
{ name: 'ユーザ', to: '/users/settings', visible: false, subMenu: [
|
|
|
|
|
{ name: '一覧', to: '/users', visible: false },
|
|
|
|
|
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
|
|
|
|
{ name: '設定', to: '/users/settings', visible: Boolean (user) }] },
|
|
|
|
|
{ name: '法規', visible: false, subMenu: [
|
|
|
|
|
{ name: '利用規約', to: '/tos' }] }]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default (({ user }: Props) => {
|
|
|
|
|
const location = useLocation ()
|
|
|
|
|
|
|
|
|
@@ -26,25 +80,30 @@ export default (({ user }: Props) => {
|
|
|
|
|
const itemsRef = useRef<(HTMLAnchorElement | null)[]> ([])
|
|
|
|
|
const navRef = useRef<HTMLDivElement | null> (null)
|
|
|
|
|
|
|
|
|
|
const measure = () => {
|
|
|
|
|
const measure = (idx: number) => {
|
|
|
|
|
const nav = navRef.current
|
|
|
|
|
const el = itemsRef.current[activeIdx]
|
|
|
|
|
if (!(nav) || !(el) || activeIdx < 0)
|
|
|
|
|
return
|
|
|
|
|
const el = itemsRef.current[idx < 0 ? visibleMenu.length : idx]
|
|
|
|
|
|
|
|
|
|
if (!(nav) || !(el))
|
|
|
|
|
{
|
|
|
|
|
setHL ({ left: 0, width: 0, visible: true })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const navRect = nav.getBoundingClientRect ()
|
|
|
|
|
const elRect = el.getBoundingClientRect ()
|
|
|
|
|
|
|
|
|
|
setHl ({ left: elRect.left - navRect.left,
|
|
|
|
|
setHL ({ left: elRect.left - navRect.left,
|
|
|
|
|
width: elRect.width,
|
|
|
|
|
visible: true })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [hl, setHl] = useState<{ left: number; width: number; visible: boolean }> ({
|
|
|
|
|
const [hl, setHL] = useState<{ left: number; width: number; visible: boolean }> ({
|
|
|
|
|
left: 0,
|
|
|
|
|
width: 0,
|
|
|
|
|
visible: false })
|
|
|
|
|
const [menuOpen, setMenuOpen] = useState (false)
|
|
|
|
|
const [moreVsbl, setMoreVsbl] = useState (false)
|
|
|
|
|
const [openItemIdx, setOpenItemIdx] = useState (-1)
|
|
|
|
|
const [wikiId, setWikiId] = useState<number | null> (WikiIdBus.get ())
|
|
|
|
|
|
|
|
|
@@ -62,51 +121,10 @@ export default (({ user }: Props) => {
|
|
|
|
|
queryKey: tagsKeys.show (effectiveTitle),
|
|
|
|
|
queryFn: () => fetchTagByName (effectiveTitle) })
|
|
|
|
|
|
|
|
|
|
const postCount = tag?.postCount ?? 0
|
|
|
|
|
|
|
|
|
|
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) && wikiId)
|
|
|
|
|
const wikiTitle = location.pathname.split ('/')[2] ?? ''
|
|
|
|
|
const menu: Menu = [
|
|
|
|
|
{ name: '広場', to: '/posts', subMenu: [
|
|
|
|
|
{ name: '一覧', to: '/posts' },
|
|
|
|
|
{ name: '検索', to: '/posts/search' },
|
|
|
|
|
{ name: '追加', to: '/posts/new' },
|
|
|
|
|
{ name: '履歴', to: '/posts/changes' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
|
|
|
|
{ name: 'タグ', to: '/tags', subMenu: [
|
|
|
|
|
{ name: 'マスタ', to: '/tags' },
|
|
|
|
|
{ name: '別名タグ', to: '/tags/aliases', visible: false },
|
|
|
|
|
{ name: '上位タグ', to: '/tags/implications', visible: false },
|
|
|
|
|
{ name: 'ニコニコ連携', to: '/tags/nico' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] },
|
|
|
|
|
// { name: '素材', to: '/materials', subMenu: [
|
|
|
|
|
// { name: '一覧', to: '/materials' },
|
|
|
|
|
// { name: '検索', to: '/materials/search', visible: false },
|
|
|
|
|
// { name: '追加', to: '/materials/new' },
|
|
|
|
|
// { name: '履歴', to: '/materials/changes', visible: false },
|
|
|
|
|
// { name: 'ヘルプ', to: '/wiki/ヘルプ:素材集' }] },
|
|
|
|
|
{ name: '上映会', to: '/theatres/1', base: '/theatres', subMenu: [
|
|
|
|
|
{ name: <>第 1 会場</>, to: '/theatres/1' },
|
|
|
|
|
{ name: 'CyTube', to: '//cytube.mm428.net/r/deernijika' },
|
|
|
|
|
{ name: <>ニジカ放送局第 1 チャンネル</>,
|
|
|
|
|
to: '//www.youtube.com/watch?v=DCU3hL4Uu6A' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:上映会' }] },
|
|
|
|
|
{ name: 'Wiki', to: '/wiki/ヘルプ:ホーム', base: '/wiki', subMenu: [
|
|
|
|
|
{ name: '検索', to: '/wiki' },
|
|
|
|
|
{ name: '新規', to: '/wiki/new' },
|
|
|
|
|
{ name: '全体履歴', to: '/wiki/changes' },
|
|
|
|
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' },
|
|
|
|
|
{ component: <Separator/>, visible: wikiPageFlg },
|
|
|
|
|
{ name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`,
|
|
|
|
|
visible: wikiPageFlg },
|
|
|
|
|
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
|
|
|
|
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] },
|
|
|
|
|
{ name: 'ユーザ', to: '/users/settings', subMenu: [
|
|
|
|
|
{ name: '一覧', to: '/users', visible: false },
|
|
|
|
|
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
|
|
|
|
{ name: '設定', to: '/users/settings', visible: Boolean (user) }] }]
|
|
|
|
|
|
|
|
|
|
const activeIdx = menu.findIndex (item => location.pathname.startsWith (item.base || item.to))
|
|
|
|
|
const menu = menuOutline ({ tag, wikiId, user, pathName: location.pathname })
|
|
|
|
|
const visibleMenu = menu.filter ((item): item is MenuVisibleItem => item.visible ?? true)
|
|
|
|
|
const activeIdx =
|
|
|
|
|
visibleMenu.findIndex (item => location.pathname.startsWith (item.base || item.to))
|
|
|
|
|
|
|
|
|
|
const prevActiveIdxRef = useRef<number> (activeIdx)
|
|
|
|
|
|
|
|
|
@@ -119,28 +137,24 @@ export default (({ user }: Props) => {
|
|
|
|
|
const dir = dirRef.current
|
|
|
|
|
|
|
|
|
|
useLayoutEffect (() => {
|
|
|
|
|
if (activeIdx < 0)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
const raf = requestAnimationFrame (measure)
|
|
|
|
|
const onResize = () => requestAnimationFrame (measure)
|
|
|
|
|
const raf = requestAnimationFrame (() => measure (moreVsbl ? -1 : activeIdx))
|
|
|
|
|
const onResize = () => requestAnimationFrame (() => measure (moreVsbl ? -1 : activeIdx))
|
|
|
|
|
|
|
|
|
|
addEventListener ('resize', onResize)
|
|
|
|
|
return () => {
|
|
|
|
|
cancelAnimationFrame (raf)
|
|
|
|
|
removeEventListener ('resize', onResize)
|
|
|
|
|
}
|
|
|
|
|
}, [activeIdx])
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
useEffect (() => {
|
|
|
|
|
const unsubscribe = WikiIdBus.subscribe (setWikiId)
|
|
|
|
|
return () => unsubscribe ()
|
|
|
|
|
}, [])
|
|
|
|
|
}, [activeIdx])
|
|
|
|
|
|
|
|
|
|
useEffect (() => {
|
|
|
|
|
setMenuOpen (false)
|
|
|
|
|
setOpenItemIdx (menu.findIndex (item => (
|
|
|
|
|
location.pathname.startsWith (item.base || item.to))))
|
|
|
|
|
setOpenItemIdx (activeIdx)
|
|
|
|
|
}, [location])
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
@@ -167,17 +181,39 @@ export default (({ user }: Props) => {
|
|
|
|
|
transform: `translateX(${ hl.left }px)`,
|
|
|
|
|
opacity: hl.visible ? 1 : 0 }}/>
|
|
|
|
|
|
|
|
|
|
{menu.map ((item, i) => (
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
key={i}
|
|
|
|
|
to={item.to}
|
|
|
|
|
ref={(el: (HTMLAnchorElement | null)) => {
|
|
|
|
|
itemsRef.current[i] = el
|
|
|
|
|
}}
|
|
|
|
|
className={cn ('relative z-10 flex h-full items-center px-5',
|
|
|
|
|
(i === openItemIdx) && 'font-bold')}>
|
|
|
|
|
{item.name}
|
|
|
|
|
</PrefetchLink>))}
|
|
|
|
|
{visibleMenu.map ((item, i) => (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={item.to}
|
|
|
|
|
layoutId={`menu-${ item.name }`}
|
|
|
|
|
animate={{ opacity: moreVsbl ? 0 : 1 }}
|
|
|
|
|
transition={{ opacity: { duration: .12 },
|
|
|
|
|
layout: { duration: .2, ease: 'easeOut' } }}
|
|
|
|
|
style={{ pointerEvents: moreVsbl ? 'none' : 'auto' }}
|
|
|
|
|
onMouseEnter={() => setMoreVsbl (false)}>
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to={item.to}
|
|
|
|
|
ref={(el: (HTMLAnchorElement | null)) => {
|
|
|
|
|
itemsRef.current[i] = el
|
|
|
|
|
}}
|
|
|
|
|
className={cn ('relative z-10 flex h-full items-center px-5',
|
|
|
|
|
(i === openItemIdx) && 'font-bold')}>
|
|
|
|
|
{item.name}
|
|
|
|
|
</PrefetchLink>
|
|
|
|
|
</motion.div>))}
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to="/more"
|
|
|
|
|
ref={(el: (HTMLAnchorElement | null)) => {
|
|
|
|
|
itemsRef.current[visibleMenu.length] = el
|
|
|
|
|
}}
|
|
|
|
|
onClick={() => setMoreVsbl (false)}
|
|
|
|
|
onMouseEnter={() => {
|
|
|
|
|
setMoreVsbl (true)
|
|
|
|
|
measure (-1)
|
|
|
|
|
}}
|
|
|
|
|
className={cn ('relative z-10 flex h-full items-center px-5',
|
|
|
|
|
(openItemIdx < 0 || moreVsbl) && 'font-bold')}>
|
|
|
|
|
その他 »
|
|
|
|
|
</PrefetchLink>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
@@ -195,36 +231,115 @@ export default (({ user }: Props) => {
|
|
|
|
|
</a>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<div className="relative hidden md:flex bg-yellow-200 dark:bg-red-950
|
|
|
|
|
items-center w-full min-h-[40px] overflow-hidden">
|
|
|
|
|
<AnimatePresence initial={false} custom={dir}>
|
|
|
|
|
<motion.div
|
|
|
|
|
key={activeIdx}
|
|
|
|
|
custom={dir}
|
|
|
|
|
variants={{ enter: (d: -1 | 1) => ({ y: d * 24, opacity: 0 }),
|
|
|
|
|
centre: { y: 0, opacity: 1 },
|
|
|
|
|
exit: (d: -1 | 1) => ({ y: (-d) * 24, opacity: 0 }) }}
|
|
|
|
|
className="absolute inset-0 flex items-center px-3"
|
|
|
|
|
initial="enter"
|
|
|
|
|
animate="centre"
|
|
|
|
|
exit="exit"
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}>
|
|
|
|
|
{(menu[activeIdx]?.subMenu ?? [])
|
|
|
|
|
.filter (item => item.visible ?? true)
|
|
|
|
|
.map ((item, i) => (
|
|
|
|
|
'component' in item
|
|
|
|
|
? <Fragment key={`c-${ i }`}>{item.component}</Fragment>
|
|
|
|
|
: (
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
key={`l-${ i }`}
|
|
|
|
|
to={item.to}
|
|
|
|
|
target={item.to.slice (0, 2) === '//' ? '_blank' : undefined}
|
|
|
|
|
className="h-full flex items-center px-3">
|
|
|
|
|
{item.name}
|
|
|
|
|
</PrefetchLink>)))}
|
|
|
|
|
</motion.div>
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
</div>
|
|
|
|
|
<AnimatePresence initial={false}>
|
|
|
|
|
<motion.div
|
|
|
|
|
key="submenu-shell"
|
|
|
|
|
layout
|
|
|
|
|
className="relative hidden md:block overflow-hidden
|
|
|
|
|
bg-yellow-200 dark:bg-red-950"
|
|
|
|
|
style={{ height: moreVsbl ? 40 * menu.length : (activeIdx < 0 ? 0 : 40) }}
|
|
|
|
|
onMouseLeave={() => {
|
|
|
|
|
if (moreVsbl)
|
|
|
|
|
setMoreVsbl (false)
|
|
|
|
|
}}
|
|
|
|
|
transition={{ layout: { duration: .2, ease: 'easeOut' } }}
|
|
|
|
|
onAnimationComplete={() => {
|
|
|
|
|
measure (moreVsbl ? -1 : activeIdx)
|
|
|
|
|
}}>
|
|
|
|
|
{moreVsbl
|
|
|
|
|
? (
|
|
|
|
|
menu.map ((item, i) => (
|
|
|
|
|
<div key={i} className="relative h-[40px]">
|
|
|
|
|
<div className="absolute inset-0 flex items-center px-3">
|
|
|
|
|
<motion.div
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}
|
|
|
|
|
{...((item.visible ?? true)
|
|
|
|
|
? { layoutId: `menu-${ item.name }` }
|
|
|
|
|
: { initial: { x: 40, y: -40, opacity: 0 },
|
|
|
|
|
animate: { x: 0, y: 0, opacity: 1 },
|
|
|
|
|
exit: { x: 40, y: -40, opacity: 0 } })}
|
|
|
|
|
className="z-10 h-full flex items-center px-3 font-bold w-24">
|
|
|
|
|
<h2>{item.name}</h2>
|
|
|
|
|
</motion.div>
|
|
|
|
|
{item.subMenu
|
|
|
|
|
.filter (subItem => subItem.visible ?? true)
|
|
|
|
|
.map ((subItem, j) => (
|
|
|
|
|
'component' in subItem
|
|
|
|
|
? (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={`c-${ i }-${ j }`}
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}
|
|
|
|
|
{...((visibleMenu[activeIdx]?.name
|
|
|
|
|
=== item.name)
|
|
|
|
|
? { layoutId: `submenu-${ item.name }-${ j }` }
|
|
|
|
|
: { initial: { y: -40, opacity: 0 },
|
|
|
|
|
animate: { y: 0, opacity: 1 },
|
|
|
|
|
exit: { y: -40, opacity: 0 } })}>
|
|
|
|
|
{subItem.component}
|
|
|
|
|
</motion.div>)
|
|
|
|
|
: (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={`l-${ i }-${ j }`}
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}
|
|
|
|
|
{...((visibleMenu[activeIdx]?.name
|
|
|
|
|
=== item.name)
|
|
|
|
|
? { layoutId: `submenu-${ item.name }-${ j }` }
|
|
|
|
|
: { initial: { y: -40, opacity: 0 },
|
|
|
|
|
animate: { y: 0, opacity: 1 },
|
|
|
|
|
exit: { y: -40, opacity: 0 } })}>
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to={subItem.to}
|
|
|
|
|
target={subItem.to.slice (0, 2) === '//' ? '_blank' : undefined}
|
|
|
|
|
onClick={() => setMoreVsbl (false)}
|
|
|
|
|
className="h-full flex items-center px-3">
|
|
|
|
|
{subItem.name}
|
|
|
|
|
</PrefetchLink>
|
|
|
|
|
</motion.div>)))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>)))
|
|
|
|
|
: ((visibleMenu[activeIdx]?.subMenu ?? []).length > 0
|
|
|
|
|
&& (
|
|
|
|
|
<div className="relative h-[40px]">
|
|
|
|
|
<AnimatePresence initial={false} custom={dir}>
|
|
|
|
|
<motion.div
|
|
|
|
|
key={activeIdx}
|
|
|
|
|
custom={dir}
|
|
|
|
|
variants={{ enter: (d: -1 | 1) => ({ y: d * 24, opacity: 0 }),
|
|
|
|
|
centre: { y: 0, opacity: 1 },
|
|
|
|
|
exit: (d: -1 | 1) => ({ y: (-d) * 24, opacity: 0 }) }}
|
|
|
|
|
className="absolute inset-0 flex items-center px-3"
|
|
|
|
|
initial="enter"
|
|
|
|
|
animate="centre"
|
|
|
|
|
exit="exit"
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}>
|
|
|
|
|
{(visibleMenu[activeIdx]?.subMenu ?? [])
|
|
|
|
|
.filter (item => item.visible ?? true)
|
|
|
|
|
.map ((item, i) => (
|
|
|
|
|
'component' in item
|
|
|
|
|
? (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={`c-${ i }`}
|
|
|
|
|
transition={{ layout: { duration: .2, ease: 'easeOut' } }}
|
|
|
|
|
layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}>
|
|
|
|
|
{item.component}
|
|
|
|
|
</motion.div>)
|
|
|
|
|
: (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={`l-${ i }`}
|
|
|
|
|
transition={{ layout: { duration: .2, ease: 'easeOut' } }}
|
|
|
|
|
layoutId={`submenu-${ visibleMenu[activeIdx].name }-${ i }`}>
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to={item.to}
|
|
|
|
|
target={item.to.slice (0, 2) === '//' ? '_blank' : undefined}
|
|
|
|
|
className="h-full flex items-center px-3">
|
|
|
|
|
{item.name}
|
|
|
|
|
</PrefetchLink>
|
|
|
|
|
</motion.div>)))}
|
|
|
|
|
</motion.div>
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
</div>))}
|
|
|
|
|
</motion.div>
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
|
|
|
|
|
<AnimatePresence initial={false}>
|
|
|
|
|
{menuOpen && (
|
|
|
|
@@ -241,7 +356,7 @@ export default (({ user }: Props) => {
|
|
|
|
|
exit="closed"
|
|
|
|
|
transition={{ duration: .2, ease: 'easeOut' }}>
|
|
|
|
|
<Separator/>
|
|
|
|
|
{menu.map ((item, i) => (
|
|
|
|
|
{visibleMenu.map ((item, i) => (
|
|
|
|
|
<Fragment key={i}>
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to={i === openItemIdx ? item.to : '#'}
|
|
|
|
@@ -294,6 +409,16 @@ export default (({ user }: Props) => {
|
|
|
|
|
</motion.div>)}
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
</Fragment>))}
|
|
|
|
|
<PrefetchLink
|
|
|
|
|
to="/more"
|
|
|
|
|
ref={(el: (HTMLAnchorElement | null)) => {
|
|
|
|
|
itemsRef.current[visibleMenu.length] = el
|
|
|
|
|
}}
|
|
|
|
|
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
|
|
|
|
((openItemIdx < 0)
|
|
|
|
|
&& 'font-bold bg-yellow-50 dark:bg-red-950'))}>
|
|
|
|
|
その他 »
|
|
|
|
|
</PrefetchLink>
|
|
|
|
|
<TopNavUser user={user} sp/>
|
|
|
|
|
<Separator/>
|
|
|
|
|
</motion.div>)}
|
|
|
|
|