タグ D&D 時の表示位置修正 (#268) #400

マージ済み
みてるぞ が 8 個のコミットを feature/268 から main へマージ 2026-07-08 00:01:08 +09:00
2個のファイルの変更38行の追加16行の削除
コミット c0879ac117 の変更だけを表示してゐます - すべてのコミットを表示
+12 -9
ファイルの表示
@@ -57,7 +57,8 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
isDragging: dragging } = useDraggable ({ id: dndId, isDragging: dragging } = useDraggable ({ id: dndId,
data: { kind: 'tag', data: { kind: 'tag',
tagId: tag.id, tagId: tag.id,
parentTagId } }) parentTagId,
nestLevel } })
const { setNodeRef: setDropRef, isOver: over } = useDroppable ({ const { setNodeRef: setDropRef, isOver: over } = useDroppable ({
id: dndId, id: dndId,
@@ -90,18 +91,20 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
e.stopPropagation () e.stopPropagation ()
} }
}} }}
ref={node => { ref={setDropRef}
setDragRef (node)
setDropRef (node)
}}
style={style}
className={cn ( className={cn (
'min-w-0 max-w-full overflow-hidden rounded select-none', 'min-w-0 max-w-full overflow-hidden rounded select-none',
sp && 'touch-pan-y',
over && 'ring-2 ring-offset-2')} over && 'ring-2 ring-offset-2')}
{...attributes} >
{...listeners}>
<motion.div <motion.div
className="flex min-w-0 max-w-full items-baseline overflow-hidden" ref={setDragRef}
style={style}
className={cn (
'inline-flex min-w-0 max-w-full items-baseline overflow-hidden',
sp && 'touch-pan-y')}
{...attributes}
{...listeners}
transition={{ layout: layoutTransition }} transition={{ layout: layoutTransition }}
layoutId={animationMode === 'off' layoutId={animationMode === 'off'
? undefined ? undefined
+26 -7
ファイルの表示
@@ -2,6 +2,8 @@ import { DndContext,
DragOverlay, DragOverlay,
MouseSensor, MouseSensor,
TouchSensor, TouchSensor,
pointerWithin,
rectIntersection,
useDroppable, useDroppable,
useSensor, useSensor,
useSensors } from '@dnd-kit/core' useSensors } from '@dnd-kit/core'
@@ -183,6 +185,10 @@ type Props = {
post: Post post: Post
sp?: boolean } sp?: boolean }
type ActiveTagDrag = {
tagId: number
nestLevel: number }
const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => { const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
sp = Boolean (sp) sp = Boolean (sp)
@@ -212,7 +218,7 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
return tagsTmp return tagsTmp
}, [post]) }, [post])
const [activeTagId, setActiveTagId] = useState<number | null> (null) const [activeTagDrag, setActiveTagDrag] = useState<ActiveTagDrag | null> (null)
const [dragging, setDragging] = useState (false) const [dragging, setDragging] = useState (false)
const [saving, setSaving] = useState (false) const [saving, setSaving] = useState (false)
const [tags, setTags] = useState (baseTags) const [tags, setTags] = useState (baseTags)
@@ -328,9 +334,20 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
<TagSearch/> <TagSearch/>
<DndContext <DndContext
sensors={sensors} sensors={sensors}
collisionDetection={args => {
const byPointer = pointerWithin (args)
return byPointer.length > 0 ? byPointer : rectIntersection (args)
}}
onDragStart={e => { onDragStart={e => {
if (e.active.data.current?.kind === 'tag') if (e.active.data.current?.kind === 'tag')
setActiveTagId (e.active.data.current?.tagId ?? null) {
const tagId = e.active.data.current?.tagId
const nestLevel = e.active.data.current?.nestLevel
setActiveTagDrag (
tagId == null || nestLevel == null
? null
: { tagId, nestLevel })
}
setDragging (true) setDragging (true)
suppressClickRef.current = true suppressClickRef.current = true
document.body.style.userSelect = 'none' document.body.style.userSelect = 'none'
@@ -341,13 +358,13 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
suppressClickRef.current = false}, { capture: true, once: true }) suppressClickRef.current = false}, { capture: true, once: true })
}} }}
onDragCancel={() => { onDragCancel={() => {
setActiveTagId (null) setActiveTagDrag (null)
setDragging (false) setDragging (false)
document.body.style.userSelect = '' document.body.style.userSelect = ''
suppressClickRef.current = false suppressClickRef.current = false
}} }}
onDragEnd={async e => { onDragEnd={async e => {
setActiveTagId (null) setActiveTagDrag (null)
setDragging (false) setDragging (false)
await onDragEnd (e) await onDragEnd (e)
document.body.style.userSelect = '' document.body.style.userSelect = ''
@@ -443,9 +460,11 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
<DragOverlay adjustScale={false}> <DragOverlay adjustScale={false}>
<div className="pointer-events-none"> <div className="pointer-events-none">
{activeTagId != null && (() => { {activeTagDrag != null && (() => {
const tag = findTag (tags, activeTagId) const tag = findTag (tags, activeTagDrag.tagId)
return tag && <TagLink tag={tag}/> return (
tag
&& <TagLink tag={tag} nestLevel={activeTagDrag.nestLevel}/>)
}) ()} }) ()}
</div> </div>
</DragOverlay> </DragOverlay>