Automatically update folder overview

& remove listeners when closing the file
This commit is contained in:
Lost Paul 2024-08-13 11:32:55 +02:00
parent 5d3a77d711
commit 52b0905d0d
3 changed files with 140 additions and 63 deletions

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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;