mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 19:36:12 +01:00
feat(views/board): set up dragging
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { setupHorizontalScrollViaWheel } from "../../widget_utils";
|
import { setupHorizontalScrollViaWheel } from "../../widget_utils";
|
||||||
import ViewMode, { ViewModeArgs } from "../view_mode";
|
import ViewMode, { ViewModeArgs } from "../view_mode";
|
||||||
import { getBoardData } from "./data";
|
import { getBoardData } from "./data";
|
||||||
|
import attributeService from "../../../services/attributes";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="board-view">
|
<div class="board-view">
|
||||||
@@ -22,10 +23,24 @@ const TPL = /*html*/`
|
|||||||
.board-view-container .board-column {
|
.board-view-container .board-column {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
min-height: 200px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
transition: border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column.drag-over {
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-view-container .board-column h3 {
|
.board-view-container .board-column h3 {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
border-bottom: 1px solid var(--main-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-view-container .board-note {
|
.board-view-container .board-note {
|
||||||
@@ -33,11 +48,41 @@ const TPL = /*html*/`
|
|||||||
margin: 0.65em 0;
|
margin: 0.65em 0;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
cursor: move;
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
border: 1px solid var(--main-border-color);
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.dragging {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: rotate(5deg);
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-view-container .board-note .icon {
|
.board-view-container .board-note .icon {
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-drop-indicator {
|
||||||
|
height: 3px;
|
||||||
|
background-color: var(--main-text-color);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-drop-indicator.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="board-view-container"></div>
|
<div class="board-view-container"></div>
|
||||||
@@ -52,6 +97,8 @@ export default class BoardView extends ViewMode<StateInfo> {
|
|||||||
|
|
||||||
private $root: JQuery<HTMLElement>;
|
private $root: JQuery<HTMLElement>;
|
||||||
private $container: JQuery<HTMLElement>;
|
private $container: JQuery<HTMLElement>;
|
||||||
|
private draggedNote: any = null;
|
||||||
|
private draggedNoteElement: JQuery<HTMLElement> | null = null;
|
||||||
|
|
||||||
constructor(args: ViewModeArgs) {
|
constructor(args: ViewModeArgs) {
|
||||||
super(args, "board");
|
super(args, "board");
|
||||||
@@ -65,7 +112,7 @@ export default class BoardView extends ViewMode<StateInfo> {
|
|||||||
|
|
||||||
async renderList(): Promise<JQuery<HTMLElement> | undefined> {
|
async renderList(): Promise<JQuery<HTMLElement> | undefined> {
|
||||||
this.$container.empty();
|
this.$container.empty();
|
||||||
this.renderBoard(this.$container[0]);
|
await this.renderBoard(this.$container[0]);
|
||||||
|
|
||||||
return this.$root;
|
return this.$root;
|
||||||
}
|
}
|
||||||
@@ -81,8 +128,12 @@ export default class BoardView extends ViewMode<StateInfo> {
|
|||||||
|
|
||||||
const $columnEl = $("<div>")
|
const $columnEl = $("<div>")
|
||||||
.addClass("board-column")
|
.addClass("board-column")
|
||||||
|
.attr("data-column", column)
|
||||||
.append($("<h3>").text(column));
|
.append($("<h3>").text(column));
|
||||||
|
|
||||||
|
// Setup drop zone for the column
|
||||||
|
this.setupColumnDropZone($columnEl, column);
|
||||||
|
|
||||||
for (const note of columnNotes) {
|
for (const note of columnNotes) {
|
||||||
const $iconEl = $("<span>")
|
const $iconEl = $("<span>")
|
||||||
.addClass("icon")
|
.addClass("icon")
|
||||||
@@ -90,8 +141,15 @@ export default class BoardView extends ViewMode<StateInfo> {
|
|||||||
|
|
||||||
const $noteEl = $("<div>")
|
const $noteEl = $("<div>")
|
||||||
.addClass("board-note")
|
.addClass("board-note")
|
||||||
.text(note.title); // Assuming FNote has a title property
|
.attr("data-note-id", note.noteId)
|
||||||
|
.attr("data-current-column", column)
|
||||||
|
.text(note.title);
|
||||||
|
|
||||||
$noteEl.prepend($iconEl);
|
$noteEl.prepend($iconEl);
|
||||||
|
|
||||||
|
// Setup drag functionality for the note
|
||||||
|
this.setupNoteDrag($noteEl, note);
|
||||||
|
|
||||||
$columnEl.append($noteEl);
|
$columnEl.append($noteEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,4 +157,132 @@ export default class BoardView extends ViewMode<StateInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupNoteDrag($noteEl: JQuery<HTMLElement>, note: any) {
|
||||||
|
$noteEl.attr("draggable", "true");
|
||||||
|
|
||||||
|
$noteEl.on("dragstart", (e) => {
|
||||||
|
this.draggedNote = note;
|
||||||
|
this.draggedNoteElement = $noteEl;
|
||||||
|
$noteEl.addClass("dragging");
|
||||||
|
|
||||||
|
// Set drag data
|
||||||
|
const originalEvent = e.originalEvent as DragEvent;
|
||||||
|
if (originalEvent.dataTransfer) {
|
||||||
|
originalEvent.dataTransfer.effectAllowed = "move";
|
||||||
|
originalEvent.dataTransfer.setData("text/plain", note.noteId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$noteEl.on("dragend", () => {
|
||||||
|
$noteEl.removeClass("dragging");
|
||||||
|
this.draggedNote = null;
|
||||||
|
this.draggedNoteElement = null;
|
||||||
|
|
||||||
|
// Remove all drop indicators
|
||||||
|
this.$container.find(".board-drop-indicator").removeClass("show");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupColumnDropZone($columnEl: JQuery<HTMLElement>, column: string) {
|
||||||
|
$columnEl.on("dragover", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const originalEvent = e.originalEvent as DragEvent;
|
||||||
|
if (originalEvent.dataTransfer) {
|
||||||
|
originalEvent.dataTransfer.dropEffect = "move";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.draggedNote) {
|
||||||
|
$columnEl.addClass("drag-over");
|
||||||
|
this.showDropIndicator($columnEl, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$columnEl.on("dragleave", (e) => {
|
||||||
|
// Only remove drag-over if we're leaving the column entirely
|
||||||
|
const rect = $columnEl[0].getBoundingClientRect();
|
||||||
|
const originalEvent = e.originalEvent as DragEvent;
|
||||||
|
const x = originalEvent.clientX;
|
||||||
|
const y = originalEvent.clientY;
|
||||||
|
|
||||||
|
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
|
||||||
|
$columnEl.removeClass("drag-over");
|
||||||
|
$columnEl.find(".board-drop-indicator").removeClass("show");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$columnEl.on("drop", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
$columnEl.removeClass("drag-over");
|
||||||
|
$columnEl.find(".board-drop-indicator").removeClass("show");
|
||||||
|
|
||||||
|
if (this.draggedNote && this.draggedNoteElement) {
|
||||||
|
const currentColumn = this.draggedNoteElement.attr("data-current-column");
|
||||||
|
|
||||||
|
if (currentColumn !== column) {
|
||||||
|
try {
|
||||||
|
// Update the note's status label
|
||||||
|
await attributeService.setLabel(this.draggedNote.noteId, "status", column);
|
||||||
|
|
||||||
|
// Move the note element to the new column
|
||||||
|
const dropIndicator = $columnEl.find(".board-drop-indicator.show");
|
||||||
|
if (dropIndicator.length > 0) {
|
||||||
|
dropIndicator.after(this.draggedNoteElement);
|
||||||
|
} else {
|
||||||
|
$columnEl.append(this.draggedNoteElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the data attribute
|
||||||
|
this.draggedNoteElement.attr("data-current-column", column);
|
||||||
|
|
||||||
|
// Show success feedback (optional)
|
||||||
|
console.log(`Moved note "${this.draggedNote.title}" from "${currentColumn}" to "${column}"`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update note status:", error);
|
||||||
|
// Optionally show user-facing error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private showDropIndicator($columnEl: JQuery<HTMLElement>, e: JQuery.DragOverEvent) {
|
||||||
|
const originalEvent = e.originalEvent as DragEvent;
|
||||||
|
const mouseY = originalEvent.clientY;
|
||||||
|
const columnRect = $columnEl[0].getBoundingClientRect();
|
||||||
|
const relativeY = mouseY - columnRect.top;
|
||||||
|
|
||||||
|
// Find existing drop indicator or create one
|
||||||
|
let $dropIndicator = $columnEl.find(".board-drop-indicator");
|
||||||
|
if ($dropIndicator.length === 0) {
|
||||||
|
$dropIndicator = $("<div>").addClass("board-drop-indicator");
|
||||||
|
$columnEl.append($dropIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the best position to insert the note
|
||||||
|
const $notes = this.draggedNoteElement ?
|
||||||
|
$columnEl.find(".board-note").not(this.draggedNoteElement) :
|
||||||
|
$columnEl.find(".board-note");
|
||||||
|
let insertAfterElement: HTMLElement | null = null;
|
||||||
|
|
||||||
|
$notes.each((_, noteEl) => {
|
||||||
|
const noteRect = noteEl.getBoundingClientRect();
|
||||||
|
const noteMiddle = noteRect.top + noteRect.height / 2 - columnRect.top;
|
||||||
|
|
||||||
|
if (relativeY > noteMiddle) {
|
||||||
|
insertAfterElement = noteEl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Position the drop indicator
|
||||||
|
if (insertAfterElement) {
|
||||||
|
$(insertAfterElement).after($dropIndicator);
|
||||||
|
} else {
|
||||||
|
// Insert at the beginning (after the header)
|
||||||
|
const $header = $columnEl.find("h3");
|
||||||
|
$header.after($dropIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dropIndicator.addClass("show");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user