diff --git a/docs/docs/Folder overview.md b/docs/docs/Folder overview.md index f04e2cf..9339a3b 100644 --- a/docs/docs/Folder overview.md +++ b/docs/docs/Folder overview.md @@ -1,8 +1,4 @@ # Folder overview - -This page will be updated soon and there also will be new features. -This feature/plugin creates a code block - This feature/plugin creates a [code block](https://help.obsidian.md/Editing+and+formatting/Basic+formatting+syntax#Code+blocks) that provides an overview of your entire vault or specific folders. Instructions for creating or editing a folder overview, along with additional guidance, can be found further down the page. Currently there are two styles available: **file explorer** & **list style**, with more coming soon. The **overview updates** itself **automatically** when you create, delete or modify a file. You can also integrate it into **templates**, as explained further down below this page. @@ -13,11 +9,14 @@ There are two options to create a folder overview in a note - Use the command "Insert folder overview" from the [command palette](https://help.obsidian.md/Plugins/Command+palette) - On desktop right click and click on the option "Create folder overview" ## Edit overview -If the overview shows no files or folders yet you can just click on the button that says "Edit overview". But if there are already files/folders it's a bit more complicated than the button and to then edit the overview you have to hover over the overview and click on the "Edit this block" button on the right top corner of the overview with only an icon. +If the overview shows no files or folders yet you can just click on the button that says "Edit overview". But if there are already files/folders you can use the command "Edit folder overview" from the [command palette](https://help.obsidian.md/Plugins/Command+palette) or click on the button as showed in the video below. -The video below shows the steps explained in the text. ![type:video](../assets/n5AGi3VCxF5JcNx2Wm5O.mp4) ## Default settings -To edit the settings for a new folder overview that hasn't been created yet you have to open the plugin settings and go to the folder overview tab and click on the manage button. -![Screenshot](../assets/ngkJjEcxUUTkX6eB4mSs.png) +To edit the default values for new folder overviews you can either use the command "Edit folder overview" or open the plugins settings tab and then the folder overview tab to the edit the default settings. + + +![Screenshot](../assets/screenshots/b4QOtkzJs0.png) +(On the left is the plugin settings tab and the right the view you see when you use the "Edit folder overview" command) + diff --git a/docs/docs/assets/ngkJjEcxUUTkX6eB4mSs.png b/docs/docs/assets/ngkJjEcxUUTkX6eB4mSs.png deleted file mode 100644 index 8a50481..0000000 Binary files a/docs/docs/assets/ngkJjEcxUUTkX6eB4mSs.png and /dev/null differ diff --git a/docs/docs/assets/screenshots/b4QOtkzJs0.png b/docs/docs/assets/screenshots/b4QOtkzJs0.png new file mode 100644 index 0000000..4253594 Binary files /dev/null and b/docs/docs/assets/screenshots/b4QOtkzJs0.png differ diff --git a/src/Commands.ts b/src/Commands.ts index fb38b7f..2155075 100644 --- a/src/Commands.ts +++ b/src/Commands.ts @@ -3,8 +3,8 @@ import FolderNotesPlugin from './main'; import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName, detachFolderNote } from './functions/folderNoteFunctions'; import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder'; import { getFolderPathFromString, getFileExplorerActiveFolder } from './functions/utils'; -import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; -import { applyCSSClassesToFolder } from './functions/styleFunctions'; +import { deleteExcludedFolder, getDetachedFolder, getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; +import { hideFolderNoteInFileExplorer, showFolderNoteInFileExplorer } from './functions/styleFunctions'; @@ -403,10 +403,7 @@ export class Commands { item.setTitle('Hide folder note in explorer') .setIcon('eye-off') .onClick(() => { - this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter( - (folder) => (folder.path !== file.path) && folder.showFolderNote); - this.plugin.saveSettings(false); - applyCSSClassesToFolder(file.path, this.plugin); + hideFolderNoteInFileExplorer(file.path, this.plugin); }); }); } else { @@ -414,17 +411,7 @@ export class Commands { item.setTitle('Show folder note in explorer') .setIcon('eye') .onClick(() => { - const excludedFolder = new ExcludedFolder(file.path, this.plugin.settings.excludeFolders.length, undefined, this.plugin); - excludedFolder.subFolders = false; - excludedFolder.disableSync = false; - excludedFolder.disableAutoCreate = false; - excludedFolder.disableFolderNote = false; - excludedFolder.enableCollapsing = false; - excludedFolder.excludeFromFolderOverview = false; - excludedFolder.hideInSettings = true; - excludedFolder.showFolderNote = true; - addExcludedFolder(this.plugin, excludedFolder, false); - applyCSSClassesToFolder(file.path, this.plugin); + showFolderNoteInFileExplorer(file.path, this.plugin); }); }); } diff --git a/src/events/MutationObserver.ts b/src/events/MutationObserver.ts index ba749a0..5b7aea8 100644 --- a/src/events/MutationObserver.ts +++ b/src/events/MutationObserver.ts @@ -1,9 +1,9 @@ import { Keymap, Platform } from 'obsidian'; import FolderNotesPlugin from 'src/main'; import { getFolderNote } from 'src/functions/folderNoteFunctions'; -import { handleFolderClick, handleViewHeaderClick } from './handleClick'; +import { handleViewHeaderClick } from './handleClick'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; -import { applyCSSClassesToFolder } from 'src/functions/styleFunctions'; +import { updateCSSClassesForFolder } from 'src/functions/styleFunctions'; let fileExplorerMutationObserver: MutationObserver | null = null; @@ -114,7 +114,7 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu if (!folderPath) return; folderTitle.dataset.initialized = 'true'; - await applyCSSClassesToFolder(folderPath, plugin); + await updateCSSClassesForFolder(folderPath, plugin); if (plugin.settings.frontMatterTitle.enabled) { plugin.fmtpHandler?.fmptUpdateFolderName({ id: '', result: false, path: folderPath, pathOnly: false }, false); @@ -122,12 +122,6 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu if (Platform.isMobile && plugin.settings.disableOpenFolderNoteOnClick) return; - folderTitle.addEventListener('auxclick', (event: MouseEvent) => { - if (event.button === 1) handleFolderClick(event, plugin); - }, { capture: true }); - - folderTitle.onclick = (event: MouseEvent) => handleFolderClick(event, plugin); - plugin.registerDomEvent(folderTitle, 'pointerover', (event: MouseEvent) => { plugin.hoveredElement = folderTitle; plugin.mouseEvent = event; diff --git a/src/events/handleClick.ts b/src/events/handleClick.ts index 208cc44..d5106e7 100644 --- a/src/events/handleClick.ts +++ b/src/events/handleClick.ts @@ -2,7 +2,7 @@ import { Keymap, Platform } from 'obsidian'; import FolderNotesPlugin from 'src/main'; import { openFolderNote, createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; -import { addCSSClassToTitleEL, removeCSSClassFromEL } from 'src/functions/styleFunctions'; +import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL } from 'src/functions/styleFunctions'; export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNotesPlugin) { event.stopImmediatePropagation(); @@ -38,65 +38,11 @@ export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNot } else if (event.altKey || Keymap.isModEvent(event) === 'tab') { if ((plugin.settings.altKey && event.altKey) || (plugin.settings.ctrlKey && Keymap.isModEvent(event) === 'tab')) { await createFolderNote(plugin, folderPath, true, undefined, true); - addCSSClassToTitleEL(folderPath, 'has-folder-note', plugin); - removeCSSClassFromEL(folderPath, 'has-not-folder-note', plugin); + addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin); return; } } event.target.onclick = null; event.target.click(); } - -export async function handleFolderClick(event: MouseEvent, plugin: FolderNotesPlugin) { - if (!(event.target instanceof HTMLElement)) return; - event.target.setAttribute('data-initialized', 'false'); - if (!event || !event.target) return; - - if (!event.shiftKey) { - event.stopImmediatePropagation(); - } else { - return; - } - - const folderPath = event.target.parentElement?.getAttribute('data-path'); - if (!folderPath) { return; } - - const excludedFolder = getExcludedFolder(plugin, folderPath, true); - if (excludedFolder?.disableFolderNote) { - event.target.onclick = null; - event.target.click(); - return; - } else if (excludedFolder?.enableCollapsing || plugin.settings.enableCollapsing) { - event.target.onclick = null; - event.target.click(); - } - - const folderNote = getFolderNote(plugin, folderPath); - - if (folderNote) { - if (plugin.settings.openByClick) { - return openFolderNote(plugin, folderNote, event); - } else if (plugin.settings.openWithCtrl && Keymap.isModEvent(event) === 'tab') { - return openFolderNote(plugin, folderNote, event); - } else if (plugin.settings.openWithAlt && event.altKey) { - return openFolderNote(plugin, folderNote, event); - } else { - if (plugin.settings.enableCollapsing) return; - event.target.parentElement?.click(); - return; - } - } else if (event.altKey || Keymap.isModEvent(event) === 'tab') { - if ((plugin.settings.altKey && event.altKey) || (plugin.settings.ctrlKey && Keymap.isModEvent(event) === 'tab')) { - await createFolderNote(plugin, folderPath, true, undefined, true); - addCSSClassToTitleEL(folderPath, 'has-folder-note', plugin); - removeCSSClassFromEL(folderPath, 'has-not-folder-note', plugin); - return; - } - } else if (!folderNote) { - if (plugin.settings.enableCollapsing) return; - return event.target.parentElement?.click(); - } - - event.target.onclick = null; - event.target.click(); -} diff --git a/src/events/handleCreate.ts b/src/events/handleCreate.ts index c1e6eff..0cbcaee 100644 --- a/src/events/handleCreate.ts +++ b/src/events/handleCreate.ts @@ -2,17 +2,17 @@ import { TAbstractFile, TFolder, TFile } from 'obsidian'; import FolderNotesPlugin from 'src/main'; import { createFolderNote, getFolder, getFolderNote, turnIntoFolderNote } from 'src/functions/folderNoteFunctions'; import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'; -import { removeCSSClassFromEL, addCSSClassToTitleEL } from 'src/functions/styleFunctions'; +import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl } from 'src/functions/styleFunctions'; export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) { if (!plugin.app.workspace.layoutReady) return; const folder = file.parent; if (folder instanceof TFolder) { - if (plugin.isEmptyFolderNoteFolder(folder)) { - addCSSClassToTitleEL(folder.path, 'only-has-folder-note', plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } } @@ -32,7 +32,7 @@ async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) { turnIntoFolderNote(plugin, file, newFolder); } else if (folder instanceof TFolder) { if (folder.children.length >= 1) { - removeCSSClassFromEL(folder.path, 'fn-empty-folder', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'fn-empty-folder', false, plugin); } const detachedFolder = getExcludedFolder(plugin, folder.path, true); @@ -40,8 +40,8 @@ async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) { const folderNote = getFolderNote(plugin, folder.path); if (folderNote && folderNote.path === file.path) { - addCSSClassToTitleEL(folder.path, 'has-folder-note', plugin); - addCSSClassToTitleEL(file.path, 'is-folder-note', plugin); + addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); } else if (plugin.settings.autoCreateForFiles) { if (!file.parent) { return; } const newFolder = await plugin.app.fileManager.createNewFolder(file.parent); @@ -56,7 +56,7 @@ async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin) const attachmentFolderPath = plugin.app.vault.getConfig('attachmentFolderPath') as string; const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || ''; const attachmentsAreInRootFolder = attachmentFolderPath === './' || attachmentFolderPath === ''; - addCSSClassToTitleEL(folder.path, 'fn-empty-folder', plugin); + addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, plugin); if (!plugin.settings.autoCreateForAttachmentFolder) { if (!attachmentsAreInRootFolder && cleanAttachmentFolderPath === folder.name) return; @@ -71,5 +71,5 @@ async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin) if (folderNote) return; createFolderNote(plugin, folder.path, openFile, undefined, true); - addCSSClassToTitleEL(folder.path, 'has-folder-note', plugin); + addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); } diff --git a/src/events/handleDelete.ts b/src/events/handleDelete.ts index 324c9d5..b2b644c 100644 --- a/src/events/handleDelete.ts +++ b/src/events/handleDelete.ts @@ -1,16 +1,16 @@ import { TAbstractFile, TFolder, TFile } from 'obsidian'; import FolderNotesPlugin from 'src/main'; import { getFolderNote, getFolder, deleteFolderNote } from 'src/functions/folderNoteFunctions'; -import { removeCSSClassFromEL, addCSSClassToTitleEL } from 'src/functions/styleFunctions'; +import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, hideFolderNoteInFileExplorer } from 'src/functions/styleFunctions'; import { getFolderPathFromString } from 'src/functions/utils'; export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) { const folder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(file.path)); if (folder instanceof TFolder) { - if (plugin.isEmptyFolderNoteFolder(folder)) { - addCSSClassToTitleEL(folder.path, 'only-has-folder-note', plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } } @@ -19,14 +19,15 @@ export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) { if (!folder) { return; } const folderNote = getFolderNote(plugin, folder.path); if (folderNote) { return; } - removeCSSClassFromEL(folder.path, 'has-folder-note', plugin); - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); + hideFolderNoteInFileExplorer(folder.path, plugin); } if (!(file instanceof TFolder)) { return; } const folderNote = getFolderNote(plugin, file.path); if (!folderNote) { return; } - removeCSSClassFromEL(folderNote.path, 'is-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin); if (!plugin.settings.syncDelete) { return; } deleteFolderNote(plugin, folderNote, false); } diff --git a/src/events/handleRename.ts b/src/events/handleRename.ts index e0753df..abdb5d3 100644 --- a/src/events/handleRename.ts +++ b/src/events/handleRename.ts @@ -3,39 +3,40 @@ import FolderNotesPlugin from 'src/main'; import { extractFolderName, getFolderNote, getFolderNoteFolder } from '../functions/folderNoteFunctions'; import { getExcludedFolder, addExcludedFolder, updateExcludedFolder, deleteExcludedFolder, getDetachedFolder } from '../ExcludeFolders/functions/folderFunctions'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; -import { removeCSSClassFromEL, addCSSClassToTitleEL } from 'src/functions/styleFunctions'; +import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, markFileAsFolderNote, unmarkFileAsFolderNote, unmarkFolderAsFolderNote, markFolderWithFolderNoteClasses, hideFolderNoteInFileExplorer, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions'; import { getFolderPathFromString, removeExtension, getFileNameFromPathString } from 'src/functions/utils'; export function handleRename(file: TAbstractFile, oldPath: string, plugin: FolderNotesPlugin) { const folder = file.parent; const oldFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(oldPath)); - const isRename = (file.parent?.path === getFolderPathFromString(oldPath)); + if (folder instanceof TFolder) { - if (plugin.isEmptyFolderNoteFolder(folder)) { - addCSSClassToTitleEL(folder.path, 'only-has-folder-note', plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } } if (oldFolder instanceof TFolder) { - if (plugin.isEmptyFolderNoteFolder(oldFolder)) { - addCSSClassToTitleEL(oldFolder.path, 'only-has-folder-note', plugin); + if (plugin.isEmptyFolderNoteFolder(oldFolder) && getFolderNote(plugin, oldFolder.path)) { + addCSSClassToFileExplorerEl(oldFolder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(oldFolder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(oldFolder.path, 'only-has-folder-note', true, plugin); } } if (file instanceof TFolder) { - plugin.tabManager.updateTab(file.path); - updateExcludedFolderPath(file, oldPath, plugin); - if (isRename) { - return handleFolderRename(file, oldPath, plugin); + const folder = file; + plugin.tabManager.updateTab(folder.path); + updateExcludedFolderPath(folder, oldPath, plugin); + if (isFolderRename(folder, oldPath)) { + return handleFolderRename(folder, oldPath, plugin); } else { - return handleFolderMove(file, oldPath, plugin); + return handleFolderMove(folder, oldPath, plugin); } } else if (file instanceof TFile) { - if (isRename) { + if (isFileRename(file, oldPath)) { return fmptUpdateFileName(file, oldPath, plugin); } else { return handleFileMove(file, oldPath, plugin); @@ -43,6 +44,26 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde } } +function isFileRename(file: TFile, oldPath: string): boolean { + const oldFolderPath = getFolderPathFromString(oldPath); + const newFolderPath = file.parent?.path; + const oldName = getFileNameFromPathString(oldPath); + const newName = file.name; + + return oldFolderPath === newFolderPath && oldName !== newName; +} + + +function isFolderRename(folder: TFolder, oldPath: string): boolean { + const oldName = getFileNameFromPathString(oldPath); + const newName = folder.name; + const oldParent = getFolderPathFromString(oldPath); + const newParent = folder.parent?.path; + + return oldParent === newParent && oldName !== newName; +} + + export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) { // Soon @@ -54,12 +75,19 @@ export async function handleFileMove(file: TFile, oldPath: string, plugin: Folde const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename; const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); const newFolder = getFolderNoteFolder(plugin, file, file.basename); - const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true); const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); + const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); - // file has been moved into position where it can be a folder note! - if (folderName === newFolder?.name && folderNote) { + + const isFileNowFolderNoteInNewFolder = folderName === newFolder?.name; + const isFileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName && newFolder?.path !== oldFolder.path; + + // this is for turning files into folder notes for folders that already have a folder note + // e.g. Turn into folder note for "Folder name" + const isFileNowFolderNoteWithExistingNote = folderName === newFolder?.name && folderNote; + + if (isFileNowFolderNoteWithExistingNote) { let excludedFolderExisted = true; let disabledSync = false; @@ -81,12 +109,28 @@ export async function handleFileMove(file: TFile, oldPath: string, plugin: Folde updateExcludedFolder(plugin, excludedFolder, excludedFolder); } }); - } else if (oldFolder && oldFolder.name === oldFileName && newFolder?.path !== oldFolder.path) { - // the note has been moved somewhere and is no longer a folder note - // cleanup css on the folder and note - removeCSSClassFromEL(oldFolder.path, 'has-folder-note', plugin); - removeCSSClassFromEL(file.path, 'is-folder-note', plugin); - removeCSSClassFromEL(oldPath, 'is-folder-note', plugin); + } else if (isFileNowFolderNoteInNewFolder) { + if (!excludedFolder?.disableFolderNote) { + markFileAsFolderNote(file, plugin); + if (newFolder instanceof TFolder) { + markFolderWithFolderNoteClasses(newFolder, plugin); + if (plugin.app.workspace.getActiveFile()?.path === file.path) { + removeActiveFolder(plugin); + setActiveFolder(newFolder.path, plugin); + } + } + if (oldFolder instanceof TFolder) { + hideFolderNoteInFileExplorer(oldFolder.path, plugin); + unmarkFolderAsFolderNote(oldFolder, plugin); + } + } + } else if (isFileMovedFromOldFolderNote) { + unmarkFileAsFolderNote(file, plugin); + if (oldFolder instanceof TFolder) { + removeActiveFolder(plugin); + hideFolderNoteInFileExplorer(oldFolder.path, plugin); + unmarkFolderAsFolderNote(oldFolder, plugin); + } } } @@ -96,14 +140,13 @@ export async function handleFolderRename(file: TFolder, oldPath: string, plugin: if (fileName === oldFileName) { return; } - const folderNote = getFolderNote(plugin, oldPath); if (!(folderNote instanceof TFile)) return; const excludedFolder = getExcludedFolder(plugin, file.path, true); if (excludedFolder?.disableSync && !folderNote) { - return removeCSSClassFromEL(file.path, 'has-folder-note', plugin); + return removeCSSClassFromFileExplorerEL(file.path, 'has-folder-note', false, plugin); } if (!plugin.settings.syncFolderName) { return; } @@ -143,21 +186,21 @@ export async function fmptUpdateFileName(file: TFile, oldPath: string, plugin: F const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); if (!excludedFolder?.disableFolderNote && folderName === newFolder?.name && !detachedExcludedFolder) { - addCSSClassToTitleEL(file.path, 'is-folder-note', plugin); - addCSSClassToTitleEL(newFolder.path, 'has-folder-note', plugin); + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); return; } else if (excludedFolder?.disableFolderNote || (folderName !== newFolder?.name)) { - removeCSSClassFromEL(file.path, 'is-folder-note', plugin); - removeCSSClassFromEL(newFolder?.path || '', 'has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(newFolder?.path || '', 'has-folder-note', false, plugin); } if (excludedFolder?.disableSync || !plugin.settings.syncFolderName) { return; } if (folderName === newFolder?.name) { - addCSSClassToTitleEL(file.path, 'is-folder-note', plugin); - removeCSSClassFromEL(oldFolder?.path, 'has-folder-note', plugin); - addCSSClassToTitleEL(newFolder.path, 'has-folder-note', plugin); + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(oldFolder?.path, 'has-folder-note', false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin); return; } @@ -175,12 +218,12 @@ export async function fmptUpdateFileName(file: TFile, oldPath: string, plugin: F async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: TAbstractFile, plugin: FolderNotesPlugin) { const newFolderName = extractFolderName(plugin.settings.folderNoteName, file.basename); if (!newFolderName) { - removeCSSClassFromEL(oldFolder.path, 'has-folder-note', plugin); - removeCSSClassFromEL(file.path, 'is-folder-note', plugin); + removeCSSClassFromFileExplorerEL(oldFolder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); return; } else if (newFolderName === oldFolder.name) { - addCSSClassToTitleEL(oldFolder.path, 'has-folder-note', plugin); - addCSSClassToTitleEL(file.path, 'is-folder-note', plugin); + addCSSClassToFileExplorerEl(oldFolder.path, 'has-folder-note', false, plugin); + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); return; } diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts index 89f73fe..3b05db4 100644 --- a/src/functions/folderNoteFunctions.ts +++ b/src/functions/folderNoteFunctions.ts @@ -7,7 +7,7 @@ import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcluded import { ExcludedFolder } from '../ExcludeFolders/ExcludeFolder'; import { openExcalidrawView } from './excalidraw'; import { AskForExtensionModal } from 'src/modals/AskForExtension'; -import { getEl, addCSSClassToTitleEL, removeCSSClassFromEL } from 'src/functions/styleFunctions'; +import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions'; import { getFolderNameFromPathString, getFolderPathFromString, removeExtension } from 'src/functions/utils'; const defaultExcalidrawTemplate = `--- @@ -60,7 +60,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st if (detachedFolder && folderNote?.extension !== extension && folderNote) { deleteExcludedFolder(plugin, detachedFolder); - removeCSSClassFromEL(folderNote?.path, 'is-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin); const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder; if (!folderNote || folderNote.basename !== fileName) return; let count = 1; @@ -120,16 +120,12 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st if (openFile) { if (plugin.app.workspace.getActiveFile()?.path === path) { - if (plugin.activeFolderDom) { - plugin.activeFolderDom.removeClass('fn-is-active'); - plugin.activeFolderDom = null; - } + removeActiveFolder(plugin); const folder = getFolder(plugin, folderNote); if (!folder) { return; } - plugin.activeFolderDom = getEl(folder.path, plugin); - if (plugin.activeFolderDom) plugin.activeFolderDom.addClass('fn-is-active'); + setActiveFolder(folder.path, plugin); } await leaf.openFile(folderNote); if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') { @@ -144,8 +140,8 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st const folder = plugin.app.vault.getAbstractFileByPath(folderPath); if (!(folder instanceof TFolder)) return; - addCSSClassToTitleEL(path, 'is-folder-note', plugin, true); - addCSSClassToTitleEL(folder.path, 'has-folder-note', plugin); + addCSSClassToFileExplorerEl(path, 'is-folder-note', false, plugin, true); + addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); } export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote?: TFile | null | TAbstractFile, skipConfirmation?: boolean) { @@ -156,7 +152,7 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, if (plugin.settings.showRenameConfirmation && !skipConfirmation && !detachedExcludedFolder) { return new ExistingFolderNoteModal(plugin.app, plugin, file, folder, folderNote).open(); } - removeCSSClassFromEL(folderNote.path, 'is-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin); const [excludedFolder, excludedFolderExisted, disabledSync] = await tempDisableSync(plugin, folder); @@ -190,16 +186,11 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, } await plugin.app.fileManager.renameFile(file, path); - addCSSClassToTitleEL(path, 'is-folder-note', plugin, true); - addCSSClassToTitleEL(folder.path, 'has-folder-note', plugin); + addCSSClassToFileExplorerEl(path, 'is-folder-note', false, plugin, true); + addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); - if (plugin.activeFolderDom) { - plugin.activeFolderDom.removeClass('fn-is-active'); - plugin.activeFolderDom = null; - } - - plugin.activeFolderDom = getEl(folder.path, plugin); - if (plugin.activeFolderDom) plugin.activeFolderDom.addClass('fn-is-active'); + removeActiveFolder(plugin); + setActiveFolder(folder.path, plugin); } export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder): Promise<[excludedFolder: ExcludedFolder | undefined, excludedFolderExisted: boolean, disabledSync: boolean]> { @@ -223,7 +214,7 @@ export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractFile, evt?: MouseEvent) { const path = file.path; - const focusExistingTab = plugin.settings.focusExistingTab; + const focusExistingTab = plugin.settings.focusExistingTab && plugin.settings.openInNewTab; const activeFilePath = plugin.app.workspace.getActiveFile()?.path; // If already active and not opening in new tab, do nothing @@ -265,7 +256,7 @@ export async function deleteFolderNote(plugin: FolderNotesPlugin, file: TFile, d (excludedFolder) => (excludedFolder.path !== folder.path) && excludedFolder.showFolderNote); plugin.saveSettings(false); - removeCSSClassFromEL(folder.path, 'has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); switch (plugin.settings.deleteFilesAction) { case 'trash': await plugin.app.vault.trash(file, true); diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts index 7211e55..7dd1e28 100644 --- a/src/functions/styleFunctions.ts +++ b/src/functions/styleFunctions.ts @@ -6,161 +6,186 @@ import { getFileExplorer } from './utils'; import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder'; import FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main'; -export function updateAllFileStyles(forceReload = false, plugin: FolderNotesPlugin) { +/** + * @description Refreshes the CSS classes for all folder notes in the file explorer. + */ +export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin) { if (plugin.activeFileExplorer === getFileExplorer(plugin) && !forceReload) { return; } plugin.activeFileExplorer = getFileExplorer(plugin); plugin.app.vault.getAllLoadedFiles().forEach(async (file) => { if (!(file instanceof TFolder)) { return; } const folderNote = getFolderNote(plugin, file.path); if (!folderNote) { - removeCSSClassFromEL(file?.path, 'has-folder-note', plugin); - removeCSSClassFromEL(file?.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(file?.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(file?.path, 'only-has-folder-note', true, plugin); plugin.isEmptyFolderNoteFolder(file); return; } const excludedFolder = getExcludedFolder(plugin, file.path, true); - // cleanup after ourselves - // Incase settings have changed if (excludedFolder?.disableFolderNote) { - removeCSSClassFromEL(folderNote.path, 'is-folder-note', plugin); - removeCSSClassFromEL(file.path, 'has-folder-note', plugin); - removeCSSClassFromEL(file?.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(file.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(file?.path, 'only-has-folder-note', true, plugin); } else { if (!excludedFolder?.showFolderNote) { - addCSSClassToTitleEL(folderNote.path, 'is-folder-note', plugin); + addCSSClassToFileExplorerEl(folderNote.path, 'is-folder-note', false, plugin); } - addCSSClassesToFolder(file, plugin); + markFolderWithFolderNoteClasses(file, plugin); } }); } - -export async function applyCSSClassesToFolder(folderPath: string, plugin: FolderNotesPlugin) { +/** + * @description Updates the CSS classes for a specific folder in the file explorer. + */ +export async function updateCSSClassesForFolder(folderPath: string, plugin: FolderNotesPlugin) { const folder = plugin.app.vault.getAbstractFileByPath(folderPath); if (!folder || !(folder instanceof TFolder)) { return; } const folderNote = getFolderNote(plugin, folder.path); + // console.log('Updating CSS classes for folder:', folder.path, 'Folder Note:', folderNote); const detachedFolderNote = getDetachedFolder(plugin, folder.path); if (folder.children.length === 0) { - addCSSClassToTitleEL(folder.path, 'fn-empty-folder', plugin); + addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, plugin); } if (!folderNote || detachedFolderNote) { - removeCSSClassFromEL(folder?.path, 'has-folder-note', plugin); - removeCSSClassFromEL(folder?.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder?.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder?.path, 'only-has-folder-note', true, plugin); return; } const excludedFolder = getExcludedFolder(plugin, folder.path, true); if (excludedFolder?.disableFolderNote) { - removeCSSClassFromEL(folderNote.path, 'is-folder-note', plugin); - removeCSSClassFromEL(folder.path, 'has-folder-note', plugin); - removeCSSClassFromEL(folder?.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder?.path, 'only-has-folder-note', true, plugin); } else { - addCSSClassesToFolder(folder, plugin); + markFolderWithFolderNoteClasses(folder, plugin); if (excludedFolder?.showFolderNote) { - removeCSSClassFromFolderNote(folderNote, plugin); + addCSSClassToFileExplorerEl(folder.path, 'show-folder-note-in-explorer', true, plugin); + unmarkFileAsFolderNote(folderNote, plugin); return; } - if (plugin.isEmptyFolderNoteFolder(folder)) { - addCSSClassToTitleEL(folder.path, 'only-has-folder-note', plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } } - addCSSClassesToBothFolderAndNote(folderNote, folder, plugin); + markFolderAndNoteWithClasses(folderNote, folder, plugin); } -export async function applyCSSClassesToFolderNote(filePath: string, plugin: FolderNotesPlugin) { +/** + * @description Updates the CSS classes for a folder note file in the file explorer and then also updates the folder it belongs to. + */ +export async function updateCSSClassesForFolderNote(filePath: string, plugin: FolderNotesPlugin) { const file = plugin.app.vault.getAbstractFileByPath(filePath); if (!file || !(file instanceof TFile)) { return; } const folder = getFolder(plugin, file); if (!folder || !(folder instanceof TFolder)) { return; } - applyCSSClassesToFolder(folder.path, plugin); - + updateCSSClassesForFolder(folder.path, plugin); } -export function addCSSClassesToBothFolderAndNote(file: TFile, folder: TFolder, plugin: FolderNotesPlugin) { - addCSSClassToFolderNote(file, plugin); - addCSSClassesToFolder(folder, plugin); +export function markFolderAndNoteWithClasses(file: TFile, folder: TFolder, plugin: FolderNotesPlugin) { + markFileAsFolderNote(file, plugin); + markFolderWithFolderNoteClasses(folder, plugin); } -export function removeCSSClassesFromBothFolderAndNote(folder: TFolder, file: TFile, plugin: FolderNotesPlugin) { - removeCSSClassFromFolderNote(file, plugin); - removeCSSClassesFromFolder(folder, plugin); +export function clearFolderAndNoteClasses(folder: TFolder, file: TFile, plugin: FolderNotesPlugin) { + unmarkFileAsFolderNote(file, plugin); + clearFolderNoteClassesFromFolder(folder, plugin); } -export function addCSSClassesToFolder(folder: TFolder, plugin: FolderNotesPlugin) { - addCSSClassToTitleEL(folder.path, 'has-folder-note', plugin); - if (plugin.isEmptyFolderNoteFolder(folder)) { - addCSSClassToTitleEL(folder.path, 'only-has-folder-note', plugin); +export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderNotesPlugin) { + addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin); } else { - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } } -export function addCSSClassToFolderNote(file: TFile, plugin: FolderNotesPlugin) { - addCSSClassToTitleEL(file.path, 'is-folder-note', plugin); +export function markFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) { + addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin); } -export function removeCSSClassFromFolderNote(file: TFile, plugin: FolderNotesPlugin) { - removeCSSClassFromEL(file.path, 'is-folder-note', plugin); +export function unmarkFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) { + removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin); } -export function removeCSSClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin) { - removeCSSClassFromEL(folder.path, 'has-folder-note', plugin); - removeCSSClassFromEL(folder.path, 'only-has-folder-note', plugin); +export function unmarkFolderAsFolderNote(folder: TFolder, plugin: FolderNotesPlugin) { + removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); } -export async function addCSSClassToTitleEL(path: string, cssClass: string, plugin: FolderNotesPlugin, waitForCreate = false, count = 0) { - const fileExplorerItem = getEl(path, plugin); +export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin) { + removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin); +} + +/** + * @param path Can be a folder or file path + * @returns nothing + */ +export async function addCSSClassToFileExplorerEl(path: string, cssClass: string, parent = false, plugin: FolderNotesPlugin, waitForCreate = false, count = 0) { + const fileExplorerItem = getFileExplorerElement(path, plugin); if (!fileExplorerItem) { if (waitForCreate && count < 5) { - // sleep for a second for the file-explorer event to catch up - // this is annoying as in most scanarios our plugin recieves the event before file explorer - // If we could guarrantee load order it wouldn't be an issue but we can't - // realise this is racey and needs to be fixed. await new Promise((r) => setTimeout(r, 500)); - addCSSClassToTitleEL(path, cssClass, plugin, waitForCreate, count + 1); + addCSSClassToFileExplorerEl(path, cssClass, parent, plugin, waitForCreate, count + 1); return; } return; } - fileExplorerItem.addClass(cssClass); - const viewHeaderItems = document.querySelectorAll(`[data-path='${CSS.escape(path)}']`); - viewHeaderItems.forEach((item) => { - item.addClass(cssClass); - }); + if (parent) { + const parentElement = fileExplorerItem?.parentElement; + if (parentElement) { + parentElement.addClass(cssClass); + } + } else { + fileExplorerItem.addClass(cssClass); + document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => { + item.addClass(cssClass); + }); + } } -export function removeCSSClassFromEL(path: string | undefined, cssClass: string, plugin: FolderNotesPlugin) { +/** + * @param path Can be a folder or file path + * @param cssClass The CSS class to remove from the file explorer element + * @returns nothing + */ +export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssClass: string, parent: boolean, plugin: FolderNotesPlugin) { if (!path) return; - const fileExplorerItem = getEl(path, plugin); - const viewHeaderItems = document.querySelectorAll(`[data-path='${CSS.escape(path)}']`); - viewHeaderItems.forEach((item) => { + const fileExplorerItem = getFileExplorerElement(path, plugin); + document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => { item.removeClass(cssClass); }); if (!fileExplorerItem) { return; } - fileExplorerItem.removeClass(cssClass); + if (parent) { + const parentElement = fileExplorerItem?.parentElement; + if (parentElement) { + parentElement.removeClass(cssClass); + } + return; + } else { + fileExplorerItem.removeClass(cssClass); + } } -export function getEl(path: string, plugin: FolderNotesPlugin | FolderOverviewPlugin): HTMLElement | null { +export function getFileExplorerElement(path: string, plugin: FolderNotesPlugin | FolderOverviewPlugin): HTMLElement | null { const fileExplorer = getFileExplorer(plugin); - if (!fileExplorer) { return null; } - if (!fileExplorer.view) { return null; } - if (!fileExplorer.view.fileItems) { return null; } + if (!fileExplorer?.view?.fileItems) { return null; } const fileExplorerItem = fileExplorer.view.fileItems?.[path]; - if (!fileExplorerItem) { return null; } - if (fileExplorerItem.selfEl) return fileExplorerItem.selfEl; - return fileExplorerItem.titleEl; + return fileExplorerItem?.selfEl ?? fileExplorerItem?.titleEl ?? null; } - export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin) { const excludedFolder = new ExcludedFolder(path, plugin.settings.excludeFolders.length, undefined, plugin); excludedFolder.subFolders = false; @@ -172,12 +197,30 @@ export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPl excludedFolder.hideInSettings = true; excludedFolder.showFolderNote = true; addExcludedFolder(plugin, excludedFolder, false); - applyCSSClassesToFolder(path, plugin); + addCSSClassToFileExplorerEl(path, 'show-folder-note-in-explorer', true, plugin); + updateCSSClassesForFolder(path, plugin); } -export function hideFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin) { +export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin) { plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter( - (folder) => (folder.path !== path) && folder.showFolderNote); + (folder) => (folder.path !== folderPath) && folder.showFolderNote); plugin.saveSettings(false); - applyCSSClassesToFolder(path, plugin); + removeCSSClassFromFileExplorerEL(folderPath, 'show-folder-note-in-explorer', true, plugin); + updateCSSClassesForFolder(folderPath, plugin); +} + +export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin) { + const fileExplorerItem = getFileExplorerElement(folderPath, plugin); + if (fileExplorerItem) { + fileExplorerItem.addClass('fn-is-active'); + plugin.activeFolderDom = fileExplorerItem; + } +} + +export function removeActiveFolder(plugin: FolderNotesPlugin) { + if (plugin.activeFolderDom) { + plugin.activeFolderDom.removeClass('fn-is-active'); + plugin.activeFolderDom?.removeClass('has-focus'); + plugin.activeFolderDom = null; + } } diff --git a/src/main.ts b/src/main.ts index 0e0c851..7c0bb5a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,11 @@ -import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion } from 'obsidian'; +import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice, Keymap, WorkspaceLeaf, requireApiVersion, Platform } from 'obsidian'; import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings/SettingsTab'; import { Commands } from './Commands'; import { FileExplorerWorkspaceLeaf } from './globals'; -import { handleFolderClick } from './events/handleClick'; import { registerFileExplorerObserver, unregisterFileExplorerObserver } from './events/MutationObserver'; import { handleRename } from './events/handleRename'; -import { getFolderNote, getFolder, openFolderNote } from './functions/folderNoteFunctions'; +import { getFolderNote, getFolder, openFolderNote, createFolderNote } from './functions/folderNoteFunctions'; import { handleCreate } from './events/handleCreate'; import { FrontMatterTitlePluginHandler } from './events/FrontMatterTitle'; import { FolderOverviewSettings } from './obsidian-folder-overview/src/modals/Settings'; @@ -14,7 +13,7 @@ import { FolderOverview } from './obsidian-folder-overview/src/FolderOverview'; import { TabManager } from './events/TabManager'; import './functions/ListComponent'; import { handleDelete } from './events/handleDelete'; -import { addCSSClassToTitleEL, getEl, updateAllFileStyles } from './functions/styleFunctions'; +import { addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL, refreshAllFolderStyles, setActiveFolder, removeActiveFolder } from './functions/styleFunctions'; import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions'; import { FileExplorerView, InternalPlugin } from 'obsidian-typings'; import { getFocusedItem } from './functions/utils'; @@ -110,10 +109,7 @@ export default class FolderNotesPlugin extends Plugin { })); this.registerEvent(this.app.workspace.on('file-open', async (openFile: TFile | null) => { - if (this.activeFolderDom) { - this.activeFolderDom.removeClass('fn-is-active'); - this.activeFolderDom = null; - } + removeActiveFolder(this); if (!openFile || !openFile.basename) { return; } @@ -124,8 +120,7 @@ export default class FolderNotesPlugin extends Plugin { const folderNote = getFolderNote(this, folder.path); if (!folderNote) { return; } if (folderNote.path !== openFile.path) { return; } - this.activeFolderDom = getEl(folder.path, this); - if (this.activeFolderDom) this.activeFolderDom.addClass('fn-is-active'); + setActiveFolder(folder.path, this); })); this.registerEvent(this.app.vault.on('rename', (file: TAbstractFile, oldPath: string) => { @@ -139,6 +134,19 @@ export default class FolderNotesPlugin extends Plugin { this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => { this.handleOverviewBlock(source, el, ctx); }); + + this.registerEvent( + this.app.workspace.on('layout-change', () => { + const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')[0]; + if (!fileExplorerLeaf) return; + const container = fileExplorerLeaf.view.containerEl; + if (container) { + this.registerDomEvent(container, 'click', (evt: MouseEvent) => { + this.handleFileExplorerClick(evt); + }, true); + } + }) + ); } onLayoutReady() { @@ -152,6 +160,16 @@ export default class FolderNotesPlugin extends Plugin { this.tabManager = new TabManager(this); this.tabManager.updateTabs(); + const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')[0]; + if (fileExplorerLeaf) { + const container = fileExplorerLeaf.view.containerEl; + if (container) { + this.registerDomEvent(container, 'click', (evt: MouseEvent) => { + this.handleFileExplorerClick(evt); + }, true); + } + } + const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById('file-explorer'); if (fileExplorerPlugin) { const originalRevealInFolder = fileExplorerPlugin.revealInFolder.bind(fileExplorerPlugin); @@ -219,6 +237,49 @@ export default class FolderNotesPlugin extends Plugin { }; } + handleFileExplorerClick(evt: MouseEvent) { + const target = evt.target as HTMLElement; + if (evt.shiftKey) return; + + if (Platform.isMobile && this.settings.disableOpenFolderNoteOnClick) return; + + // Check if the click is on a folder + const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement; + if (!folderTitleEl) return; + + const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content'); + if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return; + + // Ignore clicks on the collapse icon + if (target.closest('.collapse-icon')) return; + + const folderPath = folderTitleEl.getAttribute('data-path'); + if (!folderPath) return; + + const excludedFolder = getExcludedFolder(this, folderPath, true); + if (excludedFolder?.disableFolderNote) return; + + const folderNote = getFolderNote(this, folderPath); + if (!folderNote && (evt.altKey || Keymap.isModEvent(evt) === 'tab')) { + if ((this.settings.altKey && evt.altKey) || (this.settings.ctrlKey && Keymap.isModEvent(evt) === 'tab')) { + createFolderNote(this, folderPath, true, undefined, true); + addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this); + removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this); + return; + } + } + if (!(folderNote instanceof TFile)) return; + + if (this.settings.openWithCtrl && !evt.ctrlKey) return; + if (this.settings.openWithAlt && !evt.altKey) return; + + if (!this.settings.enableCollapsing) { + evt.preventDefault(); + evt.stopImmediatePropagation(); + } + + openFolderNote(this, folderNote, evt); + } handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) { const observer = new MutationObserver(() => { @@ -280,7 +341,7 @@ export default class FolderNotesPlugin extends Plugin { const attachmentsAreInRootFolder = attachmentFolderPath === './' || attachmentFolderPath === ''; const threshold = this.settings.storageLocation === 'insideFolder' ? 1 : 0; if (folder.children.length === 0) { - addCSSClassToTitleEL(folder.path, 'fn-empty-folder', this); + addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this); } if (folder.children.length === threshold) { @@ -293,7 +354,7 @@ export default class FolderNotesPlugin extends Plugin { const attachmentFolder = this.app.vault.getAbstractFileByPath(folderPath); if (attachmentFolder instanceof TFolder && folder.children.length <= threshold + 1) { if (!folder.collapsed) { - getEl(folder.path, this)?.click(); + getFileExplorerElement(folder.path, this)?.click(); } } return folder.children.length <= threshold + 1; @@ -306,7 +367,7 @@ export default class FolderNotesPlugin extends Plugin { async changeFolderNameInExplorer(folder: TFolder, newName: string | null | undefined, waitForCreate = false, count = 0) { if (!newName) newName = folder.name; - let fileExplorerItem = getEl(folder.path, this); + let fileExplorerItem = getFileExplorerElement(folder.path, this); if (!fileExplorerItem) { if (waitForCreate && count < 5) { await new Promise((r) => setTimeout(r, 500)); @@ -358,21 +419,13 @@ export default class FolderNotesPlugin extends Plugin { }); } - reloadHandlers() { - document.querySelectorAll('div.nav-folder-title-content') - .forEach((element: HTMLElement) => { - if (element.onclick) return; - element.onclick = (event: MouseEvent) => handleFolderClick(event, this); - }); - } - onunload() { unregisterFileExplorerObserver(); document.body.classList.remove('folder-notes-plugin'); document.body.classList.remove('folder-note-underline'); document.body.classList.remove('hide-folder-note'); document.body.classList.remove('fn-whitespace-stop-collapsing'); - if (this.activeFolderDom) { this.activeFolderDom.removeClass('is-active'); } + removeActiveFolder(this); if (this.fmtpHandler) { this.fmtpHandler.deleteEvent(); } @@ -405,7 +458,7 @@ export default class FolderNotesPlugin extends Plugin { await this.saveData(this.settings); // cleanup any css if we need too if ((!this.settingsOpened || reloadStyles === true) && reloadStyles !== false) { - updateAllFileStyles(true, this); + refreshAllFolderStyles(true, this); } } diff --git a/src/obsidian-folder-overview b/src/obsidian-folder-overview index 331d3ae..192ecd3 160000 --- a/src/obsidian-folder-overview +++ b/src/obsidian-folder-overview @@ -1 +1 @@ -Subproject commit 331d3aea842fea661f8979a923b466ebed161379 +Subproject commit 192ecd3cd7ca983db1a9d4a97ec90333bdbc8fd8 diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts index a6a76fb..fd33180 100644 --- a/src/settings/GeneralSettings.ts +++ b/src/settings/GeneralSettings.ts @@ -5,7 +5,7 @@ import AddSupportedFileModal from '../modals/AddSupportedFileType'; import { FrontMatterTitlePluginHandler } from '../events/FrontMatterTitle'; import ConfirmationModal from './modals/CreateFnForEveryFolder'; import { TemplateSuggest } from '../suggesters/TemplateSuggester'; -import { updateAllFileStyles } from '../functions/styleFunctions'; +import { refreshAllFolderStyles } from '../functions/styleFunctions'; import BackupWarningModal from './modals/BackupWarning'; import RenameFolderNotesModal from './modals/RenameFns'; @@ -186,7 +186,7 @@ export async function renderGeneral(settingsTab: SettingsTab) { settingsTab.plugin.settings.storageLocation = value; await settingsTab.plugin.saveSettings(); settingsTab.display(); - updateAllFileStyles(undefined, settingsTab.plugin); + refreshAllFolderStyles(undefined, settingsTab.plugin); }) ) .addButton((button) => diff --git a/styles.css b/styles.css index a7a0f85..86b8b4f 100644 --- a/styles.css +++ b/styles.css @@ -1,36 +1,26 @@ +/* ========================================================================== + General States & Utilities + ========================================================================== */ - -.fn-whitespace-stop-collapsing .nav-folder-title-content { - flex-grow: 1 !important; - display: flex; - padding-bottom: 4px !important; - padding-top: 4px !important; +.hide, +.hide-folder .folder-name, +.hide-folder-note .is-folder-note { + display: none; } -.version-1-7-2.fn-whitespace-stop-collapsing .nav-files-container .collapse-icon { - padding-top: 4px !important; +.pointer-cursor, +.has-folder-note .nav-folder-title-content:hover, +.has-folder-note.view-header-breadcrumb:hover, +.nav-folder-collapse-indicator:hover, +.fn-delete-confirmation-modal-buttons span:hover, +.fn-delete-confirmation-modal-buttons input:hover { + cursor: pointer !important; } -.mod-rtl .fn-whitespace-stop-collapsing .nav-folder-title-content { - padding-left: 8px !important; -} -.fn-whitespace-stop-collapsing .nav-folder-title { - padding-bottom: 0 !important; - padding-top: 0 !important; -} - -body:not(.mod-rtl).fn-whitespace-stop-collapsing .nav-folder-title { - padding-right: 0 !important; -} - -.mod-rtl.fn-whitespace-stop-collapsing .nav-folder-title { - padding-left: 0 !important; -} - -.fn-whitespace-stop-collapsing .nav-folder-collapse-indicator { - margin-top: 4px !important; -} +/* ========================================================================== + Tree Items + ========================================================================== */ body:not(.is-grabbing) .tree-item-self.fn-is-active:hover, body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { @@ -39,36 +29,17 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { font-weight: var(--nav-item-weight-active); } -.has-folder-note .nav-folder-title-content:hover, -.has-folder-note.view-header-breadcrumb:hover { - cursor: pointer; -} -.hide { - display: none; -} - -.hide-folder-note .is-folder-note { - display: none; -} - -.hide-folder .folder-name { - display: none; -} - -.nav-folder-collapse-indicator:hover { - cursor: pointer; -} +/* ========================================================================== + Exclude Folder Settings + ========================================================================== */ .fn-excluded-folder-heading { margin-top: 0 !important; border-top: 1px solid var(--background-modifier-border); } -.add-exclude-folder-item { - padding-bottom: 0 !important; -} - +.add-exclude-folder-item, .fn-exclude-folder-list { padding-bottom: 0 !important; } @@ -91,7 +62,10 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { width: 100%; } -/* Modal styles */ + +/* ========================================================================== + Modal Styles + ========================================================================== */ .fn-backup-warning-modal .fn-modal-button-container { display: flex; @@ -103,9 +77,13 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { padding-bottom: 0; } +.fn-confirmation-modal .setting-item { + border-top: 0 !important; + padding-top: 0 !important; +} + :not(.is-phone) .fn-confirmation-modal-button { margin-right: 0.7rem; - } :not(.is-phone) .fn-delete-confirmation-modal-buttons { @@ -114,11 +92,6 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { margin-top: 10px; } -.fn-delete-confirmation-modal-buttons span:hover, -.fn-delete-confirmation-modal-buttons input:hover { - cursor: pointer; -} - :not(.is-phone) .fn-delete-confirmation-modal-buttons .fn-confirmation-modal-button { margin-left: auto; } @@ -137,22 +110,17 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { margin-top: 10px; } -/* Folder overview */ + +/* ========================================================================== + Folder Overview + ========================================================================== */ .folder-overview-container.fv-remove-edit-button .folder-overview-edit-button { display: none; } -.fn-folder-overview-collapse-icon { - display: block !important; -} - -.fn-has-no-files .collapse-icon { - display: none !important; -} - .folder-overview-list { - margin-top: 0 !important; + margin-top: 0 !important; margin-bottom: 0 !important; padding-bottom: 1.200 !important; padding-top: 1.200 !important; @@ -162,7 +130,6 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { display: flex; } - .folder-overview-list::marker { color: var(--text-faint); } @@ -177,12 +144,17 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { grid-template-columns: repeat(3, 1fr); } +.folder-overview-grid-item { + flex: 1 1 auto; + margin: 0 1.2rem 1.2rem 0; +} + .folder-overview-grid-item-article article { - padding: 15px; - flex: 1; display: flex; flex-direction: column; justify-content: space-between; + padding: 15px; + flex: 1; } .folder-overview-grid-item-article a { @@ -204,52 +176,9 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { } -.folder-overview-grid-item { - flex: 1 1 auto; - margin-right: 1.200rem; - margin-bottom: 1.200rem; -} - -.fn-confirmation-modal .setting-item { - border-top: 0 !important; - padding-top: 0 !important; -} - -.pointer-cursor { - cursor: pointer !important; -} - - -/* Setting tab style */ -.fn-settings-tab-bar { - display: flex; - flex-direction: row; - padding-bottom: 1rem; -} - -.fn-settings-tab { - display: flex; - flex-direction: row; - align-items: center; - gap: var(--size-4-2); - padding: 10px; - border: 1px solid var(--background-modifier-border) -} - -.fn-settings-tab-active { - background-color: var(--color-accent); - color: var(--text-on-accent); -} - -.fn-settings-tab-name { - font-weight: bold; -} - -.fn-settings-tab-icon { - display: flex; -} - -/* File explorer & path styles */ +/* ========================================================================== + File Explorer & Path Styling + ========================================================================== */ .folder-note-underline .has-folder-note .nav-folder-title-content { text-decoration-line: underline; @@ -275,14 +204,57 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { font-style: italic; } -.fn-hide-collapse-icon .has-folder-note.only-has-folder-note .tree-item-icon { + +/* Collapse Icon Handling */ + +.fn-folder-overview-collapse-icon { + display: block !important; +} + +.fn-has-no-files .collapse-icon, +.fn-hide-collapse-icon .has-folder-note.only-has-folder-note .tree-item-icon, +.fn-hide-empty-collapse-icon .fn-empty-folder .tree-item-icon, +.only-has-folder-note:not(.is-collapsed):not(.show-folder-note-in-explorer) > .nav-folder-children { display: none; } -.fn-hide-empty-collapse-icon .fn-empty-folder .tree-item-icon { - display: none; + +/* ========================================================================== + Settings Tabs + ========================================================================== */ + +.fn-settings-tab-bar { + display: flex; + flex-direction: row; + padding-bottom: 1rem; } +.fn-settings-tab { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--size-4-2); + padding: 10px; + border: 1px solid var(--background-modifier-border); +} + +.fn-settings-tab-active { + background-color: var(--color-accent); + color: var(--text-on-accent); +} + +.fn-settings-tab-name { + font-weight: bold; +} + +.fn-settings-tab-icon { + display: flex; +} + + +/* ========================================================================== + Suggestion Container + ========================================================================== */ .fn-suggestion-container { position: absolute; @@ -299,12 +271,11 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { } -/* Mobile style overrides */ -.is-phone .fn-overview-folder-path .setting-item-control{ - display: block; -} +/* ========================================================================== + Whitelist Folder Input (Desktop & Mobile) + ========================================================================== */ -/* Default desktop layout */ +/* Default Desktop Layout */ .fn-whitelist-folder-input-container { display: flex; justify-content: space-between; @@ -316,9 +287,9 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { .fn-whitelist-folder-input-container input { flex-grow: 1; width: auto; - box-sizing: border-box; margin-right: 8px; - height: 40px; /* Ensure consistent height */ + height: 40px; + box-sizing: border-box; } .fn-whitelist-folder-buttons { @@ -330,12 +301,12 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { flex-shrink: 0; } -/* Mobile-specific layout */ +/* Mobile Overrides */ @media (max-width: 768px) { .fn-whitelist-folder-input-container { display: block; width: 100%; - text-align: center; /* Center elements vertically when stacked */ + text-align: center; } .fn-whitelist-folder-input-container input { @@ -350,4 +321,8 @@ body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { align-items: center; width: 100%; } -} + + .is-phone .fn-overview-folder-path .setting-item-control { + display: block; + } +} \ No newline at end of file