From 33f9c6602f00f4265870b6e847cc9e789e77bc5c Mon Sep 17 00:00:00 2001 From: miteruzo Date: Tue, 7 Jul 2026 22:34:58 +0900 Subject: [PATCH] #268 --- .../components/DraggableDroppableTagRow.tsx | 10 +- frontend/src/components/TagDetailSidebar.tsx | 152 +++++++----------- 2 files changed, 67 insertions(+), 95 deletions(-) diff --git a/frontend/src/components/DraggableDroppableTagRow.tsx b/frontend/src/components/DraggableDroppableTagRow.tsx index f1ecf20..2f1451a 100644 --- a/frontend/src/components/DraggableDroppableTagRow.tsx +++ b/frontend/src/components/DraggableDroppableTagRow.tsx @@ -58,15 +58,14 @@ const DraggableDroppableTagRow: FC = ({ tag, nestLevel, pathKey, parentTa data: { kind: 'tag', tagId: tag.id, parentTagId, - nestLevel, - dndId } }) + nestLevel } }) const { setNodeRef: setDropRef, isOver: over } = useDroppable ({ id: dndId, data: { kind: 'tag', tagId: tag.id } }) const style: CSSProperties = { transform: CSS.Translate.toString (transform), - visibility: dragging ? 'hidden' : 'visible' } + visibility: dragging && !(sp) ? 'hidden' : 'visible' } return (
= ({ tag, nestLevel, pathKey, parentTa return const dx = e.clientX - p.x const dy = e.clientY - p.y + if (dx * dx + dy * dy >= 9) armEatNextClick () }} onPointerUpCapture={() => { downPosRef.current = null }} + onPointerCancelCapture={() => { + downPosRef.current = null + }} onClickCapture={e => { if (suppressClickRef.current) { @@ -100,7 +103,6 @@ const DraggableDroppableTagRow: FC = ({ tag, nestLevel, pathKey, parentTa > { + const byPointer = pointerWithin (args) + return byPointer.length > 0 ? byPointer : rectIntersection (args) +} + +const alwaysMeasureDroppables = { + droppable: { strategy: MeasuringStrategy.Always } } as const + const renderTagTree = ( tag: TagWithSections, @@ -180,6 +188,24 @@ const DropSlot = ({ cat }: { cat: Category }) => { ) } +const EmptyCategoryDropSection = ( + { cat, children }: { cat: Category + children: ReactNode }) => { + const { setNodeRef, isOver: over } = useDroppable ({ + id: `slot:${ cat }`, + data: { kind: 'slot', cat } }) + + return ( +
+ {children} +
    +
  • + {over &&
    } +
  • +
+
) +} + type Props = { className?: string @@ -188,12 +214,7 @@ type Props = { type ActiveTagDrag = { tagId: number - nestLevel: number - dndId: string } - -type DragOverlayOffset = { - x: number - y: number } + nestLevel: number } const TagDetailSidebar: FC = ({ className, post, sp }) => { @@ -228,59 +249,16 @@ const TagDetailSidebar: FC = ({ className, post, sp }) => { const [dragging, setDragging] = useState (false) const [saving, setSaving] = useState (false) const [tags, setTags] = useState (baseTags) - const [dragOverlayOffset, setDragOverlayOffset] = - useState ({ x: 0, y: 0 }) const flatTagsByCategory = useMemo ( () => buildFlatTagByCategory (tags), [tags], ) const suppressClickRef = useRef (false) - const dragStartRectRef = useRef (null) - const offsetFrameRef = useRef (null) const sensors = useSensors ( useSensor (MouseSensor, { activationConstraint: { distance: 6 } }), useSensor (TouchSensor, { activationConstraint: { delay: 250, tolerance: 8 } })) - const dragOverlayModifiers = useMemo ( - () => ( - sp - ? [({ transform }) => ({ - ...transform, - x: transform.x + dragOverlayOffset.x, - y: transform.y + dragOverlayOffset.y })] - : []), - [dragOverlayOffset.x, dragOverlayOffset.y, sp], - ) - - useLayoutEffect (() => { - if (!(sp && dragging && activeTagDrag && dragStartRectRef.current)) - { - setDragOverlayOffset ({ x: 0, y: 0 }) - return - } - - offsetFrameRef.current = requestAnimationFrame (() => { - const node = document.querySelector ( - `[data-tag-dnd-id="${ activeTagDrag.dndId }"]`, - ) - - if (!(node && dragStartRectRef.current)) - return - - const currentRect = node.getBoundingClientRect () - const initialRect = dragStartRectRef.current - - setDragOverlayOffset ({ - x: currentRect.left - initialRect.left, - y: currentRect.top - initialRect.top }) - }) - - return () => { - if (offsetFrameRef.current != null) - cancelAnimationFrame (offsetFrameRef.current) - } - }, [activeTagDrag, dragging, sp]) const reloadTags = async (): Promise => { setTags (buildTagByCategory (await apiGet (`/posts/${ post.id }`))) @@ -383,28 +361,17 @@ const TagDetailSidebar: FC = ({ className, post, sp }) => { { - const byPointer = pointerWithin (args) - return byPointer.length > 0 ? byPointer : rectIntersection (args) - }} + collisionDetection={sp ? tagCollisionDetection : undefined} + measuring={sp ? alwaysMeasureDroppables : undefined} onDragStart={e => { if (e.active.data.current?.kind === 'tag') { const tagId = e.active.data.current?.tagId const nestLevel = e.active.data.current?.nestLevel - const dndId = e.active.data.current?.dndId - dragStartRectRef.current = - e.active.rect.current.initial - ? new DOMRect ( - e.active.rect.current.initial.left, - e.active.rect.current.initial.top, - e.active.rect.current.initial.width, - e.active.rect.current.initial.height) - : null setActiveTagDrag ( - tagId == null || nestLevel == null || !(dndId) + tagId == null || nestLevel == null ? null - : { tagId, nestLevel, dndId }) + : { tagId, nestLevel }) } setDragging (true) suppressClickRef.current = true @@ -418,16 +385,12 @@ const TagDetailSidebar: FC = ({ className, post, sp }) => { onDragCancel={() => { setActiveTagDrag (null) setDragging (false) - setDragOverlayOffset ({ x: 0, y: 0 }) - dragStartRectRef.current = null document.body.style.userSelect = '' suppressClickRef.current = false }} onDragEnd={async e => { setActiveTagDrag (null) setDragging (false) - setDragOverlayOffset ({ x: 0, y: 0 }) - dragStartRectRef.current = null await onDragEnd (e) document.body.style.userSelect = '' }} @@ -441,18 +404,26 @@ const TagDetailSidebar: FC = ({ className, post, sp }) => { if (!(categoryTags.length > 0 || dragging)) return null + const sectionTitle = ( + + + {CATEGORY_NAMES[cat]} + + ) + + if (!(categoryTags.length > 0)) + return ( + + {sectionTitle} + ) + return (
- - - {CATEGORY_NAMES[cat]} - - - + {sectionTitle}
    {(tagRelationDisplay === 'grouped' ? (tags[cat] ?? []).flatMap (tag => @@ -520,16 +491,15 @@ const TagDetailSidebar: FC = ({ className, post, sp }) => {
)} - -
- {activeTagDrag != null && (() => { - const tag = findTag (tags, activeTagDrag.tagId) - return ( - tag - && ) - }) ()} -
-
+ {!(sp) && ( + +
+ {activeTagDrag != null && (() => { + const tag = findTag (tags, activeTagDrag.tagId) + return tag && + }) ()} +
+
)} ) }