Browse Source

#184

pull/186/head
みてるぞ 2 weeks ago
parent
commit
32e1054a8f
1 changed files with 104 additions and 42 deletions
  1. +104
    -42
      frontend/src/components/TagDetailSidebar.tsx

+ 104
- 42
frontend/src/components/TagDetailSidebar.tsx View File

@@ -12,6 +12,7 @@ import TagSearch from '@/components/TagSearch'
import SectionTitle from '@/components/common/SectionTitle'
import SubsectionTitle from '@/components/common/SubsectionTitle'
import SidebarComponent from '@/components/layout/SidebarComponent'
import { toast } from '@/components/ui/use-toast'
import { API_BASE_URL } from '@/config'
import { CATEGORIES } from '@/consts'

@@ -55,6 +56,31 @@ const renderTagTree = (
}


const removeEverywhere = (
list: Tag[],
tagId: number,
): { next: Tag[]; picked?: Tag } => {
let picked: Tag | undefined

const walk = (nodes: Tag[]): Tag[] => (
nodes
.map (t => {
const children = t.children ? walk (t.children) : undefined
return children ? { ...t, children } : t
})
.filter (t => {
if (t.id === tagId)
{
picked = picked ?? t
return false
}
return true
}))

return { next: walk (list), picked }
}


const addAsChild = (
list: Tag[],
parentId: number,
@@ -76,6 +102,34 @@ const addAsChild = (
}


const attachChildOptimistic = (
prev: TagByCategory,
parentId: number,
childId: number,
): TagByCategory => {
const next: TagByCategory = { ...prev }

let picked: Tag | undefined
for (const cat of Object.keys (next) as (keyof typeof next)[])
{
const r = removeEverywhere (next[cat], childId)
next[cat] = r.next
picked = picked ?? r.picked
}
if (!(picked))
return prev

for (const cat of Object.keys (next) as (keyof typeof next)[])
{
next[cat] =
addAsChild (next[cat], parentId, picked)
.sort ((a: Tag, b: Tag) => a.name < b.name ? -1 : 1)
}

return next
}


const isDescendant = (
root: Tag,
targetId: number,
@@ -150,10 +204,10 @@ const insertRootAt = (
}


const DropSlot = ({ cat, index }: { cat: Category, index: number }) => {
const DropSlot = ({ cat }: { cat: Category }) => {
const { setNodeRef, isOver: over } = useDroppable ({
id: `slot:${ cat }:${ index }`,
data: { kind: 'slot', cat, index } })
id: `slot:${ cat }`,
data: { kind: 'slot', cat } })

return (
<li ref={setNodeRef} className="h-1">
@@ -162,12 +216,6 @@ const DropSlot = ({ cat, index }: { cat: Category, index: number }) => {
}


const removeFromRoot = (
roots: Tag[],
childId: number,
): Tag[] => roots.filter (t => t.id !== childId)


type Props = { post: Post | null }


@@ -189,15 +237,36 @@ export default (({ post }: Props) => {

const overKind = e.over?.data.current?.kind

if (!(childId) || !(overKind))
if (childId == null || !(overKind))
return

switch (overKind)
{
case 'tag':
const parentId: number | undefined = e.over?.data.current?.tagId
if (!(parentId) || childId === parentId)
if (parentId == null || childId === parentId)
return

try
{
if (fromParentId != null)
{
await axios.delete (
`${ API_BASE_URL }/tags/${ fromParentId }/children/${ childId }`,
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })
}

await axios.post (
`${ API_BASE_URL }/tags/${ parentId }/children/${ childId }`,
{ },
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })
}
catch
{
toast ({ description: '管理者権限が必要です.' })

return
}

setTags (prev => {
const child = findTag (prev, childId)
@@ -205,38 +274,33 @@ export default (({ post }: Props) => {
if (!(child) || !(parent) || isDescendant (child, parentId))
return prev

const cat = child.category
const next: TagByCategory = { ...prev }

if (fromParentId)
next[cat] = detachEdge (next[cat], fromParentId, childId)
else
next[cat] = removeFromRoot (next[cat], childId)

next[cat] = addAsChild (next[cat], parentId, child)
toast ({ description: `《${ child.name }》を《${ parent.name }》の子タグに設定しました.` })

return next
return attachChildOptimistic (prev, parentId, childId)
})

if (fromParentId)
{
await axios.delete (
`${ API_BASE_URL }/tags/${ fromParentId }/children/${ childId }`,
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })
}

await axios.post (
`${ API_BASE_URL }/tags/${ parentId }/children/${ childId }`,
{ },
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })

break

case 'slot':
const cat: Category | undefined = e.over?.data.current?.cat
const index: number | undefined = e.over?.data.current?.index
if (!(cat) || index == null)

if (fromParentId == null
|| !(cat)
|| cat !== findTag (tags, childId)?.category)
return

try
{
await axios.delete (
`${ API_BASE_URL }/tags/${ fromParentId }/children/${ childId }`,
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })
}
catch
{
toast ({ description: '管理者権限が必要です.' })

return
}

setTags (prev => {
const child = findTag (prev, childId)
@@ -245,18 +309,16 @@ export default (({ post }: Props) => {

const next: TagByCategory = { ...prev }

if (fromParentId)
if (fromParentId != null)
next[cat] = detachEdge (next[cat], fromParentId, childId) as any

next[cat] = insertRootAt (next[cat], index, child)
next[cat] = insertRootAt (next[cat], 0, child)

next[cat].sort ((a: Tag, b: Tag) => a.name < b.name ? -1 : 1)

return next
})

await axios.delete (
`${ API_BASE_URL }/tags/${ fromParentId }/children/${ childId }`,
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })

break
}
}
@@ -322,7 +384,7 @@ export default (({ post }: Props) => {
<>
{renderTagTree (tag, 0, `cat-${ cat }`, suppressClickRef, undefined)}
</>))}
<DropSlot cat={cat} index={0}/>
<DropSlot cat={cat}/>
</AnimatePresence>
</motion.ul>
</motion.div>))}


Loading…
Cancel
Save