From 476c1ffc181baf657a54860daf5081321106c3f8 Mon Sep 17 00:00:00 2001 From: Ingo Wagner Date: Wed, 23 Sep 2026 12:00:09 +0000 Subject: [PATCH] feat(web): drag-to-reorder projects in sidebar - Add @dnd-kit/core@6.3.1 + sortable@8.0.0 for mouse/touch drag reorder - SortableGroup.tsx: sortable wrapper with GripVertical handle icon - project-sidebar-order.ts: localStorage persistence (rakazo:project-sidebar-order) - Shell.tsx: DndContext + SortableContext around project sidebar - Folder icon replaces Lock icon for project entries --- apps/web/src/components/SortableGroup.tsx | 21 ++------ apps/web/src/lib/project-sidebar-order.ts | 13 ++--- apps/web/src/pages/Shell.tsx | 59 ++++++++++------------- package.json | 2 +- pnpm-lock.yaml | 12 ++--- screenshot.cjs | 21 ++++++++ screenshot2.cjs | 21 ++++++++ 7 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 screenshot.cjs create mode 100644 screenshot2.cjs diff --git a/apps/web/src/components/SortableGroup.tsx b/apps/web/src/components/SortableGroup.tsx index 4a8b270..7bb9d44 100644 --- a/apps/web/src/components/SortableGroup.tsx +++ b/apps/web/src/components/SortableGroup.tsx @@ -1,10 +1,8 @@ /** * Sortable wrapper for a project (space) sidebar group card. - * - * Wraps the card in @dnd-kit's useSortable so it can be dragged to reorder. - * Renders a grip handle icon on the title row. + * Renders a grip handle icon on the title row for drag-to-reorder. */ -import { CSS as DndCSS } from "@dnd-kit/utilities"; +import { CSS } from "@dnd-kit/utilities"; import { useSortable } from "@dnd-kit/sortable"; import { GripVertical } from "lucide-react"; import { memo, useMemo } from "react"; @@ -16,18 +14,11 @@ interface SortableGroupProps { } export const SortableGroup = memo(function SortableGroup({ group, children }: SortableGroupProps) { - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - isDragging, - } = useSortable({ id: group.spaceId }); + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: group.spaceId }); const style: React.CSSProperties = useMemo( () => ({ - transform: DndCSS.Translate.toString(transform), + transform: CSS.Translate.toString(transform), transition, opacity: isDragging ? 0.5 : undefined, zIndex: isDragging ? 1000 : undefined, @@ -38,7 +29,6 @@ export const SortableGroup = memo(function SortableGroup({ group, children }: So return (
{children} - {/* Invisible drag handle — left edge of the title row */} {group.title ? (