コミットを比較

..

31 コミット

作成者 SHA1 メッセージ 日付
みてるぞ 3898fbc156 #34 2026-07-07 00:38:18 +09:00
みてるぞ 6d8cab9822 #34 2026-07-07 00:07:12 +09:00
みてるぞ f2651a987a #34 2026-07-06 23:37:29 +09:00
みてるぞ 4478bc92ed #34 2026-07-06 23:25:14 +09:00
みてるぞ 5f5299ea39 #34 2026-07-06 23:18:52 +09:00
みてるぞ 2d9a79fecc #34 2026-07-06 23:08:27 +09:00
みてるぞ e74f16782b #34 2026-07-06 22:39:53 +09:00
みてるぞ 8ac56f74ce #34 2026-07-06 22:02:25 +09:00
みてるぞ 59a87c0e11 #34 2026-07-06 21:17:39 +09:00
みてるぞ f66532a55d #34 2026-07-06 21:12:49 +09:00
みてるぞ c3d2d8e1d1 #34 2026-07-06 20:46:16 +09:00
みてるぞ f4dea9b91e #34 2026-07-06 19:34:11 +09:00
みてるぞ f00e97e335 #34 2026-07-05 20:18:36 +09:00
みてるぞ a0c1b8ba31 #34 2026-07-05 20:18:16 +09:00
みてるぞ 021d13a262 #34 2026-07-05 20:00:58 +09:00
みてるぞ 1b6cac43c2 #34 2026-07-05 18:21:39 +09:00
みてるぞ b0cce4b7ee #34 2026-07-05 12:51:31 +09:00
みてるぞ 40cc3b2c0c Merge remote-tracking branch 'origin/main' into feature/034 2026-07-05 11:59:28 +09:00
みてるぞ 61ab4744bd #34 2026-07-05 11:57:29 +09:00
みてるぞ ca92c62049 #34 2026-07-05 11:27:45 +09:00
みてるぞ 4e3fb6bef7 #34 2026-07-05 03:43:37 +09:00
みてるぞ 3a9ac8af32 #34 2026-07-05 03:27:58 +09:00
みてるぞ 7bc9ee1e68 #34 2026-07-05 03:19:58 +09:00
みてるぞ 95b4db1fc2 #34 2026-07-05 02:59:48 +09:00
みてるぞ 4117bdd31e #34 2026-07-05 02:40:19 +09:00
みてるぞ fecbb4b354 #34 2026-07-05 02:04:07 +09:00
みてるぞ d0ea329887 #34 2026-07-05 01:51:07 +09:00
みてるぞ 3b29c3e10e #34 2026-07-05 00:03:13 +09:00
みてるぞ 518c5fa0f2 #34 2026-07-04 21:47:16 +09:00
みてるぞ 4cc2dc5441 #34 2026-07-04 19:50:40 +09:00
みてるぞ 6a57dc53bf #34 2026-07-04 19:03:50 +09:00
2個のファイルの変更43行の追加137行の削除
+22 -51
ファイルの表示
@@ -13,7 +13,6 @@ import type { CSSProperties, FC, MutableRefObject } from 'react'
import type { Tag } from '@/types' import type { Tag } from '@/types'
type Props = { type Props = {
activeDndId?: string
tag: Tag tag: Tag
nestLevel: number nestLevel: number
pathKey: string pathKey: string
@@ -22,15 +21,7 @@ type Props = {
sp?: boolean } sp?: boolean }
const DraggableDroppableTagRow: FC<Props> = ({ const DraggableDroppableTagRow: FC<Props> = ({ tag, nestLevel, pathKey, parentTagId, suppressClickRef, sp }) => {
activeDndId,
tag,
nestLevel,
pathKey,
parentTagId,
suppressClickRef,
sp,
}) => {
const behaviourSettings = useClientBehaviourSettings () const behaviourSettings = useClientBehaviourSettings ()
const animationMode = behaviourSettings.animation ?? 'normal' const animationMode = behaviourSettings.animation ?? 'normal'
const layoutTransition = clientAnimationTransition ( const layoutTransition = clientAnimationTransition (
@@ -62,23 +53,18 @@ const DraggableDroppableTagRow: FC<Props> = ({
const { attributes, const { attributes,
listeners, listeners,
setNodeRef: setDragRef, setNodeRef: setDragRef,
transform } = useDraggable ({ id: dndId, transform,
data: { kind: 'tag', isDragging: dragging } = useDraggable ({ id: dndId,
dndId, 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,
data: { kind: 'tag', tagId: tag.id } }) data: { kind: 'tag', tagId: tag.id } })
const activeDragging = activeDndId === dndId
const style: CSSProperties = { transform: CSS.Translate.toString (transform), const style: CSSProperties = { transform: CSS.Translate.toString (transform),
visibility: activeDragging ? 'hidden' : 'visible' } visibility: dragging ? 'hidden' : 'visible' }
const innerClassName = cn (
'inline-flex min-w-0 max-w-full items-baseline overflow-hidden',
sp && 'touch-pan-y')
return ( return (
<div <div
@@ -91,16 +77,12 @@ const DraggableDroppableTagRow: FC<Props> = ({
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)
{ {
@@ -108,35 +90,24 @@ const DraggableDroppableTagRow: FC<Props> = ({
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}
{activeDragging {...listeners}>
? ( <motion.div
<div className="flex min-w-0 max-w-full items-baseline overflow-hidden"
ref={setDragRef} transition={{ layout: layoutTransition }}
style={style} layoutId={animationMode === 'off'
className={innerClassName} ? undefined
{...attributes} : `tag-${ sp ? 'sp-' : '' }${ tag.id }`}>
{...listeners}> <TagLink tag={tag} nestLevel={nestLevel}/>
<TagLink tag={tag} nestLevel={nestLevel}/> </motion.div>
</div>)
: (
<motion.div
ref={setDragRef}
style={style}
className={innerClassName}
{...attributes}
{...listeners}
transition={{ layout: layoutTransition }}
layoutId={animationMode === 'off'
? undefined
: `tag-${ sp ? 'sp-' : '' }${ tag.id }`}>
<TagLink tag={tag} nestLevel={nestLevel}/>
</motion.div>)}
</div>) </div>)
} }
+21 -86
ファイルの表示
@@ -1,13 +1,11 @@
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'
import { restrictToWindowEdges, snapCenterToCursor } from '@dnd-kit/modifiers' import { restrictToWindowEdges } from '@dnd-kit/modifiers'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
@@ -27,23 +25,15 @@ 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 = (
activeDndId: string | undefined,
tag: TagWithSections, tag: TagWithSections,
nestLevel: number, nestLevel: number,
path: string, path: string,
@@ -55,7 +45,6 @@ const renderTagTree = (
const self = ( const self = (
<li key={key} className="mb-1"> <li key={key} className="mb-1">
<DraggableDroppableTagRow <DraggableDroppableTagRow
activeDndId={activeDndId}
tag={tag} tag={tag}
nestLevel={nestLevel} nestLevel={nestLevel}
pathKey={key} pathKey={key}
@@ -69,14 +58,7 @@ const renderTagTree = (
...((tag.children ...((tag.children
?.sort ((a, b) => a.name < b.name ? -1 : 1) ?.sort ((a, b) => a.name < b.name ? -1 : 1)
.flatMap (child => .flatMap (child =>
renderTagTree ( renderTagTree (child, nestLevel + 1, key, suppressClickRef, tag.id, sp)))
activeDndId,
child,
nestLevel + 1,
key,
suppressClickRef,
tag.id,
sp)))
?? [])] ?? [])]
} }
@@ -195,35 +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 = {
dndId: string
tagId: number
nestLevel: number }
const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => { const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
sp = Boolean (sp) sp = Boolean (sp)
@@ -253,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)
@@ -261,7 +220,6 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
() => buildFlatTagByCategory (tags), () => buildFlatTagByCategory (tags),
[tags], [tags],
) )
const activeDndId = activeTagDrag?.dndId
const suppressClickRef = useRef (false) const suppressClickRef = useRef (false)
@@ -370,19 +328,9 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
<TagSearch/> <TagSearch/>
<DndContext <DndContext
sensors={sensors} sensors={sensors}
collisionDetection={tagCollisionDetection}
measuring={alwaysMeasureDroppables}
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
const dndId = e.active.data.current?.dndId
setActiveTagDrag (
tagId == null || nestLevel == null || dndId == null
? null
: { dndId, tagId, nestLevel })
}
setDragging (true) setDragging (true)
suppressClickRef.current = true suppressClickRef.current = true
document.body.style.userSelect = 'none' document.body.style.userSelect = 'none'
@@ -393,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 = ''
@@ -414,31 +362,22 @@ 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 =>
renderTagTree ( renderTagTree (
activeDndId,
tag, tag,
0, 0,
`cat-${ cat }`, `cat-${ cat }`,
@@ -450,7 +389,6 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
: (flatTagsByCategory[cat] ?? []).map (tag => ( : (flatTagsByCategory[cat] ?? []).map (tag => (
<li key={`flat-${ cat }-${ tag.id }`} className="mb-1"> <li key={`flat-${ cat }-${ tag.id }`} className="mb-1">
<DraggableDroppableTagRow <DraggableDroppableTagRow
activeDndId={activeDndId}
tag={tag} tag={tag}
nestLevel={0} nestLevel={0}
pathKey={`flat-${ cat }-${ tag.id }`} pathKey={`flat-${ cat }-${ tag.id }`}
@@ -503,14 +441,11 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
</ul> </ul>
</motion.div>)} </motion.div>)}
<DragOverlay <DragOverlay adjustScale={false}>
adjustScale={false}
modifiers={[snapCenterToCursor]}
dropAnimation={animationMode === 'off' ? null : undefined}>
<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>