diff --git a/src/Commands.ts b/src/Commands.ts index 71bc48f..b79bf3b 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -36,6 +36,14 @@ export class Commands { } }); + this.plugin.addCommand({ + id: 'open-folder-overview-view', + name: 'Change folder overview settings', + callback: () => { + this.plugin.activateOverviewView(); + } + }); + this.plugin.addCommand({ id: 'create-folder-note', name: 'Create folder note with a new folder for the active note in the current folder', diff --git a/src/folderOverview/FolderOverview.ts b/src/folderOverview/FolderOverview.ts index 06dbc85..e8e3877 100644 --- a/src/folderOverview/FolderOverview.ts +++ b/src/folderOverview/FolderOverview.ts @@ -181,7 +181,7 @@ export class FolderOverview { files = sourceFolder.children; } - + files = await this.filterFiles(files, plugin, sourceFolderPath, this.yaml.depth, this.pathBlacklist); if (!this.yaml.includeTypes.includes('folder')) { @@ -361,8 +361,6 @@ export class FolderOverview { return allFiles; } - - fileMenu(file: TFile, e: MouseEvent) { const plugin = this.plugin; const fileMenu = new Menu(); @@ -466,6 +464,65 @@ export function getCodeBlockEndLine(text: string, startLine: number, count = 1) return line; } +export async function getOverviews(plugin: FolderNotesPlugin, file: TFile) { + // is an object with unkown keys + const overviews: { [key: string]: string }[] = []; + const content = await plugin.app.vault.read(file); + if (!content) return overviews; + + const yamlBlocks = content.match(/```folder-overview\n([\s\S]*?)```/g); + if (!yamlBlocks) return overviews; + for (const block of yamlBlocks) { + const yaml = parseYaml(block.replace('```folder-overview\n', '').replace('```', '')); + if (!yaml) continue; + overviews.push(yaml); + } + + return overviews; +} + +export async function updateYamlById(plugin: FolderNotesPlugin, overviewId: string, file: TFile, newYaml: yamlSettings) { + plugin.app.vault.process(file, (text) => { + const yamlBlocks = text.match(/```folder-overview\n([\s\S]*?)```/g); + if (!yamlBlocks) return text; + for (const block of yamlBlocks) { + const yaml = parseYaml(block.replace('```folder-overview\n', '').replace('```', '')); + if (!yaml) continue; + if (yaml.id === overviewId) { + const stringYaml = stringifyYaml(newYaml); + const newBlock = `\`\`\`folder-overview\n${stringYaml}\`\`\``; + text = text.replace(block, newBlock); + } + } + return text; + }); + +} + +export function parseOverviewTitle(overview: yamlSettings, plugin: FolderNotesPlugin, folder: TFolder | null) { + const sourceFolderPath = overview.folderPath; + let sourceFolder: TFolder | undefined | null; + let title = overview.title; + + if (sourceFolderPath !== '/') { + if (sourceFolderPath === '') { + sourceFolder = folder; + } else { + sourceFolder = plugin.app.vault.getAbstractFileByPath(sourceFolderPath) as TFolder; + } + } + + if (sourceFolder && sourceFolderPath !== '/') { + title = title.replace('{{folderName}}', sourceFolder.name); + } else if (sourceFolderPath == '/') { + title = title.replace('{{folderName}}', 'Vault'); + } else { + title = title.replace('{{folderName}}', ''); + } + + return title; +} + class CustomMarkdownRenderChild extends MarkdownRenderChild { folderOverview: FolderOverview; diff --git a/src/folderOverview/ModalSettings.ts b/src/folderOverview/ModalSettings.ts index 2585645..a0f3a39 100644 --- a/src/folderOverview/ModalSettings.ts +++ b/src/folderOverview/ModalSettings.ts @@ -1,10 +1,11 @@ -import { App, Modal, Setting, MarkdownPostProcessorContext, stringifyYaml, TFile, TFolder } from 'obsidian'; +import { App, Modal, Setting, MarkdownPostProcessorContext, stringifyYaml, TFile, TFolder, SettingTab } from 'obsidian'; import { yamlSettings, includeTypes, FolderOverview } from 'src/folderOverview/FolderOverview'; import FolderNotesPlugin from '../main'; import { ListComponent } from 'src/functions/ListComponent'; import { updateYaml } from 'src/folderOverview/FolderOverview'; import { FolderSuggest } from 'src/suggesters/FolderSuggester'; import { getFolderPathFromString } from 'src/functions/utils'; +import { createOverviewSettings } from "src/folderOverview/settings"; export class FolderOverviewSettings extends Modal { plugin: FolderNotesPlugin; @@ -54,313 +55,25 @@ export class FolderOverviewSettings extends Modal { updateYaml(this.plugin, this.ctx, this.el, this.yaml); } onOpen() { - this.display(); - } - display() { const { contentEl } = this; + this.display(contentEl, this.yaml, this.plugin, false, this.display, this.el, this.ctx); + } + display(contentEl: HTMLElement, yaml: yamlSettings, plugin: FolderNotesPlugin, defaultSettings: boolean, display: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: SettingTab, modal?: FolderOverviewSettings) { + modal = this ?? modal; contentEl.empty(); // close when user presses enter contentEl.addEventListener('keydown', (e) => { if (e.key === 'Enter') { - this.close(); + modal.close(); } }); - if (!this.defaultSettings) { + if (!modal.defaultSettings) { contentEl.createEl('h2', { text: 'Folder overview settings' }); } else { contentEl.createEl('h2', { text: 'Default folder overview settings' }); } - new Setting(contentEl) - .setName('Show the title') - .setDesc('Choose if the title should be shown') - .addToggle((toggle) => - toggle - .setValue(this.yaml.showTitle) - .onChange(async (value) => { - this.yaml.showTitle = value; - this.display(); - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml);; - }) - ); - if (this.yaml.showTitle) { - new Setting(contentEl) - .setName('Title') - .setDesc('Choose the title of the folder overview') - .addText((text) => - text - .setValue(this.yaml?.title || '{{folderName}} overview') - .onChange(async (value) => { - this.yaml.title = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml);; - }) - ); - } - new Setting(contentEl) - .setName('Folder path for the overview') - .setDesc('Choose the folder path for the overview') - .addSearch((search) => { - new FolderSuggest(search.inputEl, this.plugin, false) - search - .setPlaceholder('Folder path') - .setValue(this.yaml?.folderPath || '') - .onChange(async (value) => { - if (!(this.app.vault.getAbstractFileByPath(value) instanceof TFolder) && value !== '') return; - this.yaml.folderPath = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml);; - }); - }); - new Setting(contentEl) - .setName('Overview style') - .setDesc('Choose the style of the overview (grid style soon)') - .addDropdown((dropdown) => - dropdown - .addOption('list', 'List') - .addOption('explorer', 'Explorer') - .setValue(this.yaml?.style || 'list') - .onChange(async (value: 'list') => { - this.yaml.style = value; - this.display(); - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }) - ); - if (this.yaml.style === 'explorer') { - new Setting(contentEl) - .setName('Store collapsed condition') - .setDesc('Choose if the collapsed condition should be stored stored until you restart Obsidian') - .addToggle((toggle) => - toggle - .setValue(this.yaml.storeFolderCondition) - .onChange(async (value) => { - this.yaml.storeFolderCondition = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml);; - }) - ); - } - const setting = new Setting(contentEl); - setting.setName('Include types'); - const list = new ListComponent(setting.settingEl, this.yaml.includeTypes || [], ['markdown', 'folder']); - list.on('update', (values) => { - this.yaml.includeTypes = values; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - updateYaml(this.plugin, this.ctx, this.el, this.yaml); - this.display(); - }); - - if ((this.yaml?.includeTypes?.length || 0) < 8 && !this.yaml.includeTypes?.includes('all')) { - setting.addDropdown((dropdown) => { - if (!this.yaml.includeTypes) this.yaml.includeTypes = this.plugin.settings.defaultOverview.includeTypes || []; - this.yaml.includeTypes = this.yaml.includeTypes.map((type: string) => type.toLowerCase()) as includeTypes[]; - const options = [ - { value: 'markdown', label: 'Markdown' }, - { value: 'folder', label: 'Folder' }, - { value: 'canvas', label: 'Canvas' }, - { value: 'pdf', label: 'PDF' }, - { value: 'image', label: 'Image' }, - { value: 'audio', label: 'Audio' }, - { value: 'video', label: 'Video' }, - { value: 'other', label: 'All other file types' }, - { value: 'all', label: 'All file types' }, - ]; - - options.forEach((option) => { - if (!this.yaml.includeTypes?.includes(option.value as includeTypes)) { - dropdown.addOption(option.value, option.label); - } - }); - dropdown.addOption('+', '+'); - dropdown.setValue('+'); - dropdown.onChange(async (value) => { - if (value === 'all') { - this.yaml.includeTypes = this.yaml.includeTypes?.filter((type: string) => type === 'folder'); - list.setValues(this.yaml.includeTypes); - } - await list.addValue(value.toLowerCase()); - this.display(); - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - } - - let disableFileTag; - this.yaml.includeTypes?.forEach((type: string) => { - type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null; - }); - - if (disableFileTag) { - new Setting(contentEl) - .setName('Disable file tag') - .setDesc('Choose if the file tag should be shown after the file name') - .addToggle((toggle) => { - toggle - .setValue(this.yaml.disableFileTag) - .onChange(async (value) => { - this.yaml.disableFileTag = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - } - - new Setting(contentEl) - .setName('Show folder notes') - .setDesc('Choose if folder notes (the note itself and not the folder name) should be shown in the overview') - .addToggle((toggle) => - toggle - .setValue(this.yaml.showFolderNotes) - .onChange(async (value) => { - this.yaml.showFolderNotes = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }) - ); - - new Setting(contentEl) - .setName('File depth') - .setDesc('File & folder = +1 depth') - .addSlider((slider) => - slider - .setValue(this.yaml?.depth || 2) - .setLimits(1, 10, 1) - .setDynamicTooltip() - .onChange(async (value) => { - this.yaml.depth = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }) - ); - - new Setting(contentEl) - .setName('Sort files by') - .setDesc('Choose how the files should be sorted') - .addDropdown((dropdown) => - dropdown - .addOption('name', 'Name') - .addOption('created', 'Created') - .addOption('modified', 'Modified') - .setValue(this.yaml?.sortBy || 'name') - .onChange(async (value: 'name' | 'created' | 'modified') => { - this.yaml.sortBy = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }) - ) - .addDropdown((dropdown) => { - dropdown - .addOption('desc', 'Descending') - .addOption('asc', 'Ascending') - if (this.yaml.sortByAsc) { - dropdown.setValue('asc'); - } else { - dropdown.setValue('desc'); - } - dropdown.onChange(async (value) => { - if (value === 'desc') { - this.yaml.sortByAsc = false; - } else { - this.yaml.sortByAsc = true; - } - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - - if (this.yaml.style === 'list') { - new Setting(contentEl) - .setName('Show folder names of folders that appear empty in the folder overview') - .setDesc('Show the names of folders that appear to have no files/folders in the folder overview. That\'s mostly the case when you set the file depth to 1.') - .addToggle((toggle) => { - toggle - .setValue(this.yaml.showEmptyFolders) - .onChange(async (value) => { - this.yaml.showEmptyFolders = value; - this.yaml.onlyIncludeSubfolders = false; - this.display(); - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - - if (this.yaml.showEmptyFolders) { - new Setting(contentEl) - .setName('Only show first empty subfolders of current folder') - .addToggle((toggle) => { - toggle - .setValue(this.yaml.onlyIncludeSubfolders) - .onChange(async (value) => { - this.yaml.onlyIncludeSubfolders = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - } - } - - if (this.yaml.style === 'explorer') { - new Setting(contentEl) - .setName('Disable collapse icon for folder notes') - .setDesc('Remove the collapse icon next to the folder name for folder notes when they only contain the folder note itself') - .addToggle((toggle) => { - toggle - .setValue(this.yaml.disableCollapseIcon) - .onChange(async (value) => { - this.yaml.disableCollapseIcon = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - - new Setting(contentEl) - .setName('Collapse all in the tree by default') - .setDesc('Collapse every folder in the file explorer in the overview by default') - .addToggle((toggle) => { - toggle - .setValue(this.yaml.alwaysCollapse) - .onChange(async (value) => { - this.yaml.alwaysCollapse = value; - if (this.defaultSettings) { - return this.plugin.saveSettings(); - } - await updateYaml(this.plugin, this.ctx, this.el, this.yaml); - }); - }); - } + createOverviewSettings(contentEl, yaml, plugin, defaultSettings, display, el, ctx, undefined, undefined, modal); } onClose() { diff --git a/src/folderOverview/settings.ts b/src/folderOverview/settings.ts new file mode 100644 index 0000000..061f20a --- /dev/null +++ b/src/folderOverview/settings.ts @@ -0,0 +1,273 @@ +import { MarkdownPostProcessorContext, Setting, TFile, TFolder } from 'obsidian'; +import { updateYaml, updateYamlById, yamlSettings, includeTypes } from './FolderOverview'; +import { FolderSuggest } from 'src/suggesters/FolderSuggester'; +import { ListComponent } from 'src/functions/ListComponent'; +import FolderNotesPlugin from 'src/main'; +import { Callback } from 'front-matter-plugin-api-provider'; +import { SettingsTab } from 'src/settings/SettingsTab'; +import { FolderOverviewSettings } from './ModalSettings'; + +export async function createOverviewSettings(contentEl: HTMLElement, yaml: yamlSettings, plugin: FolderNotesPlugin, defaultSettings: boolean, display: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: SettingsTab, modal?: FolderOverviewSettings) { + + new Setting(contentEl) + .setName('Show the title') + .setDesc('Choose if the title should be shown') + .addToggle((toggle) => + toggle + .setValue(yaml.showTitle) + .onChange(async (value) => { + yaml.showTitle = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);; + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }) + ); + if (yaml.showTitle) { + new Setting(contentEl) + .setName('Title') + .setDesc('Choose the title of the folder overview') + .addText((text) => + text + .setValue(yaml?.title || '{{folderName}} overview') + .onChange(async (value) => { + yaml.title = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);; + }) + ); + } + + new Setting(contentEl) + .setName('Folder path for the overview') + .setDesc('Choose the folder path for the overview') + .addSearch((search) => { + new FolderSuggest(search.inputEl, plugin, false) + search + .setPlaceholder('Folder path') + .setValue(yaml?.folderPath || '') + .onChange(async (value) => { + if (!(plugin.app.vault.getAbstractFileByPath(value) instanceof TFolder) && value !== '') return; + yaml.folderPath = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);; + }); + }); + + new Setting(contentEl) + .setName('Overview style') + .setDesc('Choose the style of the overview (grid style soon)') + .addDropdown((dropdown) => + dropdown + .addOption('list', 'List') + .addOption('explorer', 'Explorer') + .setValue(yaml?.style || 'list') + .onChange(async (value: 'list') => { + yaml.style = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }) + ); + + if (yaml.style === 'explorer') { + new Setting(contentEl) + .setName('Store collapsed condition') + .setDesc('Choose if the collapsed condition should be stored stored until you restart Obsidian') + .addToggle((toggle) => + toggle + .setValue(yaml.storeFolderCondition) + .onChange(async (value) => { + yaml.storeFolderCondition = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);; + }) + ); + } + + const setting = new Setting(contentEl); + setting.setName('Include types'); + const list = new ListComponent(setting.settingEl, yaml.includeTypes || [], ['markdown', 'folder']); + list.on('update', (values) => { + yaml.includeTypes = values; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }); + + if ((yaml?.includeTypes?.length || 0) < 8 && !yaml.includeTypes?.includes('all')) { + setting.addDropdown((dropdown) => { + if (!yaml.includeTypes) yaml.includeTypes = plugin.settings.defaultOverview.includeTypes || []; + yaml.includeTypes = yaml.includeTypes.map((type: string) => type.toLowerCase()) as includeTypes[]; + const options = [ + { value: 'markdown', label: 'Markdown' }, + { value: 'folder', label: 'Folder' }, + { value: 'canvas', label: 'Canvas' }, + { value: 'pdf', label: 'PDF' }, + { value: 'image', label: 'Image' }, + { value: 'audio', label: 'Audio' }, + { value: 'video', label: 'Video' }, + { value: 'other', label: 'All other file types' }, + { value: 'all', label: 'All file types' }, + ]; + + options.forEach((option) => { + if (!yaml.includeTypes?.includes(option.value as includeTypes)) { + dropdown.addOption(option.value, option.label); + } + }); + dropdown.addOption('+', '+'); + dropdown.setValue('+'); + dropdown.onChange(async (value) => { + if (value === 'all') { + yaml.includeTypes = yaml.includeTypes?.filter((type: string) => type === 'folder'); + list.setValues(yaml.includeTypes); + } + await list.addValue(value.toLowerCase()); + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }); + }); + } + + let disableFileTag; + yaml.includeTypes?.forEach((type: string) => { + type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null; + }); + + if (disableFileTag) { + new Setting(contentEl) + .setName('Disable file tag') + .setDesc('Choose if the file tag should be shown after the file name') + .addToggle((toggle) => { + toggle + .setValue(yaml.disableFileTag) + .onChange(async (value) => { + yaml.disableFileTag = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }); + }); + } + + new Setting(contentEl) + .setName('Show folder notes') + .setDesc('Choose if folder notes (the note itself and not the folder name) should be shown in the overview') + .addToggle((toggle) => + toggle + .setValue(yaml.showFolderNotes) + .onChange(async (value) => { + yaml.showFolderNotes = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }) + ); + + new Setting(contentEl) + .setName('File depth') + .setDesc('File & folder = +1 depth') + .addSlider((slider) => + slider + .setValue(yaml?.depth || 2) + .setLimits(1, 10, 1) + .setDynamicTooltip() + .onChange(async (value) => { + yaml.depth = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }) + ); + + new Setting(contentEl) + .setName('Sort files by') + .setDesc('Choose how the files should be sorted') + .addDropdown((dropdown) => + dropdown + .addOption('name', 'Name') + .addOption('created', 'Created') + .addOption('modified', 'Modified') + .setValue(yaml?.sortBy || 'name') + .onChange(async (value: 'name' | 'created' | 'modified') => { + yaml.sortBy = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }) + ) + .addDropdown((dropdown) => { + dropdown + .addOption('desc', 'Descending') + .addOption('asc', 'Ascending') + if (yaml.sortByAsc) { + dropdown.setValue('asc'); + } else { + dropdown.setValue('desc'); + } + dropdown.onChange(async (value) => { + if (value === 'desc') { + yaml.sortByAsc = false; + } else { + yaml.sortByAsc = true; + } + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }); + }); + + if (yaml.style === 'list') { + new Setting(contentEl) + .setName('Show folder names of folders that appear empty in the folder overview') + .setDesc('Show the names of folders that appear to have no files/folders in the folder overview. That\'s mostly the case when you set the file depth to 1.') + .addToggle((toggle) => { + toggle + .setValue(yaml.showEmptyFolders) + .onChange(async (value) => { + yaml.showEmptyFolders = value; + yaml.onlyIncludeSubfolders = false; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }); + }); + + if (yaml.showEmptyFolders) { + new Setting(contentEl) + .setName('Only show first empty subfolders of current folder') + .addToggle((toggle) => { + toggle + .setValue(yaml.onlyIncludeSubfolders) + .onChange(async (value) => { + yaml.onlyIncludeSubfolders = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }); + }); + } + } + + if (yaml.style === 'explorer') { + new Setting(contentEl) + .setName('Disable collapse icon for folder notes') + .setDesc('Remove the collapse icon next to the folder name for folder notes when they only contain the folder note itself') + .addToggle((toggle) => { + toggle + .setValue(yaml.disableCollapseIcon) + .onChange(async (value) => { + yaml.disableCollapseIcon = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }); + }); + + new Setting(contentEl) + .setName('Collapse all in the tree by default') + .setDesc('Collapse every folder in the file explorer in the overview by default') + .addToggle((toggle) => { + toggle + .setValue(yaml.alwaysCollapse) + .onChange(async (value) => { + yaml.alwaysCollapse = value; + updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file); + }); + }); + } +} + +async function updateSettings(contentEl: HTMLElement, yaml: yamlSettings, plugin: FolderNotesPlugin, defaultSettings: boolean, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null) { + if (defaultSettings) { + plugin.updateOverviewView(); + return plugin.saveSettings(); + } + + if (el && ctx) { + await updateYaml(plugin, ctx, el, yaml); + } + + if (file) { + await updateYamlById(plugin, yaml.id, file, yaml); + } +} \ No newline at end of file diff --git a/src/folderOverview/view.ts b/src/folderOverview/view.ts new file mode 100644 index 0000000..4a4f44a --- /dev/null +++ b/src/folderOverview/view.ts @@ -0,0 +1,96 @@ +import { ItemView, Setting, TFile, WorkspaceLeaf, MarkdownPostProcessorContext } from 'obsidian'; +import FolderNotesPlugin from '../main'; +import { FolderOverview, getOverviews, yamlSettings, parseOverviewTitle } from './FolderOverview'; +import { FileSuggest } from 'src/suggesters/FileSuggester'; +import { createOverviewSettings } from './settings'; + +export const FOLDER_OVERVIEW_VIEW = 'folder-overview-view'; + +export class FolderOverviewView extends ItemView { + plugin: FolderNotesPlugin; + activeFile: TFile | null; + overviewId: string | null; + yaml: yamlSettings; + defaultSettings: boolean; + contentEl: HTMLElement = this.containerEl.children[1] as HTMLElement; + constructor(leaf: WorkspaceLeaf, plugin: FolderNotesPlugin) { + super(leaf); + this.plugin = plugin; + + this.registerEvent( + this.plugin.app.workspace.on('file-open', (file) => { + this.activeFile = file; + this.display(this.contentEl, this.yaml, this.plugin, this.defaultSettings, this.display, undefined, undefined, file); + }) + ); + } + + getViewType() { + return FOLDER_OVERVIEW_VIEW; + } + + getDisplayText() { + return 'Folder Overview settings'; + } + + getIcon() { + return 'settings'; + } + + async onOpen() { + this.display(this.contentEl, this.yaml, this.plugin, this.defaultSettings, this.display, undefined, undefined, this.activeFile); + } + + async display(contentEl: HTMLElement, yaml: yamlSettings, plugin: FolderNotesPlugin, defaultSettings: boolean, display: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null) { + contentEl.empty(); + contentEl.createEl('h4', { text: 'Folder Overview settings' }); + + const activeFile = plugin.app.workspace.getActiveFile(); + + if (!activeFile) { + // coulnd't get active file + contentEl.createEl('p', { text: 'No active file found' }); + return; + } + + const overviews = await getOverviews(plugin, activeFile); + + const overviewSetting = new Setting(contentEl); + overviewSetting.setName('Select overview'); + overviewSetting.setClass('fn-select-overview-setting'); + overviewSetting.addDropdown((cb) => { + const options = overviews.reduce((acc, overview) => { + acc[overview.id] = parseOverviewTitle(overview as any as yamlSettings, plugin, activeFile.parent); + return acc; + }, {} as Record); + cb.addOptions(options); + cb.addOption('default', 'Default'); + cb.setValue(yaml?.id ?? 'default'); + + if (cb.getValue() === 'default' || defaultSettings) { + yaml = plugin.settings.defaultOverview; + defaultSettings = true; + cb.setValue('default'); + } else { + yaml = overviews.find((overview) => overview.id === yaml.id) as any as yamlSettings; + defaultSettings = false; + } + + cb.onChange(async (value) => { + if (value === 'default') { + yaml = plugin.settings.defaultOverview; + defaultSettings = true; + } else { + yaml = overviews.find((overview) => overview.id === value) as any as yamlSettings; + defaultSettings = false; + } + display(contentEl, yaml, plugin, defaultSettings, display, undefined, undefined, activeFile); + }); + }); + + createOverviewSettings(contentEl, yaml, plugin, defaultSettings, display, undefined, undefined, activeFile); + + // execute display function when the default settings are changed + + } +} diff --git a/src/main.ts b/src/main.ts index d973ea1..763080d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, MenuItem, requireApiVersion } from 'obsidian'; +import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion } from 'obsidian'; import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings/SettingsTab'; import { Commands } from './Commands'; import { FileExplorerWorkspaceLeaf } from './globals'; @@ -17,6 +17,7 @@ import { addCSSClassToTitleEL, getEl, loadFileClasses } from './functions/styleF import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; import { ClipBoardManager, FileExplorerView, InternalPlugin } from 'obsidian-typings' import { getFocusedItem } from './functions/utils'; +import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './folderOverview/view'; export default class FolderNotesPlugin extends Plugin { observer: MutationObserver; settings: FolderNotesSettings; @@ -74,7 +75,7 @@ export default class FolderNotesPlugin extends Plugin { if (!folderNote) return; openFolderNote(this, folderNote); } - + const hoveredElement = this.hoveredElement; if (this.hoverLinkTriggered) return; if (!hoveredElement) return; @@ -133,18 +134,14 @@ export default class FolderNotesPlugin extends Plugin { }); if (this.app.workspace.layoutReady) { - // loadFileClasses(undefined, this); - // // this.registerEvent(this.app.workspace.on('layout-change', () => { - // // loadFileClasses(undefined, this); - // // this.tabManager?.updateTabs(); - // // })); + this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => { + return new FolderOverviewView(leaf, this); + }); } else { this.app.workspace.onLayoutReady(async () => { - // loadFileClasses(undefined, this) - // // this.registerEvent(this.app.workspace.on('layout-change', () => { - // // loadFileClasses(undefined, this); - // // this.tabManager?.updateTabs(); - // // })); + this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => { + return new FolderOverviewView(leaf, this); + }); }); } } @@ -164,7 +161,7 @@ export default class FolderNotesPlugin extends Plugin { // @ts-ignore const editMode = view.editMode ?? view.sourceMode ?? this.app.workspace.activeEditor?.editMode; if (!editMode) { return; } - + const plugin = this; // @ts-ignore @@ -233,6 +230,31 @@ export default class FolderNotesPlugin extends Plugin { } } + async activateOverviewView() { + const { workspace } = this.app; + + let leaf: WorkspaceLeaf | null = null; + const leaves = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW); + + if (leaves.length > 0) { + leaf = leaves[0]; + } else { + leaf = workspace.getRightLeaf(false); + await leaf?.setViewState({ type: FOLDER_OVERVIEW_VIEW, active: true }); + } + + if (!leaf) return; + workspace.revealLeaf(leaf); + } + + async updateOverviewView() { + const { workspace } = this.app; + const leaf = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW)[0]; + if (!leaf) return; + const view = leaf.view as FolderOverviewView; + view.display(view.contentEl, view.yaml, this, view.defaultSettings, view.display, undefined, undefined, view.activeFile); + } + isEmptyFolderNoteFolder(folder: TFolder): boolean { let attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath') as string; const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || ''; diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts index 0a821df..af2db76 100644 --- a/src/settings/FolderOverviewSettings.ts +++ b/src/settings/FolderOverviewSettings.ts @@ -3,6 +3,7 @@ import { SettingsTab } from "./SettingsTab"; import { ListComponent } from 'src/functions/ListComponent'; import { includeTypes } from 'src/folderOverview/FolderOverview'; import { FolderSuggest } from "src/suggesters/FolderSuggester"; +import { createOverviewSettings } from "src/folderOverview/settings"; export async function renderFolderOverview(settingsTab: SettingsTab) { const { plugin } = settingsTab; @@ -10,251 +11,5 @@ export async function renderFolderOverview(settingsTab: SettingsTab) { const containerEl = settingsTab.settingsPage; containerEl.createEl('p', { text: 'Change the default settings for folder overviews', cls: 'setting-item-description' }); - new Setting(containerEl) - .setName('Show the title') - .setDesc('Choose if the title should be shown') - .addToggle((toggle) => - toggle - .setValue(overviewSettings.showTitle) - .onChange(async (value) => { - overviewSettings.showTitle = value; - settingsTab.display(); - plugin.saveSettings(); - }) - ); - - if (overviewSettings.showTitle) { - new Setting(containerEl) - .setName('Title') - .setDesc('Choose the title of the folder overview') - .addText((text) => - text - .setValue(overviewSettings?.title || '{{folderName}} overview') - .onChange(async (value) => { - overviewSettings.title = value; - plugin.saveSettings(); - }) - ); - } - new Setting(containerEl) - .setName('Folder path for the overview') - .setDesc('Choose the folder path for the overview') - .addSearch((search) => { - new FolderSuggest(search.inputEl, plugin, false) - search - .setPlaceholder('Folder path') - .setValue(overviewSettings?.folderPath || '') - .onChange(async (value) => { - if (!(this.app.vault.getAbstractFileByPath(value) instanceof TFolder) && value !== '') return; - overviewSettings.folderPath = value; - plugin.saveSettings(); - }); - }); - new Setting(containerEl) - .setName('Overview style') - .setDesc('Choose the style of the overview (grid style soon)') - .addDropdown((dropdown) => - dropdown - .addOption('list', 'List') - .addOption('explorer', 'Explorer') - .setValue(overviewSettings?.style || 'list') - .onChange(async (value: 'list') => { - overviewSettings.style = value; - settingsTab.display(); - plugin.saveSettings(); - }) - ); - if (overviewSettings.style === 'explorer') { - new Setting(containerEl) - .setName('Store collapsed condition') - .setDesc('Choose if the collapsed condition should be stored stored until you restart Obsidian') - .addToggle((toggle) => - toggle - .setValue(overviewSettings.storeFolderCondition) - .onChange(async (value) => { - overviewSettings.storeFolderCondition = value; - plugin.saveSettings(); - }) - ); - } - const setting = new Setting(containerEl) - .setName('Include types'); - - const list = new ListComponent(setting.settingEl, overviewSettings?.includeTypes || [], ['markdown', 'folder']); - list.on('update', (values) => { - overviewSettings.includeTypes = values; - settingsTab.display(); - plugin.saveSettings(); - }); - - if ((overviewSettings?.includeTypes?.length || 0) < 8 && !overviewSettings.includeTypes?.includes('all')) { - setting.addDropdown((dropdown) => { - overviewSettings.includeTypes = overviewSettings.includeTypes.map((type: string) => type.toLowerCase()) as includeTypes[]; - const options = [ - { value: 'markdown', label: 'Markdown' }, - { value: 'folder', label: 'Folder' }, - { value: 'canvas', label: 'Canvas' }, - { value: 'pdf', label: 'PDF' }, - { value: 'image', label: 'Image' }, - { value: 'audio', label: 'Audio' }, - { value: 'video', label: 'Video' }, - { value: 'other', label: 'All other file types' }, - { value: 'all', label: 'All file types' }, - ]; - - options.forEach((option) => { - if (!overviewSettings.includeTypes?.includes(option.value as includeTypes)) { - dropdown.addOption(option.value, option.label); - } - }); - dropdown.addOption('+', '+'); - dropdown.setValue('+'); - dropdown.onChange(async (value) => { - if (value === 'all') { - overviewSettings.includeTypes = overviewSettings.includeTypes?.filter((type: string) => type === 'folder'); - list.setValues(overviewSettings.includeTypes); - } - await list.addValue(value.toLowerCase()); - settingsTab.display(); - plugin.saveSettings(); - }); - }); - } - - let disableFileTag; - overviewSettings.includeTypes?.forEach((type: string) => { - type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null; - }); - if (disableFileTag) { - new Setting(containerEl) - .setName('Disable file tag') - .setDesc('Choose if the file tag should be shown after the file name') - .addToggle((toggle) => { - toggle - .setValue(overviewSettings.disableFileTag) - .onChange(async (value) => { - overviewSettings.disableFileTag = value; - plugin.saveSettings(); - - }); - }); - } - new Setting(containerEl) - .setName('Show folder notes') - .setDesc('Choose if folder notes (the note itself and not the folder name) should be shown in the overview') - .addToggle((toggle) => - toggle - .setValue(overviewSettings.showFolderNotes) - .onChange(async (value) => { - overviewSettings.showFolderNotes = value; - plugin.saveSettings(); - - }) - ); - - if (overviewSettings.style !== 'explorer') { - new Setting(containerEl) - .setName('File depth') - .setDesc('File & folder = +1 depth') - .addSlider((slider) => - slider - .setValue(overviewSettings?.depth || 2) - .setLimits(1, 10, 1) - .onChange(async (value) => { - overviewSettings.depth = value; - plugin.saveSettings(); - - }) - ); - } - - new Setting(containerEl) - .setName('Sort files by') - .setDesc('Choose how the files should be sorted') - .addDropdown((dropdown) => - dropdown - .addOption('name', 'Name') - .addOption('created', 'Created') - .addOption('modified', 'Modified') - .setValue(overviewSettings?.sortBy || 'name') - .onChange(async (value: 'name' | 'created' | 'modified') => { - overviewSettings.sortBy = value; - plugin.saveSettings(); - - }) - ) - .addDropdown((dropdown) => { - dropdown - .addOption('desc', 'Descending') - .addOption('asc', 'Ascending') - if (overviewSettings.sortByAsc) { - dropdown.setValue('asc'); - } else { - dropdown.setValue('desc'); - } - dropdown.onChange(async (value) => { - if (value === 'desc') { - overviewSettings.sortByAsc = false; - } else { - overviewSettings.sortByAsc = true; - } - plugin.saveSettings(); - - }); - }); - if (overviewSettings.style === 'list') { - new Setting(containerEl) - .setName('Show folder names of folders that appear empty in the folder overview') - .setDesc('Show the names of folders that appear to have no files/folders in the folder overview. That\'s mostly the case when you set the file depth to 1.') - .addToggle((toggle) => { - toggle - .setValue(overviewSettings.showEmptyFolders) - .onChange(async (value) => { - overviewSettings.showEmptyFolders = value; - overviewSettings.onlyIncludeSubfolders = false; - settingsTab.display(); - plugin.saveSettings(); - - }); - }); - - if (overviewSettings.showEmptyFolders) { - new Setting(containerEl) - .setName('Only show first empty subfolders of current folder') - .addToggle((toggle) => { - toggle - .setValue(overviewSettings.onlyIncludeSubfolders) - .onChange(async (value) => { - overviewSettings.onlyIncludeSubfolders = value; - plugin.saveSettings(); - }); - }); - } - } - - if (overviewSettings.style === 'explorer') { - new Setting(containerEl) - .setName('Disable collapse icon for folder notes') - .setDesc('Remove the collapse icon next to the folder name for folder notes when they only contain the folder note itself') - .addToggle((toggle) => { - toggle - .setValue(overviewSettings.disableCollapseIcon) - .onChange(async (value) => { - overviewSettings.disableCollapseIcon = value; - plugin.saveSettings(); - }); - }); - - new Setting(containerEl) - .setName('Collapse all in the tree by default') - .setDesc('Collapse every folder in the file explorer in the overview by default') - .addToggle((toggle) => { - toggle - .setValue(overviewSettings.alwaysCollapse) - .onChange(async (value) => { - overviewSettings.alwaysCollapse = value; - plugin.saveSettings(); - }); - }); - } + createOverviewSettings(containerEl, overviewSettings, plugin, true, settingsTab.display, undefined, undefined, undefined, settingsTab); } diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 005bdf2..dc15c32 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -1,4 +1,4 @@ -import { App, Notice, PluginSettingTab, TFile, TFolder } from 'obsidian'; +import { App, Notice, PluginSettingTab, TFile, TFolder, MarkdownPostProcessorContext } from 'obsidian'; import FolderNotesPlugin from '../main'; import { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; @@ -111,6 +111,7 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { storeFolderCondition: true, showFolderNotes: false, disableCollapseIcon: true, + alwaysCollapse: false, }, useSubmenus: true, syncMove: true, @@ -225,32 +226,34 @@ export class SettingsTab extends PluginSettingTab { } - display(): void { - this.plugin.settingsOpened = true; - const { containerEl } = this; + display(contentEl?: HTMLElement, yaml?: yamlSettings, plugin?: FolderNotesPlugin, defaultSettings?: boolean, display?: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: this) { + plugin = this?.plugin ?? plugin; + plugin.settingsOpened = true; + settingsTab = this ?? settingsTab; + const { containerEl } = settingsTab; containerEl.empty(); const tabBar = containerEl.createEl('nav', { cls: 'fn-settings-tab-bar' }); - for (const [tabId, tabInfo] of Object.entries(this.TABS)) { + for (const [tabId, tabInfo] of Object.entries(settingsTab.TABS)) { const tabEl = tabBar.createEl('div', { cls: 'fn-settings-tab' }); const tabName = tabEl.createEl('div', { cls: 'fn-settings-tab-name', text: tabInfo.name }); - if (this.plugin.settings.settingsTab.toLocaleLowerCase() === tabId.toLocaleLowerCase()) { + if (plugin.settings.settingsTab.toLocaleLowerCase() === tabId.toLocaleLowerCase()) { tabEl.addClass('fn-settings-tab-active'); } tabEl.addEventListener('click', () => { // @ts-ignore for (const tabEl of tabBar.children) { tabEl.removeClass('fn-settings-tab-active'); - this.plugin.settings.settingsTab = tabId.toLocaleLowerCase(); - this.plugin.saveSettings(); + plugin.settings.settingsTab = tabId.toLocaleLowerCase(); + plugin.saveSettings(); } tabEl.addClass('fn-settings-tab-active'); - this.renderSettingsPage(tabId); + settingsTab.renderSettingsPage(tabId); }); } - this.settingsPage = containerEl.createDiv({ cls: 'fn-settings-page' }); - this.renderSettingsPage(this.plugin.settings.settingsTab); + settingsTab.settingsPage = containerEl.createDiv({ cls: 'fn-settings-page' }); + settingsTab.renderSettingsPage(plugin.settings.settingsTab); } updateFolderNotes(newTemplate: string) {