diff --git a/src/folderOverview/FileExplorer.ts b/src/folderOverview/FileExplorer.ts index e1edcf9..d38aaaf 100644 --- a/src/folderOverview/FileExplorer.ts +++ b/src/folderOverview/FileExplorer.ts @@ -55,15 +55,9 @@ export async function renderFileExplorer(plugin: FolderNotesPlugin, ctx: Markdow } }); - function handleVaultChange(eventType: string) { + folderOverview.on('vault-change', async () => { renderFileExplorer(plugin, ctx, root, yaml, pathBlacklist, folderOverview); - console.log(eventType); - } - - plugin.app.vault.on('rename', () => handleVaultChange('renamed')); - plugin.app.vault.on('create', () => handleVaultChange('created')); - plugin.app.vault.on('delete', () => handleVaultChange('deleted')); - + }); if (tFolder instanceof TFolder && !yaml.folderPath.trim().includes('/')) { addFiles(tFolder.children, overviewList, folderOverview); @@ -143,31 +137,7 @@ async function addFiles(files: TAbstractFile[], childrenElement: HTMLElement, fo } fileTitle.oncontextmenu = (e) => { - const fileMenu = new Menu(); - fileMenu.addSeparator(); - - fileMenu.addItem((item) => { - item.setTitle('Rename'); - item.setIcon('pencil'); - item.onClick(async () => { - plugin.app.fileManager.promptForFileRename(child) - }); - }); - - fileMenu.addItem((item) => { - item.setTitle('Delete'); - item.setIcon('trash'); - item.dom.addClass('is-warning'); - item.dom.setAttribute('data-section', 'danger') - item.onClick(() => { - plugin.app.fileManager.promptForDeletion(child) - }); - }); - - fileMenu.addSeparator(); - - plugin.app.workspace.trigger('file-menu', fileMenu, child, "folder-overview-file-context-menu", null); - fileMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + folderOverview.fileMenu(child, e); } fileTitle.createDiv({ @@ -229,32 +199,7 @@ function createFolderEL(plugin: FolderNotesPlugin, child: TFolder, folderOvervie }) folderTitle.oncontextmenu = (e) => { - const fileMenu = new Menu(); - fileMenu.addSeparator(); - - fileMenu.addItem((item) => { - item.setTitle('Rename'); - item.setIcon('pencil'); - item.onClick(async () => { - console.log('child', child) - new NewFolderNameModal(plugin.app, plugin, child).open(); - }); - }); - - fileMenu.addItem((item) => { - item.setTitle('Delete'); - item.setIcon('trash'); - item.dom.addClass('is-warning'); - item.dom.setAttribute('data-section', 'danger') - item.onClick(() => { - plugin.app.fileManager.promptForFolderDeletion(child) - }); - }); - - fileMenu.addSeparator(); - - plugin.app.workspace.trigger('file-menu', fileMenu, child, "folder-overview-file-context-menu", null); - fileMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + folderOverview.folderMenu(child, e); } } diff --git a/src/folderOverview/FolderOverview.ts b/src/folderOverview/FolderOverview.ts index f12d680..3fb111d 100644 --- a/src/folderOverview/FolderOverview.ts +++ b/src/folderOverview/FolderOverview.ts @@ -1,4 +1,4 @@ -import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile, stringifyYaml, Notice } from 'obsidian'; +import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile, stringifyYaml, Notice, Menu, MarkdownRenderChild } from 'obsidian'; import { getFolderNote } from '../functions/folderNoteFunctions'; import FolderNotesPlugin from '../main'; import { FolderOverviewSettings } from './ModalSettings'; @@ -7,6 +7,8 @@ import { getFolderPathFromString } from '../functions/utils'; import { getEl } from 'src/functions/styleFunctions'; import { renderFileExplorer } from './FileExplorer'; import { renderListOverview } from './ListStyle'; +import NewFolderNameModal from 'src/modals/NewFolderName'; +import { CustomEventEmitter } from 'src/events/EventEmitter'; export type includeTypes = 'folder' | 'markdown' | 'canvas' | 'other' | 'pdf' | 'image' | 'audio' | 'video' | 'all'; @@ -30,6 +32,7 @@ export type yamlSettings = { }; export class FolderOverview { + emitter: CustomEventEmitter; yaml: yamlSettings; plugin: FolderNotesPlugin; ctx: MarkdownPostProcessorContext; @@ -42,7 +45,10 @@ export class FolderOverview { sourceFolder: TFolder | undefined; root: HTMLElement; listEl: HTMLUListElement; + + private eventListeners: (() => void)[] = []; constructor(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, source: string, el: HTMLElement) { + this.emitter = new CustomEventEmitter(); let yaml: yamlSettings = parseYaml(source); if (!yaml) { yaml = {} as yamlSettings; } const includeTypes = yaml?.includeTypes || plugin.settings.defaultOverview.includeTypes || ['folder', 'markdown']; @@ -71,24 +77,72 @@ export class FolderOverview { showFolderNotes: yaml?.showFolderNotes === undefined || yaml?.showFolderNotes === null ? plugin.settings.defaultOverview.showFolderNotes : yaml?.showFolderNotes, disableCollapseIcon: yaml?.disableCollapseIcon === undefined || yaml?.disableCollapseIcon === null ? plugin.settings.defaultOverview.disableCollapseIcon : yaml?.disableCollapseIcon, } + + const customChild = new CustomMarkdownRenderChild(el, this); + ctx.addChild(customChild); + } + + on(event: string, listener: (data?: any) => void) { + this.emitter.on(event, listener); + } + + off(event: string, listener: (data?: any) => void) { + this.emitter.off(event, listener); + } + + private emit(event: string, data?: any) { + this.emitter.emit(event, data); + } + + handleVaultChange(eventType: string) { + this.emit(`vault-change`, eventType); + } + + disconnectListeners() { + this.eventListeners.forEach(unregister => unregister()); + this.eventListeners = []; + } + + registerListeners() { + const plugin = this.plugin; + const handleRename = () => this.handleVaultChange('renamed'); + const handleCreate = () => this.handleVaultChange('created'); + const handleDelete = () => this.handleVaultChange('deleted'); + + plugin.app.vault.on('rename', handleRename); + plugin.app.vault.on('create', handleCreate); + plugin.app.vault.on('delete', handleDelete); + + this.eventListeners.push(() => plugin.app.vault.off('rename', handleRename)); + this.eventListeners.push(() => plugin.app.vault.off('create', handleCreate)); + this.eventListeners.push(() => plugin.app.vault.off('delete', handleDelete)); } create(plugin: FolderNotesPlugin, source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) { el.empty(); el.parentElement?.classList.add('folder-overview-container'); + const root = el.createEl('div', { cls: 'folder-overview' }); this.root = root; + const titleEl = root.createEl('h1', { cls: 'folder-overview-title' }); + const ul = root.createEl('ul', { cls: 'folder-overview-list' }); this.listEl = ul; + if (this.yaml.includeTypes.length === 0) { return this.addEditButton(root); } let files: TAbstractFile[] = []; + const sourceFile = plugin.app.vault.getAbstractFileByPath(ctx.sourcePath); if (!sourceFile) return; + let sourceFolderPath = this.yaml.folderPath || getFolderPathFromString(ctx.sourcePath); if (!ctx.sourcePath.includes('/')) { sourceFolderPath = '/'; } + + this.registerListeners(); + let sourceFolder: TFolder | undefined; if (sourceFolderPath !== '/') { @@ -296,6 +350,64 @@ export class FolderOverview { }); return allFiles; } + + fileMenu(file: TFile, e: MouseEvent) { + const plugin = this.plugin; + const fileMenu = new Menu(); + fileMenu.addSeparator(); + + fileMenu.addItem((item) => { + item.setTitle('Rename'); + item.setIcon('pencil'); + item.onClick(async () => { + plugin.app.fileManager.promptForFileRename(file) + }); + }); + + fileMenu.addItem((item) => { + item.setTitle('Delete'); + item.setIcon('trash'); + item.dom.addClass('is-warning'); + item.dom.setAttribute('data-section', 'danger') + item.onClick(() => { + plugin.app.fileManager.promptForDeletion(file) + }); + }); + + fileMenu.addSeparator(); + + plugin.app.workspace.trigger('file-menu', fileMenu, file, "folder-overview-file-context-menu", null); + fileMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + } + + folderMenu(folder: TFolder, e: MouseEvent) { + const plugin = this.plugin; + const folderMenu = new Menu(); + folderMenu.addSeparator(); + + folderMenu.addItem((item) => { + item.setTitle('Rename'); + item.setIcon('pencil'); + item.onClick(async () => { + new NewFolderNameModal(plugin.app, plugin, folder).open(); + }); + }); + + folderMenu.addItem((item) => { + item.setTitle('Delete'); + item.setIcon('trash'); + item.dom.addClass('is-warning'); + item.dom.setAttribute('data-section', 'danger') + item.onClick(() => { + plugin.app.fileManager.promptForFolderDeletion(folder) + }); + }); + + folderMenu.addSeparator(); + + plugin.app.workspace.trigger('file-menu', folderMenu, folder, "folder-overview-folder-context-menu", null); + folderMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + } } export async function updateYaml(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, el: HTMLElement, yaml: yamlSettings) { @@ -332,4 +444,17 @@ export function getCodeBlockEndLine(text: string, startLine: number, count = 1) count++; } return line; +} + + +class CustomMarkdownRenderChild extends MarkdownRenderChild { + folderOverview: FolderOverview; + constructor(el: HTMLElement, folderOverview: FolderOverview) { + super(el); + this.folderOverview = folderOverview; + } + + onunload() { + this.folderOverview.disconnectListeners(); + } } \ No newline at end of file diff --git a/src/folderOverview/ListStyle.ts b/src/folderOverview/ListStyle.ts index dfc4a6e..42f7455 100644 --- a/src/folderOverview/ListStyle.ts +++ b/src/folderOverview/ListStyle.ts @@ -21,7 +21,7 @@ export function renderListOverview(plugin: FolderNotesPlugin, ctx: MarkdownPostP files = folderOverview.sortFiles(files.filter(f => f instanceof TFile)); folders.forEach((file) => { if (file instanceof TFolder) { - const folderItem = addFolderList(plugin, ul, folderOverview.pathBlacklist, file); + const folderItem = addFolderList(plugin, ul, folderOverview.pathBlacklist, file, folderOverview); if (!folderItem) { return; } goThroughFolders(plugin, folderItem, file, folderOverview.yaml.depth, sourceFolderPath, ctx, folderOverview.yaml, folderOverview.pathBlacklist, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); } @@ -33,7 +33,7 @@ export function renderListOverview(plugin: FolderNotesPlugin, ctx: MarkdownPostP }); } -export function addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], folder: TFolder) { +export function addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], folder: TFolder, folderOverview: FolderOverview) { const folderItem = list.createEl('li', { cls: 'folder-overview-list folder-list' }); const folderNote = getFolderNote(plugin, folder.path); if (folderNote instanceof TFile) { @@ -43,6 +43,9 @@ export function addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement } else { const folderName = folderItem.createEl('span', { cls: 'folder-overview-list-item folder-name-item' }); folderName.innerText = folder.name; + folderName.oncontextmenu = (e) => { + folderOverview.folderMenu(folder, e); + } } return folderItem; } @@ -62,7 +65,7 @@ function goThroughFolders(plugin: FolderNotesPlugin, list: HTMLLIElement | HTMLU const ul = list.createEl('ul', { cls: 'folder-overview-list' }); folders.forEach((file) => { if (file instanceof TFolder) { - const folderItem = addFolderList(plugin, ul, pathBlacklist, file); + const folderItem = addFolderList(plugin, ul, pathBlacklist, file, folderOverview); if (!folderItem) return; goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag, folderOverview); } @@ -92,6 +95,10 @@ function addFileList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIE if (pathBlacklist.includes(file.path)) return; } const listItem = list.createEl('li', { cls: 'folder-overview-list file-link' }); + listItem.oncontextmenu = (e) => { + e.stopImmediatePropagation(); + folderOverview.fileMenu(file, e); + } const nameItem = listItem.createEl('div', { cls: 'folder-overview-list-item' }); const link = nameItem.createEl('a', { cls: 'internal-link', href: file.path }); link.innerText = file.basename;