mirror of
https://github.com/zadam/trilium.git
synced 2025-10-21 23:59:02 +02:00
chore(react/collections/table): add drop indicator
This commit is contained in:
parent
2e4791d377
commit
d9af0461ef
@ -106,7 +106,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--main-background-color);
|
background-color: var(--main-background-color);
|
||||||
border: 1px solid var(--main-border-color);
|
border: 1px solid var(--main-border-color);
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.15s ease;
|
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.15s ease, margin-top 0.2s ease;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,12 +144,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.board-view-container .board-note.dragging {
|
.board-view-container .board-note.dragging {
|
||||||
opacity: 0.8;
|
opacity: 0.5;
|
||||||
transform: rotate(5deg);
|
transform: rotate(5deg);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.shift-down {
|
||||||
|
margin-top: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
.board-view-container .board-note.editing {
|
.board-view-container .board-note.editing {
|
||||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||||
border-color: var(--main-text-color);
|
border-color: var(--main-text-color);
|
||||||
@ -171,16 +175,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.board-drop-indicator {
|
.board-drop-indicator {
|
||||||
height: 3px;
|
height: 2px;
|
||||||
background-color: var(--main-text-color);
|
background: linear-gradient(90deg, transparent, var(--main-text-color) 20%, var(--main-text-color) 80%, transparent);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin: 0.25em 0;
|
margin: -1px 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.15s ease;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-drop-indicator.show {
|
.board-drop-indicator.show {
|
||||||
opacity: 1;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-drop-indicator {
|
.column-drop-indicator {
|
||||||
|
@ -22,8 +22,9 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
|
|||||||
const [ statusAttribute ] = useNoteLabel(parentNote, "board:groupBy");
|
const [ statusAttribute ] = useNoteLabel(parentNote, "board:groupBy");
|
||||||
const [ byColumn, setByColumn ] = useState<ColumnMap>();
|
const [ byColumn, setByColumn ] = useState<ColumnMap>();
|
||||||
const [ columns, setColumns ] = useState<string[]>();
|
const [ columns, setColumns ] = useState<string[]>();
|
||||||
const [ draggedCard, setDraggedCard ] = useState<{ noteId: string, fromColumn: string } | null>(null);
|
const [ draggedCard, setDraggedCard ] = useState<{ noteId: 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);
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
getBoardData(parentNote, statusAttribute ?? "status", viewConfig ?? {}).then(({ byColumn, newPersistedData }) => {
|
getBoardData(parentNote, statusAttribute ?? "status", viewConfig ?? {}).then(({ byColumn, newPersistedData }) => {
|
||||||
@ -82,6 +83,8 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
|
|||||||
setDraggedCard={setDraggedCard}
|
setDraggedCard={setDraggedCard}
|
||||||
dropTarget={dropTarget}
|
dropTarget={dropTarget}
|
||||||
setDropTarget={setDropTarget}
|
setDropTarget={setDropTarget}
|
||||||
|
dropPosition={dropPosition}
|
||||||
|
setDropPosition={setDropPosition}
|
||||||
onCardDrop={refresh}
|
onCardDrop={refresh}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@ -101,22 +104,44 @@ function Column({
|
|||||||
setDraggedCard,
|
setDraggedCard,
|
||||||
dropTarget,
|
dropTarget,
|
||||||
setDropTarget,
|
setDropTarget,
|
||||||
|
dropPosition,
|
||||||
|
setDropPosition,
|
||||||
onCardDrop
|
onCardDrop
|
||||||
}: {
|
}: {
|
||||||
parentNote: FNote,
|
parentNote: FNote,
|
||||||
column: string,
|
column: string,
|
||||||
columnItems?: { note: FNote, branch: FBranch }[],
|
columnItems?: { note: FNote, branch: FBranch }[],
|
||||||
statusAttribute: string,
|
statusAttribute: string,
|
||||||
draggedCard: { noteId: string, fromColumn: string } | null,
|
draggedCard: { noteId: string, fromColumn: string, index: number } | null,
|
||||||
setDraggedCard: (card: { noteId: string, fromColumn: string } | null) => void,
|
setDraggedCard: (card: { noteId: string, fromColumn: string, index: number } | null) => void,
|
||||||
dropTarget: string | null,
|
dropTarget: string | null,
|
||||||
setDropTarget: (target: string | null) => void,
|
setDropTarget: (target: string | null) => void,
|
||||||
|
dropPosition: { column: string, index: number } | null,
|
||||||
|
setDropPosition: (position: { column: string, index: number } | null) => void,
|
||||||
onCardDrop: () => void
|
onCardDrop: () => void
|
||||||
}) {
|
}) {
|
||||||
const handleDragOver = useCallback((e: DragEvent) => {
|
const handleDragOver = useCallback((e: DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDropTarget(column);
|
setDropTarget(column);
|
||||||
}, [column, setDropTarget]);
|
|
||||||
|
// Calculate drop position based on mouse position
|
||||||
|
const cards = Array.from(e.currentTarget.querySelectorAll('.board-note'));
|
||||||
|
const mouseY = e.clientY;
|
||||||
|
|
||||||
|
let newIndex = cards.length;
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
const card = cards[i] as HTMLElement;
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
const cardMiddle = rect.top + rect.height / 2;
|
||||||
|
|
||||||
|
if (mouseY < cardMiddle) {
|
||||||
|
newIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDropPosition({ column, index: newIndex });
|
||||||
|
}, [column, setDropTarget, setDropPosition]);
|
||||||
|
|
||||||
const handleDragLeave = useCallback((e: DragEvent) => {
|
const handleDragLeave = useCallback((e: DragEvent) => {
|
||||||
const relatedTarget = e.relatedTarget as HTMLElement;
|
const relatedTarget = e.relatedTarget as HTMLElement;
|
||||||
@ -124,19 +149,25 @@ function Column({
|
|||||||
|
|
||||||
if (!currentTarget.contains(relatedTarget)) {
|
if (!currentTarget.contains(relatedTarget)) {
|
||||||
setDropTarget(null);
|
setDropTarget(null);
|
||||||
|
setDropPosition(null);
|
||||||
}
|
}
|
||||||
}, [setDropTarget]);
|
}, [setDropTarget, setDropPosition]);
|
||||||
|
|
||||||
const handleDrop = useCallback(async (e: DragEvent) => {
|
const handleDrop = useCallback(async (e: DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDropTarget(null);
|
setDropTarget(null);
|
||||||
|
setDropPosition(null);
|
||||||
|
|
||||||
if (draggedCard && draggedCard.fromColumn !== column) {
|
if (draggedCard) {
|
||||||
await changeColumn(draggedCard.noteId, column, statusAttribute);
|
// For now, just handle column changes
|
||||||
onCardDrop();
|
// TODO: Add position/order handling
|
||||||
|
if (draggedCard.fromColumn !== column) {
|
||||||
|
await changeColumn(draggedCard.noteId, column, statusAttribute);
|
||||||
|
onCardDrop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setDraggedCard(null);
|
setDraggedCard(null);
|
||||||
}, [draggedCard, column, statusAttribute, setDraggedCard, setDropTarget, onCardDrop]);
|
}, [draggedCard, 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' : ''}`}
|
||||||
@ -151,15 +182,35 @@ function Column({
|
|||||||
title="Click to edit column title" />
|
title="Click to edit column title" />
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{(columnItems ?? []).map(({ note, branch }) => (
|
{(columnItems ?? []).map(({ note, branch }, index) => {
|
||||||
<Card
|
const showIndicatorBefore = dropPosition?.column === column &&
|
||||||
note={note}
|
dropPosition.index === index &&
|
||||||
branch={branch}
|
draggedCard?.noteId !== note.noteId;
|
||||||
column={column}
|
const shouldShift = dropPosition?.column === column &&
|
||||||
setDraggedCard={setDraggedCard}
|
dropPosition.index <= index &&
|
||||||
isDragging={draggedCard?.noteId === note.noteId}
|
draggedCard?.noteId !== note.noteId &&
|
||||||
/>
|
draggedCard !== null;
|
||||||
))}
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showIndicatorBefore && (
|
||||||
|
<div className="board-drop-indicator show" />
|
||||||
|
)}
|
||||||
|
<Card
|
||||||
|
note={note}
|
||||||
|
branch={branch}
|
||||||
|
column={column}
|
||||||
|
index={index}
|
||||||
|
setDraggedCard={setDraggedCard}
|
||||||
|
isDragging={draggedCard?.noteId === note.noteId}
|
||||||
|
shouldShift={shouldShift}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{dropPosition?.column === column && dropPosition.index === (columnItems?.length ?? 0) && (
|
||||||
|
<div className="board-drop-indicator show" />
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="board-new-item" onClick={() => createNewItem(parentNote, column)}>
|
<div className="board-new-item" onClick={() => createNewItem(parentNote, column)}>
|
||||||
<Icon icon="bx bx-plus" />{" "}
|
<Icon icon="bx bx-plus" />{" "}
|
||||||
@ -172,22 +223,26 @@ function Column({
|
|||||||
function Card({
|
function Card({
|
||||||
note,
|
note,
|
||||||
column,
|
column,
|
||||||
|
index,
|
||||||
setDraggedCard,
|
setDraggedCard,
|
||||||
isDragging
|
isDragging,
|
||||||
|
shouldShift
|
||||||
}: {
|
}: {
|
||||||
note: FNote,
|
note: FNote,
|
||||||
branch: FBranch,
|
branch: FBranch,
|
||||||
column: string,
|
column: string,
|
||||||
setDraggedCard: (card: { noteId: string, fromColumn: string } | null) => void,
|
index: number,
|
||||||
isDragging: boolean
|
setDraggedCard: (card: { noteId: string, fromColumn: string, index: number } | null) => void,
|
||||||
|
isDragging: boolean,
|
||||||
|
shouldShift?: boolean
|
||||||
}) {
|
}) {
|
||||||
const colorClass = note.getColorClass() || '';
|
const colorClass = note.getColorClass() || '';
|
||||||
|
|
||||||
const handleDragStart = useCallback((e: DragEvent) => {
|
const handleDragStart = useCallback((e: DragEvent) => {
|
||||||
e.dataTransfer!.effectAllowed = 'move';
|
e.dataTransfer!.effectAllowed = 'move';
|
||||||
e.dataTransfer!.setData('text/plain', note.noteId);
|
e.dataTransfer!.setData('text/plain', note.noteId);
|
||||||
setDraggedCard({ noteId: note.noteId, fromColumn: column });
|
setDraggedCard({ noteId: note.noteId, fromColumn: column, index });
|
||||||
}, [note.noteId, column, setDraggedCard]);
|
}, [note.noteId, column, index, setDraggedCard]);
|
||||||
|
|
||||||
const handleDragEnd = useCallback(() => {
|
const handleDragEnd = useCallback(() => {
|
||||||
setDraggedCard(null);
|
setDraggedCard(null);
|
||||||
@ -195,7 +250,7 @@ function Card({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`board-note ${colorClass} ${isDragging ? 'dragging' : ''}`}
|
className={`board-note ${colorClass} ${isDragging ? 'dragging' : ''} ${shouldShift ? 'shift-down' : ''}`}
|
||||||
draggable="true"
|
draggable="true"
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user