mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
Replace relative imports (../../../) with TypeScript path aliases (@/) throughout the codebase for improved readability and maintainability. - Configure path alias in tsconfig.json with @ mapping to src/ - Update esbuild from v0.13.12 to v0.25.9 for modern features - Add alias configuration to esbuild.config.mjs for build support - Refactor build configuration to use context API for watch mode - Update all component and utility imports to use @/ prefix - Fix incorrect CSS import paths in habit and settings components - Maintain consistent import structure across 148 files This change simplifies import statements, makes the codebase more maintainable by avoiding deeply nested relative paths, and modernizes the build toolchain.
200 lines
No EOL
4.9 KiB
TypeScript
200 lines
No EOL
4.9 KiB
TypeScript
import { Component, App } from "obsidian";
|
|
import { TreeNode, ProjectNodeData } from "@/types/tree";
|
|
import { TreeComponent } from "@/components/ui/tree/TreeComponent";
|
|
import { Task } from "@/types/task";
|
|
import {
|
|
buildProjectTreeFromTasks,
|
|
findNodeByPath,
|
|
getAllDescendants
|
|
} from "@/core/project-tree-builder";
|
|
import TaskProgressBarPlugin from "@/index";
|
|
|
|
/**
|
|
* Project tree component for hierarchical project display
|
|
*/
|
|
export class ProjectTreeComponent extends Component {
|
|
private treeComponent: TreeComponent<ProjectNodeData>;
|
|
private projectTree: TreeNode<ProjectNodeData> | null = null;
|
|
private allTasks: Task[] = [];
|
|
|
|
// Events
|
|
public onNodeSelected?: (selectedNodes: Set<string>, tasks: Task[]) => void;
|
|
public onMultiSelectToggled?: (isMultiSelect: boolean) => void;
|
|
|
|
constructor(
|
|
private parentEl: HTMLElement,
|
|
private app: App,
|
|
private plugin: TaskProgressBarPlugin
|
|
) {
|
|
super();
|
|
}
|
|
|
|
onload(): void {
|
|
// Create tree component with project-specific configuration
|
|
this.treeComponent = new TreeComponent<ProjectNodeData>(
|
|
this.parentEl,
|
|
{
|
|
classPrefix: 'project-tree',
|
|
indentSize: 24, // 使用稍大的缩进以适应项目层级
|
|
showToggle: true,
|
|
enableSelection: true,
|
|
enableMultiSelect: true,
|
|
stateKey: 'task-genius-project-tree-state',
|
|
autoExpandLevel: 1,
|
|
|
|
renderContent: (node, contentEl) => {
|
|
// Project name
|
|
const nameEl = contentEl.createSpan({
|
|
cls: "project-tree-item-name",
|
|
text: node.name
|
|
});
|
|
|
|
// Task count badges
|
|
const countsEl = contentEl.createSpan({
|
|
cls: "project-tree-item-counts"
|
|
});
|
|
|
|
// Direct task count
|
|
if (node.data.directTaskCount > 0) {
|
|
countsEl.createSpan({
|
|
cls: "project-tree-item-count-direct",
|
|
text: node.data.directTaskCount.toString()
|
|
});
|
|
}
|
|
|
|
// Total task count (if has children)
|
|
if (node.children.length > 0 && node.data.totalTaskCount > node.data.directTaskCount) {
|
|
countsEl.createSpan({
|
|
cls: "project-tree-item-count-total",
|
|
text: node.data.totalTaskCount.toString()
|
|
});
|
|
}
|
|
},
|
|
|
|
iconResolver: (node) => {
|
|
// Use different icons based on node state
|
|
if (node.children.length > 0) {
|
|
return node.isExpanded ? "folder-open" : "folder";
|
|
}
|
|
return "file";
|
|
},
|
|
|
|
onNodeSelected: (selectedNodes) => {
|
|
// Get tasks for selected nodes
|
|
const tasks = this.getTasksForSelection(selectedNodes);
|
|
|
|
// Trigger event
|
|
if (this.onNodeSelected) {
|
|
this.onNodeSelected(selectedNodes, tasks);
|
|
}
|
|
},
|
|
|
|
onMultiSelectToggled: (isMultiSelect) => {
|
|
if (this.onMultiSelectToggled) {
|
|
this.onMultiSelectToggled(isMultiSelect);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
this.addChild(this.treeComponent);
|
|
this.treeComponent.load();
|
|
}
|
|
|
|
/**
|
|
* Build tree from tasks
|
|
*/
|
|
public buildTree(tasks: Task[]): void {
|
|
this.allTasks = tasks;
|
|
|
|
// Build project tree
|
|
const separator = this.plugin.settings.projectPathSeparator || "/";
|
|
this.projectTree = buildProjectTreeFromTasks(tasks, separator);
|
|
|
|
// Set tree in component
|
|
if (this.projectTree) {
|
|
this.treeComponent.setTree(this.projectTree);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the project tree directly (instead of building from tasks)
|
|
*/
|
|
public setTree(tree: TreeNode<ProjectNodeData>, tasks: Task[]): void {
|
|
this.projectTree = tree;
|
|
this.allTasks = tasks;
|
|
|
|
// Set tree in component
|
|
this.treeComponent.setTree(tree);
|
|
}
|
|
|
|
/**
|
|
* Get tasks for current selection (includes child nodes)
|
|
*/
|
|
private getTasksForSelection(selectedNodes: Set<string>): Task[] {
|
|
if (!this.projectTree || selectedNodes.size === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Collect all task IDs from selected nodes and their children
|
|
const taskIds = new Set<string>();
|
|
|
|
for (const nodePath of selectedNodes) {
|
|
const node = findNodeByPath(this.projectTree, nodePath);
|
|
if (node) {
|
|
// Add all tasks from this node (includes children)
|
|
node.data.allTaskIds.forEach(id => taskIds.add(id));
|
|
}
|
|
}
|
|
|
|
// Filter tasks by collected IDs
|
|
return this.allTasks.filter(task => taskIds.has(task.id));
|
|
}
|
|
|
|
/**
|
|
* Set multi-select mode
|
|
*/
|
|
public setMultiSelectMode(enabled: boolean): void {
|
|
this.treeComponent.setMultiSelectMode(enabled);
|
|
}
|
|
|
|
/**
|
|
* Get selected paths
|
|
*/
|
|
public getSelectedPaths(): Set<string> {
|
|
return this.treeComponent.getSelectedPaths();
|
|
}
|
|
|
|
/**
|
|
* Set selected paths
|
|
*/
|
|
public setSelectedPaths(paths: Set<string>): void {
|
|
this.treeComponent.setSelectedPaths(paths);
|
|
}
|
|
|
|
/**
|
|
* Clear selection
|
|
*/
|
|
public clearSelection(): void {
|
|
this.treeComponent.clearSelection();
|
|
}
|
|
|
|
/**
|
|
* Expand all nodes
|
|
*/
|
|
public expandAll(): void {
|
|
this.treeComponent.expandAll();
|
|
}
|
|
|
|
/**
|
|
* Collapse all nodes
|
|
*/
|
|
public collapseAll(): void {
|
|
this.treeComponent.collapseAll();
|
|
}
|
|
|
|
onunload(): void {
|
|
// The tree component will be cleaned up automatically
|
|
// as it's added as a child component
|
|
}
|
|
} |