import { setupHorizontalScrollViaWheel } from "../../widget_utils"; import ViewMode, { ViewModeArgs } from "../view_mode"; import { getBoardData } from "./data"; import attributeService from "../../../services/attributes"; const TPL = /*html*/`
`; export interface StateInfo { }; export default class BoardView extends ViewMode { private $root: JQuery; private $container: JQuery; private draggedNote: any = null; private draggedNoteElement: JQuery | null = null; constructor(args: ViewModeArgs) { super(args, "board"); this.$root = $(TPL); setupHorizontalScrollViaWheel(this.$root); this.$container = this.$root.find(".board-view-container"); args.$parent.append(this.$root); } async renderList(): Promise | undefined> { this.$container.empty(); await this.renderBoard(this.$container[0]); return this.$root; } private async renderBoard(el: HTMLElement) { const data = await getBoardData(this.noteIds, "status"); for (const column of data.byColumn.keys()) { const columnNotes = data.byColumn.get(column); if (!columnNotes) { continue; } const $columnEl = $("
") .addClass("board-column") .attr("data-column", column) .append($("

").text(column)); // Setup drop zone for the column this.setupColumnDropZone($columnEl, column); for (const note of columnNotes) { const $iconEl = $("") .addClass("icon") .addClass(note.getIcon()); const $noteEl = $("
") .addClass("board-note") .attr("data-note-id", note.noteId) .attr("data-current-column", column) .text(note.title); $noteEl.prepend($iconEl); // Setup drag functionality for the note this.setupNoteDrag($noteEl, note); $columnEl.append($noteEl); } $(el).append($columnEl); } } private setupNoteDrag($noteEl: JQuery, 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, 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, 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 = $("
").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"); } }