fix(gui): fix double-row artifact and scroll reset on dnd drop

Two regressions introduced by the listId/UUID change:

- Scroll reset: crypto.randomUUID() in useMemo regenerated all listIds
  on every re-fetch, causing React to unmount/remount all rows and lose
  scroll position. Fixed by using String(r.index) instead — index is
  stable across re-fetches for items that haven't moved, so React can
  reconcile rows in place.

- Double-row artifact: on drop, isDragging becomes false before the
  DragOverlay drop animation completes, making the real row visible at
  the destination while the overlay is still animating there. Fixed by
  using defaultDropAnimationSideEffects to force opacity:0 on the
  original element for the duration of the drop animation.
This commit is contained in:
nekochanfood 2026-05-30 12:12:24 +09:00
commit 21a0e19722

View file

@ -9,6 +9,7 @@ import {
DragOverlay,
type DragStartEvent,
defaultDropAnimation,
defaultDropAnimationSideEffects,
type Modifier,
PointerSensor,
useSensor,
@ -86,6 +87,15 @@ const restrictToVerticalAxis: Modifier = ({ transform }) => ({
const DRAG_OVERLAY_MODIFIERS = [restrictToVerticalAxis];
const customDropAnimation: typeof defaultDropAnimation = {
...defaultDropAnimation,
sideEffects: defaultDropAnimationSideEffects({
styles: {
active: { opacity: "0" },
},
}),
};
const TABLE_HEAD = [
"", // checkbox
"general:name",
@ -201,7 +211,7 @@ function PageBody() {
const userRepos = result.data?.user_repositories;
const augmentedUserRepos = useMemo<UserRepoWithListId[]>(
() => (userRepos ?? []).map((r) => ({ ...r, listId: crypto.randomUUID() })),
() => (userRepos ?? []).map((r) => ({ ...r, listId: String(r.index) })),
[userRepos],
);
@ -415,7 +425,7 @@ function PageBody() {
</VStack>
<DragOverlay
modifiers={DRAG_OVERLAY_MODIFIERS}
dropAnimation={guiAnimation ? defaultDropAnimation : null}
dropAnimation={guiAnimation ? customDropAnimation : null}
>
{activeId ? (
<RepositoryDragOverlay