Browse Source

#184

pull/186/head
みてるぞ 3 weeks ago
parent
commit
7ba814b5f9
1 changed files with 92 additions and 15 deletions
  1. +92
    -15
      frontend/src/components/TagDetailSidebar.tsx

+ 92
- 15
frontend/src/components/TagDetailSidebar.tsx View File

@@ -1,4 +1,8 @@
import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
import { DndContext,
PointerSensor,
useDroppable,
useSensor,
useSensors } from '@dnd-kit/core'
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useRef, useState } from 'react'

@@ -168,6 +172,44 @@ const findTag = (
}


const detachEdge = (
nodes: Tag[],
parentId: number,
childId: number,
): Tag[] => nodes.map (t => {
if (t.id === parentId)
{
const children = (t.children ?? []).filter (c => c.id !== childId)
return { ...t, children }
}
return t.children ? { ...t, children: detachEdge (t.children, parentId, childId) } : t
})


const insertRootAt = (
roots: Tag[],
index: number,
tag: Tag,
): Tag[] => {
const without = roots.filter (t => t.id !== tag.id)
const next = without.slice ()
next.splice (Math.min (Math.max (index, 0), next.length), 0, tag)
return next
}


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

return (
<li ref={setNodeRef} className="h-1">
{over && <div className="h-0.5 w-full rounded bg-sky-400"/>}
</li>)
}


type Props = { post: Post | null }


@@ -180,26 +222,58 @@ export default (({ post }: Props) => {
useSensor (PointerSensor, { activationConstraint: { distance: 6 } }))

const onDragEnd = async (e: DragEndEvent) => {
const activeKind = e.active.data.current?.kind
if (activeKind !== 'tag')
return

const childId: number | undefined = e.active.data.current?.tagId
const parentId: number | undefined = e.over?.data.current?.tagId
const fromParentId: number | undefined = e.active.data.current?.parentTagId

if (!(childId) || !(parentId))
return
const overKind = e.over?.data.current?.kind

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

setTags (prev => {
const child = findTag (prev, childId)
const parent = findTag (prev, parentId)
if (!(child) || !(parent))
return prev
if (overKind === 'tag')
{
const parentId: number | undefined = e.over?.data.current?.tagId
if (!(parentId) || childId === parentId)
return

setTags (prev => {
const child = findTag (prev, childId)
const parent = findTag (prev, parentId)
if (!(child) || !(parent) || isDescendant (child, parentId))
return prev

return attachChildOptimistic (prev, parentId, childId)
})

if (isDescendant (child, parentId))
return prev
return
}

return attachChildOptimistic (prev, parentId, childId)
})
if (overKind === 'slot')
{
const cat: Category | undefined = e.over?.data.current?.cat
const index: number | undefined = e.over?.data.current?.index
if (!(cat) || index == null)
return

setTags (prev => {
const child = findTag (prev, childId)
if (!(child))
return prev

const next: TagByCategory = { ...prev }

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

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

return next
})
}
}

const categoryNames: Record<Category, string> = {
@@ -260,7 +334,10 @@ export default (({ post }: Props) => {
<motion.ul layout>
<AnimatePresence initial={false}>
{tags[cat].map (tag => (
renderTagTree (tag, 0, `cat-${ cat }`, suppressClickRef, undefined)))}
<>
{renderTagTree (tag, 0, `cat-${ cat }`, suppressClickRef, undefined)}
</>))}
<DropSlot cat={cat} index={0}/>
</AnimatePresence>
</motion.ul>
</motion.div>))}


Loading…
Cancel
Save