このコミットが含まれているのは:
2026-07-07 21:01:57 +09:00
コミット 2d6af0aa5b
2個のファイルの変更70行の追加6行の削除
+3 -1
ファイルの表示
@@ -58,7 +58,8 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
data: { kind: 'tag',
tagId: tag.id,
parentTagId,
nestLevel } })
nestLevel,
dndId } })
const { setNodeRef: setDropRef, isOver: over } = useDroppable ({
id: dndId,
@@ -99,6 +100,7 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
>
<motion.div
ref={setDragRef}
data-tag-dnd-id={dndId}
style={style}
className={cn (
'inline-flex min-w-0 max-w-full items-baseline overflow-hidden',
+67 -5
ファイルの表示
@@ -2,6 +2,7 @@ import { DndContext,
DragOverlay,
MouseSensor,
TouchSensor,
type Modifier,
pointerWithin,
rectIntersection,
useDroppable,
@@ -10,7 +11,7 @@ import { DndContext,
import { restrictToWindowEdges } from '@dnd-kit/modifiers'
import { useQueryClient } from '@tanstack/react-query'
import { motion } from 'framer-motion'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import DraggableDroppableTagRow from '@/components/DraggableDroppableTagRow'
import PrefetchLink from '@/components/PrefetchLink'
@@ -187,7 +188,12 @@ type Props = {
type ActiveTagDrag = {
tagId: number
nestLevel: number }
nestLevel: number
dndId: string }
type DragOverlayOffset = {
x: number
y: number }
const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
@@ -222,16 +228,59 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
const [dragging, setDragging] = useState (false)
const [saving, setSaving] = useState (false)
const [tags, setTags] = useState (baseTags)
const [dragOverlayOffset, setDragOverlayOffset] =
useState<DragOverlayOffset> ({ x: 0, y: 0 })
const flatTagsByCategory = useMemo<TagByCategory> (
() => buildFlatTagByCategory (tags),
[tags],
)
const suppressClickRef = useRef (false)
const dragStartRectRef = useRef<DOMRect | null> (null)
const offsetFrameRef = useRef<number | null> (null)
const sensors = useSensors (
useSensor (MouseSensor, { activationConstraint: { distance: 6 } }),
useSensor (TouchSensor, { activationConstraint: { delay: 250, tolerance: 8 } }))
const dragOverlayModifiers = useMemo<Modifier[]> (
() => (
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<HTMLElement> (
`[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<void> => {
setTags (buildTagByCategory (await apiGet<Post> (`/posts/${ post.id }`)))
@@ -343,10 +392,19 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
{
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
tagId == null || nestLevel == null || !(dndId)
? null
: { tagId, nestLevel })
: { tagId, nestLevel, dndId })
}
setDragging (true)
suppressClickRef.current = true
@@ -360,12 +418,16 @@ const TagDetailSidebar: FC<Props> = ({ 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 = ''
}}
@@ -458,7 +520,7 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
</ul>
</motion.div>)}
<DragOverlay adjustScale={false}>
<DragOverlay adjustScale={false} modifiers={dragOverlayModifiers}>
<div className="pointer-events-none">
{activeTagDrag != null && (() => {
const tag = findTag (tags, activeTagDrag.tagId)