From d1bf3c4693c415a223984d06daf60364d970c99b Mon Sep 17 00:00:00 2001 From: KubrickCode Date: Sat, 20 Sep 2025 07:19:57 +0000 Subject: [PATCH] Fix input focus loss in GroupCommandList #19 Replace useSortableList hook with direct DndContext implementation to prevent DOM recreation during renders. --- .../src/components/group-command-list.tsx | 90 +++++++++++++++---- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/src/web-view/src/components/group-command-list.tsx b/src/web-view/src/components/group-command-list.tsx index 71ba0493..1dc448af 100644 --- a/src/web-view/src/components/group-command-list.tsx +++ b/src/web-view/src/components/group-command-list.tsx @@ -1,9 +1,23 @@ import { Plus, FolderPlus } from "lucide-react"; +import { useCallback, useMemo } from "react"; +import { + DndContext, + closestCenter, + KeyboardSensor, + PointerSensor, + useSensor, + type DragEndEvent, +} from "@dnd-kit/core"; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, +} from "@dnd-kit/sortable"; import { type ButtonConfig } from "../types"; import { Button } from "~/core"; import { GroupCommandItem } from "./group-command-item"; import { useCommandOperations } from "../hooks/use-command-operations"; -import { useSortableList } from "../hooks/use-sortable-list"; const MAX_NESTING_DEPTH = 2; // 0-indexed, so 3 levels total (0,1,2) @@ -31,11 +45,40 @@ export const GroupCommandList = ({ addGroup, } = useCommandOperations(commands, onChange); - const { SortableWrapper } = useSortableList({ - items: commands, - onReorder: onChange, + const pointerSensor = useSensor(PointerSensor, { + activationConstraint: { + distance: 8, + }, }); + const keyboardSensor = useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }); + + const sensors = useMemo(() => [pointerSensor, keyboardSensor], [pointerSensor, keyboardSensor]); + + const sortableItemIds = useMemo(() => + commands.map((_, index) => `${index}`), + [commands] + ); + + const handleDragEnd = useCallback( + (event: DragEndEvent) => { + const { active, over } = event; + + if (over && active.id !== over.id) { + const oldIndex = Number(active.id); + const newIndex = Number(over.id); + + if (!isNaN(oldIndex) && !isNaN(newIndex)) { + const newItems = arrayMove(commands, oldIndex, newIndex); + onChange(newItems); + } + } + }, + [commands, onChange] + ); + return (
{title && ( @@ -44,21 +87,30 @@ export const GroupCommandList = ({
)} - -
- {commands.map((command, index) => ( - onEditGroup(index) : undefined} - /> - ))} -
-
+ + +
+ {commands.map((command, index) => ( + onEditGroup(index) : undefined} + /> + ))} +
+
+