|
- import { Fragment, useEffect, useState } from 'react'
-
- import TagLink from '@/components/TagLink'
- import SidebarComponent from '@/components/layout/SidebarComponent'
- import { apiGet } from '@/lib/api'
-
- import type { FC, ReactNode } from 'react'
-
- import type { Tag } from '@/types'
-
- type TagWithDepth = Tag & {
- hasChildren: boolean
- children: TagWithDepth[] }
-
-
- const setChildrenById = (
- tags: TagWithDepth[],
- targetId: number,
- children: TagWithDepth[],
- ): TagWithDepth[] => (
- tags.map (tag => {
- if (tag.id === targetId)
- return { ...tag, children }
-
- if (tag.children.length === 0)
- return tag
-
- return { ...tag,
- children: (setChildrenById (tag.children, targetId, children)
- .filter (t => t.category !== 'meme' || t.hasChildren)) }
- }))
-
-
- export default (() => {
- const [tags, setTags] = useState<TagWithDepth[]> ([])
- const [openTags, setOpenTags] = useState<Record<number, boolean>> ({ })
- const [tagFetchedFlags, setTagFetchedFlags] = useState<Record<number, boolean>> ({ })
-
- useEffect (() => {
- void (async () => {
- setTags ((await apiGet<TagWithDepth[]> ('/tags/with-depth'))
- .filter (t => t.category !== 'meme' || t.hasChildren))
- }) ()
- }, [])
-
- const renderTags = (ts: TagWithDepth[], nestLevel = 0): ReactNode => (
- ts.map (t => (
- <Fragment key={t.id}>
- <li>
- <div className="flex">
- <div className="flex-none w-4">
- {t.hasChildren && (
- <a
- href="#"
- onClick={async e => {
- e.preventDefault ()
- if (!(tagFetchedFlags[t.id]))
- {
- try
- {
- const data =
- await apiGet<TagWithDepth[]> (
- '/tags/with-depth', { params: { parent: String (t.id) } })
- setTags (prev => setChildrenById (prev, t.id, data))
- setTagFetchedFlags (prev => ({ ...prev, [t.id]: true }))
- }
- catch
- {
- ;
- }
- }
- setOpenTags (prev => ({ ...prev, [t.id]: !(prev[t.id]) }))
- }}>
- {openTags[t.id] ? <>−</> : '+'}
- </a>)}
- </div>
- <div className="flex-1 truncate">
- <TagLink
- tag={t}
- nestLevel={nestLevel}
- title={t.name}
- withCount={false}
- withWiki={false}
- to={`/materials?tag=${ encodeURIComponent (t.name) }`}/>
- </div>
- </div>
- </li>
- {openTags[t.id] && renderTags (t.children, nestLevel + 1)}
- </Fragment>)))
-
- return (
- <SidebarComponent>
- <ul>
- {renderTags (tags)}
- </ul>
- </SidebarComponent>)
- }) satisfies FC
|