アイヌ語追加 (#428) (#433)

Reviewed-on: #433
Co-authored-by: miteruzo <miteruzo@naver.com>
このコミットはプルリクエスト #433 でマージされました。
このコミットが含まれているのは:
2026-10-20 20:44:14 +09:00
committed by みてるぞ
コミット 084a090cab
294個のファイルの変更、13978行の追加、3461行の削除
+40 -30
ファイルの表示
@@ -1,19 +1,20 @@
import { useQuery } from '@tanstack/react-query'
import { useEffect, useRef, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import SidebarComponent from '@/components/layout/SidebarComponent'
import TagLink from '@/components/TagLink'
import { fetchMaterialTagTree, parseMaterialFilter } from '@/lib/materials'
import { materialsKeys } from '@/lib/queryKeys'
import { cn } from '@/lib/utils'
import { useUITranslation } from '@/lib/useUITranslation'
import type { CSSProperties, Dispatch, FC, ReactNode, SetStateAction } from 'react'
import type { MaterialFilter, MaterialSidebarTag, Tag } from '@/types'
const FILTERS: MaterialFilter[] = ['missing', 'present', 'any']
const FILTER_LABELS: Record<MaterialFilter, string> = { present: '有', missing: '無', any: '全' }
const px = (value: string): number => {
@@ -83,7 +84,7 @@ const tagSelectionShellClass = (selected: boolean): string =>
const updateMaterialFilterQuery = (
pathname: string,
locationSearch: string,
navigate: ReturnType<typeof useNavigate>,
navigate: ReturnType<typeof useLocaleNavigate>,
materialFilter: MaterialFilter,
) => {
const qs = new URLSearchParams (locationSearch)
@@ -95,24 +96,27 @@ const updateMaterialFilterQuery = (
const MaterialFilterButtons: FC<{ materialFilter: MaterialFilter
onChange: (materialFilter: MaterialFilter) => void }> = (
{ materialFilter, onChange },
) => (
<div className="flex flex-wrap gap-2 justify-end md:justify-start flex-center">
<label className="my-auto text-sm font-bold">素材:</label>
{FILTERS.map (value => (
<button
key={value}
type="button"
onClick={() => onChange (value)}
className={cn (
'rounded-full border px-3 py-1 text-sm',
(materialFilter === value
? ['border-sky-500 bg-sky-50 text-sky-700 dark:border-sky-400',
'dark:bg-sky-950 dark:text-sky-100']
: ['border-neutral-300 bg-white text-neutral-700 dark:border-stone-700',
'dark:bg-stone-900 dark:text-stone-200']))}>
{FILTER_LABELS[value]}
</button>))}
</div>)
) => {
const { label } = useUITranslation ('materials')
return (
<div className="flex flex-wrap gap-2 justify-end md:justify-start flex-center">
<label className="my-auto text-sm font-bold">{label ('sidebar.label')}</label>
{FILTERS.map (value => (
<button
key={value}
type="button"
onClick={() => onChange (value)}
className={cn (
'rounded-full border px-3 py-1 text-sm',
(materialFilter === value
? ['border-sky-500 bg-sky-50 text-sky-700 dark:border-sky-400',
'dark:bg-sky-950 dark:text-sky-100']
: ['border-neutral-300 bg-white text-neutral-700 dark:border-stone-700',
'dark:bg-stone-900 dark:text-stone-200']))}>
{label (`sidebar.${value}`)}
</button>))}
</div>)
}
const MaterialTreeNode: FC<{
@@ -125,10 +129,11 @@ const MaterialTreeNode: FC<{
tag: MaterialSidebarTag
}> = ({ materialFilter, nestLevel = 0, onChildren, openTags, selectedTagId,
setOpenTags, tag }) => {
const locale = useLocale ()
const open = Boolean (openTags[tag.id])
const { data } = useQuery ({
queryKey: materialsKeys.tree ({ parentId: tag.id, materialFilter }),
queryFn: () => fetchMaterialTagTree ({ parentId: tag.id, materialFilter }),
queryKey: materialsKeys.tree ({ parentId: tag.id, materialFilter }, locale),
queryFn: () => fetchMaterialTagTree ({ parentId: tag.id, materialFilter }, locale),
enabled: open && tag.hasChildren && tag.children.length === 0})
useEffect (() => {
@@ -200,6 +205,7 @@ const MobileMaterialTreeNode: FC<{ depth?: number
tag,
},
) => {
const locale = useLocale ()
const open = Boolean (openTags[tag.id])
const tagColumnRef = useRef<HTMLDivElement | null> (null)
const chipRef = useRef<HTMLDivElement | null> (null)
@@ -210,8 +216,8 @@ const MobileMaterialTreeNode: FC<{ depth?: number
const [tagLinkInlineSizePx, setTagLinkInlineSizePx] = useState<number | null> (null)
const [childAvailableInlineSizePx, setChildAvailableInlineSizePx] = useState<number | null> (null)
const { data } = useQuery ({
queryKey: materialsKeys.tree ({ parentId: tag.id, materialFilter }),
queryFn: () => fetchMaterialTagTree ({ parentId: tag.id, materialFilter }),
queryKey: materialsKeys.tree ({ parentId: tag.id, materialFilter }, locale),
queryFn: () => fetchMaterialTagTree ({ parentId: tag.id, materialFilter }, locale),
enabled: open && tag.hasChildren && tag.children.length === 0})
useEffect (() => {
@@ -382,8 +388,10 @@ const MobileMaterialTreeNode: FC<{ depth?: number
const MaterialSidebar: FC = () => {
const { label } = useUITranslation ('materials')
const locale = useLocale ()
const location = useLocation ()
const navigate = useNavigate ()
const navigate = useLocaleNavigate ()
const qs = new URLSearchParams (location.search)
const materialFilter = parseMaterialFilter (qs.get ('material_filter'), 'any')
const selectedTagId = Number (qs.get ('tag_id') ?? 0) || null
@@ -395,8 +403,8 @@ const MaterialSidebar: FC = () => {
useState<number | null> (null)
const { data: rootTags = [], isLoading, isError } = useQuery ({
queryKey: materialsKeys.tree ({ parentId: null, materialFilter }),
queryFn: () => fetchMaterialTagTree ({ parentId: null, materialFilter })})
queryKey: materialsKeys.tree ({ parentId: null, materialFilter }, locale),
queryFn: () => fetchMaterialTagTree ({ parentId: null, materialFilter }, locale)})
useEffect (() => {
setDesktopTags (rootTags)
@@ -494,10 +502,12 @@ const MaterialSidebar: FC = () => {
materialFilter={materialFilter}
onChange={handleFilterChange}/>
{isLoading && (
<p className="text-sm text-neutral-500 dark:text-stone-400">読込中……</p>)}
<p className="text-sm text-neutral-500 dark:text-stone-400">
{label ('shared:messages.loadingEllipsis')}
</p>)}
{isError && (
<p className="text-sm text-red-600 dark:text-red-300">
タグ一覧の取得に失敗しました.
{label ('sidebar.error')}
</p>)}
{(!isLoading && !isError) && (
<ul>