From 78b9838fde2936bd2f8f9ff4e458693489e72d7d Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Mon, 12 Aug 2024 22:27:38 +0200 Subject: [PATCH] File menu context menu for file explorer overview --- src/folderOverview/FileExplorer.ts | 295 +++++++++++++++++++++++++++ src/folderOverview/FolderOverview.ts | 275 ++----------------------- src/folderOverview/Grid.ts | 1 + src/folderOverview/ListStyle.ts | 101 +++++++++ src/globals.d.ts | 8 + src/modals/NewFolderName.ts | 74 +++++++ 6 files changed, 495 insertions(+), 259 deletions(-) create mode 100644 src/folderOverview/FileExplorer.ts create mode 100644 src/folderOverview/Grid.ts create mode 100644 src/folderOverview/ListStyle.ts create mode 100644 src/modals/NewFolderName.ts diff --git a/src/folderOverview/FileExplorer.ts b/src/folderOverview/FileExplorer.ts new file mode 100644 index 0000000..872813e --- /dev/null +++ b/src/folderOverview/FileExplorer.ts @@ -0,0 +1,295 @@ +import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile, stringifyYaml, Notice, Menu } from 'obsidian'; +import { getFolderNote } from '../functions/folderNoteFunctions'; +import FolderNotesPlugin from '../main'; +import { FolderOverviewSettings } from './ModalSettings'; +import { getExcludedFolder } from '../ExcludeFolders/functions/folderFunctions'; +import { getFolderPathFromString } from '../functions/utils'; +import { getEl } from 'src/functions/styleFunctions'; +import { FolderOverview, yamlSettings } from './FolderOverview'; +import FolderNameModal from 'src/modals/FolderName'; +import NewFolderNameModal from 'src/modals/NewFolderName'; + +export async function renderFileExplorer(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[], folderOverview: FolderOverview) { + const folder = getEl(yaml.folderPath) + let folderElement = folder?.parentElement; + const source = ctx.sourcePath; + const overviewList = root.querySelector('ul.folder-overview-list') as HTMLElement; + overviewList?.empty(); + if (!overviewList) return; + + + let tFolder = plugin.app.vault.getAbstractFileByPath(yaml.folderPath); + if (!tFolder && yaml.folderPath.trim() == '') { + if (ctx.sourcePath.includes('/')) { + tFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath)); + } else { + yaml.folderPath = '/'; + tFolder = plugin.app.vault.getAbstractFileByPath('/'); + } + } + + if (!folderElement && !tFolder) return; + + folderElement = document.querySelector('div.nav-files-container') as HTMLElement; + if (!folderElement) return; + const newFolderElement = folderElement.cloneNode(true) as HTMLElement; + newFolderElement.querySelectorAll('div.nav-folder-title ').forEach((el) => { + const folder = plugin.app.vault.getAbstractFileByPath(el.getAttribute('data-path') || ''); + if (!(folder instanceof TFolder)) return; + if (yaml.storeFolderCondition) { + if (folder.collapsed) { + el.classList.add('is-collapsed'); + } else { + el.classList.remove('is-collapsed'); + } + } else { + if (el.parentElement?.classList.contains('is-collapsed')) { + folder.collapsed = true; + } else { + folder.collapsed = false; + } + } + if (el.classList.contains('has-folder-note')) { + const folderNote = getFolderNote(plugin, folder.path); + if (folderNote) { pathBlacklist.push(folderNote.path); } + } + }); + + function handleVaultChange(eventType: string) { + 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); + } else if (yaml.folderPath.trim() === '/') { + const rootFiles: TAbstractFile[] = []; + if (yaml.includeTypes.includes('folder')) { + plugin.app.vault.getAllLoadedFiles().filter(f => f instanceof TFolder).forEach((file) => { + if (!file.path.includes('/')) { + rootFiles.push(file); + } + }); + } else { + rootFiles.push(...plugin.app.vault.getAllLoadedFiles()); + } + addFiles(rootFiles, overviewList, folderOverview); + } + + newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => { + if (el instanceof HTMLElement) { + el.onclick = () => { + const path = el.parentElement?.getAttribute('data-path'); + if (!path) return; + const folder = plugin.app.vault.getAbstractFileByPath(path); + handleCollapseClick(el, plugin, yaml, pathBlacklist, source, folderOverview, folder); + } + } + }); +} + +async function addFiles(files: TAbstractFile[], childrenElement: HTMLElement, folderOverview: FolderOverview) { + const plugin = folderOverview.plugin; + const pathBlacklist = folderOverview.pathBlacklist; + const yaml = folderOverview.yaml; + const folders = folderOverview.sortFiles(files.filter((file) => file instanceof TFolder)); + const filesWithoutFolders = folderOverview.sortFiles(files.filter((file) => !(file instanceof TFolder))); + + for (const child of folders) { + if (child instanceof TFolder) { + createFolderEL(plugin, child, folderOverview, childrenElement); + } + } + + for (const child of filesWithoutFolders) { + if (child instanceof TFile) { + if (pathBlacklist.includes(child.path) && !yaml.showFolderNotes) { continue; } + const extension = child.extension.toLowerCase() == 'md' ? 'markdown' : child.extension.toLowerCase(); + const includeTypes = yaml.includeTypes; + + if (includeTypes.length > 0 && !includeTypes.includes('all')) { + if ((extension === 'md' || extension === 'markdown') && !includeTypes.includes('markdown')) continue; + if (extension === 'canvas' && !includeTypes.includes('canvas')) continue; + if (extension === 'pdf' && !includeTypes.includes('pdf')) continue; + const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']; + if (imageTypes.includes(extension) && !includeTypes.includes('image')) continue; + const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv']; + if (videoTypes.includes(extension) && !includeTypes.includes('video')) continue; + const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus']; + if (audioTypes.includes(extension) && includeTypes.includes('audio')) continue; + const allTypes = ['markdown', 'md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes]; + if (!allTypes.includes(extension) && !includeTypes.includes('other')) continue; + } + + const fileElement = childrenElement.createDiv({ + cls: 'tree-item nav-file', + }); + + const fileTitle = fileElement.createDiv({ + cls: 'tree-item-self is-clickable nav-file-title pointer-cursor', + attr: { + 'data-path': child.path, + 'draggable': 'true' + }, + }) + + fileTitle.onclick = () => { + plugin.app.workspace.openLinkText(child.path, child.path, true); + } + + // 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 }); + // } + + fileTitle.createDiv({ + cls: 'tree-item-inner nav-file-title-content', + text: child.basename, + }); + + if (child.extension !== 'md' && !yaml.disableFileTag) { + fileTitle.createDiv({ + cls: 'nav-file-tag', + text: child.extension + }); + } + } + } +} + + +function handleCollapseClick(el: HTMLElement, plugin: FolderNotesPlugin, yaml: yamlSettings, pathBlacklist: string[], sourcePath: string, folderOverview: FolderOverview, folder?: TFolder | undefined | null | TAbstractFile) { + el.classList.toggle('is-collapsed'); + if (el.classList.contains('is-collapsed')) { + if (!(folder instanceof TFolder)) return; + folder.collapsed = true; + el.parentElement?.parentElement?.childNodes[1]?.remove(); + } else { + if (!(folder instanceof TFolder)) return; + folder.collapsed = false; + const folderElement = el.parentElement?.parentElement; + if (!folderElement) return; + const childrenElement = folderElement.createDiv({ cls: 'tree-item-children nav-folder-children' }); + let files = folderOverview.sortFiles(folder.children); + files = folderOverview.filterFiles(files, plugin, folder.path, yaml.depth || 1, pathBlacklist); + addFiles(files, childrenElement, folderOverview); + } +} + +function createFolderEL(plugin: FolderNotesPlugin, child: TFolder, folderOverview: FolderOverview, childrenElement: HTMLElement) { + const pathBlacklist = folderOverview.pathBlacklist; + const source = folderOverview.source; + const folderNote = getFolderNote(plugin, child.path); + const yaml = folderOverview.yaml; + let folderTitle: HTMLElement | null = null; + let folderElement: HTMLElement | null = null; + + if (folderNote) { pathBlacklist.push(folderNote.path); } + const excludedFolder = getExcludedFolder(plugin, child.path, true); + if (excludedFolder?.excludeFromFolderOverview) { return; } + const svg = ''; + if (yaml.includeTypes.includes('folder')) { + folderElement = childrenElement.createDiv({ + cls: 'tree-item nav-folder', + }); + folderTitle = folderElement.createDiv({ + cls: 'tree-item-self is-clickable nav-folder-title', + attr: { + 'data-path': child.path, + 'draggable': 'true' + }, + }) + + 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 }); + } + } + + if (!child.collapsed) { + if (yaml.includeTypes.includes('folder')) { + folderTitle?.classList.remove('is-collapsed'); + const childrenElement = folderElement?.createDiv({ cls: 'tree-item-children nav-folder-children' }); + if (childrenElement) { + addFiles(child.children, childrenElement, folderOverview); + } + } else { + addFiles(child.children, childrenElement, folderOverview); + } + } else { + folderTitle?.classList.add('is-collapsed'); + } + if (folderNote) { folderTitle?.classList.add('has-folder-note') } + if (folderNote && child.children.length === 1 && yaml.disableCollapseIcon) { folderTitle?.classList.add('fn-has-no-files') } + + const collapseIcon = folderTitle?.createDiv({ + cls: 'tree-item-icon collapse-icon nav-folder-collapse-indicator fn-folder-overview-collapse-icon', + }); + + if (child.collapsed) { + collapseIcon?.classList.add('is-collapsed'); + } + if (collapseIcon) { + collapseIcon.innerHTML = svg; + collapseIcon.onclick = () => { + handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, source, folderOverview, child); + } + } + + folderTitle?.createDiv({ + cls: 'tree-item-inner nav-folder-title-content', + text: child.name, + }); +} \ No newline at end of file diff --git a/src/folderOverview/FolderOverview.ts b/src/folderOverview/FolderOverview.ts index c9c3b5b..f12d680 100644 --- a/src/folderOverview/FolderOverview.ts +++ b/src/folderOverview/FolderOverview.ts @@ -5,6 +5,8 @@ import { FolderOverviewSettings } from './ModalSettings'; import { getExcludedFolder } from '../ExcludeFolders/functions/folderFunctions'; import { getFolderPathFromString } from '../functions/utils'; import { getEl } from 'src/functions/styleFunctions'; +import { renderFileExplorer } from './FileExplorer'; +import { renderListOverview } from './ListStyle'; export type includeTypes = 'folder' | 'markdown' | 'canvas' | 'other' | 'pdf' | 'image' | 'audio' | 'video' | 'all'; @@ -37,6 +39,9 @@ export class FolderOverview { pathBlacklist: string[] = []; folders: TFolder[] = []; sourceFilePath: string; + sourceFolder: TFolder | undefined; + root: HTMLElement; + listEl: HTMLUListElement; constructor(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, source: string, el: HTMLElement) { let yaml: yamlSettings = parseYaml(source); if (!yaml) { yaml = {} as yamlSettings; } @@ -46,6 +51,9 @@ export class FolderOverview { this.source = source; this.el = el; this.sourceFilePath = this.ctx.sourcePath + console.log(this.sourceFilePath) + console.log(plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath))) + this.sourceFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath)) as TFolder; this.yaml = { id: yaml?.id || crypto.randomUUID(), folderPath: yaml?.folderPath === undefined || yaml?.folderPath === null ? getFolderPathFromString(ctx.sourcePath) : yaml?.folderPath, @@ -69,13 +77,18 @@ export class FolderOverview { 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 = '/'; + } let sourceFolder: TFolder | undefined; if (sourceFolderPath !== '/') { @@ -149,26 +162,13 @@ export class FolderOverview { } }); } else if (this.yaml.style === 'list') { - const folders = this.sortFiles(files.filter(f => f instanceof TFolder)); - files = this.sortFiles(files.filter(f => f instanceof TFile)); - folders.forEach((file) => { - if (file instanceof TFolder) { - const folderItem = this.addFolderList(plugin, ul, this.pathBlacklist, file); - if (!folderItem) { return; } - this.goThroughFolders(plugin, folderItem, file, this.yaml.depth, sourceFolderPath, ctx, this.yaml, this.pathBlacklist, this.yaml.includeTypes, this.yaml.disableFileTag); - } - }); - files.forEach((file) => { - if (file instanceof TFile) { - this.addFileList(plugin, ul, this.pathBlacklist, file, this.yaml.includeTypes, this.yaml.disableFileTag); - } - }); + renderListOverview(plugin, ctx, root, this.yaml, this.pathBlacklist, this); } else if (this.yaml.style === 'explorer') { if (this.plugin.app.workspace.layoutReady) { - this.cloneFileExplorerView(plugin, ctx, root, this.yaml, this.pathBlacklist); + renderFileExplorer(plugin, ctx, root, this.yaml, this.pathBlacklist, this); } else { this.plugin.app.workspace.onLayoutReady(() => { - this.cloneFileExplorerView(plugin, ctx, root, this.yaml, this.pathBlacklist); + renderFileExplorer(plugin, ctx, root, this.yaml, this.pathBlacklist, this); }); } } @@ -203,209 +203,6 @@ export class FolderOverview { }, { capture: true }); } - cloneFileExplorerView(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[]) { - const folder = getEl(this.yaml.folderPath) - let folderElement = folder?.parentElement; - let tFolder = plugin.app.vault.getAbstractFileByPath(this.yaml.folderPath); - if (!tFolder && yaml.folderPath.trim() == '') { - tFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath)); - } - if (!folderElement && yaml.folderPath.trim() !== '') return; - folderElement = document.querySelector('div.nav-files-container') as HTMLElement; - if (!folderElement) return; - const newFolderElement = folderElement.cloneNode(true) as HTMLElement; - newFolderElement.querySelectorAll('div.nav-folder-title ').forEach((el) => { - const folder = plugin.app.vault.getAbstractFileByPath(el.getAttribute('data-path') || ''); - if (!(folder instanceof TFolder)) return; - if (this.yaml.storeFolderCondition) { - if (folder.collapsed) { - el.classList.add('is-collapsed'); - } else { - el.classList.remove('is-collapsed'); - } - } else { - if (el.parentElement?.classList.contains('is-collapsed')) { - folder.collapsed = true; - } else { - folder.collapsed = false; - } - } - if (el.classList.contains('has-folder-note')) { - const folderNote = getFolderNote(plugin, folder.path); - if (folderNote) { this.pathBlacklist.push(folderNote.path); } - } - }); - - if (tFolder instanceof TFolder) { - this.addFiles(tFolder.children, root); - } else if (yaml.folderPath.trim() === '/') { - const rootFiles: TAbstractFile[] = []; - plugin.app.vault.getAllLoadedFiles().filter(f => f instanceof TFolder).forEach((file) => { - if (!file.path.includes('/')) { - rootFiles.push(file); - } - }); - this.addFiles(rootFiles, root); - } - - newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => { - if (el instanceof HTMLElement) { - el.onclick = () => { - const path = el.parentElement?.getAttribute('data-path'); - if (!path) return; - const folder = plugin.app.vault.getAbstractFileByPath(path); - this.handleCollapseClick(el, plugin, yaml, pathBlacklist, this.source, folder); - } - } - }); - } - - async addFiles(files: TAbstractFile[], childrenElement: HTMLElement) { - const folders = this.sortFiles(files.filter((file) => file instanceof TFolder)); - const filesWithoutFolders = this.sortFiles(files.filter((file) => !(file instanceof TFolder))); - for (const child of folders) { - if (child instanceof TFolder) { - const folderNote = getFolderNote(this.plugin, child.path); - if (folderNote) { this.pathBlacklist.push(folderNote.path); } - const excludedFolder = getExcludedFolder(this.plugin, child.path, true); - if (excludedFolder?.excludeFromFolderOverview) { continue; } - const svg = ''; - const folderElement = childrenElement.createDiv({ - cls: 'tree-item nav-folder', - }); - const folderTitle = folderElement.createDiv({ - cls: 'tree-item-self is-clickable nav-folder-title', - attr: { - 'data-path': child.path, - 'draggable': 'true' - }, - }) - if (!child.collapsed) { - folderTitle.classList.remove('is-collapsed'); - const childrenElement = folderElement?.createDiv({ cls: 'tree-item-children nav-folder-children' }); - this.addFiles(child.children, childrenElement); - } else { - folderTitle.classList.add('is-collapsed'); - } - if (folderNote) { folderTitle.classList.add('has-folder-note') } - if (folderNote && child.children.length === 1 && this.yaml.disableCollapseIcon) { folderTitle.classList.add('fn-has-no-files') } - - const collapseIcon = folderTitle.createDiv({ - cls: 'tree-item-icon collapse-icon nav-folder-collapse-indicator fn-folder-overview-collapse-icon', - }); - - if (child.collapsed) { - collapseIcon.classList.add('is-collapsed'); - } - - collapseIcon.innerHTML = svg; - collapseIcon.onclick = () => { - this.handleCollapseClick(collapseIcon, this.plugin, this.yaml, this.pathBlacklist, this.source, child); - } - - folderTitle.createDiv({ - cls: 'tree-item-inner nav-folder-title-content', - text: child.name, - }); - } - } - for (const child of filesWithoutFolders) { - if (child instanceof TFile) { - if (this.pathBlacklist.includes(child.path) && !this.yaml.showFolderNotes) { continue; } - const extension = child.extension.toLowerCase() == 'md' ? 'markdown' : child.extension.toLowerCase(); - const includeTypes = this.yaml.includeTypes; - - if (includeTypes.length > 0 && !includeTypes.includes('all')) { - if ((extension === 'md' || extension === 'markdown') && !includeTypes.includes('markdown')) continue; - if (extension === 'canvas' && !includeTypes.includes('canvas')) continue; - if (extension === 'pdf' && !includeTypes.includes('pdf')) continue; - const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']; - if (imageTypes.includes(extension) && !includeTypes.includes('image')) continue; - const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv']; - if (videoTypes.includes(extension) && !includeTypes.includes('video')) continue; - const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus']; - if (audioTypes.includes(extension) && includeTypes.includes('audio')) continue; - const allTypes = ['markdown', 'md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes]; - if (!allTypes.includes(extension) && !includeTypes.includes('other')) continue; - } - - const fileElement = childrenElement.createDiv({ - cls: 'tree-item nav-file', - }); - - const fileTitle = fileElement.createDiv({ - cls: 'tree-item-self is-clickable nav-file-title pointer-cursor', - attr: { - 'data-path': child.path, - 'draggable': 'true' - }, - }) - - fileTitle.onclick = () => { - this.plugin.app.workspace.openLinkText(child.path, child.path, true); - } - - fileTitle.createDiv({ - cls: 'tree-item-inner nav-file-title-content', - text: child.basename, - }); - - if (child.extension !== 'md') { - fileTitle.createDiv({ - cls: 'nav-file-tag', - text: child.extension - }); - } - } - } - } - - handleCollapseClick(el: HTMLElement, plugin: FolderNotesPlugin, yaml: yamlSettings, pathBlacklist: string[], sourcePath: string, folder?: TFolder | undefined | null | TAbstractFile) { - el.classList.toggle('is-collapsed'); - if (el.classList.contains('is-collapsed')) { - if (!(folder instanceof TFolder)) return; - folder.collapsed = true; - el.parentElement?.parentElement?.childNodes[1]?.remove(); - } else { - if (!(folder instanceof TFolder)) return; - folder.collapsed = false; - const folderElement = el.parentElement?.parentElement; - if (!folderElement) return; - const childrenElement = folderElement.createDiv({ cls: 'tree-item-children nav-folder-children' }); - let files = this.sortFiles(folder.children); - files = this.filterFiles(files, plugin, folder.path, this.yaml.depth || 1, pathBlacklist); - this.addFiles(files, childrenElement); - } - } - - - goThroughFolders(plugin: FolderNotesPlugin, list: HTMLLIElement | HTMLUListElement, folder: TFolder, - depth: number, sourceFolderPath: string, ctx: MarkdownPostProcessorContext, yaml: yamlSettings, - pathBlacklist: string[], includeTypes: string[], disableFileTag: boolean) { - if (sourceFolderPath === '') { - depth--; - } - let files = this.filterFiles(folder.children, plugin, sourceFolderPath, depth, pathBlacklist); - files = this.sortFiles(files.filter((file) => !(file instanceof TFolder))); - if (this.yaml.sortByAsc) { - files = files.reverse(); - } - const folders = this.sortFiles(files.filter((file) => file instanceof TFolder)); - const ul = list.createEl('ul', { cls: 'folder-overview-list' }); - folders.forEach((file) => { - if (file instanceof TFolder) { - const folderItem = this.addFolderList(plugin, ul, pathBlacklist, file); - if (!folderItem) return; - this.goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag); - } - }); - files.forEach((file) => { - if (file instanceof TFile) { - this.addFileList(plugin, ul, pathBlacklist, file, includeTypes, disableFileTag); - } - }); - } - filterFiles(files: TAbstractFile[], plugin: FolderNotesPlugin, sourceFolderPath: string, depth: number, pathBlacklist: string[]) { return files.filter((file) => { if (pathBlacklist.includes(file.path) && !this.yaml.showFolderNotes) { return false; } @@ -486,46 +283,6 @@ export class FolderOverview { }); } - addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], folder: TFolder) { - const folderItem = list.createEl('li', { cls: 'folder-overview-list folder-list' }); - const folderNote = getFolderNote(plugin, folder.path); - if (folderNote instanceof TFile) { - const folderNoteLink = folderItem.createEl('a', { cls: 'folder-overview-list-item folder-name-item internal-link', href: folderNote.path }); - folderNoteLink.innerText = folder.name; - pathBlacklist.push(folderNote.path); - } else { - const folderName = folderItem.createEl('span', { cls: 'folder-overview-list-item folder-name-item' }); - folderName.innerText = folder.name; - } - return folderItem; - } - - addFileList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], file: TFile, includeTypes: string[], disableFileTag: boolean) { - if (includeTypes.length > 0 && !includeTypes.includes('all')) { - if (file.extension === 'md' && !includeTypes.includes('markdown')) return; - if (file.extension === 'canvas' && !includeTypes.includes('canvas')) return; - if (file.extension === 'pdf' && !includeTypes.includes('pdf')) return; - const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']; - if (imageTypes.includes(file.extension) && !includeTypes.includes('image')) return; - const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv']; - if (videoTypes.includes(file.extension) && !includeTypes.includes('video')) return; - const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus']; - if (audioTypes.includes(file.extension) && includeTypes.includes('audio')) return; - const allTypes = ['md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes]; - if (!allTypes.includes(file.extension) && !includeTypes.includes('other')) return; - } - if (!this.yaml.showFolderNotes) { - if (pathBlacklist.includes(file.path)) return; - } - const listItem = list.createEl('li', { cls: 'folder-overview-list file-link' }); - 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; - if (file.extension !== 'md' && !disableFileTag) { - nameItem.createDiv({ cls: 'nav-file-tag' }).innerText = file.extension; - } - } - getAllFiles(files: TAbstractFile[], sourceFolderPath: string, depth: number) { const allFiles: TAbstractFile[] = []; files.forEach((file) => { diff --git a/src/folderOverview/Grid.ts b/src/folderOverview/Grid.ts new file mode 100644 index 0000000..693da49 --- /dev/null +++ b/src/folderOverview/Grid.ts @@ -0,0 +1 @@ +export {} \ No newline at end of file diff --git a/src/folderOverview/ListStyle.ts b/src/folderOverview/ListStyle.ts new file mode 100644 index 0000000..dfc4a6e --- /dev/null +++ b/src/folderOverview/ListStyle.ts @@ -0,0 +1,101 @@ +import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile, stringifyYaml, Notice, Menu } from 'obsidian'; +import { getFolderNote } from '../functions/folderNoteFunctions'; +import FolderNotesPlugin from '../main'; +import { FolderOverviewSettings } from './ModalSettings'; +import { getExcludedFolder } from '../ExcludeFolders/functions/folderFunctions'; +import { getFolderPathFromString } from '../functions/utils'; +import { getEl } from 'src/functions/styleFunctions'; +import { FolderOverview, yamlSettings } from './FolderOverview'; +import FolderNameModal from 'src/modals/FolderName'; +import NewFolderNameModal from 'src/modals/NewFolderName'; + +export function renderListOverview(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[], folderOverview: FolderOverview) { + if (!folderOverview.sourceFolder) { return; } + let files = folderOverview.sourceFolder.children; + if (!files) { return; } + const ul = folderOverview.listEl; + const sourceFolderPath = folderOverview.sourceFolder.path; + + + const folders = folderOverview.sortFiles(files.filter(f => f instanceof TFolder)); + files = folderOverview.sortFiles(files.filter(f => f instanceof TFile)); + folders.forEach((file) => { + if (file instanceof TFolder) { + const folderItem = addFolderList(plugin, ul, folderOverview.pathBlacklist, file); + if (!folderItem) { return; } + goThroughFolders(plugin, folderItem, file, folderOverview.yaml.depth, sourceFolderPath, ctx, folderOverview.yaml, folderOverview.pathBlacklist, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); + } + }); + files.forEach((file) => { + if (file instanceof TFile) { + addFileList(plugin, ul, folderOverview.pathBlacklist, file, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); + } + }); +} + +export function addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], folder: TFolder) { + const folderItem = list.createEl('li', { cls: 'folder-overview-list folder-list' }); + const folderNote = getFolderNote(plugin, folder.path); + if (folderNote instanceof TFile) { + const folderNoteLink = folderItem.createEl('a', { cls: 'folder-overview-list-item folder-name-item internal-link', href: folderNote.path }); + folderNoteLink.innerText = folder.name; + pathBlacklist.push(folderNote.path); + } else { + const folderName = folderItem.createEl('span', { cls: 'folder-overview-list-item folder-name-item' }); + folderName.innerText = folder.name; + } + return folderItem; +} + +function goThroughFolders(plugin: FolderNotesPlugin, list: HTMLLIElement | HTMLUListElement, folder: TFolder, + depth: number, sourceFolderPath: string, ctx: MarkdownPostProcessorContext, yaml: yamlSettings, + pathBlacklist: string[], includeTypes: string[], disableFileTag: boolean, folderOverview: FolderOverview) { + if (sourceFolderPath === '') { + depth--; + } + let files = folderOverview.filterFiles(folder.children, plugin, sourceFolderPath, depth, pathBlacklist); + files = folderOverview.sortFiles(files.filter((file) => !(file instanceof TFolder))); + if (folderOverview.yaml.sortByAsc) { + files = files.reverse(); + } + const folders = folderOverview.sortFiles(files.filter((file) => file instanceof TFolder)); + const ul = list.createEl('ul', { cls: 'folder-overview-list' }); + folders.forEach((file) => { + if (file instanceof TFolder) { + const folderItem = addFolderList(plugin, ul, pathBlacklist, file); + if (!folderItem) return; + goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag, folderOverview); + } + }); + files.forEach((file) => { + if (file instanceof TFile) { + addFileList(plugin, ul, pathBlacklist, file, includeTypes, disableFileTag, folderOverview); + } + }); +} + +function addFileList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], file: TFile, includeTypes: string[], disableFileTag: boolean, folderOverview: FolderOverview) { + if (includeTypes.length > 0 && !includeTypes.includes('all')) { + if (file.extension === 'md' && !includeTypes.includes('markdown')) return; + if (file.extension === 'canvas' && !includeTypes.includes('canvas')) return; + if (file.extension === 'pdf' && !includeTypes.includes('pdf')) return; + const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']; + if (imageTypes.includes(file.extension) && !includeTypes.includes('image')) return; + const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv']; + if (videoTypes.includes(file.extension) && !includeTypes.includes('video')) return; + const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus']; + if (audioTypes.includes(file.extension) && includeTypes.includes('audio')) return; + const allTypes = ['md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes]; + if (!allTypes.includes(file.extension) && !includeTypes.includes('other')) return; + } + if (!folderOverview.yaml.showFolderNotes) { + if (pathBlacklist.includes(file.path)) return; + } + const listItem = list.createEl('li', { cls: 'folder-overview-list file-link' }); + 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; + if (file.extension !== 'md' && !disableFileTag) { + nameItem.createDiv({ cls: 'nav-file-tag' }).innerText = file.extension; + } +} \ No newline at end of file diff --git a/src/globals.d.ts b/src/globals.d.ts index 95fc16a..4f5d328 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -22,6 +22,14 @@ declare module 'obsidian' { newName: string | null; collapsed: boolean; } + interface MenuItem { + dom: HTMLElement; + } + interface FileManager { + promptForFileRename: (file: TAbstractFile) => void; + promptForDeletion: (file: TAbstractFile) => void; + promptForFolderDeletion: (folder: TFolder) => void; + } class ListComponent { containerEl: HTMLElement; diff --git a/src/modals/NewFolderName.ts b/src/modals/NewFolderName.ts new file mode 100644 index 0000000..073a4c3 --- /dev/null +++ b/src/modals/NewFolderName.ts @@ -0,0 +1,74 @@ +import { App, Modal, Setting, TFolder } from 'obsidian'; +import FolderNotesPlugin from '../main'; +export default class NewFolderNameModal extends Modal { + plugin: FolderNotesPlugin; + app: App; + folder: TFolder; + constructor(app: App, plugin: FolderNotesPlugin, folder: TFolder) { + super(app); + this.plugin = plugin; + this.app = app; + this.folder = folder; + } + onOpen() { + const { contentEl } = this; + + contentEl.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + this.saveFolderName() + this.close(); + } + }); + + this.modalEl.classList.add('mod-file-rename') + const modalTitle = this.modalEl.querySelector('div.modal-title') + console.log(modalTitle) + if (modalTitle) { + modalTitle.textContent = 'Folder title' + } + + const textarea = contentEl.createEl('textarea', { + text: this.folder.name.replace(this.plugin.settings.folderNoteType, ''), + attr: { + placeholder: 'Enter the name of the folder', + rows: '1', + spellcheck: 'false', + class: 'rename-textarea' + } + }) + + textarea.addEventListener('focus', function() { + this.select(); + }); + + textarea.focus() + + const buttonContainer = this.modalEl.createDiv({ cls: 'modal-button-container' }); + const saveButton = buttonContainer.createEl('button', { text: 'Save', cls: 'mod-cta' }); + saveButton.addEventListener('click', async () => { + this.saveFolderName() + this.close() + }) + + const cancelButton = buttonContainer.createEl('button', { text: 'Cancel', cls: 'mod-cancel' }); + cancelButton.addEventListener('click', () => { + this.close() + }) + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } + + saveFolderName() { + const textarea = this.contentEl.querySelector('textarea') + if (textarea) { + const newName = textarea.value.trim() + if (newName.trim() !== '') { + if (!this.app.vault.getAbstractFileByPath(this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim())) { + this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim()); + } + } + } + } +}