このコミットが含まれているのは:
2026-10-17 19:59:18 +09:00
コミット f063fc54bb
17個のファイルの変更、276行の追加、123行の削除
+54 -37
ファイルの表示
@@ -1,5 +1,6 @@
import { useQuery } from '@tanstack/react-query'
import { AnimatePresence, motion } from 'framer-motion'
import { Globe } from 'lucide-react'
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
@@ -8,9 +9,7 @@ import Separator from '@/components/MenuSeparator'
import PrefetchLink from '@/components/PrefetchLink'
import TopNavUser from '@/components/TopNavUser'
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
import { LOCALE_LABELS,
SUPPORTED_LOCALES,
isLocaleCode,
import { isLocaleCode,
localeFromPath,
switchLocalePath,
unlocalisedPath } from '@/lib/locales'
@@ -25,9 +24,10 @@ import { fetchWikiPage } from '@/lib/wiki'
import type { FC, MouseEvent } from 'react'
import type { AvailableLocale } from '@/lib/locales'
import type { Material, Menu, MenuVisibleItem, Tag, User } from '@/types'
type Props = { user: User | null }
type Props = { user: User | null; locales: AvailableLocale[] }
type NavDensity = 'auto' | 'comfortable' | 'compact'
@@ -116,8 +116,9 @@ export const menuOutline = (
}
const TopNav: FC<Props> = ({ user }) => {
const TopNav: FC<Props> = ({ user, locales }) => {
const locale = useLocale ()
const localeName = locales.find (entry => entry.code === locale)?.name
const navigate = useLocaleNavigate ()
const location = useLocation ()
const behaviourSettings = useClientBehaviourSettings ()
@@ -229,7 +230,7 @@ const TopNav: FC<Props> = ({ user }) => {
if (layout == null || bar == null || primary == null)
return
const userLink = bar.querySelector<HTMLElement> ('.top-nav-user-desktop')
const account = bar.querySelector<HTMLElement> ('.top-nav-account')
let raf = 0
let active = true
@@ -252,9 +253,9 @@ const TopNav: FC<Props> = ({ user }) => {
layout.setAttribute ('data-density', candidate)
const style = getComputedStyle (bar)
const required = primary.getBoundingClientRect ().width
+ (userLink?.getBoundingClientRect ().width ?? 0)
+ (account?.getBoundingClientRect ().width ?? 0)
+ parseFloat (style.paddingLeft) + parseFloat (style.paddingRight)
+ (userLink == null ? 0 : parseFloat (style.columnGap))
+ (account == null ? 0 : parseFloat (style.columnGap))
if (required <= bar.clientWidth)
return candidate
}
@@ -283,8 +284,8 @@ const TopNav: FC<Props> = ({ user }) => {
typeof ResizeObserver === 'undefined' ? null : new ResizeObserver (schedule)
observer?.observe (bar)
observer?.observe (primary)
if (userLink != null)
observer?.observe (userLink)
if (account != null)
observer?.observe (account)
update ()
addEventListener ('resize', schedule)
document.fonts?.addEventListener ('loadingdone', schedule)
@@ -297,7 +298,7 @@ const TopNav: FC<Props> = ({ user }) => {
removeEventListener ('resize', schedule)
document.fonts?.removeEventListener ('loadingdone', schedule)
}
}, [activeIdx, moreVsbl, desktop, density, user?.name])
}, [activeIdx, moreVsbl, desktop, density, user?.name, locale, locales])
useEffect (() => {
const unsubscribe = WikiIdBus.subscribe (setWikiId)
@@ -309,6 +310,32 @@ const TopNav: FC<Props> = ({ user }) => {
setOpenItemIdx (activeIdx)
}, [activeIdx, location])
const localeSelector = (
<label className="top-nav-menu-link relative flex h-8 shrink-0 items-center gap-1
rounded px-1 focus-within:ring-2">
<Globe aria-hidden="true" size={16}/>
<span className="top-nav-locale-name whitespace-nowrap text-sm">
{localeName}
</span>
<select
aria-label={localeName}
value={locale}
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
onChange={event => {
const nextLocale = event.target.value
if (isLocaleCode (nextLocale)
&& locales.some (entry => entry.code === nextLocale))
{
navigate (switchLocalePath (location, nextLocale))
}
}}>
{locales.map (entry => isLocaleCode (entry.code) && (
<option key={entry.code} value={entry.code}>
{entry.name}
</option>))}
</select>
</label>)
return (
<div ref={layoutRef} className="top-nav-layout w-full min-w-0 shrink-0"
data-desktop={desktop} data-density={density}>
@@ -374,35 +401,25 @@ const TopNav: FC<Props> = ({ user }) => {
</div>
</div>
<TopNavUser user={user}/>
<div className="top-nav-account ml-auto flex min-w-0 items-center gap-2">
<TopNavUser user={user}/>
{localeSelector}
</div>
<button
type="button"
className="top-nav-mobile-toggle shrink-0 whitespace-nowrap
ml-auto border-0 bg-transparent pr-4"
onClick={() => {
setMenuOpen (!(menuOpen))
}}>
{menuOpen ? '×' : 'Menu'}
</button>
<div className="top-nav-mobile-controls ml-auto flex shrink-0 items-center gap-2">
{menuOpen && localeSelector}
<button
type="button"
className="top-nav-mobile-toggle shrink-0 whitespace-nowrap
border-0 bg-transparent pr-4"
onClick={() => {
setMenuOpen (!(menuOpen))
}}>
{menuOpen ? '×' : 'Menu'}
</button>
</div>
</nav>
<div className="flex min-w-0 justify-end px-2">
<select
aria-label={LOCALE_LABELS[locale]}
value={locale}
className="max-w-full rounded border border-gray-300 bg-white text-xs text-gray-900
dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
onChange={event => {
const nextLocale = event.target.value
if (isLocaleCode (nextLocale))
navigate (switchLocalePath (location, nextLocale))
}}>
{SUPPORTED_LOCALES.map (code => (
<option key={code} value={code}>{LOCALE_LABELS[code]}</option>))}
</select>
</div>
<AnimatePresence initial={false}>
<motion.div
key="submenu-shell"
+1 -2
ファイルの表示
@@ -23,8 +23,7 @@ const TopNavUser: FC<Props> = ({ user, sp }) => {
return (
<>
{sp && <Separator/>}
<PrefetchLink to="/users/settings"
className={className}>
<PrefetchLink to="/users/settings" className={className}>
{user.name || '名もなきニジラー'}
</PrefetchLink>
</>)