#23 グローバル・メニュー
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import axios from 'axios'
|
||||
import toCamel from 'camelcase-keys'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Link, useLocation, /* useNavigate */ } from 'react-router-dom'
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import Separator from '@/components/MenuSeparator'
|
||||
import { API_BASE_URL } from '@/config'
|
||||
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -14,16 +15,12 @@ type Props = { user: User | null }
|
||||
|
||||
export default ({ user }: Props) => {
|
||||
const location = useLocation ()
|
||||
// const navigate = useNavigate ()
|
||||
const navigate = useNavigate ()
|
||||
|
||||
// const [activeIndex, setActiveIndex] = useState (-1)
|
||||
const [menuOpen, setMenuOpen] = useState (false)
|
||||
const [openItemIdx, setOpenItemIdx] = useState (-1)
|
||||
const [postCount, setPostCount] = useState<number | null> (null)
|
||||
// const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)
|
||||
// const [suggestions, setSuggestions] = useState<WikiPage[]> ([])
|
||||
// const [tagSearch, setTagSearch] = useState ('')
|
||||
// const [userSearch, setUserSearch] = useState ('')
|
||||
const [wikiId, setWikiId] = useState<number | null> (WikiIdBus.get ())
|
||||
// const [wikiSearch, setWikiSearch] = useState ('')
|
||||
|
||||
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) && wikiId)
|
||||
const wikiTitle = location.pathname.split ('/')[2]
|
||||
@@ -44,8 +41,7 @@ export default ({ user }: Props) => {
|
||||
{ name: '新規', to: '/wiki/new' },
|
||||
{ name: '全体履歴', to: '/wiki/changes' },
|
||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' },
|
||||
{ component: <span className="flex items-center px-2">|</span>,
|
||||
visible: wikiPageFlg },
|
||||
{ component: <Separator />, visible: wikiPageFlg },
|
||||
{ name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`,
|
||||
visible: wikiPageFlg },
|
||||
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
||||
@@ -55,61 +51,16 @@ export default ({ user }: Props) => {
|
||||
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
||||
{ name: '設定', to: '/users/settings', visible: Boolean (user) }] }]
|
||||
|
||||
// const whenTagSearchChanged = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// // TODO: 実装
|
||||
//
|
||||
// setTagSearch (ev.target.value)
|
||||
//
|
||||
// const q: string = ev.target.value.split (' ').at (-1)
|
||||
// if (!(q))
|
||||
// {
|
||||
// // setSuggestions ([])
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// const whenWikiSearchChanged = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// // TODO: 実装
|
||||
//
|
||||
// setWikiSearch (ev.target.value)
|
||||
//
|
||||
// const q: string = ev.target.value.split (' ').at (-1)
|
||||
// if (!(q))
|
||||
// {
|
||||
// // setSuggestions ([])
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// const whenUserSearchChanged = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// // TODO: 実装
|
||||
//
|
||||
// setUserSearch (ev.target.value)
|
||||
//
|
||||
// const q: string = ev.target.value.split (' ').at (-1)
|
||||
// if (!(q))
|
||||
// {
|
||||
// // setSuggestions ([])
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// const handleKeyDown = (ev: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
// if (ev.key === 'Enter' && wikiSearch.length && (!(suggestionsVsbl) || activeIndex < 0))
|
||||
// {
|
||||
// navigate (`/wiki/${ encodeURIComponent (wikiSearch) }`)
|
||||
// setSuggestionsVsbl (false)
|
||||
// }
|
||||
// }
|
||||
|
||||
// const handleTagSelect = (tag: Tag) => {
|
||||
// }
|
||||
|
||||
useEffect (() => {
|
||||
const unsubscribe = WikiIdBus.subscribe (setWikiId)
|
||||
return () => unsubscribe ()
|
||||
}, [])
|
||||
|
||||
useEffect (() => {
|
||||
setOpenItemIdx (menu.findIndex (item => (
|
||||
location.pathname.startsWith (item.base || item.to))))
|
||||
}, [location])
|
||||
|
||||
useEffect (() => {
|
||||
if (!(wikiId))
|
||||
return
|
||||
@@ -136,16 +87,18 @@ export default ({ user }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<nav className="px-3 flex justify-between items-center w-full min-h-[48px]
|
||||
bg-yellow-50 dark:bg-red-975">
|
||||
bg-yellow-200 dark:bg-red-975 md:bg-yellow-50">
|
||||
<div className="flex items-center gap-2 h-full">
|
||||
<Link to="/" className="mx-4 text-xl font-bold
|
||||
text-pink-600 hover:text-pink-400
|
||||
dark:text-pink-300 dark:hover:text-pink-100">
|
||||
<Link to="/"
|
||||
className="mx-4 text-xl font-bold text-pink-600 hover:text-pink-400
|
||||
dark:text-pink-300 dark:hover:text-pink-100">
|
||||
ぼざクリ タグ広場
|
||||
</Link>
|
||||
{menu.map (item => (
|
||||
<Link to={item.to}
|
||||
className={cn ('h-full flex items-center',
|
||||
|
||||
{menu.map ((item, i) => (
|
||||
<Link key={i}
|
||||
to={item.to}
|
||||
className={cn ('hidden md:flex h-full items-center',
|
||||
(location.pathname.startsWith (item.base || item.to)
|
||||
? 'bg-yellow-200 dark:bg-red-950 px-4 font-bold'
|
||||
: 'px-2'))}>
|
||||
@@ -153,23 +106,83 @@ export default ({ user }: Props) => {
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{user && (
|
||||
<div className="ml-auto pr-4">
|
||||
<div className="hidden md:block ml-auto pr-4">
|
||||
<Link to="/users/settings"
|
||||
className="font-bold text-red-600 hover:text-red-400
|
||||
dark:text-yellow-400 dark:hover:text-yellow-200">
|
||||
{user.name || '名もなきニジラー'}
|
||||
</Link>
|
||||
</div>)}
|
||||
|
||||
<a href="#"
|
||||
className="md:hidden ml-auto pr-4
|
||||
text-pink-600 hover:text-pink-400
|
||||
dark:text-pink-300 dark:hover:text-pink-100"
|
||||
onClick={ev => {
|
||||
ev.preventDefault ()
|
||||
setMenuOpen (!(menuOpen))
|
||||
}}>
|
||||
{menuOpen ? '×' : 'Menu'}
|
||||
</a>
|
||||
</nav>
|
||||
<div className={cn ('bg-yellow-200 dark:bg-red-950',
|
||||
'text-white px-3 flex items-center w-full min-h-[40px]')}>
|
||||
|
||||
<div className="hidden md:flex bg-yellow-200 dark:bg-red-950
|
||||
items-center w-full min-h-[40px]">
|
||||
{menu.find (item => location.pathname.startsWith (item.base || item.to))?.subMenu
|
||||
.filter (item => item.visible ?? true)
|
||||
.map (item => 'component' in item ? item.component : (
|
||||
<Link to={item.to} className="h-full flex items-center px-3">
|
||||
.map ((item, i) => 'component' in item ? item.component : (
|
||||
<Link key={i}
|
||||
to={item.to}
|
||||
className="h-full flex items-center px-3">
|
||||
{item.name}
|
||||
</Link>))}
|
||||
</div>
|
||||
|
||||
<div className={cn (menuOpen ? 'flex flex-col md:hidden' : 'hidden',
|
||||
'bg-yellow-200 dark:bg-red-975 items-start')}>
|
||||
<Separator />
|
||||
{menu.map ((item, i) => (
|
||||
<>
|
||||
<a key={i}
|
||||
href="#"
|
||||
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
||||
i === openItemIdx && 'font-bold bg-yellow-50 dark:bg-red-950')}
|
||||
onClick={ev => {
|
||||
ev.preventDefault ()
|
||||
if (i === openItemIdx)
|
||||
{
|
||||
setMenuOpen (false)
|
||||
navigate (item.to)
|
||||
}
|
||||
else
|
||||
setOpenItemIdx (i)
|
||||
}}>
|
||||
{item.name}
|
||||
</a>
|
||||
{i === openItemIdx && (
|
||||
item.subMenu
|
||||
.filter (subItem => subItem.visible ?? true)
|
||||
.map ((subItem, j) => 'component' in subItem ? subItem.component : (
|
||||
<Link key={j}
|
||||
to={subItem.to}
|
||||
className="w-full min-h-[36px] flex items-center pl-12
|
||||
bg-yellow-50 dark:bg-red-950">
|
||||
{subItem.name}
|
||||
</Link>)))}
|
||||
</>))}
|
||||
{user && (
|
||||
<>
|
||||
<Separator />
|
||||
<Link to="/users/settings"
|
||||
className="w-full min-h-[40px] flex items-center pl-8
|
||||
font-bold text-red-600 hover:text-red-400
|
||||
dark:text-yellow-400 dark:hover:text-yellow-200">
|
||||
{user.name || '名もなきニジラー'}
|
||||
</Link>
|
||||
</>)}
|
||||
<Separator />
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user