mirror of
https://github.com/zadam/trilium.git
synced 2025-10-29 18:49:00 +01:00
chore(react/collections/table): set up column dragging
This commit is contained in:
parent
ce0da3fb80
commit
c30c9a7360
@ -144,7 +144,10 @@ function useViewModeConfig<T extends object>(note: FNote | null | undefined, vie
|
|||||||
if (!note || !viewType) return;
|
if (!note || !viewType) return;
|
||||||
const viewStorage = new ViewModeStorage<T>(note, viewType);
|
const viewStorage = new ViewModeStorage<T>(note, viewType);
|
||||||
viewStorage.restore().then(config => {
|
viewStorage.restore().then(config => {
|
||||||
const storeFn = (config: T) => viewStorage.store(config);
|
const storeFn = (config: T) => {
|
||||||
|
setViewConfig([ config, storeFn ]);
|
||||||
|
viewStorage.store(config);
|
||||||
|
};
|
||||||
setViewConfig([ config, storeFn ]);
|
setViewConfig([ config, storeFn ]);
|
||||||
});
|
});
|
||||||
}, [ note, viewType ]);
|
}, [ note, viewType ]);
|
||||||
|
|||||||
@ -66,9 +66,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.board-view-container .board-column.column-dragging {
|
.board-view-container .board-column.column-dragging {
|
||||||
opacity: 0.6;
|
opacity: 0.5;
|
||||||
transform: scale(0.98);
|
transform: scale(0.98) rotate(2deg);
|
||||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
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 {
|
.board-view-container .board-column h3 input {
|
||||||
@ -185,20 +186,19 @@
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-drop-indicator {
|
.column-drop-placeholder {
|
||||||
width: 4px;
|
width: 250px;
|
||||||
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);
|
|
||||||
flex-shrink: 0;
|
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 {
|
.column-drop-placeholder.show {
|
||||||
opacity: 1;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-new-item {
|
.board-new-item {
|
||||||
|
|||||||
@ -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 [ draggedCard, setDraggedCard ] = useState<{ noteId: string, branchId: string, fromColumn: string, index: number } | null>(null);
|
||||||
const [ dropTarget, setDropTarget ] = useState<string | null>(null);
|
const [ dropTarget, setDropTarget ] = useState<string | null>(null);
|
||||||
const [ dropPosition, setDropPosition ] = useState<{ column: string, index: number } | null>(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<number | null>(null);
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
getBoardData(parentNote, statusAttribute ?? "status", viewConfig ?? {}).then(({ byColumn, newPersistedData }) => {
|
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 ]);
|
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 }) => {
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
// Check if any changes affect our board
|
// Check if any changes affect our board
|
||||||
const hasRelevantChanges =
|
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 (
|
return (
|
||||||
<div className="board-view">
|
<div className="board-view">
|
||||||
<div className="board-view-container">
|
<div
|
||||||
{byColumn && columns?.map(column => (
|
className="board-view-container"
|
||||||
<Column
|
onDragOver={handleColumnDragOver}
|
||||||
column={column}
|
onDrop={handleContainerDrop}
|
||||||
columnItems={byColumn.get(column)}
|
>
|
||||||
parentNote={parentNote}
|
{byColumn && columns?.map((column, index) => (
|
||||||
statusAttribute={statusAttribute ?? "status"}
|
<>
|
||||||
draggedCard={draggedCard}
|
{columnDropPosition === index && draggedColumn?.column !== column && (
|
||||||
setDraggedCard={setDraggedCard}
|
<div className="column-drop-placeholder show" />
|
||||||
dropTarget={dropTarget}
|
)}
|
||||||
setDropTarget={setDropTarget}
|
<Column
|
||||||
dropPosition={dropPosition}
|
column={column}
|
||||||
setDropPosition={setDropPosition}
|
columnIndex={index}
|
||||||
onCardDrop={refresh}
|
columnItems={byColumn.get(column)}
|
||||||
/>
|
parentNote={parentNote}
|
||||||
|
statusAttribute={statusAttribute ?? "status"}
|
||||||
|
draggedCard={draggedCard}
|
||||||
|
setDraggedCard={setDraggedCard}
|
||||||
|
dropTarget={dropTarget}
|
||||||
|
setDropTarget={setDropTarget}
|
||||||
|
dropPosition={dropPosition}
|
||||||
|
setDropPosition={setDropPosition}
|
||||||
|
onCardDrop={refresh}
|
||||||
|
draggedColumn={draggedColumn}
|
||||||
|
setDraggedColumn={setDraggedColumn}
|
||||||
|
isDraggingColumn={draggedColumn?.column === column}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
))}
|
))}
|
||||||
|
{columnDropPosition === columns?.length && draggedColumn && (
|
||||||
|
<div className="column-drop-placeholder show" />
|
||||||
|
)}
|
||||||
|
|
||||||
<AddNewColumn viewConfig={viewConfig} saveConfig={saveConfig} />
|
<AddNewColumn viewConfig={viewConfig} saveConfig={saveConfig} />
|
||||||
</div>
|
</div>
|
||||||
@ -95,6 +163,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
|
|||||||
function Column({
|
function Column({
|
||||||
parentNote,
|
parentNote,
|
||||||
column,
|
column,
|
||||||
|
columnIndex,
|
||||||
columnItems,
|
columnItems,
|
||||||
statusAttribute,
|
statusAttribute,
|
||||||
draggedCard,
|
draggedCard,
|
||||||
@ -103,10 +172,14 @@ function Column({
|
|||||||
setDropTarget,
|
setDropTarget,
|
||||||
dropPosition,
|
dropPosition,
|
||||||
setDropPosition,
|
setDropPosition,
|
||||||
onCardDrop
|
onCardDrop,
|
||||||
|
draggedColumn,
|
||||||
|
setDraggedColumn,
|
||||||
|
isDraggingColumn
|
||||||
}: {
|
}: {
|
||||||
parentNote: FNote,
|
parentNote: FNote,
|
||||||
column: string,
|
column: string,
|
||||||
|
columnIndex: number,
|
||||||
columnItems?: { note: FNote, branch: FBranch }[],
|
columnItems?: { note: FNote, branch: FBranch }[],
|
||||||
statusAttribute: string,
|
statusAttribute: string,
|
||||||
draggedCard: { noteId: string, branchId: string, fromColumn: string, index: number } | null,
|
draggedCard: { noteId: string, branchId: string, fromColumn: string, index: number } | null,
|
||||||
@ -115,9 +188,24 @@ function Column({
|
|||||||
setDropTarget: (target: string | null) => void,
|
setDropTarget: (target: string | null) => void,
|
||||||
dropPosition: { column: string, index: number } | null,
|
dropPosition: { column: string, index: number } | null,
|
||||||
setDropPosition: (position: { column: string, index: number } | null) => void,
|
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) => {
|
const handleDragOver = useCallback((e: DragEvent) => {
|
||||||
|
if (draggedColumn) return; // Don't handle card drops when dragging columns
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDropTarget(column);
|
setDropTarget(column);
|
||||||
|
|
||||||
@ -151,6 +239,7 @@ function Column({
|
|||||||
}, [setDropTarget, setDropPosition]);
|
}, [setDropTarget, setDropPosition]);
|
||||||
|
|
||||||
const handleDrop = useCallback(async (e: DragEvent) => {
|
const handleDrop = useCallback(async (e: DragEvent) => {
|
||||||
|
if (draggedColumn) return; // Don't handle card drops when dragging columns
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDropTarget(null);
|
setDropTarget(null);
|
||||||
setDropPosition(null);
|
setDropPosition(null);
|
||||||
@ -189,15 +278,20 @@ function Column({
|
|||||||
onCardDrop();
|
onCardDrop();
|
||||||
}
|
}
|
||||||
setDraggedCard(null);
|
setDraggedCard(null);
|
||||||
}, [draggedCard, dropPosition, columnItems, column, statusAttribute, setDraggedCard, setDropTarget, setDropPosition, onCardDrop]);
|
}, [draggedCard, draggedColumn, dropPosition, columnItems, column, statusAttribute, setDraggedCard, setDropTarget, setDropPosition, onCardDrop]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`board-column ${dropTarget === column && draggedCard?.fromColumn !== column ? 'drag-over' : ''}`}
|
className={`board-column ${dropTarget === column && draggedCard?.fromColumn !== column ? 'drag-over' : ''} ${isDraggingColumn ? 'column-dragging' : ''}`}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
>
|
>
|
||||||
<h3>
|
<h3
|
||||||
|
draggable="true"
|
||||||
|
onDragStart={handleColumnDragStart}
|
||||||
|
onDragEnd={handleColumnDragEnd}
|
||||||
|
>
|
||||||
<span>{column}</span>
|
<span>{column}</span>
|
||||||
<span
|
<span
|
||||||
className="edit-icon icon bx bx-edit-alt"
|
className="edit-icon icon bx bx-edit-alt"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user