mirror of
https://github.com/brendan-ch/todo-tracker-obsidian.git
synced 2026-04-19 08:10:29 +00:00
Initial commit
This commit is contained in:
117
src/views/todo-sidebar-view.ts
Normal file
117
src/views/todo-sidebar-view.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { ItemView, TFile, WorkspaceLeaf } from 'obsidian';
|
||||
import type { TodoItem } from '../core/types';
|
||||
import { parseTodos } from '../core/todo-parser';
|
||||
import { toggleTodo } from '../core/todo-transformer';
|
||||
import { createTodoItemEl } from './todo-item-component';
|
||||
|
||||
export const TODO_VIEW_TYPE = 'todo-tracker-view';
|
||||
|
||||
export class TodoSidebarView extends ItemView {
|
||||
private currentFile: TFile | null = null;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf) {
|
||||
super(leaf);
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return TODO_VIEW_TYPE;
|
||||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
return 'Todo Tracker';
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
return 'check-square';
|
||||
}
|
||||
|
||||
async onOpen(): Promise<void> {
|
||||
await this.refresh();
|
||||
|
||||
// Listen for active leaf changes
|
||||
this.registerEvent(
|
||||
this.app.workspace.on('active-leaf-change', () => {
|
||||
this.refresh();
|
||||
})
|
||||
);
|
||||
|
||||
// Listen for file modifications
|
||||
this.registerEvent(
|
||||
this.app.vault.on('modify', (file) => {
|
||||
if (this.currentFile && file.path === this.currentFile.path) {
|
||||
this.refresh();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async onClose(): Promise<void> {
|
||||
// Cleanup is handled automatically by registerEvent
|
||||
}
|
||||
|
||||
async refresh(): Promise<void> {
|
||||
const activeFile = this.app.workspace.getActiveFile();
|
||||
|
||||
// Check if it's a markdown file
|
||||
if (!activeFile || activeFile.extension !== 'md') {
|
||||
this.currentFile = null;
|
||||
this.renderEmpty('Open a markdown file to see its todos');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentFile = activeFile;
|
||||
|
||||
const content = await this.app.vault.read(activeFile);
|
||||
const { todos } = parseTodos(content);
|
||||
|
||||
if (todos.length === 0) {
|
||||
this.renderEmpty('No todos in this note');
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderTodos(todos, activeFile);
|
||||
}
|
||||
|
||||
private renderEmpty(message: string): void {
|
||||
const container = this.contentEl;
|
||||
container.empty();
|
||||
container.addClass('todo-tracker-container');
|
||||
|
||||
const emptyEl = container.createDiv({ cls: 'todo-tracker-empty' });
|
||||
emptyEl.setText(message);
|
||||
}
|
||||
|
||||
private renderTodos(todos: TodoItem[], file: TFile): void {
|
||||
const container = this.contentEl;
|
||||
container.empty();
|
||||
container.addClass('todo-tracker-container');
|
||||
|
||||
// Header with file name
|
||||
const headerEl = container.createDiv({ cls: 'todo-tracker-header' });
|
||||
headerEl.setText(file.basename);
|
||||
|
||||
// Todo list
|
||||
const listEl = container.createEl('ul', { cls: 'todo-tracker-list' });
|
||||
|
||||
for (const todo of todos) {
|
||||
const itemEl = createTodoItemEl(listEl, todo, {
|
||||
onToggle: () => this.handleToggle(file, todo),
|
||||
onMoveClick: () => this.handleMoveClick(todo, file),
|
||||
});
|
||||
listEl.appendChild(itemEl);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleToggle(file: TFile, todo: TodoItem): Promise<void> {
|
||||
await this.app.vault.process(file, (content) => {
|
||||
return toggleTodo(content, todo.lineNumber);
|
||||
});
|
||||
}
|
||||
|
||||
private handleMoveClick(todo: TodoItem, file: TFile): void {
|
||||
// Import dynamically to avoid circular dependencies
|
||||
import('../modals/note-select-modal').then(({ NoteSelectModal }) => {
|
||||
new NoteSelectModal(this.app, todo, file).open();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user