From 5173962ee8d4003ee3ca3bf3418cd96d356179c9 Mon Sep 17 00:00:00 2001
From: Lost Paul <70213368+LostPaul@users.noreply.github.com>
Date: Sun, 11 Aug 2024 15:58:05 +0200
Subject: [PATCH] Improved the list setting type
---
src/events/EventEmitter.ts | 22 +++
src/folderOverview/ModalSettings.ts | 56 +++---
src/functions/ListComponent.ts | 115 ++++-------
src/settings/FolderOverviewSettings.ts | 251 ++++++++++++++++++++++++-
src/settings/GeneralSettings.ts | 11 +-
5 files changed, 338 insertions(+), 117 deletions(-)
create mode 100644 src/events/EventEmitter.ts
diff --git a/src/events/EventEmitter.ts b/src/events/EventEmitter.ts
new file mode 100644
index 0000000..81a7429
--- /dev/null
+++ b/src/events/EventEmitter.ts
@@ -0,0 +1,22 @@
+export class CustomEventEmitter {
+ private events: { [key: string]: Array<(data?: any) => void> } = {};
+
+ on(event: string, listener: (data?: any) => void) {
+ if (!this.events[event]) {
+ this.events[event] = [];
+ }
+ this.events[event].push(listener);
+ }
+
+ off(event: string, listener: (data?: any) => void) {
+ if (!this.events[event]) return;
+
+ this.events[event] = this.events[event].filter((l) => l !== listener);
+ }
+
+ emit(event: string, data?: any) {
+ if (!this.events[event]) return;
+
+ this.events[event].forEach((listener) => listener(data));
+ }
+}
\ No newline at end of file
diff --git a/src/folderOverview/ModalSettings.ts b/src/folderOverview/ModalSettings.ts
index aeb499d..db4bc8a 100644
--- a/src/folderOverview/ModalSettings.ts
+++ b/src/folderOverview/ModalSettings.ts
@@ -1,7 +1,7 @@
import { App, Modal, Setting, MarkdownPostProcessorContext, stringifyYaml, TFile, TFolder } from 'obsidian';
import { yamlSettings, includeTypes, FolderOverview } from 'src/folderOverview/FolderOverview';
import FolderNotesPlugin from '../main';
-import ListComponent from 'src/functions/ListComponent';
+import { ListComponent } from 'src/functions/ListComponent';
import { updateYaml } from 'src/folderOverview/FolderOverview';
import { FolderSuggest } from 'src/suggesters/FolderSuggester';
import { getFolderPathFromString } from 'src/functions/utils';
@@ -152,12 +152,16 @@ export class FolderOverviewSettings extends Modal {
}
const setting = new Setting(contentEl);
setting.setName('Include types');
- const list = setting.createList((list: ListComponent) =>
- list
- .addModal(this)
- .setValues(this.yaml?.includeTypes || this.plugin.settings.defaultOverview.includeTypes || [])
- .addResetButton()
- );
+ 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 || [];
@@ -184,10 +188,8 @@ export class FolderOverviewSettings extends Modal {
dropdown.onChange(async (value) => {
if (value === 'all') {
this.yaml.includeTypes = this.yaml.includeTypes?.filter((type: string) => type === 'folder');
- // @ts-ignore
list.setValues(this.yaml.includeTypes);
}
- // @ts-ignore
await list.addValue(value.toLowerCase());
this.display();
if (this.defaultSettings) {
@@ -197,10 +199,12 @@ export class FolderOverviewSettings extends Modal {
});
});
}
+
let disableFileTag;
this.yaml.includeTypes?.forEach((type: string) => {
type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null;
});
+
if (disableFileTag) {
new Setting(contentEl)
.setName('Disable file tag')
@@ -217,6 +221,7 @@ export class FolderOverviewSettings extends Modal {
});
});
}
+
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')
@@ -232,23 +237,21 @@ export class FolderOverviewSettings extends Modal {
})
);
- if (this.yaml.style !== 'explorer') {
- new Setting(contentEl)
- .setName('File depth')
- .setDesc('File & folder = +1 depth')
- .addSlider((slider) =>
- slider
- .setValue(this.yaml?.depth || 2)
- .setLimits(1, 10, 1)
- .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('File depth')
+ .setDesc('File & folder = +1 depth')
+ .addSlider((slider) =>
+ slider
+ .setValue(this.yaml?.depth || 2)
+ .setLimits(1, 10, 1)
+ .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')
@@ -288,6 +291,7 @@ export class FolderOverviewSettings extends Modal {
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')
diff --git a/src/functions/ListComponent.ts b/src/functions/ListComponent.ts
index 235ef06..df34fff 100644
--- a/src/functions/ListComponent.ts
+++ b/src/functions/ListComponent.ts
@@ -1,53 +1,50 @@
-import { Setting } from 'obsidian';
-import { FolderOverviewSettings } from '../folderOverview/ModalSettings';
-import { includeTypes } from 'src/folderOverview/FolderOverview';
-import { updateYaml } from 'src/folderOverview/FolderOverview';
-import { SettingsTab } from 'src/settings/SettingsTab';
-export default class ListComponent {
+import { CustomEventEmitter } from "src/events/EventEmitter";
+
+export class ListComponent {
+ emitter: CustomEventEmitter;
containerEl: HTMLElement;
controlEl: HTMLElement;
emptyStateEl: HTMLElement;
listEl: HTMLElement;
values: string[];
- modal: FolderOverviewSettings | undefined;
- settings: SettingsTab | undefined;
- constructor(containerEl: HTMLElement) {
+ defaultValues: string[];
+ constructor(containerEl: HTMLElement, values: string[] = [], defaultValues: string[] = []) {
+ this.emitter = new CustomEventEmitter();
this.containerEl = containerEl;
this.controlEl = containerEl.querySelector('.setting-item-control') || containerEl;
this.listEl = this.controlEl.createDiv('setting-command-hotkeys');
+ this.addResetButton();
+ this.setValues(values);
+ this.defaultValues = defaultValues;
}
- addModal(modal: FolderOverviewSettings) {
- this.modal = modal;
- this.values = modal.yaml.includeTypes || [];
- return this;
+
+ on(event: string, listener: (data?: any) => void) {
+ this.emitter.on(event, listener);
}
- addSettings(settings: SettingsTab) {
- this.settings = settings;
- return this;
+
+ off(event: string, listener: (data?: any) => void) {
+ this.emitter.off(event, listener);
}
+
+ private emit(event: string, data?: any) {
+ this.emitter.emit(event, data);
+ }
+
setValues(values: string[]) {
- this.listEl.empty();
+ this.removeElements();
this.values = values;
- if (this.modal) {
- this.modal.yaml.includeTypes = values as includeTypes[];
- }
if (values.length !== 0) {
values.forEach((value) => {
this.addElement(value);
});
}
- if (this.modal && this.modal.defaultSettings) {
- this.modal.plugin.saveSettings();
- return this;
- } else if (this.settings) {
- this.settings.plugin.settings.supportedFileTypes = values;
- this.settings.plugin.saveSettings();
- return this;
- }
- if (!this.modal) return this;
- updateYaml(this.modal.plugin, this.modal.ctx, this.modal.el, this.modal.yaml);
- return this;
+ this.emit('update', this.values);
}
+
+ removeElements() {
+ this.listEl.empty();
+ }
+
addElement(value: string) {
this.listEl.createSpan('setting-hotkey', (span) => {
if (value.toLocaleLowerCase() === 'md') {
@@ -55,8 +52,8 @@ export default class ListComponent {
} else {
span.innerText = value;
}
+ span.setAttribute('extension', value);
const removeSpan = span.createEl('span', { cls: 'ofn-list-item-remove setting-hotkey-icon' });
- // add svg icon
const svg = '';
const svgElement = removeSpan.createEl('span', { cls: 'ofn-list-item-remove-icon' });
svgElement.innerHTML = svg;
@@ -66,60 +63,28 @@ export default class ListComponent {
});
});
}
+
async addValue(value: string) {
this.values.push(value);
this.addElement(value);
- if (this.settings) {
- this.settings.plugin.settings.supportedFileTypes = this.values;
- this.settings.plugin.saveSettings();
- }
- if (!this.modal) return this;
- this.modal.yaml.includeTypes = this.values as includeTypes[];
- return this;
+ this.emit('add', value);
+ this.emit('update', this.values);
}
+
addResetButton() {
const resetButton = this.controlEl.createEl('span', { cls: 'clickable-icon setting-restore-hotkey-button' });
const svg = '';
resetButton.innerHTML = svg;
resetButton.onClickEvent((e) => {
- if (this.modal) {
- this.modal.plugin.loadSettings();
- this.setValues(this.modal.plugin.settings.defaultOverview.includeTypes || []);
- this.modal.display();
- } else if (this.settings) {
- this.setValues(['md', 'canvas']);
- this.settings.display();
- }
+ this.setValues(this.defaultValues);
});
return this;
}
+
removeValue(value: string) {
- if (value === 'all') {
- if (this.modal) {
- this.modal.plugin.loadSettings();
- this.setValues(this.modal.plugin.settings.defaultOverview.includeTypes || []);
- this.modal.display();
- } else if (this.settings) {
- this.setValues(['md', 'canvas']);
- this.settings.display();
- }
- } else {
- this.values = this.values.filter((v) => v !== value);
- this.setValues(this.values);
- if (this.modal) {
- this.modal.display();
- } else if (this.settings) {
- this.settings.display();
- }
- }
+ this.values = this.values.filter((v) => v !== value);
+ this.listEl.find(`[extension="${value}"]`).remove();
+ this.emit('remove', value);
+ this.emit('update', this.values);
}
-}
-
-function createList(this: Setting, cb: (list: ListComponent) => ListComponent) {
- const list = new ListComponent(this.settingEl);
- cb(list);
- return list;
-}
-
-// ListComponent.prototype.setValues = setValues;
-(Setting as any).prototype.createList = createList;
+}
\ No newline at end of file
diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts
index 91e05b9..5b93733 100644
--- a/src/settings/FolderOverviewSettings.ts
+++ b/src/settings/FolderOverviewSettings.ts
@@ -1,18 +1,249 @@
-import { Setting } from "obsidian";
+import { Setting, TFolder } from "obsidian";
import { SettingsTab } from "./SettingsTab";
-import { FolderOverviewSettings } from '../folderOverview/ModalSettings';
+import { ListComponent } from 'src/functions/ListComponent';
+import { includeTypes } from 'src/folderOverview/FolderOverview';
+import { FolderSuggest } from "src/suggesters/FolderSuggester";
export async function renderFolderOverview(settingsTab: SettingsTab) {
+ const { plugin } = settingsTab;
+ let overviewSettings = plugin.settings.defaultOverview;
const containerEl = settingsTab.settingsPage;
+ containerEl.createEl('p', { text: 'Change the default settings for folder overviews', cls: 'setting-item-description' });
+
new Setting(containerEl)
- .setName('Manage folder overview defaults')
- .setDesc('Manage the default settings for the folder overview plugin')
- .addButton((button) =>
- button
- .setButtonText('Manage')
- .setCta()
- .onClick(async () => {
- new FolderOverviewSettings(settingsTab.plugin.app, settingsTab.plugin, settingsTab.plugin.settings.defaultOverview, null, null, true).open();
+ .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();
+ });
+ });
+ }
+
}
diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts
index 183c728..68afa80 100644
--- a/src/settings/GeneralSettings.ts
+++ b/src/settings/GeneralSettings.ts
@@ -1,6 +1,6 @@
import { Setting, Platform, SettingTab } from "obsidian";
import { SettingsTab } from "./SettingsTab";
-import ListComponent from '../functions/ListComponent';
+import { ListComponent } from '../functions/ListComponent';
import AddSupportedFileModal from '../modals/AddSupportedFileType';
import { FrontMatterTitlePluginHandler } from '../events/FrontMatterTitle';
import ConfirmationModal from "../modals/ConfirmCreation";
@@ -90,10 +90,10 @@ export async function renderGeneral(settingsTab: SettingsTab) {
'Adding more file types may cause performance issues becareful when adding more file types and don\'t add too many.',
)
setting0.setDesc(desc0);
- const list = setting0.createList((list: ListComponent) => {
- list.addSettings(settingsTab)
- list.setValues(settingsTab.plugin.settings.supportedFileTypes || ['md', 'canvas']);
- list.addResetButton();
+ const list = new ListComponent(setting0.settingEl,settingsTab.plugin.settings.supportedFileTypes || [], ['md', 'canvas'] );
+ list.on('update', async (values: string[]) => {
+ settingsTab.plugin.settings.supportedFileTypes = values;
+ await settingsTab.plugin.saveSettings();
})
if (!settingsTab.plugin.settings.supportedFileTypes.includes('md') || !settingsTab.plugin.settings.supportedFileTypes.includes('canvas') || !settingsTab.plugin.settings.supportedFileTypes.includes('excalidraw')) {
@@ -116,7 +116,6 @@ export async function renderGeneral(settingsTab: SettingsTab) {
if (value === 'custom') {
return new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open();
}
- // @ts-ignore
await list.addValue(value.toLowerCase());
settingsTab.display();
settingsTab.plugin.saveSettings();