diff --git a/apps/client/src/widgets/view_widgets/board_view/differential_renderer.ts b/apps/client/src/widgets/view_widgets/board_view/differential_renderer.ts index 9aa602ae0..1188e9807 100644 --- a/apps/client/src/widgets/view_widgets/board_view/differential_renderer.ts +++ b/apps/client/src/widgets/view_widgets/board_view/differential_renderer.ts @@ -38,7 +38,7 @@ export class DifferentialBoardRenderer { this.viewStorage = viewStorage; } - async renderBoard(refreshApi: boolean = false): Promise { + async renderBoard(refreshApi = false): Promise { // Refresh API data if requested if (refreshApi) { this.api = await BoardApi.build(this.parentNote, this.viewStorage); @@ -234,17 +234,55 @@ export class DifferentialBoardRenderer { for (let i = 0; i < newCards.length; i++) { const item = newCards[i]; const noteId = item.note.noteId; - let $existingCard = $cardContainer.find(`[data-note-id="${noteId}"]`); + const $existingCard = $cardContainer.find(`[data-note-id="${noteId}"]`); const isNewCard = !oldCardIds.includes(noteId); if ($existingCard.length) { - // Update existing card if title changed + // Check for changes in title, icon, or color const currentTitle = $existingCard.text().trim(); + const currentIconClass = $existingCard.attr('data-icon-class'); + const currentColorClass = $existingCard.attr('data-color-class') || ''; + + const newIconClass = item.note.getIcon(); + const newColorClass = item.note.getColorClass() || ''; + + let hasChanges = false; + + // Update title if changed if (currentTitle !== item.note.title) { $existingCard.contents().filter(function() { return this.nodeType === 3; // Text nodes }).remove(); $existingCard.append(document.createTextNode(item.note.title)); + hasChanges = true; + } + + // Update icon if changed + if (currentIconClass !== newIconClass) { + const $icon = $existingCard.find('.icon'); + $icon.removeClass().addClass('icon').addClass(newIconClass); + $existingCard.attr('data-icon-class', newIconClass); + hasChanges = true; + } + + // Update color if changed + if (currentColorClass !== newColorClass) { + // Remove old color class if it exists + if (currentColorClass) { + $existingCard.removeClass(currentColorClass); + } + // Add new color class if it exists + if (newColorClass) { + $existingCard.addClass(newColorClass); + } + $existingCard.attr('data-color-class', newColorClass); + hasChanges = true; + } + + // Add subtle animation if there were changes + if (hasChanges) { + $existingCard.addClass('card-updated'); + setTimeout(() => $existingCard.removeClass('card-updated'), 300); } // Ensure card is in correct position @@ -343,19 +381,27 @@ export class DifferentialBoardRenderer { return $columnEl; } - private createCard(note: any, branch: any, column: string, isNewCard: boolean = false): JQuery { + private createCard(note: any, branch: any, column: string, isNewCard = false): JQuery { const $iconEl = $("") .addClass("icon") .addClass(note.getIcon()); + const colorClass = note.getColorClass() || ''; + const $noteEl = $("
") .addClass("board-note") .attr("data-note-id", note.noteId) .attr("data-branch-id", branch.branchId) .attr("data-current-column", column) .attr("data-icon-class", note.getIcon()) + .attr("data-color-class", colorClass) .text(note.title); + // Add color class to the card if it exists + if (colorClass) { + $noteEl.addClass(colorClass); + } + $noteEl.prepend($iconEl); // Only add quick edit click handler for existing cards (not new ones) @@ -448,7 +494,7 @@ export class DifferentialBoardRenderer { $card.empty().append($editContainer); $input.focus().select(); - const finishEdit = async (save: boolean = true) => { + const finishEdit = async (save = true) => { if (!$card.hasClass('editing')) { return; // Already finished } diff --git a/apps/client/src/widgets/view_widgets/board_view/index.ts b/apps/client/src/widgets/view_widgets/board_view/index.ts index 366aff876..1c09047cd 100644 --- a/apps/client/src/widgets/view_widgets/board_view/index.ts +++ b/apps/client/src/widgets/view_widgets/board_view/index.ts @@ -140,6 +140,16 @@ const TPL = /*html*/` to { opacity: 0; transform: translateY(-10px); } } + .board-view-container .board-note.card-updated { + animation: cardUpdate 0.3s ease-in-out; + } + + @keyframes cardUpdate { + 0% { transform: scale(1); } + 50% { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } + 100% { transform: scale(1); } + } + .board-view-container .board-note:hover { transform: translateY(-2px); box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35); @@ -596,6 +606,8 @@ export default class BoardView extends ViewMode { loadResults.getNoteIds().some(noteId => this.noteIds.includes(noteId)) || // React to changes in branches for subchildren (e.g., moved, added, or removed notes) loadResults.getBranchRows().some(branch => this.noteIds.includes(branch.noteId!)) || + // React to changes in note icon or color. + loadResults.getAttributeRows().some(attr => [ "iconClass", "color" ].includes(attr.name ?? "") && this.noteIds.includes(attr.noteId ?? "")) || // React to attachment change loadResults.getAttachmentRows().some(att => att.ownerId === this.parentNote.noteId && att.title === "board.json") || // React to changes in "groupBy"