From 4299fc15d6700fd0507615baf248794bab80353f Mon Sep 17 00:00:00 2001 From: Aleix Soler Date: Wed, 16 Apr 2025 15:03:30 +0200 Subject: [PATCH] feat: Minmal design --- main.ts | 10 +- styles.css | 163 ++++++++++++++ ui/status-dropdown.ts | 512 ++++++++++++++++++------------------------ 3 files changed, 395 insertions(+), 290 deletions(-) diff --git a/main.ts b/main.ts index d37071a..9a2be53 100644 --- a/main.ts +++ b/main.ts @@ -147,6 +147,11 @@ export default class NoteStatus extends Plugin { this.statusDropdown = new StatusDropdown(this.app, this.settings, this.statusService); this.explorerIntegration = new ExplorerIntegration(this.app, this.settings, this.statusService); this.statusContextMenu = new StatusContextMenu(this.app, this.settings, this.statusService); + + // Register event to update toolbar button when plugin is loaded + this.app.workspace.onLayoutReady(() => { + this.statusDropdown.update(this.getCurrentStatuses()); + }); } /** @@ -369,6 +374,7 @@ export default class NoteStatus extends Plugin { } else { this.lastActiveFile = null; this.statusBar.update(['unknown']); + this.statusDropdown.update(['unknown']); // Add this line to update toolbar when no file is open } })); @@ -460,11 +466,13 @@ export default class NoteStatus extends Plugin { const activeFile = this.app.workspace.getActiveFile(); if (!activeFile || activeFile.extension !== 'md') { this.statusBar.update(['unknown']); + this.statusDropdown.update(['unknown']); // Add this line to update toolbar return; } - + const statuses = this.statusService.getFileStatuses(activeFile); this.statusBar.update(statuses); + this.statusDropdown.update(statuses); // Add this line to update toolbar } catch (error) { console.error('Error checking note status:', error); if (!this.hasShownErrorNotification) { diff --git a/styles.css b/styles.css index 5394ddb..64425eb 100644 --- a/styles.css +++ b/styles.css @@ -985,4 +985,167 @@ .note-status-pane { display: none !important; } +} + + + +/* Toolbar button styling */ +.note-status-toolbar-button-container { + display: flex; + align-items: center; + margin-right: 4px; +} + +.note-status-toolbar-button { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: var(--radius-s, 4px); + color: var(--text-normal); + padding: 6px; + cursor: pointer; + transition: all 0.15s var(--ease-out); + position: relative; +} + +.note-status-toolbar-button:hover { + background-color: var(--background-modifier-hover); + color: var(--text-accent); +} + +.note-status-toolbar-badge-container { + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.note-status-toolbar-icon { + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.note-status-count-badge { + position: absolute; + top: -5px; + right: -8px; + background-color: var(--interactive-accent); + color: var(--text-on-accent); + border-radius: 10px; + font-size: 10px; + min-width: 14px; + height: 14px; + padding: 0 3px; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; +} + +/* Toolbar popover styling */ +.note-status-toolbar-popover { + width: 280px; + max-width: 90vw; + border-radius: var(--radius-m, 8px); + overflow: hidden; + box-shadow: var(--shadow-m), 0 0 15px rgba(0, 0, 0, 0.1); + border: 1px solid var(--background-modifier-border); + background: var(--background-primary); +} + +.note-status-popover-header { + padding: 12px; + border-bottom: 1px solid var(--background-modifier-border); + background: var(--background-secondary-alt); + display: flex; + align-items: center; +} + +.note-status-popover-title { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + color: var(--text-normal); +} + +.note-status-popover-icon { + display: flex; + align-items: center; + justify-content: center; + color: var(--text-accent); +} + +.note-status-popover-chips { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 12px; + background: var(--background-primary-alt); + max-height: 120px; + overflow-y: auto; + border-bottom: 1px solid var(--background-modifier-border); +} + +/* Empty indicator for popover */ +.note-status-popover-chips .note-status-empty-indicator { + width: 100%; + min-height: auto; + padding: 8px 0; + justify-content: flex-start; + background: transparent; +} + +/* Ensure the button container has proper z-index and positioning */ +.note-status-toolbar-button-container { + display: flex !important; + align-items: center !important; + margin-right: 4px !important; + position: relative !important; + z-index: 10 !important; +} + +/* Ensure button is visible with proper sizing */ +.note-status-toolbar-button { + display: flex !important; + align-items: center !important; + justify-content: center !important; + background: transparent !important; + border: none !important; + border-radius: var(--radius-s, 4px) !important; + color: var(--text-normal) !important; + padding: 6px !important; + cursor: pointer !important; + transition: all 0.15s var(--ease-out) !important; + position: relative !important; + width: 28px !important; + height: 28px !important; + min-width: 28px !important; + min-height: 28px !important; +} + +/* Make icons more visible */ +.note-status-toolbar-icon { + font-size: 16px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; +} + +/* Alternative selectors for toolbar button placement */ +.view-header .view-actions .note-status-toolbar-button-container, +.view-header-actions .note-status-toolbar-button-container, +.workspace-ribbon.mod-right .note-status-toolbar-button-container { + display: flex !important; + visibility: visible !important; + opacity: 1 !important; +} + +/* Debug outline to help identify the button (you can remove this after debugging) */ +.note-status-toolbar-button-container { + outline: 1px solid rgba(255, 0, 0, 0.3) !important; } \ No newline at end of file diff --git a/ui/status-dropdown.ts b/ui/status-dropdown.ts index c72187e..b2f158c 100644 --- a/ui/status-dropdown.ts +++ b/ui/status-dropdown.ts @@ -3,7 +3,7 @@ import { NoteStatusSettings, Status } from '../models/types'; import { StatusService } from '../services/status-service'; /** - * Enhanced status dropdown with improved UI/UX and touch support + * Enhanced status dropdown with toolbar integration */ export class StatusDropdown { private app: any; @@ -15,6 +15,8 @@ export class StatusDropdown { private statusPopover?: HTMLElement; private isPopoverOpen = false; private clickOutsideHandler: any; + private toolbarButtonContainer?: HTMLElement; + private toolbarButton?: HTMLElement; // Animation timings private readonly ANIMATION_DURATION = 220; @@ -26,6 +28,118 @@ export class StatusDropdown { // Bind methods to preserve this context this.handleClickOutside = this.handleClickOutside.bind(this); + + // Initialize toolbar button after layout is ready + this.app.workspace.onLayoutReady(() => { + this.initToolbarButton(); + }); + } + + /** + * Initialize the toolbar button in the Obsidian ribbon + */ + private initToolbarButton(): void { + // Wait for next tick to ensure the UI is fully rendered + setTimeout(() => { + // Try different selectors to find the toolbar + const toolbarContainer = document.querySelector('.view-header-actions') || + document.querySelector('.view-actions') || + document.querySelector('.workspace-ribbon.mod-right'); + + if (!toolbarContainer) { + console.error('Note Status: Could not find toolbar container'); + return; + } + + // Create button container for proper positioning + this.toolbarButtonContainer = document.createElement('div'); + this.toolbarButtonContainer.addClass('note-status-toolbar-button-container'); + + // Create the button element + this.toolbarButton = document.createElement('button'); + this.toolbarButton.addClass('note-status-toolbar-button', 'clickable-icon'); + this.toolbarButton.setAttribute('aria-label', 'Note Status'); + + // Update initial button state + this.updateToolbarButton(); + + // Add click handler + this.toolbarButton.addEventListener('click', (e) => { + e.stopPropagation(); + this.toggleStatusPopover(this.toolbarButton); + }); + + // Add the button to the container + this.toolbarButtonContainer.appendChild(this.toolbarButton); + + // Insert at specific position - before the last element (which is usually the more options button) + // If there's no children or insertion fails, just append it + try { + if (toolbarContainer.children.length > 0) { + // Insert before the last element (more options button) + toolbarContainer.insertBefore( + this.toolbarButtonContainer, + toolbarContainer.children[toolbarContainer.children.length - 1] + ); + } else { + toolbarContainer.appendChild(this.toolbarButtonContainer); + } + + console.log('Note Status: Toolbar button added successfully'); + } catch (error) { + console.error('Note Status: Error inserting toolbar button', error); + // Fallback - just append it + toolbarContainer.appendChild(this.toolbarButtonContainer); + } + }, 500); // Waiting 500ms to ensure the UI is ready + } + + /** + * Updates the toolbar button appearance based on current statuses + */ + private updateToolbarButton(): void { + if (!this.toolbarButton) return; + + // Clear existing content + this.toolbarButton.empty(); + + // Check if we have a valid status + const hasValidStatus = this.currentStatuses.length > 0 && + !this.currentStatuses.every(status => status === 'unknown'); + + // Create badge container + const badgeContainer = document.createElement('div'); + badgeContainer.addClass('note-status-toolbar-badge-container'); + + if (hasValidStatus) { + // Show primary status icon and indicator for multiple statuses + const primaryStatus = this.currentStatuses[0]; + const statusInfo = this.statusService.getAllStatuses().find(s => s.name === primaryStatus); + + if (statusInfo) { + // Primary status icon + const iconSpan = document.createElement('span'); + iconSpan.addClass(`note-status-toolbar-icon`, `status-${primaryStatus}`); + iconSpan.textContent = statusInfo.icon; + badgeContainer.appendChild(iconSpan); + + // Add count indicator if multiple statuses + if (this.settings.useMultipleStatuses && this.currentStatuses.length > 1) { + const countBadge = document.createElement('span'); + countBadge.addClass('note-status-count-badge'); + countBadge.textContent = `+${this.currentStatuses.length - 1}`; + badgeContainer.appendChild(countBadge); + } + } + } else { + // Show default status icon + const iconSpan = document.createElement('span'); + iconSpan.addClass('note-status-toolbar-icon', 'status-unknown'); + iconSpan.textContent = '📌'; // Default tag icon + badgeContainer.appendChild(iconSpan); + } + + this.toolbarButton.appendChild(badgeContainer); } /** @@ -39,8 +153,13 @@ export class StatusDropdown { this.currentStatuses = [...currentStatuses]; // Create a copy to ensure it's updated } - // Clear and render fresh dropdown - force a complete UI rebuild - this.render(); + // Update toolbar button + this.updateToolbarButton(); + + // Clear and render fresh dropdown if it exists + if (this.dropdownContainer) { + this.render(); + } } /** @@ -48,172 +167,78 @@ export class StatusDropdown { */ public updateSettings(settings: NoteStatusSettings): void { this.settings = settings; - this.render(); + this.updateToolbarButton(); + if (this.dropdownContainer) { + this.render(); + } } /** * Render or remove the dropdown based on settings */ public render(): void { - // Get current active view - const view = this.app.workspace.getActiveViewOfType(MarkdownView); - - // Find all existing dropdowns and remove them - document.querySelectorAll('.note-status-dropdown').forEach(el => el.remove()); - this.dropdownContainer = undefined; - - // If setting is turned off or no active markdown view, don't render - if (!this.settings.showStatusDropdown || !view) { + // Remove existing popover if open + if (this.isPopoverOpen) { + this.closeStatusPopover(); + } + } + + /** + * Toggle the status popover + */ + private toggleStatusPopover(targetEl?: HTMLElement): void { + if (this.isPopoverOpen) { + this.closeStatusPopover(); + } else { + this.openStatusPopover(targetEl); + } + } + + /** + * Open the status selection popover + * @param targetEl - The element that triggered the popover (for positioning) + */ + private openStatusPopover(targetEl?: HTMLElement): void { + if (this.isPopoverOpen) { + this.closeStatusPopover(); return; } - - // Get container for the dropdown - const contentEl = view.contentEl; - // Create new dropdown container - const dropdownEl = document.createElement('div'); - dropdownEl.addClass('note-status-dropdown', this.settings.dropdownPosition); + this.isPopoverOpen = true; - // Add animation class - dropdownEl.addClass('note-status-animate-in'); + // Create popover element + this.statusPopover = document.createElement('div'); + this.statusPopover.addClass('note-status-popover', 'note-status-toolbar-popover'); + document.body.appendChild(this.statusPopover); - // Insert at correct position - if (this.settings.dropdownPosition === 'top') { - contentEl.insertBefore(dropdownEl, contentEl.firstChild); - } else { - contentEl.appendChild(dropdownEl); - } - - // Store reference - this.dropdownContainer = dropdownEl; - - // Create header with label and toggle button - this.createDropdownHeader(); + // Create header + const headerEl = this.statusPopover.createDiv({ cls: 'note-status-popover-header' }); - // Create chips display for current statuses - this.createStatusChips(); - - // Add "Add Status" button that shows popover - this.createAddStatusButton(); - - // Remove animation class after animation completes - setTimeout(() => { - dropdownEl.removeClass('note-status-animate-in'); - }, this.ANIMATION_DURATION); - } - - /** - * Create the dropdown header with label and control buttons - */ - private createDropdownHeader(): void { - if (!this.dropdownContainer) return; - - const headerEl = this.dropdownContainer.createDiv({ cls: 'note-status-dropdown-header' }); - - // Create title with icon - const titleEl = headerEl.createDiv({ cls: 'note-status-dropdown-title' }); - const iconContainer = titleEl.createDiv({ cls: 'note-status-dropdown-icon' }); + // Title with icon + const titleEl = headerEl.createDiv({ cls: 'note-status-popover-title' }); + const iconContainer = titleEl.createDiv({ cls: 'note-status-popover-icon' }); setIcon(iconContainer, 'tag'); - titleEl.createSpan({ text: 'Note Status', cls: 'note-status-dropdown-label' }); + titleEl.createSpan({ text: 'Note Status', cls: 'note-status-popover-label' }); - // Add action buttons - const actionsEl = headerEl.createDiv({ cls: 'note-status-dropdown-actions' }); - - // Toggle multi/single mode button - const toggleModeButton = actionsEl.createEl('button', { - cls: 'note-status-action-button', - attr: { - 'aria-label': this.settings.useMultipleStatuses ? 'Switch to single status mode' : 'Switch to multiple statuses mode', - 'title': this.settings.useMultipleStatuses ? 'Switch to single status mode' : 'Switch to multiple statuses mode' - } - }); - setIcon(toggleModeButton, this.settings.useMultipleStatuses ? 'list-minus' : 'list-plus'); - - toggleModeButton.addEventListener('click', async (e) => { - e.stopPropagation(); - - // Add animation - toggleModeButton.addClass('note-status-button-active'); - - // Update setting - this.settings.useMultipleStatuses = !this.settings.useMultipleStatuses; - - // Trigger settings save - window.dispatchEvent(new CustomEvent('note-status:settings-changed')); - - // Re-render the dropdown - setTimeout(() => { - this.render(); - new Notice(`Switched to ${this.settings.useMultipleStatuses ? 'multiple' : 'single'} status mode`); - }, 150); - }); - - // Close button - const closeButton = actionsEl.createEl('button', { - cls: 'note-status-action-button', - attr: { - 'aria-label': 'Hide status bar', - 'title': 'Hide status bar' - } - }); - setIcon(closeButton, 'x'); - - closeButton.addEventListener('click', (e) => { - e.stopPropagation(); - - // Add animation - this.dropdownContainer?.addClass('note-status-animate-out'); - - setTimeout(() => { - this.settings.showStatusDropdown = false; - this.render(); - - // Trigger settings save - window.dispatchEvent(new CustomEvent('note-status:settings-changed')); - - new Notice('Status dropdown hidden'); - }, this.ANIMATION_DURATION); - }); - } - - /** - * Create the chips display for current statuses - */ - private createStatusChips(): void { - if (!this.dropdownContainer) return; - - // Make sure previous container is removed - if (this.statusChipsContainer) { - this.statusChipsContainer.remove(); - } - - // Create container for status chips - this.statusChipsContainer = this.dropdownContainer.createDiv({ - cls: 'note-status-chips-container' - }); + // Current status chips + const chipsContainer = this.statusPopover.createDiv({ cls: 'note-status-popover-chips' }); // Show 'No status' indicator if no statuses or only unknown status if (this.currentStatuses.length === 0 || (this.currentStatuses.length === 1 && this.currentStatuses[0] === 'unknown')) { - if (this.statusChipsContainer) { - this.statusChipsContainer.createDiv({ - cls: 'note-status-empty-indicator', - text: 'No status assigned' - }); - } - return; - } - - // Add chip for each status - if (this.statusChipsContainer) { + chipsContainer.createDiv({ + cls: 'note-status-empty-indicator', + text: 'No status assigned' + }); + } else { + // Add chip for each status this.currentStatuses.forEach(status => { if (status === 'unknown') return; // Skip unknown status const statusObj = this.statusService.getAllStatuses().find(s => s.name === status); if (!statusObj) return; - if (!this.statusChipsContainer) return; - const chipEl = this.statusChipsContainer.createDiv({ + const chipEl = chipsContainer.createDiv({ cls: `note-status-chip status-${status}` }); @@ -254,6 +279,15 @@ export class StatusDropdown { // Get updated statuses const updatedStatuses = this.statusService.getFileStatuses(this.app.workspace.getActiveFile()); + // Update current statuses + this.currentStatuses = updatedStatuses; + + // Update toolbar button + this.updateToolbarButton(); + + // Refresh chips + this.openStatusPopover(targetEl); + // Dispatch event for UI update window.dispatchEvent(new CustomEvent('note-status:status-changed', { detail: { statuses: updatedStatuses } @@ -261,65 +295,8 @@ export class StatusDropdown { }, 150); }); } - - // Make the whole chip clickable if in single status mode - if (!this.settings.useMultipleStatuses) { - chipEl.addClass('clickable'); - chipEl.addEventListener('click', (e) => { - this.openStatusPopover(chipEl); - }); - } }); } - } - - /** - * Create the add status button - */ - private createAddStatusButton(): void { - if (!this.dropdownContainer) return; - - // Only show add button if using multiple statuses or no status is set - if (!this.settings.useMultipleStatuses && - this.currentStatuses.length > 0 && - this.currentStatuses[0] !== 'unknown') { - return; - } - - const addButton = this.dropdownContainer.createDiv({ - cls: 'note-status-add-button', - attr: { - 'aria-label': 'Add status', - 'title': 'Add status' - } - }); - - const iconContainer = addButton.createSpan({ cls: 'note-status-add-icon' }); - setIcon(iconContainer, 'plus-circle'); - addButton.createSpan({ text: 'Add status', cls: 'note-status-add-text' }); - - // Add click handler to open status popover - addButton.addEventListener('click', (e) => { - this.openStatusPopover(addButton); - }); - } - - /** - * Open the status selection popover - * @param targetEl - The element that triggered the popover (for positioning) - */ - private openStatusPopover(targetEl?: HTMLElement): void { - if (this.isPopoverOpen) { - this.closeStatusPopover(); - return; - } - - this.isPopoverOpen = true; - - // Create popover element - if (!this.dropdownContainer) return; - - this.statusPopover = this.dropdownContainer.createDiv({ cls: 'note-status-popover' }); // Create search filter const searchContainer = this.statusPopover.createDiv({ cls: 'note-status-popover-search' }); @@ -387,42 +364,33 @@ export class StatusDropdown { // Add selection animation optionEl.addClass('note-status-option-selecting'); - // First close the popover if in single status mode to improve perceived performance - if (!this.settings.useMultipleStatuses) { - this.closeStatusPopover(); - } - // Apply status changes after brief delay for animation setTimeout(async () => { if (this.settings.useMultipleStatuses) { await this.statusService.toggleNoteStatus(status.name); } else { await this.statusService.updateNoteStatuses([status.name]); + // Close popover in single status mode + this.closeStatusPopover(); } // Get fresh status from file const freshStatuses = this.statusService.getFileStatuses(activeFile); - // Force-update both the current instance and trigger global update - // Update dropdown display first + // Update current statuses and toolbar button this.currentStatuses = [...freshStatuses]; - this.render(); + this.updateToolbarButton(); - // Then notify other components like status bar + // Refresh status options if popover still open + if (this.isPopoverOpen && this.settings.useMultipleStatuses) { + populateOptions(searchInput.value); + } + + // Dispatch events for UI update window.dispatchEvent(new CustomEvent('note-status:status-changed', { detail: { statuses: freshStatuses } })); - - // Also dispatch the refresh-ui event to update the status panel window.dispatchEvent(new CustomEvent('note-status:refresh-ui')); - - // Keep popover open for multi-select mode - if (this.settings.useMultipleStatuses) { - // Re-open the popover to maintain multi-select mode - setTimeout(() => { - this.openStatusPopover(targetEl); - }, 10); - } }, 150); } catch (error) { console.error('Error updating status:', error); @@ -440,8 +408,8 @@ export class StatusDropdown { populateOptions(searchInput.value); }); - // Position the popover relative to the target element - this.positionPopover(targetEl); + // Position the popover relative to the toolbar button + this.positionToolbarPopover(targetEl); // Add animation class this.statusPopover.addClass('note-status-popover-animate-in'); @@ -462,69 +430,25 @@ export class StatusDropdown { } /** - * Position the popover relative to the target element + * Position the popover relative to the toolbar button */ - private positionPopover(targetEl?: HTMLElement): void { - if (!this.statusPopover || !this.dropdownContainer) return; + private positionToolbarPopover(targetEl?: HTMLElement): void { + if (!this.statusPopover || !targetEl) return; // Reset positioning - this.statusPopover.style.position = 'absolute'; - this.statusPopover.style.top = ''; - this.statusPopover.style.bottom = ''; - this.statusPopover.style.left = ''; - this.statusPopover.style.right = ''; - this.statusPopover.style.transform = ''; + this.statusPopover.style.position = 'fixed'; + this.statusPopover.style.zIndex = '999'; - if (targetEl) { - // Get target element's position - const targetRect = targetEl.getBoundingClientRect(); - const containerRect = this.dropdownContainer.getBoundingClientRect(); - - // Calculate popover position relative to the dropdown container - const top = targetRect.bottom - containerRect.top; - const left = targetRect.left - containerRect.left; - - // Position popover above or below target element based on available space - const viewportHeight = window.innerHeight; - const spaceBelow = viewportHeight - targetRect.bottom; - const availableStatuses = this.statusService.getAllStatuses().filter(s => s.name !== 'unknown'); - const popoverHeight = Math.min(300, availableStatuses.length * 40 + 60); // Estimate popover height - - if (spaceBelow < popoverHeight && targetRect.top > popoverHeight) { - // Position above if not enough space below but enough space above - this.statusPopover.style.bottom = `${containerRect.bottom - targetRect.top}px`; - this.statusPopover.style.maxHeight = `${targetRect.top - 20}px`; - this.statusPopover.classList.add('popover-top'); - this.statusPopover.classList.remove('popover-bottom'); - } else { - // Position below (default) - this.statusPopover.style.top = `${top}px`; - this.statusPopover.style.maxHeight = `${viewportHeight - targetRect.bottom - 20}px`; - this.statusPopover.classList.add('popover-bottom'); - this.statusPopover.classList.remove('popover-top'); - } - - // Center horizontally relative to target - const targetWidth = targetRect.width; - const popoverWidth = Math.min(250, targetWidth * 1.5); - this.statusPopover.style.width = `${popoverWidth}px`; - this.statusPopover.style.left = `${left + (targetWidth / 2) - (popoverWidth / 2)}px`; - - // Ensure popover doesn't go out of container bounds - const leftBound = parseInt(this.statusPopover.style.left); - if (leftBound < 10) { - this.statusPopover.style.left = '10px'; - } else if (leftBound + popoverWidth > containerRect.width - 10) { - this.statusPopover.style.left = `${containerRect.width - popoverWidth - 10}px`; - } - } else { - // Default positioning if no target element - this.statusPopover.style.top = '100%'; - this.statusPopover.style.left = '50%'; - this.statusPopover.style.transform = 'translateX(-50%)'; - this.statusPopover.style.width = '250px'; - this.statusPopover.classList.add('popover-bottom'); - } + // Get target element's position + const targetRect = targetEl.getBoundingClientRect(); + + // Position below the button + this.statusPopover.style.top = `${targetRect.bottom + 5}px`; + this.statusPopover.style.right = `${window.innerWidth - targetRect.right}px`; + + // Set max height based on viewport + const maxHeight = window.innerHeight - targetRect.bottom - 40; + this.statusPopover.style.maxHeight = `${maxHeight}px`; } /** @@ -540,7 +464,8 @@ export class StatusDropdown { * Handle click outside the popover */ private handleClickOutside(e: MouseEvent) { - if (this.statusPopover && !this.statusPopover.contains(e.target as Node)) { + if (this.statusPopover && !this.statusPopover.contains(e.target as Node) && + this.toolbarButton && !this.toolbarButton.contains(e.target as Node)) { this.closeStatusPopover(); } } @@ -643,6 +568,10 @@ export class StatusDropdown { // Get updated statuses const updatedStatuses = this.statusService.getFileStatuses(activeFile); + // Update current statuses and toolbar button + this.currentStatuses = updatedStatuses; + this.updateToolbarButton(); + // Dispatch events for UI update window.dispatchEvent(new CustomEvent('note-status:status-changed', { detail: { statuses: updatedStatuses } @@ -654,6 +583,10 @@ export class StatusDropdown { item.onClick(async () => { await this.statusService.updateNoteStatus(status.name); + // Update current statuses and toolbar button + this.currentStatuses = [status.name]; + this.updateToolbarButton(); + // Dispatch events for UI update window.dispatchEvent(new CustomEvent('note-status:status-changed', { detail: { statuses: [status.name] } @@ -698,12 +631,13 @@ export class StatusDropdown { * Remove dropdown when plugin is unloaded */ public unload(): void { - if (this.dropdownContainer) { - this.dropdownContainer.remove(); - this.dropdownContainer = undefined; - } - - // Remove any popover and event listeners + // Close any open popover this.closeStatusPopover(); + + // Remove toolbar button + if (this.toolbarButtonContainer) { + this.toolbarButtonContainer.remove(); + this.toolbarButtonContainer = undefined; + } } } \ No newline at end of file