From 52751ae9d451e986d345e2e0c54bd2430169b80f Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Fri, 3 Apr 2026 18:42:16 +0200 Subject: [PATCH] fix: use onDragEnd on Reorder.Item instead of onPointerUp on Group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach used onPointerUp on the Reorder.Group which fired unreliably — triggering on non-drag clicks and sometimes not at all after a drag. Moving to onDragEnd on each Reorder.Item gives clean, predictable drag-to-reorder behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client/components/CandidateListItem.tsx | 3 +++ src/client/routes/threads/$threadId.tsx | 11 +++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/client/components/CandidateListItem.tsx b/src/client/components/CandidateListItem.tsx index 282153f..d69e479 100644 --- a/src/client/components/CandidateListItem.tsx +++ b/src/client/components/CandidateListItem.tsx @@ -31,6 +31,7 @@ interface CandidateListItemProps { isActive: boolean; onStatusChange: (status: "researching" | "ordered" | "arrived") => void; delta?: CandidateDelta; + onDragEnd?: () => void; } const RANK_COLORS = ["#D4AF37", "#C0C0C0", "#CD7F32"]; // gold, silver, bronze @@ -53,6 +54,7 @@ export function CandidateListItem({ isActive, onStatusChange, delta, + onDragEnd, }: CandidateListItemProps) { const controls = useDragControls(); const { weight, price } = useFormatters(); @@ -217,6 +219,7 @@ export function CandidateListItem({ value={candidate} dragControls={controls} dragListener={false} + onDragEnd={onDragEnd} className={sharedClassName} > {innerContent} diff --git a/src/client/routes/threads/$threadId.tsx b/src/client/routes/threads/$threadId.tsx index 52b5ebf..a9a0799 100644 --- a/src/client/routes/threads/$threadId.tsx +++ b/src/client/routes/threads/$threadId.tsx @@ -36,12 +36,11 @@ function ThreadDetailPage() { thread?.categoryId ?? 0, ); - const [tempItems, setTempItems] = - useState( - null, - ); + const [tempItems, setTempItems] = useState< + NonNullable["candidates"] | null + >(null); - // Clear tempItems when server data changes (biome-ignore: thread?.candidates is intentional dep) + // Clear tempItems when server data changes // biome-ignore lint/correctness/useExhaustiveDependencies: thread?.candidates is the intended trigger useEffect(() => { setTempItems(null); @@ -227,7 +226,6 @@ function ThreadDetailPage() { axis="y" values={displayItems} onReorder={setTempItems} - onPointerUp={handleDragEnd} className="flex flex-col gap-2" > {displayItems.map((candidate, index) => ( @@ -243,6 +241,7 @@ function ThreadDetailPage() { }) } delta={deltas[candidate.id]} + onDragEnd={handleDragEnd} /> ))}