")
.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");
}
}