ぼざクリ タグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

145 lines
5.7 KiB

  1. import React, { useState, useEffect } from 'react'
  2. import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
  3. import SettingsDialogue from './SettingsDialogue'
  4. import { Button } from './ui/button'
  5. import { cn } from '@/lib/utils'
  6. import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
  7. import type { User } from '@/types'
  8. type Props = { user: User
  9. setUser: (user: User) => void }
  10. const enum Menu { None,
  11. Post,
  12. Deerjikist,
  13. Tag,
  14. Wiki }
  15. const TopNav: React.FC = ({ user, setUser }: Props) => {
  16. const location = useLocation ()
  17. const navigate = useNavigate ()
  18. const [settingsVsbl, setSettingsVsbl] = useState (false)
  19. const [selectedMenu, setSelectedMenu] = useState<Menu> (Menu.None)
  20. const [wikiId, setWikiId] = useState (WikiIdBus.get ())
  21. const [wikiSearch, setWikiSearch] = useState ('')
  22. const [activeIndex, setActiveIndex] = useState (-1)
  23. const [suggestions, setSuggestions] = useState<WikiPage[]> ([])
  24. const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)
  25. const MyLink = ({ to, title, menu, base }: { to: string
  26. title: string
  27. menu?: Menu
  28. base?: string }) => (
  29. <Link to={to} className={cn ('hover:text-orange-500 h-full flex items-center',
  30. (location.pathname.startsWith (base ?? to)
  31. ? 'bg-gray-700 px-4 font-bold'
  32. : 'px-2'))}>
  33. {title}
  34. </Link>)
  35. const whenWikiSearchChanged = e => {
  36. setWikiSearch (e.target.value)
  37. const q: string = e.target.value.split (' ').at (-1)
  38. if (!(q))
  39. {
  40. setSuggestions ([])
  41. return
  42. }
  43. // void (axios.get(`${ API_BASE_URL }/`))
  44. }
  45. const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  46. if (e.key === 'Enter' && wikiSearch.length && (!(suggestionsVsbl) || activeIndex < 0))
  47. {
  48. navigate (`/wiki/${ encodeURIComponent (wikiSearch) }`)
  49. setSuggestionsVsbl (false)
  50. }
  51. }
  52. const handleTagSelect = (tag: Tag) => {
  53. }
  54. useEffect (() => {
  55. WikiIdBus.subscribe (setWikiId)
  56. }, [])
  57. useEffect (() => {
  58. if (location.pathname.startsWith ('/posts'))
  59. setSelectedMenu (Menu.Post)
  60. else if (location.pathname.startsWith ('/deerjikists'))
  61. setSelectedMenu (Menu.Deerjikist)
  62. else if (location.pathname.startsWith ('/tags'))
  63. setSelectedMenu (Menu.Tag)
  64. else if (location.pathname.startsWith ('/wiki'))
  65. setSelectedMenu (Menu.Wiki)
  66. else
  67. setSelectedMenu (Menu.None)
  68. }, [location])
  69. return (
  70. <>
  71. <nav className="bg-gray-800 text-white px-3 flex justify-between items-center w-full min-h-[48px]">
  72. <div className="flex items-center gap-2 h-full">
  73. <Link to="/posts" className="mx-4 text-xl font-bold text-orange-500">ぼざクリ タグ広場</Link>
  74. <MyLink to="/posts" title="広場" />
  75. <MyLink to="/deerjikists" title="ニジラー" />
  76. <MyLink to="/tags" title="タグ" />
  77. <MyLink to="/wiki/ヘルプ:ホーム" base="/wiki" title="Wiki" />
  78. </div>
  79. <div className="ml-auto pr-4">
  80. <Button onClick={() => setSettingsVsbl (true)}>{user?.name || '名もなきニジラー'}</Button>
  81. <SettingsDialogue visible={settingsVsbl}
  82. onVisibleChange={setSettingsVsbl}
  83. user={user}
  84. setUser={setUser} />
  85. </div>
  86. </nav>
  87. {(() => {
  88. const className = 'bg-gray-700 text-white px-3 flex items-center w-full min-h-[40px]'
  89. const subClass = 'hover:text-orange-500 h-full flex items-center px-3'
  90. const inputBox = 'flex items-center px-3 mx-2'
  91. const Separator = () => <span className="flex items-center px-2">|</span>
  92. switch (selectedMenu)
  93. {
  94. case Menu.Post:
  95. return (
  96. <div className={className}>
  97. <Link to="/posts" className={subClass}>一覧</Link>
  98. <Link to="/posts/new" className={subClass}>投稿追加</Link>
  99. <Link to="/wiki/ヘルプ:広場" className={subClass}>ヘルプ</Link>
  100. </div>)
  101. case Menu.Wiki:
  102. return (
  103. <div className={className}>
  104. <input type="text"
  105. className={inputBox}
  106. placeholder="Wiki 検索"
  107. value={wikiSearch}
  108. onChange={whenWikiSearchChanged}
  109. onFocus={() => setSuggestionsVsbl (true)}
  110. onBlur={() => setSuggestionsVsbl (false)}
  111. onKeyDown={handleKeyDown} />
  112. <Link to="/wiki" className={subClass}>検索</Link>
  113. <Link to="/wiki/new" className={subClass}>新規</Link>
  114. <Link to="/wiki/changes" className={subClass}>全体履歴</Link>
  115. <Link to="/wiki/ヘルプ:Wiki" className={subClass}>ヘルプ</Link>
  116. {/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) &&
  117. <>
  118. <Separator />
  119. <Link to={`/posts?tags=${ location.pathname.split ('/')[2] }`} className={subClass}>広場</Link>
  120. <Link to={`/wiki/changes?id=${ wikiId }`} className={subClass}>履歴</Link>
  121. <Link to={`/wiki/${ wikiId || location.pathname.split ('/')[2] }/edit`} className={subClass}>編輯</Link>
  122. </>}
  123. </div>)
  124. }
  125. }) ()}
  126. </>)
  127. }
  128. export default TopNav