lostpaul_obsidian-folder-ov.../src/settings.ts
2025-03-01 14:46:28 +01:00

425 lines
15 KiB
TypeScript

import { MarkdownPostProcessorContext, normalizePath, PluginSettingTab, Setting, TFile, TFolder } from 'obsidian';
import { updateYaml, updateYamlById, overviewSettings, includeTypes } from './FolderOverview';
import { FolderSuggest } from './suggesters/FolderSuggester';
import { ListComponent } from './utils/ListComponent';
import { FolderOverviewSettings } from './modals/Settings';
import FolderOverviewPlugin from './main';
import FolderNotesPlugin from '../../main';
export const DEFAULT_SETTINGS: overviewSettings = {
id: '',
folderPath: '',
title: '{{folderName}} overview',
showTitle: false,
depth: 3,
includeTypes: ['folder', 'markdown'],
style: 'list',
disableFileTag: false,
sortBy: 'name',
sortByAsc: true,
showEmptyFolders: false,
onlyIncludeSubfolders: false,
storeFolderCondition: true,
showFolderNotes: false,
disableCollapseIcon: true,
alwaysCollapse: false,
autoSync: true,
allowDragAndDrop: true,
};
export class SettingsTab extends PluginSettingTab {
plugin: FolderOverviewPlugin;
constructor(plugin: FolderOverviewPlugin) {
super(plugin.app, plugin);
}
display() {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('p', { text: 'Edit the default settings of folder overviews', cls: 'setting-item-description' });
this.display = this.display.bind(this);
createOverviewSettings(containerEl, this.plugin.settings, this.plugin, this.plugin.settings, this.display, undefined, undefined, undefined, this);
}
}
const createOrReplaceSetting = (
container: HTMLElement,
section: string,
changedSection: string | null,
renderSetting: (el: HTMLElement) => void
) => {
let sectionContainer = container.querySelector(`.setting-${section}`);
if (sectionContainer) {
if (changedSection === section || changedSection === 'all') {
sectionContainer.empty();
renderSetting(sectionContainer as HTMLElement);
return;
} else {
return;
}
}
sectionContainer = container.createDiv({ cls: `setting-${section} overview-setting-item-fv` });
renderSetting(sectionContainer as HTMLElement);
};
export async function createOverviewSettings(contentEl: HTMLElement, yaml: overviewSettings, plugin: FolderOverviewPlugin | FolderNotesPlugin, defaultSettings: overviewSettings, display: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: PluginSettingTab, modal?: FolderOverviewSettings, changedSection?: string | null) {
changedSection = changedSection ?? null;
createOrReplaceSetting(contentEl, 'auto-sync', changedSection, (settingEl) => {
new Setting(settingEl)
.setName('Auto sync')
.setDesc('Choose if the overview should automatically update when you delete, create or rename a file/folder')
.addToggle((toggle) =>
toggle
.setValue(yaml.autoSync)
.onChange(async (value) => {
yaml.autoSync = value;
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal);
})
);
});
createOrReplaceSetting(contentEl, 'allow-drag-and-drop', changedSection, (settingEl) => {
new Setting(settingEl)
.setName('Allow drag and drop')
.setDesc('Choose if you want to allow drag and drop in the overview')
.addToggle((toggle) =>
toggle
.setValue(yaml.allowDragAndDrop)
.onChange(async (value) => {
yaml.allowDragAndDrop = value;
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal);
})
);
});
createOrReplaceSetting(contentEl, 'showTitle', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal);
})
);
});
createOrReplaceSetting(contentEl, 'title-container-fn', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
})
);
});
createOrReplaceSetting(contentEl, 'folder-path', changedSection, (settingEl) => {
const folderPathSetting = new Setting(settingEl)
.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 (value.trim() !== '') {
value = normalizePath(value);
}
if (!(plugin.app.vault.getAbstractFileByPath(value) instanceof TFolder) && value !== '') return;
yaml.folderPath = value;
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
});
});
folderPathSetting.settingEl.classList.add('fn-overview-folder-path');
});
createOrReplaceSetting(contentEl, 'overview-style', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal);
})
);
});
createOrReplaceSetting(contentEl, 'include-types', changedSection, (settingEl) => {
const setting = new Setting(settingEl);
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);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, 'include-types');
});
if ((yaml?.includeTypes?.length || 0) < 8 && !yaml.includeTypes?.includes('all')) {
setting.addDropdown((dropdown) => {
if (!yaml.includeTypes) yaml.includeTypes = (plugin instanceof FolderNotesPlugin) ? plugin.settings.defaultOverview.includeTypes : plugin.settings.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);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, 'include-types');
});
});
}
});
createOrReplaceSetting(contentEl, 'file-tag', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
});
});
});
createOrReplaceSetting(contentEl, 'show-folder-notes', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
})
);
});
createOrReplaceSetting(contentEl, 'file-depth', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
})
);
});
createOrReplaceSetting(contentEl, 'sort-files', changedSection, (settingEl) => {
new Setting(settingEl)
.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) => {
yaml.sortByAsc = value === 'asc';
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
});
});
});
createOrReplaceSetting(contentEl, 'show-empty-folders', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal);
});
});
});
createOrReplaceSetting(contentEl, 'show-empty-folders-only-first-level', changedSection, (settingEl) => {
new Setting(settingEl)
.setName('Only show empty folders which are on the first level of the folder overview')
.addToggle((toggle) => {
toggle
.setValue(yaml.onlyIncludeSubfolders)
.onChange(async (value) => {
yaml.onlyIncludeSubfolders = value;
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
});
});
});
createOrReplaceSetting(contentEl, 'disable-collapse-icon', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
});
});
});
createOrReplaceSetting(contentEl, 'store-collapse-condition', changedSection, (settingEl) => {
new Setting(settingEl)
.setName('Store collapsed condition')
.setDesc('Choose if the collapsed condition should be 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);
})
);
});
createOrReplaceSetting(contentEl, 'collapse-all-by-default', changedSection, (settingEl) => {
new Setting(settingEl)
.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);
});
});
});
updateSettings(contentEl, yaml, plugin, defaultSettings, el, ctx, file);
}
async function updateSettings(contentEl: HTMLElement, yaml: overviewSettings, plugin: FolderOverviewPlugin | FolderNotesPlugin, defaultSettings: overviewSettings, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null) {
let showDisableFileTag = false;
yaml.includeTypes?.forEach((type: string) => {
if (type !== 'markdown' && type !== 'folder') {
showDisableFileTag = true;
}
});
if (yaml.includeTypes.length === 0) {
showDisableFileTag = false;
}
toggleSections(contentEl, {
'setting-title-container-fn': yaml.showTitle,
'setting-store-collapse-condition': yaml.style === 'explorer',
'setting-file-tag': showDisableFileTag,
'setting-show-empty-folders': yaml.style === 'list',
'setting-show-empty-folders-only-first-level': yaml.showEmptyFolders && yaml.style === 'list',
'setting-disable-collapse-icon': yaml.style === 'explorer',
'setting-collapse-all-by-default': yaml.style === 'explorer',
'setting-allow-drag-and-drop': yaml.style === 'explorer',
});
if (!yaml.id) {
plugin.saveSettings();
if (file === undefined) {
plugin.updateOverviewView(plugin);
}
return;
}
if (el && ctx) {
await updateYaml(plugin, ctx, el, yaml);
}
if (file) {
await updateYamlById(plugin, yaml.id, file, yaml);
}
}
function refresh(contentEl: HTMLElement, yaml: overviewSettings, plugin: FolderOverviewPlugin | FolderNotesPlugin, defaultSettings: overviewSettings, display: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: PluginSettingTab, modal?: FolderOverviewSettings, changedSection?: string) {
if (file) {
contentEl = contentEl.parentElement as HTMLElement;
}
display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection);
}
function toggleSections(contentEl: HTMLElement, sections: Record<string, boolean>) {
Object.entries(sections).forEach(([sectionClass, shouldShow]) => {
const sections = contentEl.querySelectorAll(`.${sectionClass}`);
sections.forEach((section) => {
if (shouldShow && section) {
section.classList.remove('hide');
} else {
section?.classList.add('hide');
}
});
});
}