diff --git a/package.json b/package.json index 30d2f8fe..31aac18e 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "react-color": "^2.19.3", "react-dom": "19.2.1", "react-dropzone": "^14.3.8", - "react-grid-layout": "^2.2.2", + "react-grid-layout": "git+https://github.com/urjitc/react-grid-layout.git", "react-helmet-async": "^2.0.5", "react-hook-form": "^7.69.0", "react-icons": "^5.5.0", diff --git a/src/components/workspace-canvas/WorkspaceGrid.tsx b/src/components/workspace-canvas/WorkspaceGrid.tsx index 874fdb7d..ff5bdfd8 100644 --- a/src/components/workspace-canvas/WorkspaceGrid.tsx +++ b/src/components/workspace-canvas/WorkspaceGrid.tsx @@ -131,7 +131,7 @@ export function WorkspaceGrid({ }, []); // Handle drag start - with RGL v2, this only fires after 3px movement (real drag, not click) - const handleDragStart = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | undefined) => { + const handleDragStart = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => { // Check if the click originated from a dropdown menu - if so, don't start drag const target = e.target as HTMLElement; if ( @@ -156,7 +156,7 @@ export function WorkspaceGrid({ }, [onDragStart, onGridDragStateChange]); // Handle drag to detect folder hover based on cursor position - const handleDrag = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | undefined) => { + const handleDrag = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => { const draggedItemId = draggedItemIdRef.current; if (!draggedItemId || !e) return; @@ -245,7 +245,7 @@ export function WorkspaceGrid({ }, [selectedCardIds]); // Handle resize start - track which item is being resized - const handleResizeStart = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | undefined) => { + const handleResizeStart = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => { hasUserInteractedRef.current = true; // Track which item is being resized (same as drag) if (!oldItem) return; @@ -257,7 +257,7 @@ export function WorkspaceGrid({ // Handle drag stop - with RGL v2, this only fires for actual drags (not clicks) // Click handling is now done by individual card components via their onClick handlers - const handleDragStop = useCallback((newLayout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | undefined) => { + const handleDragStop = useCallback((newLayout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => { const draggedItemId = draggedItemIdRef.current; // If no drag was started (e.g., click on dropdown), exit early @@ -393,7 +393,7 @@ export function WorkspaceGrid({ // Handle resize to enforce constraints // Note cards can transition between compact (w=1, h=4) and expanded (w>=2, h>=9) modes // based on EITHER width or height changes, allowing vertical-only resizing to trigger mode switches - const handleResize = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | undefined) => { + const handleResize = useCallback((layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => { // Enforce custom constraints for YouTube and single-column items if (!newItem || !oldItem) return; const itemData = allItemsRef.current.find(i => i.id === newItem.i); diff --git a/src/lib/workspace-state/grid-layout-helpers.ts b/src/lib/workspace-state/grid-layout-helpers.ts index d54da685..04617c36 100644 --- a/src/lib/workspace-state/grid-layout-helpers.ts +++ b/src/lib/workspace-state/grid-layout-helpers.ts @@ -1,4 +1,4 @@ -import type { Layout, LayoutItem } from "react-grid-layout"; +import type { Layout, LayoutItem } from "react-grid-layout"; import type { Item, CardType, LayoutPosition, ResponsiveLayouts } from "./types"; /** @@ -92,6 +92,22 @@ export function itemsToLayout(items: Item[], breakpoint: 'lg' | 'xxs' = 'lg'): L }; } + // Folders are anchor items - they act as obstacles but can be dragged/resized + if (item.type === 'folder') { + return { + i: item.id, + x: layout?.x ?? 0, + y: layout?.y ?? 0, + w: layout?.w ?? DEFAULT_CARD_DIMENSIONS[item.type].w, + h: layout?.h ?? DEFAULT_CARD_DIMENSIONS[item.type].h, + minW: 1, + minH: 4, + maxW: 4, + maxH: 25, + anchor: true, // Anchor items act as obstacles but can be moved + }; + } + // Default constraints for other card types return { i: item.id,