コミットを比較
31 コミット
| 作成者 | SHA1 | 日付 | |
|---|---|---|---|
| 3898fbc156 | |||
| 6d8cab9822 | |||
| f2651a987a | |||
| 4478bc92ed | |||
| 5f5299ea39 | |||
| 2d9a79fecc | |||
| e74f16782b | |||
| 8ac56f74ce | |||
| 59a87c0e11 | |||
| f66532a55d | |||
| c3d2d8e1d1 | |||
| f4dea9b91e | |||
| f00e97e335 | |||
| a0c1b8ba31 | |||
| 021d13a262 | |||
| 1b6cac43c2 | |||
| b0cce4b7ee | |||
| 40cc3b2c0c | |||
| 61ab4744bd | |||
| ca92c62049 | |||
| 4e3fb6bef7 | |||
| 3a9ac8af32 | |||
| 7bc9ee1e68 | |||
| 95b4db1fc2 | |||
| 4117bdd31e | |||
| fecbb4b354 | |||
| d0ea329887 | |||
| 3b29c3e10e | |||
| 518c5fa0f2 | |||
| 4cc2dc5441 | |||
| 6a57dc53bf |
@@ -57,8 +57,7 @@ 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,
|
||||||
@@ -78,16 +77,12 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
|
|||||||
return
|
return
|
||||||
const dx = e.clientX - p.x
|
const dx = e.clientX - p.x
|
||||||
const dy = e.clientY - p.y
|
const dy = e.clientY - p.y
|
||||||
|
|
||||||
if (dx * dx + dy * dy >= 9)
|
if (dx * dx + dy * dy >= 9)
|
||||||
armEatNextClick ()
|
armEatNextClick ()
|
||||||
}}
|
}}
|
||||||
onPointerUpCapture={() => {
|
onPointerUpCapture={() => {
|
||||||
downPosRef.current = null
|
downPosRef.current = null
|
||||||
}}
|
}}
|
||||||
onPointerCancelCapture={() => {
|
|
||||||
downPosRef.current = null
|
|
||||||
}}
|
|
||||||
onClickCapture={e => {
|
onClickCapture={e => {
|
||||||
if (suppressClickRef.current)
|
if (suppressClickRef.current)
|
||||||
{
|
{
|
||||||
@@ -95,20 +90,18 @@ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTa
|
|||||||
e.stopPropagation ()
|
e.stopPropagation ()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
ref={setDropRef}
|
ref={node => {
|
||||||
|
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
|
||||||
ref={setDragRef}
|
className="flex min-w-0 max-w-full items-baseline overflow-hidden"
|
||||||
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
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { DndContext,
|
import { DndContext,
|
||||||
DragOverlay,
|
DragOverlay,
|
||||||
MeasuringStrategy,
|
|
||||||
MouseSensor,
|
MouseSensor,
|
||||||
TouchSensor,
|
TouchSensor,
|
||||||
pointerWithin,
|
|
||||||
useDroppable,
|
useDroppable,
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors } from '@dnd-kit/core'
|
useSensors } from '@dnd-kit/core'
|
||||||
@@ -27,20 +25,13 @@ import { postsKeys, tagsKeys } from '@/lib/queryKeys'
|
|||||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||||
import { dateString, originalCreatedAtString } from '@/lib/utils'
|
import { dateString, originalCreatedAtString } from '@/lib/utils'
|
||||||
|
|
||||||
import type { CollisionDetection, DragEndEvent } from '@dnd-kit/core'
|
import type { DragEndEvent } from '@dnd-kit/core'
|
||||||
import type { FC, MutableRefObject, ReactNode } from 'react'
|
import type { FC, MutableRefObject, ReactNode } from 'react'
|
||||||
|
|
||||||
import type { Category, Post, TagWithSections } from '@/types'
|
import type { Category, Post, TagWithSections } from '@/types'
|
||||||
|
|
||||||
type TagByCategory = { [key in Category]: TagWithSections[] }
|
type TagByCategory = { [key in Category]: TagWithSections[] }
|
||||||
|
|
||||||
const tagCollisionDetection: CollisionDetection = args => {
|
|
||||||
return pointerWithin (args)
|
|
||||||
}
|
|
||||||
|
|
||||||
const alwaysMeasureDroppables = {
|
|
||||||
droppable: { strategy: MeasuringStrategy.Always } } as const
|
|
||||||
|
|
||||||
|
|
||||||
const renderTagTree = (
|
const renderTagTree = (
|
||||||
tag: TagWithSections,
|
tag: TagWithSections,
|
||||||
@@ -186,34 +177,12 @@ const DropSlot = ({ cat }: { cat: Category }) => {
|
|||||||
</li>)
|
</li>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmptyCategoryDropSection = (
|
|
||||||
{ cat, children }: { cat: Category
|
|
||||||
children: ReactNode }) => {
|
|
||||||
const { setNodeRef, isOver: over } = useDroppable ({
|
|
||||||
id: `slot:${ cat }`,
|
|
||||||
data: { kind: 'slot', cat } })
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={setNodeRef} className="my-3">
|
|
||||||
{children}
|
|
||||||
<ul>
|
|
||||||
<li className="h-1">
|
|
||||||
{over && <div className="h-0.5 w-full rounded bg-sky-400"/>}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
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)
|
||||||
@@ -243,7 +212,7 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
|
|||||||
return tagsTmp
|
return tagsTmp
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
const [activeTagDrag, setActiveTagDrag] = useState<ActiveTagDrag | null> (null)
|
const [activeTagId, setActiveTagId] = useState<number | 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)
|
||||||
@@ -359,18 +328,9 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
|
|||||||
<TagSearch/>
|
<TagSearch/>
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={sp ? tagCollisionDetection : undefined}
|
|
||||||
measuring={sp ? alwaysMeasureDroppables : undefined}
|
|
||||||
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'
|
||||||
@@ -381,13 +341,13 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
|
|||||||
suppressClickRef.current = false}, { capture: true, once: true })
|
suppressClickRef.current = false}, { capture: true, once: true })
|
||||||
}}
|
}}
|
||||||
onDragCancel={() => {
|
onDragCancel={() => {
|
||||||
setActiveTagDrag (null)
|
setActiveTagId (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 => {
|
||||||
setActiveTagDrag (null)
|
setActiveTagId (null)
|
||||||
setDragging (false)
|
setDragging (false)
|
||||||
await onDragEnd (e)
|
await onDragEnd (e)
|
||||||
document.body.style.userSelect = ''
|
document.body.style.userSelect = ''
|
||||||
@@ -402,26 +362,18 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
|
|||||||
if (!(categoryTags.length > 0 || dragging))
|
if (!(categoryTags.length > 0 || dragging))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
const sectionTitle = (
|
|
||||||
<SubsectionTitle>
|
|
||||||
<motion.div
|
|
||||||
layoutId={animationMode === 'off'
|
|
||||||
? undefined
|
|
||||||
: `tag-${ sp ? 'sp-' : '' }${ cat }`}
|
|
||||||
transition={{ layout: layoutTransition }}>
|
|
||||||
{CATEGORY_NAMES[cat]}
|
|
||||||
</motion.div>
|
|
||||||
</SubsectionTitle>)
|
|
||||||
|
|
||||||
if (!(categoryTags.length > 0))
|
|
||||||
return (
|
|
||||||
<EmptyCategoryDropSection cat={cat} key={cat}>
|
|
||||||
{sectionTitle}
|
|
||||||
</EmptyCategoryDropSection>)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-3" key={cat}>
|
<div className="my-3" key={cat}>
|
||||||
{sectionTitle}
|
<SubsectionTitle>
|
||||||
|
<motion.div
|
||||||
|
layoutId={animationMode === 'off'
|
||||||
|
? undefined
|
||||||
|
: `tag-${ sp ? 'sp-' : '' }${ cat }`}
|
||||||
|
transition={{ layout: layoutTransition }}>
|
||||||
|
{CATEGORY_NAMES[cat]}
|
||||||
|
</motion.div>
|
||||||
|
</SubsectionTitle>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{(tagRelationDisplay === 'grouped'
|
{(tagRelationDisplay === 'grouped'
|
||||||
? (tags[cat] ?? []).flatMap (tag =>
|
? (tags[cat] ?? []).flatMap (tag =>
|
||||||
@@ -491,9 +443,9 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
|
|||||||
|
|
||||||
<DragOverlay adjustScale={false}>
|
<DragOverlay adjustScale={false}>
|
||||||
<div className="pointer-events-none">
|
<div className="pointer-events-none">
|
||||||
{activeTagDrag != null && (() => {
|
{activeTagId != null && (() => {
|
||||||
const tag = findTag (tags, activeTagDrag.tagId)
|
const tag = findTag (tags, activeTagId)
|
||||||
return tag && <TagLink tag={tag} nestLevel={activeTagDrag.nestLevel}/>
|
return tag && <TagLink tag={tag}/>
|
||||||
}) ()}
|
}) ()}
|
||||||
</div>
|
</div>
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする