From 96fe76cfe60725eeb2fe8036a27ffed09269213d Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 13 Mar 2025 13:05:30 -0600 Subject: [PATCH] add a dragging --- .../ui/sidebar/SidebarCollapsible.stories.tsx | 11 +- .../ui/sidebar/SidebarCollapsible.tsx | 130 +++++++++++++++++- 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/web/src/components/ui/sidebar/SidebarCollapsible.stories.tsx b/web/src/components/ui/sidebar/SidebarCollapsible.stories.tsx index 4840e9276..bb5d31271 100644 --- a/web/src/components/ui/sidebar/SidebarCollapsible.stories.tsx +++ b/web/src/components/ui/sidebar/SidebarCollapsible.stories.tsx @@ -1,8 +1,8 @@ import type { Meta, StoryObj } from '@storybook/react'; import { SidebarCollapsible } from './SidebarCollapsible'; -//import { Home, Settings, User } from 'lucide-react'; import { HouseModern, MapSettings, User } from '../icons/NucleoIconOutlined'; -import { BusterRoutes } from '@/routes'; +import { BusterRoutes } from '../../../routes'; +import React from 'react'; const meta: Meta = { title: 'UI/Sidebar/SidebarCollapsible', @@ -47,3 +47,10 @@ export const Default: Story = { ] } }; + +export const Sortable: Story = { + args: { + ...Default.args, + isSortable: true + } +}; diff --git a/web/src/components/ui/sidebar/SidebarCollapsible.tsx b/web/src/components/ui/sidebar/SidebarCollapsible.tsx index db3b91c03..73580f6a6 100644 --- a/web/src/components/ui/sidebar/SidebarCollapsible.tsx +++ b/web/src/components/ui/sidebar/SidebarCollapsible.tsx @@ -10,6 +10,26 @@ import { type ISidebarGroup } from './interfaces'; import { SidebarItem } from './SidebarItem'; import { CaretDown } from '../icons/NucleoIconFilled'; import { cn } from '@/lib/classMerge'; +import { + DndContext, + closestCenter, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, + DragOverlay, + DragStartEvent, + DragEndEvent +} from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, + useSortable +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { useMemoizedFn } from '@/hooks'; interface SidebarTriggerProps { label: string; @@ -39,6 +59,47 @@ const SidebarTrigger: React.FC = React.memo(({ label, isOpe SidebarTrigger.displayName = 'SidebarTrigger'; +interface SortableSidebarItemProps { + item: ISidebarGroup['items'][0]; + active?: boolean; +} + +const SortableSidebarItem: React.FC = React.memo(({ item, active }) => { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ + id: item.id, + disabled: item.disabled + }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0 : 1 + }; + + const handleClick = (e: React.MouseEvent) => { + if (isDragging) { + e.preventDefault(); + e.stopPropagation(); + } + }; + + return ( +
+
e.stopPropagation() : undefined}> + +
+
+ ); +}); + +SortableSidebarItem.displayName = 'SortableSidebarItem'; + export const SidebarCollapsible: React.FC = React.memo( ({ label, @@ -50,6 +111,34 @@ export const SidebarCollapsible: React.FC { const [isOpen, setIsOpen] = React.useState(defaultOpen); + const [sortedItems, setSortedItems] = React.useState(items); + const [draggingId, setDraggingId] = React.useState(null); + + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates + }) + ); + + const handleDragStart = useMemoizedFn((event: DragStartEvent) => { + setDraggingId(event.active.id as string); + }); + + const handleDragEnd = useMemoizedFn((event: DragEndEvent) => { + const { active, over } = event; + setDraggingId(null); + + if (active.id !== over?.id) { + setSortedItems((items) => { + const oldIndex = items.findIndex((item) => item.id === active.id); + const newIndex = items.findIndex((item) => item.id === over?.id); + return arrayMove(items, oldIndex, newIndex); + }); + } + }); + + const draggingItem = draggingId ? sortedItems.find((item) => item.id === draggingId) : null; return ( @@ -74,13 +163,40 @@ export const SidebarCollapsible: React.FC
- {items.map((item) => ( - - ))} + {isSortable ? ( + + item.id)} + strategy={verticalListSortingStrategy}> + {sortedItems.map((item) => ( + + ))} + + + {draggingId && draggingItem ? ( +
+ +
+ ) : null} +
+
+ ) : ( + items.map((item) => ( + + )) + )}