feat(settings): add workspace selector component

Add a new workspace selector component to the settings tab that allows
users to switch between different workspace configurations. The selector
displays the current workspace icon and name, and provides a dropdown
menu to switch between available workspaces.

- Create WorkspaceSettingsSelector component with workspace switching
- Add styling for the workspace selector UI
- Support workspace icon display and hover effects
- Integrate with existing workspace manager
This commit is contained in:
Quorafind 2025-09-27 08:53:08 +08:00
parent 41f1d95576
commit 27c426c474
2 changed files with 278 additions and 0 deletions

View file

@ -0,0 +1,148 @@
import { Menu, setIcon } from "obsidian";
import type TaskProgressBarPlugin from "@/index";
import { WorkspaceData } from "@/experimental/v2/types/workspace";
import { t } from "@/translations/helper";
import { TaskProgressBarSettingTab } from "@/setting";
export class WorkspaceSettingsSelector {
private containerEl: HTMLElement;
private plugin: TaskProgressBarPlugin;
private settingTab: TaskProgressBarSettingTab;
private currentWorkspaceId: string;
private buttonEl: HTMLElement;
constructor(
containerEl: HTMLElement,
plugin: TaskProgressBarPlugin,
settingTab: TaskProgressBarSettingTab
) {
this.containerEl = containerEl;
this.plugin = plugin;
this.settingTab = settingTab;
this.currentWorkspaceId =
plugin.workspaceManager?.getActiveWorkspace().id || "";
this.render();
}
private render() {
// Create workspace selector container
const selectorContainer = this.containerEl.createDiv({
cls: "workspace-settings-selector",
});
if (!this.plugin.workspaceManager) return;
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
this.buttonEl = selectorContainer.createDiv({
cls: "workspace-settings-selector-button",
});
// Workspace icon
const workspaceIcon = this.buttonEl.createDiv({
cls: "workspace-icon",
});
setIcon(workspaceIcon, currentWorkspace.icon || "layers");
// Workspace name
const workspaceName = this.buttonEl.createSpan({
cls: "workspace-name",
text: currentWorkspace.name,
});
// Dropdown arrow
const dropdownIcon = this.buttonEl.createDiv({
cls: "workspace-dropdown-icon",
});
setIcon(dropdownIcon, "chevron-down");
// Click handler
this.buttonEl.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
this.showWorkspaceMenu(e);
});
}
private showWorkspaceMenu(event: MouseEvent) {
if (!this.plugin.workspaceManager) return;
const menu = new Menu();
const workspaces = this.plugin.workspaceManager.getAllWorkspaces();
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
// Add workspace items for switching
workspaces.forEach((workspace) => {
menu.addItem((item) => {
item.setTitle(workspace.name)
.setIcon(workspace.icon || "layers")
.onClick(async () => {
await this.switchWorkspace(workspace.id);
});
// Mark current workspace with checkmark
if (workspace.id === currentWorkspace.id) {
item.setChecked(true);
}
});
});
// Add separator
menu.addSeparator();
// Add "Manage Workspaces..." option to navigate to settings
menu.addItem((item) => {
item.setTitle(t("Manage Workspaces..."))
.setIcon("settings")
.onClick(() => {
// Navigate to workspace settings tab
this.settingTab.switchToTab("workspaces");
});
});
menu.showAtMouseEvent(event);
}
private async switchWorkspace(workspaceId: string) {
if (!this.plugin.workspaceManager) return;
// Switch workspace
await this.plugin.workspaceManager.setActiveWorkspace(workspaceId);
this.currentWorkspaceId = workspaceId;
// Update button display
this.updateDisplay();
// Trigger settings reload to reflect workspace change
await this.plugin.saveSettings();
this.settingTab.applySettingsUpdate();
}
private updateDisplay() {
if (!this.plugin.workspaceManager || !this.buttonEl) return;
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
// Update icon
const iconEl = this.buttonEl.querySelector(".workspace-icon");
if (iconEl) {
iconEl.empty();
setIcon(iconEl as HTMLElement, currentWorkspace.icon || "layers");
}
// Update name
const nameEl = this.buttonEl.querySelector(".workspace-name");
if (nameEl) {
nameEl.textContent = currentWorkspace.name;
}
}
public setWorkspace(workspaceId: string) {
this.currentWorkspaceId = workspaceId;
this.updateDisplay();
}
}

View file

@ -0,0 +1,130 @@
/* Workspace Settings Selector Styles */
/* Main container for the header bar */
.tg-settings-header-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
width: 100%;
}
/* Container for workspace selector */
.tg-workspace-selector-container {
flex-shrink: 0;
}
/* Workspace selector button */
.workspace-settings-selector {
display: inline-block;
}
.workspace-settings-selector-button {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background-color: var(--background-modifier-form-field);
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
cursor: pointer;
transition: all 0.15s ease;
min-width: 150px;
height: 34px; /* Match search bar height */
}
.workspace-settings-selector-button:hover {
background-color: var(--background-modifier-hover);
border-color: var(--background-modifier-border-hover);
}
.workspace-settings-selector-button:active {
background-color: var(--background-modifier-active-hover);
}
/* Workspace icon */
.workspace-settings-selector-button .workspace-icon {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
flex-shrink: 0;
}
.workspace-settings-selector-button .workspace-icon svg {
width: 16px;
height: 16px;
fill: var(--text-muted);
}
/* Workspace name */
.workspace-settings-selector-button .workspace-name {
flex: 1;
font-size: 14px;
color: var(--text-normal);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Dropdown arrow icon */
.workspace-settings-selector-button .workspace-dropdown-icon {
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
flex-shrink: 0;
margin-left: auto;
}
.workspace-settings-selector-button .workspace-dropdown-icon svg {
width: 12px;
height: 12px;
fill: var(--text-muted);
transition: transform 0.15s ease;
}
.workspace-settings-selector-button:hover .workspace-dropdown-icon svg {
fill: var(--text-normal);
}
/* Adjust search container to take remaining space */
.tg-settings-header-bar .tg-settings-search-container {
flex: 1;
max-width: none;
}
/* Ensure search input container fills width */
.tg-settings-header-bar .tg-settings-search-input-container {
width: 100%;
}
/* Main container adjustments */
.tg-settings-main-container {
width: 100%;
}
/* Ensure search results appear below the header bar */
.tg-settings-main-container .tg-settings-search-results {
margin-top: 8px;
}
/* Mobile responsive adjustments */
@media (max-width: 768px) {
.tg-settings-header-bar {
flex-direction: column;
align-items: stretch;
gap: 8px;
}
.workspace-settings-selector-button {
width: 100%;
min-width: unset;
}
.tg-settings-header-bar .tg-settings-search-container {
width: 100%;
}
}