From c30c9a736059aefd9d25c5a374a69e690d251c52 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Sep 2025 18:42:32 +0300 Subject: [PATCH] chore(react/collections/table): set up column dragging --- .../src/widgets/collections/NoteList.tsx | 5 +- .../src/widgets/collections/board/index.css | 26 ++-- .../src/widgets/collections/board/index.tsx | 134 +++++++++++++++--- 3 files changed, 131 insertions(+), 34 deletions(-) diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx index 8e462e44b..fe62d0254 100644 --- a/apps/client/src/widgets/collections/NoteList.tsx +++ b/apps/client/src/widgets/collections/NoteList.tsx @@ -144,7 +144,10 @@ function useViewModeConfig(note: FNote | null | undefined, vie if (!note || !viewType) return; const viewStorage = new ViewModeStorage(note, viewType); viewStorage.restore().then(config => { - const storeFn = (config: T) => viewStorage.store(config); + const storeFn = (config: T) => { + setViewConfig([ config, storeFn ]); + viewStorage.store(config); + }; setViewConfig([ config, storeFn ]); }); }, [ note, viewType ]); diff --git a/apps/client/src/widgets/collections/board/index.css b/apps/client/src/widgets/collections/board/index.css index 9e672e22d..dc1d2f10d 100644 --- a/apps/client/src/widgets/collections/board/index.css +++ b/apps/client/src/widgets/collections/board/index.css @@ -66,9 +66,10 @@ } .board-view-container .board-column.column-dragging { - opacity: 0.6; - transform: scale(0.98); + opacity: 0.5; + transform: scale(0.98) rotate(2deg); transition: opacity 0.2s ease, transform 0.2s ease; + box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.3); } .board-view-container .board-column h3 input { @@ -185,20 +186,19 @@ opacity: 0.6; } -.column-drop-indicator { - width: 4px; - background-color: var(--main-text-color); - border-radius: 2px; - opacity: 0; - transition: opacity 0.2s ease; - height: 100%; - z-index: 1000; - box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); +.column-drop-placeholder { + width: 250px; flex-shrink: 0; + height: 200px; + border-radius: 8px; + background-color: rgba(0, 0, 0, 0.1); + opacity: 0; + transition: opacity 0.15s ease; + margin: 0 0.5em; } -.column-drop-indicator.show { - opacity: 1; +.column-drop-placeholder.show { + opacity: 0.6; } .board-new-item { diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx index 1fa651f94..1fee7e5c6 100644 --- a/apps/client/src/widgets/collections/board/index.tsx +++ b/apps/client/src/widgets/collections/board/index.tsx @@ -26,6 +26,8 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC const [ draggedCard, setDraggedCard ] = useState<{ noteId: string, branchId: string, fromColumn: string, index: number } | null>(null); const [ dropTarget, setDropTarget ] = useState(null); const [ dropPosition, setDropPosition ] = useState<{ column: string, index: number } | null>(null); + const [ draggedColumn, setDraggedColumn ] = useState<{ column: string, index: number } | null>(null); + const [ columnDropPosition, setColumnDropPosition ] = useState(null); function refresh() { getBoardData(parentNote, statusAttribute ?? "status", viewConfig ?? {}).then(({ byColumn, newPersistedData }) => { @@ -46,6 +48,26 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC useEffect(refresh, [ parentNote, noteIds ]); + const handleColumnDrop = useCallback((fromIndex: number, toIndex: number) => { + if (!columns || fromIndex === toIndex) return; + + const newColumns = [...columns]; + const [movedColumn] = newColumns.splice(fromIndex, 1); + newColumns.splice(toIndex, 0, movedColumn); + + // Update view config with new column order + const newViewConfig = { + ...viewConfig, + columns: newColumns.map(col => ({ value: col })) + }; + + saveConfig(newViewConfig); + setColumns(newColumns); + console.log("New columns are ", newColumns); + setDraggedColumn(null); + setColumnDropPosition(null); + }, [columns, viewConfig, saveConfig]); + useTriliumEvent("entitiesReloaded", ({ loadResults }) => { // Check if any changes affect our board const hasRelevantChanges = @@ -67,24 +89,70 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC } }); + const handleColumnDragOver = useCallback((e: DragEvent) => { + if (!draggedColumn) return; + e.preventDefault(); + + const container = e.currentTarget as HTMLElement; + const columns = Array.from(container.querySelectorAll('.board-column')); + const mouseX = e.clientX; + + let newIndex = columns.length; + for (let i = 0; i < columns.length; i++) { + const col = columns[i] as HTMLElement; + const rect = col.getBoundingClientRect(); + const colMiddle = rect.left + rect.width / 2; + + if (mouseX < colMiddle) { + newIndex = i; + break; + } + } + + setColumnDropPosition(newIndex); + }, [draggedColumn]); + + const handleContainerDrop = useCallback((e: DragEvent) => { + e.preventDefault(); + if (draggedColumn && columnDropPosition !== null) { + handleColumnDrop(draggedColumn.index, columnDropPosition); + } + }, [draggedColumn, columnDropPosition, handleColumnDrop]); + return (
-
- {byColumn && columns?.map(column => ( - +
+ {byColumn && columns?.map((column, index) => ( + <> + {columnDropPosition === index && draggedColumn?.column !== column && ( +
+ )} + + ))} + {columnDropPosition === columns?.length && draggedColumn && ( +
+ )}
@@ -95,6 +163,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC function Column({ parentNote, column, + columnIndex, columnItems, statusAttribute, draggedCard, @@ -103,10 +172,14 @@ function Column({ setDropTarget, dropPosition, setDropPosition, - onCardDrop + onCardDrop, + draggedColumn, + setDraggedColumn, + isDraggingColumn }: { parentNote: FNote, column: string, + columnIndex: number, columnItems?: { note: FNote, branch: FBranch }[], statusAttribute: string, draggedCard: { noteId: string, branchId: string, fromColumn: string, index: number } | null, @@ -115,9 +188,24 @@ function Column({ setDropTarget: (target: string | null) => void, dropPosition: { column: string, index: number } | null, setDropPosition: (position: { column: string, index: number } | null) => void, - onCardDrop: () => void + onCardDrop: () => void, + draggedColumn: { column: string, index: number } | null, + setDraggedColumn: (column: { column: string, index: number } | null) => void, + isDraggingColumn: boolean }) { + const handleColumnDragStart = useCallback((e: DragEvent) => { + e.dataTransfer!.effectAllowed = 'move'; + e.dataTransfer!.setData('text/plain', column); + setDraggedColumn({ column, index: columnIndex }); + e.stopPropagation(); // Prevent card drag from interfering + }, [column, columnIndex, setDraggedColumn]); + + const handleColumnDragEnd = useCallback(() => { + setDraggedColumn(null); + }, [setDraggedColumn]); + const handleDragOver = useCallback((e: DragEvent) => { + if (draggedColumn) return; // Don't handle card drops when dragging columns e.preventDefault(); setDropTarget(column); @@ -151,6 +239,7 @@ function Column({ }, [setDropTarget, setDropPosition]); const handleDrop = useCallback(async (e: DragEvent) => { + if (draggedColumn) return; // Don't handle card drops when dragging columns e.preventDefault(); setDropTarget(null); setDropPosition(null); @@ -189,15 +278,20 @@ function Column({ onCardDrop(); } setDraggedCard(null); - }, [draggedCard, dropPosition, columnItems, column, statusAttribute, setDraggedCard, setDropTarget, setDropPosition, onCardDrop]); + }, [draggedCard, draggedColumn, dropPosition, columnItems, column, statusAttribute, setDraggedCard, setDropTarget, setDropPosition, onCardDrop]); + return (
-

+

{column}