mirror of
https://github.com/lostpaul/obsidian-folder-notes.git
synced 2026-07-22 07:40:24 +00:00
Save progress
This commit is contained in:
parent
98158cbef9
commit
4962ac5236
16 changed files with 454 additions and 300 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "folder-notes",
|
||||
"name": "Folder notes",
|
||||
"version": "1.7.23-9-beta",
|
||||
"version": "1.7.23-10-beta",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.",
|
||||
"author": "Lost Paul",
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote,
|
|||
import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder';
|
||||
import { getFolderPathFromString } from './functions/utils';
|
||||
import { getExcludedFolderByPattern } from './ExcludeFolders/functions/patternFunctions';
|
||||
import { getDetachedFolder, getExcludedFolder, getExcludedFoldersByPath } from './ExcludeFolders/functions/folderFunctions';
|
||||
import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcludedFolder, getExcludedFoldersByPath, updateExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
|
||||
import ExcludedFolderSettings from './ExcludeFolders/modals/ExcludeFolderSettings'
|
||||
import { ExcludePattern } from './ExcludeFolders/ExcludePattern';
|
||||
import PatternSettings from './ExcludeFolders/modals/PatternSettings';
|
||||
import { loadFolderClasses } from './functions/styleFunctions';
|
||||
|
||||
export class Commands {
|
||||
plugin: FolderNotesPlugin;
|
||||
|
|
@ -34,6 +35,7 @@ export class Commands {
|
|||
turnIntoFolderNote(this.plugin, file, folder, folderNote);
|
||||
}
|
||||
});
|
||||
|
||||
this.plugin.addCommand({
|
||||
id: 'create-folder-note',
|
||||
name: 'Create folder note with a new folder for the active note in the current folder',
|
||||
|
|
@ -58,6 +60,7 @@ export class Commands {
|
|||
this.plugin.saveSettings();
|
||||
}
|
||||
})
|
||||
|
||||
this.plugin.addCommand({
|
||||
id: 'create-folder-note-for-current-folder',
|
||||
name: 'Create markdown folder note for current folder of active note',
|
||||
|
|
@ -199,7 +202,7 @@ export class Commands {
|
|||
}
|
||||
|
||||
fileCommands() {
|
||||
this.plugin.registerEvent(this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => {
|
||||
this.plugin.registerEvent(this.app.workspace.on('file-menu', async (menu: Menu, file: TAbstractFile) => {
|
||||
let folder: TAbstractFile | TFolder | null = file.parent;
|
||||
if (file instanceof TFile) {
|
||||
if (this.plugin.settings.storageLocation === 'insideFolder') {
|
||||
|
|
@ -217,14 +220,14 @@ export class Commands {
|
|||
|
||||
if (folder instanceof TFolder) {
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
const excludedFolder = getExcludedFolder(this.plugin, folder.path, true)
|
||||
const excludedFolder = await getExcludedFolder(this.plugin, folder.path, true)
|
||||
if (folderNote?.path === file.path && !excludedFolder?.detached) { return; }
|
||||
} else if (file.parent instanceof TFolder) {
|
||||
folder = file.parent;
|
||||
}
|
||||
}
|
||||
|
||||
menu.addItem((item) => {
|
||||
menu.addItem(async (item) => {
|
||||
if (Platform.isDesktop && !Platform.isTablet && this.plugin.settings.useSubmenus) {
|
||||
item
|
||||
.setTitle('Folder Note Commands')
|
||||
|
|
@ -276,10 +279,10 @@ export class Commands {
|
|||
});
|
||||
}
|
||||
if (!(file instanceof TFolder)) return;
|
||||
const excludedFolder = getExcludedFolder(this.plugin, file.path, false)
|
||||
const excludedFolder = await getExcludedFolder(this.plugin, file.path, false)
|
||||
const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path)
|
||||
|
||||
if (excludedFolder) {
|
||||
|
||||
if (excludedFolder && !excludedFolder.hideNote) {
|
||||
// I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded
|
||||
// subMenu.addItem((item) => {
|
||||
// item.setTitle('Manage excluded folder')
|
||||
|
|
@ -339,7 +342,6 @@ export class Commands {
|
|||
item.setTitle('Detach folder note')
|
||||
.setIcon('unlink')
|
||||
.onClick(() => {
|
||||
console.log('folderNote', folderNote)
|
||||
detachFolderNote(this.plugin, folderNote);
|
||||
});
|
||||
});
|
||||
|
|
@ -353,6 +355,39 @@ export class Commands {
|
|||
});
|
||||
});
|
||||
|
||||
if (this.plugin.settings.hideFolderNote) {
|
||||
if (excludedFolder?.hideNote) {
|
||||
subMenu.addItem((item) => {
|
||||
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.hideNote);
|
||||
this.plugin.saveSettings(false);
|
||||
loadFolderClasses(true, file, this.plugin);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
subMenu.addItem((item) => {
|
||||
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.hideNote = true;
|
||||
excludedFolder.subFolders = false;
|
||||
excludedFolder.disableSync = false;
|
||||
excludedFolder.disableAutoCreate = false;
|
||||
excludedFolder.disableFolderNote = false;
|
||||
excludedFolder.enableCollapsing = false;
|
||||
excludedFolder.excludeFromFolderOverview = false;
|
||||
excludedFolder.hideInSettings = true;
|
||||
addExcludedFolder(this.plugin, excludedFolder, false);
|
||||
loadFolderClasses(true, file, this.plugin);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
subMenu.addItem((item) => {
|
||||
item.setTitle('Create markdown folder note')
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export class ExcludedFolder {
|
|||
hideInSettings: boolean;
|
||||
detached: boolean;
|
||||
detachedFilePath?: string;
|
||||
hideNote: boolean;
|
||||
constructor(path: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {
|
||||
this.type = 'folder';
|
||||
this.id = id || crypto.randomUUID();
|
||||
|
|
@ -27,5 +28,6 @@ export class ExcludedFolder {
|
|||
this.excludeFromFolderOverview = plugin.settings.excludeFolderDefaultSettings.excludeFromFolderOverview;
|
||||
this.string = '';
|
||||
this.hideInSettings = false;
|
||||
this.hideNote = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ export class ExcludePattern {
|
|||
hideInSettings: boolean;
|
||||
detached: boolean;
|
||||
detachedFilePath?: string;
|
||||
hideNote: boolean;
|
||||
constructor(pattern: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {
|
||||
this.type = 'pattern';
|
||||
this.id = id || crypto.randomUUID();
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { getWhitelistedFolder } from './whitelistFolderFunctions';
|
|||
import { WhitelistedFolder } from '../WhitelistFolder';
|
||||
import { WhitelistedPattern } from '../WhitelistPattern';
|
||||
|
||||
export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, includeDetached: boolean, pathOnly?: boolean, ignoreWhitelist?: boolean) {
|
||||
export async function getExcludedFolder(plugin: FolderNotesPlugin, path: string, includeDetached: boolean, pathOnly?: boolean, ignoreWhitelist?: boolean) {
|
||||
let excludedFolder = {} as ExcludedFolder | ExcludePattern | undefined;
|
||||
const whitelistedFolder = getWhitelistedFolder(plugin, path) as WhitelistedFolder | WhitelistedPattern | undefined;
|
||||
const folderName = getFolderNameFromPathString(path);
|
||||
let matchedPatterns = getExcludedFoldersByPattern(plugin, folderName);
|
||||
const excludedFolders = getExcludedFoldersByPath(plugin, path);
|
||||
let matchedPatterns = await getExcludedFoldersByPattern(plugin, folderName);
|
||||
const excludedFolders = await getExcludedFoldersByPath(plugin, path);
|
||||
if (pathOnly) { matchedPatterns = []; }
|
||||
let combinedExcludedFolders = [...matchedPatterns, ...excludedFolders];
|
||||
|
||||
|
|
@ -31,7 +31,9 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu
|
|||
'enableCollapsing',
|
||||
'excludeFromFolderOverview',
|
||||
'detached',
|
||||
'hideInSettings'
|
||||
'hideInSettings',
|
||||
'hideNote',
|
||||
'id'
|
||||
];
|
||||
|
||||
if (combinedExcludedFolders.length > 0) {
|
||||
|
|
@ -51,14 +53,12 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu
|
|||
if (excludedFolder?.detached) { ignoreWhitelist = true; }
|
||||
|
||||
if (whitelistedFolder && excludedFolder && !ignoreWhitelist) {
|
||||
console.log('whitelistedFolder', whitelistedFolder)
|
||||
excludedFolder.disableAutoCreate ? excludedFolder.disableAutoCreate = !whitelistedFolder.enableAutoCreate : '';
|
||||
excludedFolder.disableFolderNote ? excludedFolder.disableFolderNote = !whitelistedFolder.enableFolderNote : '';
|
||||
excludedFolder.disableSync ? excludedFolder.disableSync = !whitelistedFolder.enableSync : '';
|
||||
excludedFolder.enableCollapsing = whitelistedFolder.enableCollapsing;
|
||||
excludedFolder.excludeFromFolderOverview ? excludedFolder.excludeFromFolderOverview = !whitelistedFolder.showInFolderOverview : '';
|
||||
} else if (excludedFolder && Object.keys(excludedFolder).length === 0) {
|
||||
console.log('Hello from the other side')
|
||||
excludedFolder = {
|
||||
type: 'folder',
|
||||
id: '',
|
||||
|
|
@ -72,7 +72,8 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu
|
|||
position: 0,
|
||||
excludeFromFolderOverview: false,
|
||||
hideInSettings: false,
|
||||
detached: false
|
||||
detached: false,
|
||||
hideNote: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,12 +119,12 @@ export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string
|
|||
});
|
||||
}
|
||||
|
||||
export function addExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
|
||||
export function addExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder, reloadStyles = true) {
|
||||
plugin.settings.excludeFolders.push(excludedFolder);
|
||||
plugin.saveSettings(true);
|
||||
plugin.saveSettings(reloadStyles);
|
||||
}
|
||||
|
||||
export function deleteExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
|
||||
export async function deleteExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
|
||||
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id || folder.type === 'pattern');
|
||||
plugin.saveSettings(true);
|
||||
resyncArray(plugin);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export async function addObserver(plugin: FolderNotesPlugin) {
|
|||
const breadcrumbs = element.parentElement?.querySelectorAll('span.view-header-breadcrumb');
|
||||
if (!breadcrumbs) return;
|
||||
let path = '';
|
||||
breadcrumbs.forEach((breadcrumb: HTMLElement) => {
|
||||
breadcrumbs.forEach(async (breadcrumb: HTMLElement) => {
|
||||
if (breadcrumb.hasAttribute('old-name')) {
|
||||
path += breadcrumb.getAttribute('old-name') + '/';
|
||||
} else {
|
||||
|
|
@ -66,7 +66,7 @@ export async function addObserver(plugin: FolderNotesPlugin) {
|
|||
breadcrumb.setAttribute('old-name', folder.name || '');
|
||||
breadcrumb.innerText = folder.newName || '';
|
||||
}
|
||||
const excludedFolder = getExcludedFolder(plugin, folderPath, true)
|
||||
const excludedFolder = await getExcludedFolder(plugin, folderPath, true)
|
||||
if (excludedFolder?.disableFolderNote) return;
|
||||
const folderNote = getFolderNote(plugin, folderPath);
|
||||
if (folderNote) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNot
|
|||
|
||||
const folderPath = event.target.getAttribute('data-path');
|
||||
if (!folderPath) { return; }
|
||||
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
|
||||
const excludedFolder = await getExcludedFolder(plugin, folderPath, true);
|
||||
if (excludedFolder?.disableFolderNote) {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
|
|
@ -38,7 +38,7 @@ 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');
|
||||
addCSSClassToTitleEL(plugin, folderPath, 'has-folder-note');
|
||||
removeCSSClassFromEL(folderPath, 'has-not-folder-note');
|
||||
return;
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ export async function handleFolderClick(event: MouseEvent, plugin: FolderNotesPl
|
|||
const folderPath = event.target.parentElement?.getAttribute('data-path');
|
||||
if (!folderPath) { return; }
|
||||
|
||||
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
|
||||
const excludedFolder = await getExcludedFolder(plugin, folderPath, true);
|
||||
if (excludedFolder?.disableFolderNote) {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
|
|
@ -70,7 +70,6 @@ export async function handleFolderClick(event: MouseEvent, plugin: FolderNotesPl
|
|||
}
|
||||
|
||||
const folderNote = getFolderNote(plugin, folderPath);
|
||||
console.log('folderNote', folderNote);
|
||||
|
||||
if (folderNote) {
|
||||
if (plugin.settings.openByClick) {
|
||||
|
|
@ -87,7 +86,7 @@ export async function handleFolderClick(event: MouseEvent, plugin: FolderNotesPl
|
|||
} 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');
|
||||
addCSSClassToTitleEL(plugin, folderPath, 'has-folder-note');
|
||||
removeCSSClassFromEL(folderPath, 'has-not-folder-note');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { createFolderNote, getFolder, getFolderNote } from 'src/functions/folder
|
|||
import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
|
||||
import { removeCSSClassFromEL, addCSSClassToTitleEL } from 'src/functions/styleFunctions';
|
||||
|
||||
export function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) {
|
||||
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');
|
||||
addCSSClassToTitleEL(plugin, folder.path, 'only-has-folder-note');
|
||||
} else {
|
||||
removeCSSClassFromEL(folder.path, 'only-has-folder-note');
|
||||
}
|
||||
|
|
@ -22,8 +22,8 @@ export function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) {
|
|||
const folderNote = getFolderNote(plugin, folder.path);
|
||||
|
||||
if (folderNote && folderNote.path === file.path) {
|
||||
addCSSClassToTitleEL(folder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(file.path, 'is-folder-note');
|
||||
addCSSClassToTitleEL(plugin, folder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, file.path, 'is-folder-note');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -45,12 +45,12 @@ export function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) {
|
|||
openFile = false;
|
||||
}
|
||||
|
||||
const excludedFolder = getExcludedFolder(plugin, file.path, true);
|
||||
const excludedFolder = await getExcludedFolder(plugin, file.path, true);
|
||||
if (excludedFolder?.disableAutoCreate) return;
|
||||
|
||||
const folderNote = getFolderNote(plugin, file.path);
|
||||
if (folderNote) return;
|
||||
|
||||
createFolderNote(plugin, file.path, openFile, undefined, true);
|
||||
addCSSClassToTitleEL(file.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, file.path, 'has-folder-note');
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde
|
|||
const isRename = (file.parent?.path === getFolderPathFromString(oldPath))
|
||||
if (folder instanceof TFolder) {
|
||||
if (plugin.isEmptyFolderNoteFolder(folder)) {
|
||||
addCSSClassToTitleEL(folder.path, 'only-has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, folder.path, 'only-has-folder-note');
|
||||
} else {
|
||||
removeCSSClassFromEL(folder.path, 'only-has-folder-note');
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde
|
|||
|
||||
if (oldFolder instanceof TFolder) {
|
||||
if (plugin.isEmptyFolderNoteFolder(oldFolder)) {
|
||||
addCSSClassToTitleEL(oldFolder.path, 'only-has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, oldFolder.path, 'only-has-folder-note');
|
||||
} else {
|
||||
removeCSSClassFromEL(oldFolder.path, 'only-has-folder-note');
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ export function handleFileMove(file: TFile, oldPath: string, plugin: FolderNotes
|
|||
|
||||
}
|
||||
|
||||
export function handleFolderRename(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
|
||||
export async function handleFolderRename(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
|
||||
const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', file.name);
|
||||
const oldFileName = plugin.settings.folderNoteName.replace('{{folder_name}}', getFileNameFromPathString(oldPath));
|
||||
// console.log(fileName)
|
||||
|
|
@ -63,7 +63,7 @@ export function handleFolderRename(file: TFolder, oldPath: string, plugin: Folde
|
|||
if (!(folderNote instanceof TFile)) return;
|
||||
|
||||
|
||||
const excludedFolder = getExcludedFolder(plugin, file.path, true);
|
||||
const excludedFolder = await getExcludedFolder(plugin, file.path, true);
|
||||
if (excludedFolder?.disableSync && !folderNote) {
|
||||
return removeCSSClassFromEL(file.path, 'has-folder-note');
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ export function handleFolderRename(file: TFolder, oldPath: string, plugin: Folde
|
|||
plugin.app.fileManager.renameFile(folderNote, newPath);
|
||||
}
|
||||
|
||||
export function handleFileRename(file: TFile, oldPath: string, plugin: FolderNotesPlugin) {
|
||||
export async function handleFileRename(file: TFile, oldPath: string, plugin: FolderNotesPlugin) {
|
||||
const oldFileName = removeExtension(getFileNameFromPathString(oldPath));
|
||||
const newFileName = file.basename;
|
||||
if (oldFileName === newFileName) { return; }
|
||||
|
|
@ -100,15 +100,15 @@ export function handleFileRename(file: TFile, oldPath: string, plugin: FolderNot
|
|||
const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename;
|
||||
const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName) || oldFileName;
|
||||
const newFolder = getFolderNoteFolder(plugin, file, file.basename);
|
||||
let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true);
|
||||
let excludedFolder = await getExcludedFolder(plugin, newFolder?.path || '', true);
|
||||
const detachedExcludedFolder = getDetachedFolder(plugin, newFolder?.path || '')
|
||||
const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file);
|
||||
|
||||
|
||||
|
||||
if (!excludedFolder?.disableFolderNote && folderName === newFolder?.name && !detachedExcludedFolder) {
|
||||
addCSSClassToTitleEL(file.path, 'is-folder-note');
|
||||
addCSSClassToTitleEL(newFolder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, file.path, 'is-folder-note');
|
||||
addCSSClassToTitleEL(plugin, newFolder.path, 'has-folder-note');
|
||||
return;
|
||||
} else if (excludedFolder?.disableFolderNote || (folderName !== newFolder?.name)) {
|
||||
removeCSSClassFromEL(file.path, 'is-folder-note');
|
||||
|
|
@ -143,9 +143,9 @@ export function handleFileRename(file: TFile, oldPath: string, plugin: FolderNot
|
|||
});
|
||||
}
|
||||
if (folderName === newFolder?.name) {
|
||||
addCSSClassToTitleEL(file.path, 'is-folder-note');
|
||||
addCSSClassToTitleEL(plugin, file.path, 'is-folder-note');
|
||||
removeCSSClassFromEL(oldFolder?.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(newFolder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, newFolder.path, 'has-folder-note');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -175,8 +175,8 @@ async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder:
|
|||
removeCSSClassFromEL(file.path, 'is-folder-note');
|
||||
return;
|
||||
} else if (newFolderName === oldFolder.name) {
|
||||
addCSSClassToTitleEL(oldFolder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(file.path, 'is-folder-note');
|
||||
addCSSClassToTitleEL(plugin, oldFolder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, file.path, 'is-folder-note');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,230 +9,338 @@ import { FolderOverview, yamlSettings } from './FolderOverview';
|
|||
import FolderNameModal from 'src/modals/FolderName';
|
||||
import NewFolderNameModal from 'src/modals/NewFolderName';
|
||||
|
||||
export async function renderFileExplorer(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[], folderOverview: FolderOverview) {
|
||||
const folder = getEl(yaml.folderPath)
|
||||
let folderElement = folder?.parentElement;
|
||||
const source = ctx.sourcePath;
|
||||
const overviewList = folderOverview.listEl;
|
||||
overviewList?.empty();
|
||||
if (!overviewList) return;
|
||||
export class FileExplorerOverview {
|
||||
plugin: FolderNotesPlugin;
|
||||
folderOverview: FolderOverview;
|
||||
pathBlacklist: string[];
|
||||
source: string;
|
||||
yaml: yamlSettings;
|
||||
root: HTMLElement;
|
||||
|
||||
|
||||
let tFolder = plugin.app.vault.getAbstractFileByPath(yaml.folderPath);
|
||||
if (!tFolder && yaml.folderPath.trim() == '') {
|
||||
if (ctx.sourcePath.includes('/')) {
|
||||
tFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath));
|
||||
} else {
|
||||
yaml.folderPath = '/';
|
||||
tFolder = plugin.app.vault.getAbstractFileByPath('/');
|
||||
}
|
||||
eventListeners: (() => void)[] = [];
|
||||
constructor(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[], folderOverview: FolderOverview) {
|
||||
this.plugin = plugin;
|
||||
this.folderOverview = folderOverview;
|
||||
this.pathBlacklist = pathBlacklist;
|
||||
this.source = ctx.sourcePath;
|
||||
this.yaml = yaml;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
if (!folderElement && !tFolder) return;
|
||||
disconnectListeners() {
|
||||
this.eventListeners.forEach((unregister) => {
|
||||
unregister();
|
||||
});
|
||||
this.eventListeners = [];
|
||||
}
|
||||
|
||||
folderElement = document.querySelectorAll('.nav-files-container')[0] as HTMLElement;
|
||||
if (!folderElement) return;
|
||||
const newFolderElement = folderElement.cloneNode(true) as HTMLElement;
|
||||
newFolderElement.querySelectorAll('div.nav-folder-title').forEach((el) => {
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(el.getAttribute('data-path') || '');
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
if (yaml.storeFolderCondition) {
|
||||
if (folder.collapsed) {
|
||||
el.classList.add('is-collapsed');
|
||||
async renderFileExplorer() {
|
||||
this.disconnectListeners();
|
||||
const plugin = this.plugin;
|
||||
const ctx = this.folderOverview.ctx;
|
||||
const root = this.folderOverview.root;
|
||||
const yaml = this.folderOverview.yaml;
|
||||
const folderOverview = this.folderOverview;
|
||||
const folder = getEl(yaml.folderPath)
|
||||
let folderElement = folder?.parentElement;
|
||||
const source = ctx.sourcePath;
|
||||
const overviewList = folderOverview.listEl;
|
||||
overviewList?.empty();
|
||||
if (!overviewList) return;
|
||||
|
||||
let tFolder = plugin.app.vault.getAbstractFileByPath(yaml.folderPath);
|
||||
if (!tFolder && yaml.folderPath.trim() == '') {
|
||||
if (ctx.sourcePath.includes('/')) {
|
||||
tFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath));
|
||||
} else {
|
||||
el.classList.remove('is-collapsed');
|
||||
yaml.folderPath = '/';
|
||||
tFolder = plugin.app.vault.getAbstractFileByPath('/');
|
||||
}
|
||||
} else {
|
||||
if (el.parentElement?.classList.contains('is-collapsed')) {
|
||||
folder.collapsed = true;
|
||||
}
|
||||
|
||||
if (!folderElement && !tFolder) return;
|
||||
// wait until the file explorer is loaded
|
||||
|
||||
|
||||
folderElement = document.querySelectorAll('.nav-files-container')[0] as HTMLElement;
|
||||
console.log('folderElement', folderElement);
|
||||
if (!folderElement) return;
|
||||
const newFolderElement = folderElement.cloneNode(true) as HTMLElement;
|
||||
console.log('newFolderElement', newFolderElement);
|
||||
|
||||
newFolderElement.querySelectorAll('div.nav-folder-title').forEach((el) => {
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(el.getAttribute('data-path') || '');
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
if (yaml.storeFolderCondition) {
|
||||
if (folder.collapsed) {
|
||||
el.classList.add('is-collapsed');
|
||||
} else {
|
||||
el.classList.remove('is-collapsed');
|
||||
}
|
||||
} else {
|
||||
folder.collapsed = false;
|
||||
if (el.parentElement?.classList.contains('is-collapsed')) {
|
||||
folder.collapsed = true;
|
||||
} else {
|
||||
folder.collapsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (el.classList.contains('has-folder-note')) {
|
||||
const folderNote = getFolderNote(plugin, folder.path);
|
||||
if (folderNote) { pathBlacklist.push(folderNote.path); }
|
||||
}
|
||||
});
|
||||
if (el.classList.contains('has-folder-note')) {
|
||||
const folderNote = getFolderNote(plugin, folder.path);
|
||||
if (folderNote) { folderOverview.pathBlacklist.push(folderNote.path); }
|
||||
}
|
||||
});
|
||||
|
||||
folderOverview.on('vault-change', async () => {
|
||||
renderFileExplorer(plugin, ctx, root, yaml, pathBlacklist, folderOverview);
|
||||
});
|
||||
const debouncedRenderFileExplorer = this.debounce(() => this.renderFileExplorer(), 300);
|
||||
|
||||
if (tFolder instanceof TFolder) {
|
||||
addFiles(tFolder.children, overviewList, folderOverview);
|
||||
const handleVaultChange = () => {
|
||||
debouncedRenderFileExplorer();
|
||||
}
|
||||
|
||||
this.eventListeners.push(() => {
|
||||
folderOverview.off('vault-change', handleVaultChange);
|
||||
});
|
||||
|
||||
folderOverview.on('vault-change', handleVaultChange);
|
||||
|
||||
if (tFolder instanceof TFolder) {
|
||||
this.addFiles(tFolder.children, overviewList, folderOverview);
|
||||
}
|
||||
|
||||
newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.onclick = () => {
|
||||
const path = el.parentElement?.getAttribute('data-path');
|
||||
if (!path) return;
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(path);
|
||||
this.handleCollapseClick(el, plugin, yaml, this.pathBlacklist, source, folderOverview, folder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.onclick = () => {
|
||||
const path = el.parentElement?.getAttribute('data-path');
|
||||
if (!path) return;
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(path);
|
||||
handleCollapseClick(el, plugin, yaml, pathBlacklist, source, folderOverview, folder);
|
||||
debounce(func: Function, wait: number) {
|
||||
let timeout: number | undefined;
|
||||
return (...args: any[]) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
addFiles(files: TAbstractFile[], childrenElement: HTMLElement, folderOverview: FolderOverview) {
|
||||
const plugin = folderOverview.plugin;
|
||||
const pathBlacklist = folderOverview.pathBlacklist;
|
||||
const yaml = folderOverview.yaml;
|
||||
const folders = folderOverview.sortFiles(files.filter((file) => file instanceof TFolder));
|
||||
const filesWithoutFolders = folderOverview.sortFiles(files.filter((file) => !(file instanceof TFolder)));
|
||||
|
||||
for (const child of folders) {
|
||||
if (child instanceof TFolder) {
|
||||
this.createFolderEL(plugin, child, folderOverview, childrenElement);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function addFiles(files: TAbstractFile[], childrenElement: HTMLElement, folderOverview: FolderOverview) {
|
||||
const plugin = folderOverview.plugin;
|
||||
const pathBlacklist = folderOverview.pathBlacklist;
|
||||
const yaml = folderOverview.yaml;
|
||||
const folders = folderOverview.sortFiles(files.filter((file) => file instanceof TFolder));
|
||||
const filesWithoutFolders = folderOverview.sortFiles(files.filter((file) => !(file instanceof TFolder)));
|
||||
for (const child of filesWithoutFolders) {
|
||||
if (child instanceof TFile) {
|
||||
if (pathBlacklist.includes(child.path) && !yaml.showFolderNotes) { continue; }
|
||||
const extension = child.extension.toLowerCase() == 'md' ? 'markdown' : child.extension.toLowerCase();
|
||||
const includeTypes = yaml.includeTypes;
|
||||
|
||||
for (const child of folders) {
|
||||
if (child instanceof TFolder) {
|
||||
createFolderEL(plugin, child, folderOverview, childrenElement);
|
||||
if (includeTypes.length > 0 && !includeTypes.includes('all')) {
|
||||
if ((extension === 'md' || extension === 'markdown') && !includeTypes.includes('markdown')) continue;
|
||||
if (extension === 'canvas' && !includeTypes.includes('canvas')) continue;
|
||||
if (extension === 'pdf' && !includeTypes.includes('pdf')) continue;
|
||||
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];
|
||||
if (imageTypes.includes(extension) && !includeTypes.includes('image')) continue;
|
||||
const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv'];
|
||||
if (videoTypes.includes(extension) && !includeTypes.includes('video')) continue;
|
||||
const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus'];
|
||||
if (audioTypes.includes(extension) && includeTypes.includes('audio')) continue;
|
||||
const allTypes = ['markdown', 'md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes];
|
||||
if (!allTypes.includes(extension) && !includeTypes.includes('other')) continue;
|
||||
}
|
||||
|
||||
const fileElement = childrenElement.createDiv({
|
||||
cls: 'tree-item nav-file',
|
||||
});
|
||||
|
||||
const fileTitle = fileElement.createDiv({
|
||||
cls: 'tree-item-self is-clickable nav-file-title pointer-cursor',
|
||||
attr: {
|
||||
'data-path': child.path,
|
||||
'draggable': 'true'
|
||||
},
|
||||
})
|
||||
|
||||
fileTitle.addEventListener('dragover', e => {
|
||||
e.preventDefault();
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder) {
|
||||
plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.move-into-folder", { folder: child.parent?.name || '' }));
|
||||
const folderEL = folderOverview.getElFromOverview(child.parent?.path || '')
|
||||
if (folderEL) {
|
||||
folderEL.parentElement?.classList.add('is-being-dragged-over');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileTitle.addEventListener('dragleave', e => {
|
||||
const folderEL = folderOverview.getElFromOverview(child.parent?.path || '')
|
||||
if (folderEL) {
|
||||
folderEL.parentElement?.classList.remove('is-being-dragged-over');
|
||||
}
|
||||
});
|
||||
|
||||
fileTitle.addEventListener('drop', e => {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder) {
|
||||
plugin.app.fileManager.renameFile(draggable.file, child.parent?.path + '/' + draggable.file.name);
|
||||
}
|
||||
});
|
||||
|
||||
fileTitle.onclick = () => {
|
||||
plugin.app.workspace.openLinkText(child.path, child.path, true);
|
||||
}
|
||||
|
||||
fileTitle.oncontextmenu = (e) => {
|
||||
folderOverview.fileMenu(child, e);
|
||||
}
|
||||
|
||||
fileTitle.createDiv({
|
||||
cls: 'tree-item-inner nav-file-title-content',
|
||||
text: child.basename,
|
||||
});
|
||||
|
||||
if (child.extension !== 'md' && !yaml.disableFileTag) {
|
||||
fileTitle.createDiv({
|
||||
cls: 'nav-file-tag',
|
||||
text: child.extension
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of filesWithoutFolders) {
|
||||
if (child instanceof TFile) {
|
||||
if (pathBlacklist.includes(child.path) && !yaml.showFolderNotes) { continue; }
|
||||
const extension = child.extension.toLowerCase() == 'md' ? 'markdown' : child.extension.toLowerCase();
|
||||
const includeTypes = yaml.includeTypes;
|
||||
handleCollapseClick(el: HTMLElement, plugin: FolderNotesPlugin, yaml: yamlSettings, pathBlacklist: string[], sourcePath: string, folderOverview: FolderOverview, folder?: TFolder | undefined | null | TAbstractFile) {
|
||||
el.classList.toggle('is-collapsed');
|
||||
if (el.classList.contains('is-collapsed')) {
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
folder.collapsed = true;
|
||||
el.parentElement?.parentElement?.childNodes[1]?.remove();
|
||||
} else {
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
folder.collapsed = false;
|
||||
const folderElement = el.parentElement?.parentElement;
|
||||
if (!folderElement) return;
|
||||
const childrenElement = folderElement.createDiv({ cls: 'tree-item-children nav-folder-children' });
|
||||
let files = folderOverview.sortFiles(folder.children);
|
||||
files = folderOverview.filterFiles(files, plugin, folder.path, yaml.depth || 1, pathBlacklist);
|
||||
this.addFiles(files, childrenElement, folderOverview);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeTypes.length > 0 && !includeTypes.includes('all')) {
|
||||
if ((extension === 'md' || extension === 'markdown') && !includeTypes.includes('markdown')) continue;
|
||||
if (extension === 'canvas' && !includeTypes.includes('canvas')) continue;
|
||||
if (extension === 'pdf' && !includeTypes.includes('pdf')) continue;
|
||||
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];
|
||||
if (imageTypes.includes(extension) && !includeTypes.includes('image')) continue;
|
||||
const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv'];
|
||||
if (videoTypes.includes(extension) && !includeTypes.includes('video')) continue;
|
||||
const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus'];
|
||||
if (audioTypes.includes(extension) && includeTypes.includes('audio')) continue;
|
||||
const allTypes = ['markdown', 'md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes];
|
||||
if (!allTypes.includes(extension) && !includeTypes.includes('other')) continue;
|
||||
}
|
||||
async createFolderEL(plugin: FolderNotesPlugin, child: TFolder, folderOverview: FolderOverview, childrenElement: HTMLElement) {
|
||||
const pathBlacklist = folderOverview.pathBlacklist;
|
||||
const source = folderOverview.source;
|
||||
const folderNote = getFolderNote(plugin, child.path);
|
||||
const yaml = folderOverview.yaml;
|
||||
let folderTitle: HTMLElement | null = null;
|
||||
let folderElement: HTMLElement | null = null;
|
||||
|
||||
const fileElement = childrenElement.createDiv({
|
||||
cls: 'tree-item nav-file',
|
||||
if (folderNote) { pathBlacklist.push(folderNote.path); }
|
||||
const excludedFolder = await getExcludedFolder(plugin, child.path, true);
|
||||
if (excludedFolder?.excludeFromFolderOverview) { return; }
|
||||
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon right-triangle"><path d="M3 8L12 17L21 8"></path></svg>';
|
||||
if (yaml.includeTypes.includes('folder')) {
|
||||
folderElement = childrenElement.createDiv({
|
||||
cls: 'tree-item nav-folder',
|
||||
});
|
||||
|
||||
const fileTitle = fileElement.createDiv({
|
||||
cls: 'tree-item-self is-clickable nav-file-title pointer-cursor',
|
||||
folderTitle = folderElement.createDiv({
|
||||
cls: 'tree-item-self is-clickable nav-folder-title',
|
||||
attr: {
|
||||
'data-path': child.path,
|
||||
'draggable': 'true'
|
||||
},
|
||||
})
|
||||
|
||||
fileTitle.onclick = () => {
|
||||
plugin.app.workspace.openLinkText(child.path, child.path, true);
|
||||
}
|
||||
|
||||
fileTitle.oncontextmenu = (e) => {
|
||||
folderOverview.fileMenu(child, e);
|
||||
}
|
||||
|
||||
fileTitle.createDiv({
|
||||
cls: 'tree-item-inner nav-file-title-content',
|
||||
text: child.basename,
|
||||
folderTitle.draggable = true;
|
||||
folderTitle.addEventListener('dragstart', e => {
|
||||
const dragManager = plugin.app.dragManager;
|
||||
const dragData = dragManager.dragFolder(e, child);
|
||||
dragManager.onDragStart(e, dragData);
|
||||
folderTitle?.classList.add('is-being-dragged');
|
||||
});
|
||||
|
||||
if (child.extension !== 'md' && !yaml.disableFileTag) {
|
||||
fileTitle.createDiv({
|
||||
cls: 'nav-file-tag',
|
||||
text: child.extension
|
||||
});
|
||||
folderTitle.addEventListener('dragend', e => {
|
||||
folderTitle?.classList.remove('is-being-dragged');
|
||||
});
|
||||
|
||||
folderTitle.addEventListener('dragover', e => {
|
||||
e.preventDefault();
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder) {
|
||||
folderElement?.classList.add('is-being-dragged-over');
|
||||
plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.move-into-folder", { folder: child.name }));
|
||||
}
|
||||
});
|
||||
|
||||
folderTitle.addEventListener('dragleave', e => {
|
||||
folderElement?.classList.remove('is-being-dragged-over');
|
||||
});
|
||||
|
||||
// handle the drop event
|
||||
folderTitle.addEventListener('drop', e => {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder) {
|
||||
plugin.app.fileManager.renameFile(draggable.file, child.path + '/' + draggable.file.name);
|
||||
}
|
||||
});
|
||||
|
||||
folderTitle.oncontextmenu = (e) => {
|
||||
folderOverview.folderMenu(child, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleCollapseClick(el: HTMLElement, plugin: FolderNotesPlugin, yaml: yamlSettings, pathBlacklist: string[], sourcePath: string, folderOverview: FolderOverview, folder?: TFolder | undefined | null | TAbstractFile) {
|
||||
console.log('el', el);
|
||||
el.classList.toggle('is-collapsed');
|
||||
console.log('el', el);
|
||||
if (el.classList.contains('is-collapsed')) {
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
folder.collapsed = true;
|
||||
el.parentElement?.parentElement?.childNodes[1]?.remove();
|
||||
} else {
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
folder.collapsed = false;
|
||||
const folderElement = el.parentElement?.parentElement;
|
||||
if (!folderElement) return;
|
||||
const childrenElement = folderElement.createDiv({ cls: 'tree-item-children nav-folder-children' });
|
||||
let files = folderOverview.sortFiles(folder.children);
|
||||
files = folderOverview.filterFiles(files, plugin, folder.path, yaml.depth || 1, pathBlacklist);
|
||||
addFiles(files, childrenElement, folderOverview);
|
||||
}
|
||||
}
|
||||
|
||||
function createFolderEL(plugin: FolderNotesPlugin, child: TFolder, folderOverview: FolderOverview, childrenElement: HTMLElement) {
|
||||
const pathBlacklist = folderOverview.pathBlacklist;
|
||||
const source = folderOverview.source;
|
||||
const folderNote = getFolderNote(plugin, child.path);
|
||||
const yaml = folderOverview.yaml;
|
||||
let folderTitle: HTMLElement | null = null;
|
||||
let folderElement: HTMLElement | null = null;
|
||||
|
||||
if (folderNote) { pathBlacklist.push(folderNote.path); }
|
||||
const excludedFolder = getExcludedFolder(plugin, child.path, true);
|
||||
if (excludedFolder?.excludeFromFolderOverview) { return; }
|
||||
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon right-triangle"><path d="M3 8L12 17L21 8"></path></svg>';
|
||||
if (yaml.includeTypes.includes('folder')) {
|
||||
folderElement = childrenElement.createDiv({
|
||||
cls: 'tree-item nav-folder',
|
||||
});
|
||||
folderTitle = folderElement.createDiv({
|
||||
cls: 'tree-item-self is-clickable nav-folder-title',
|
||||
attr: {
|
||||
'data-path': child.path,
|
||||
'draggable': 'true'
|
||||
},
|
||||
})
|
||||
|
||||
folderTitle.oncontextmenu = (e) => {
|
||||
folderOverview.folderMenu(child, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!child.collapsed) {
|
||||
if (yaml.includeTypes.includes('folder')) {
|
||||
folderTitle?.classList.remove('is-collapsed');
|
||||
const childrenElement = folderElement?.createDiv({ cls: 'tree-item-children nav-folder-children' });
|
||||
if (childrenElement) {
|
||||
addFiles(child.children, childrenElement, folderOverview);
|
||||
if (!child.collapsed) {
|
||||
if (yaml.includeTypes.includes('folder')) {
|
||||
folderTitle?.classList.remove('is-collapsed');
|
||||
const childrenElement = folderElement?.createDiv({ cls: 'tree-item-children nav-folder-children' });
|
||||
if (childrenElement) {
|
||||
this.addFiles(child.children, childrenElement, folderOverview);
|
||||
}
|
||||
} else {
|
||||
this.addFiles(child.children, childrenElement, folderOverview);
|
||||
}
|
||||
} else {
|
||||
addFiles(child.children, childrenElement, folderOverview);
|
||||
folderTitle?.classList.add('is-collapsed');
|
||||
}
|
||||
} else {
|
||||
folderTitle?.classList.add('is-collapsed');
|
||||
}
|
||||
if (folderNote) { folderTitle?.classList.add('has-folder-note') }
|
||||
if (folderNote && child.children.length === 1 && yaml.disableCollapseIcon) { folderTitle?.classList.add('fn-has-no-files') }
|
||||
if (folderNote) { folderTitle?.classList.add('has-folder-note') }
|
||||
if (folderNote && child.children.length === 1 && yaml.disableCollapseIcon) { folderTitle?.classList.add('fn-has-no-files') }
|
||||
|
||||
const collapseIcon = folderTitle?.createDiv({
|
||||
cls: 'tree-item-icon collapse-icon nav-folder-collapse-indicator fn-folder-overview-collapse-icon',
|
||||
});
|
||||
const collapseIcon = folderTitle?.createDiv({
|
||||
cls: 'tree-item-icon collapse-icon nav-folder-collapse-indicator fn-folder-overview-collapse-icon',
|
||||
});
|
||||
|
||||
if (child.collapsed) {
|
||||
collapseIcon?.classList.add('is-collapsed');
|
||||
}
|
||||
if (collapseIcon) {
|
||||
collapseIcon.innerHTML = svg;
|
||||
collapseIcon.onclick = () => {
|
||||
handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, source, folderOverview, child);
|
||||
if (child.collapsed) {
|
||||
collapseIcon?.classList.add('is-collapsed');
|
||||
}
|
||||
if (collapseIcon) {
|
||||
collapseIcon.innerHTML = svg;
|
||||
collapseIcon.onclick = () => {
|
||||
this.handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, source, folderOverview, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const folderTitleText = folderTitle?.createDiv({
|
||||
cls: 'tree-item-inner nav-folder-title-content',
|
||||
text: child.name,
|
||||
});
|
||||
const folderTitleText = folderTitle?.createDiv({
|
||||
cls: 'tree-item-inner nav-folder-title-content',
|
||||
text: child.name,
|
||||
});
|
||||
|
||||
if (folderTitleText && !folderNote) {
|
||||
folderTitleText.onclick = () => {
|
||||
const collapseIcon = folderTitle?.querySelectorAll('.tree-item-icon')[0] as HTMLElement;
|
||||
if (collapseIcon) {
|
||||
handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, source, folderOverview, child);
|
||||
if (folderTitleText && !folderNote) {
|
||||
folderTitleText.onclick = () => {
|
||||
const collapseIcon = folderTitle?.querySelectorAll('.tree-item-icon')[0] as HTMLElement;
|
||||
if (collapseIcon) {
|
||||
this.handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, source, folderOverview, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { FolderOverviewSettings } from './ModalSettings';
|
|||
import { getExcludedFolder } from '../ExcludeFolders/functions/folderFunctions';
|
||||
import { getFolderPathFromString } from '../functions/utils';
|
||||
import { getEl } from 'src/functions/styleFunctions';
|
||||
import { renderFileExplorer } from './FileExplorer';
|
||||
import { FileExplorerOverview } from './FileExplorer';
|
||||
import { renderListOverview } from './ListStyle';
|
||||
import NewFolderNameModal from 'src/modals/NewFolderName';
|
||||
import { CustomEventEmitter } from 'src/events/EventEmitter';
|
||||
|
|
@ -46,7 +46,7 @@ export class FolderOverview {
|
|||
root: HTMLElement;
|
||||
listEl: HTMLUListElement;
|
||||
|
||||
private eventListeners: (() => void)[] = [];
|
||||
eventListeners: (() => void)[] = [];
|
||||
constructor(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, source: string, el: HTMLElement) {
|
||||
this.emitter = new CustomEventEmitter();
|
||||
let yaml: yamlSettings = parseYaml(source);
|
||||
|
|
@ -216,11 +216,12 @@ export class FolderOverview {
|
|||
} else if (this.yaml.style === 'list') {
|
||||
renderListOverview(plugin, ctx, root, this.yaml, this.pathBlacklist, this);
|
||||
} else if (this.yaml.style === 'explorer') {
|
||||
const fileExplorerOverview = new FileExplorerOverview(plugin, ctx, root, this.yaml, this.pathBlacklist, this);
|
||||
if (this.plugin.app.workspace.layoutReady) {
|
||||
renderFileExplorer(plugin, ctx, root, this.yaml, this.pathBlacklist, this);
|
||||
fileExplorerOverview.renderFileExplorer();
|
||||
} else {
|
||||
this.plugin.app.workspace.onLayoutReady(() => {
|
||||
renderFileExplorer(plugin, ctx, root, this.yaml, this.pathBlacklist, this);
|
||||
fileExplorerOverview.renderFileExplorer();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -256,12 +257,12 @@ export class FolderOverview {
|
|||
}
|
||||
|
||||
filterFiles(files: TAbstractFile[], plugin: FolderNotesPlugin, sourceFolderPath: string, depth: number, pathBlacklist: string[]) {
|
||||
return files.filter((file) => {
|
||||
return files.filter(async (file) => {
|
||||
if (pathBlacklist.includes(file.path) && !this.yaml.showFolderNotes) { return false; }
|
||||
const folderPath = getFolderPathFromString(file.path);
|
||||
if (!folderPath.startsWith(sourceFolderPath) && sourceFolderPath !== '/') { return false; }
|
||||
if (file.path === this.sourceFilePath) { return false; }
|
||||
const excludedFolder = getExcludedFolder(plugin, file.path, true);
|
||||
const excludedFolder = await getExcludedFolder(plugin, file.path, true);
|
||||
if (excludedFolder?.excludeFromFolderOverview) { return false; }
|
||||
if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < depth) {
|
||||
return true;
|
||||
|
|
@ -355,7 +356,7 @@ export class FolderOverview {
|
|||
fileMenu.addSeparator();
|
||||
|
||||
fileMenu.addItem((item) => {
|
||||
item.setTitle('Rename');
|
||||
item.setTitle(window.i18next.t("plugins.file-explorer.menu-opt-rename"));
|
||||
item.setIcon('pencil');
|
||||
item.onClick(async () => {
|
||||
plugin.app.fileManager.promptForFileRename(file)
|
||||
|
|
@ -363,7 +364,7 @@ export class FolderOverview {
|
|||
});
|
||||
|
||||
fileMenu.addItem((item) => {
|
||||
item.setTitle('Delete');
|
||||
item.setTitle(window.i18next.t("plugins.file-explorer.menu-opt-delete"));
|
||||
item.setIcon('trash');
|
||||
item.dom.addClass('is-warning');
|
||||
item.dom.setAttribute('data-section', 'danger')
|
||||
|
|
@ -406,6 +407,13 @@ export class FolderOverview {
|
|||
plugin.app.workspace.trigger('file-menu', folderMenu, folder, "folder-overview-folder-context-menu", null);
|
||||
folderMenu.showAtPosition({ x: e.pageX, y: e.pageY });
|
||||
}
|
||||
|
||||
getElFromOverview(path: string): HTMLElement | null {
|
||||
const el = this.listEl.querySelector(`[data-path="${path}"]`) as HTMLElement | null;
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export async function updateYaml(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, el: HTMLElement, yaml: yamlSettings) {
|
||||
|
|
|
|||
|
|
@ -62,12 +62,9 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
|
|||
const folderNote = getFolderNote(plugin, folderPath)
|
||||
removeCSSClassFromEL(folderNote?.path, 'is-folder-note');
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder;
|
||||
console.log('folderNote', folderNote);
|
||||
console.log('fileName', fileName)
|
||||
if (!folderNote || folderNote.basename !== fileName) return;
|
||||
let count = 1;
|
||||
let newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`;
|
||||
console.log(newName)
|
||||
while (count < 100 && plugin.app.vault.getAbstractFileByPath(newName)) {
|
||||
count++;
|
||||
newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`;
|
||||
|
|
@ -90,7 +87,6 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
|
|||
if (!existingNote) {
|
||||
let content = '';
|
||||
if (extension !== '.md') {
|
||||
console.log('create 1')
|
||||
if (plugin.settings.templatePath && folderNoteType.split('.').pop() == plugin.settings.templatePath.split('.').pop()) {
|
||||
const templateFile = plugin.app.vault.getAbstractFileByPath(plugin.settings.templatePath);
|
||||
if (templateFile instanceof TFile) {
|
||||
|
|
@ -114,8 +110,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
|
|||
content = '{}'
|
||||
}
|
||||
}
|
||||
console.log('create 2')
|
||||
console.log('path', path)
|
||||
|
||||
file = await plugin.app.vault.create(path, content);
|
||||
} else {
|
||||
file = existingNote;
|
||||
|
|
@ -150,8 +145,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', true);
|
||||
addCSSClassToTitleEL(folder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, path, 'is-folder-note', true);
|
||||
addCSSClassToTitleEL(plugin, folder.path, 'has-folder-note');
|
||||
}
|
||||
|
||||
export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote?: TFile | null | TAbstractFile, skipConfirmation?: boolean) {
|
||||
|
|
@ -196,8 +191,8 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile,
|
|||
}
|
||||
|
||||
await plugin.app.fileManager.renameFile(file, path);
|
||||
addCSSClassToTitleEL(path, 'is-folder-note', true);
|
||||
addCSSClassToTitleEL(folder.path, 'has-folder-note');
|
||||
addCSSClassToTitleEL(plugin, path, 'is-folder-note', true);
|
||||
addCSSClassToTitleEL(plugin, folder.path, 'has-folder-note');
|
||||
|
||||
if (plugin.activeFolderDom) {
|
||||
plugin.activeFolderDom.removeClass('fn-is-active');
|
||||
|
|
@ -209,7 +204,7 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile,
|
|||
}
|
||||
|
||||
export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder): Promise<[excludedFolder: ExcludedFolder | undefined,excludedFolderExisted: boolean, disabledSync: boolean]> {
|
||||
let excludedFolder = getExcludedFolder(plugin, folder.path, false);
|
||||
let excludedFolder = await getExcludedFolder(plugin, folder.path, false);
|
||||
let excludedFolderExisted = true;
|
||||
let disabledSync = false;
|
||||
|
||||
|
|
@ -331,7 +326,6 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) {
|
|||
excludedFolder.excludeFromFolderOverview = false;
|
||||
excludedFolder.detached = true;
|
||||
excludedFolder.detachedFilePath = file.path;
|
||||
console.log('excludedFolder', excludedFolder);
|
||||
addExcludedFolder(plugin, excludedFolder);
|
||||
}
|
||||
|
||||
|
|
|
|||
11
src/globals.d.ts
vendored
11
src/globals.d.ts
vendored
|
|
@ -1,17 +1,6 @@
|
|||
import { Plugin, TAbstractFile, View, WorkspaceLeaf } from 'obsidian';
|
||||
|
||||
declare module 'obsidian' {
|
||||
interface App {
|
||||
internalPlugins: {
|
||||
plugins: {
|
||||
[pluginId: string]: Plugin & {
|
||||
[pluginImplementations: string]: unknown;
|
||||
};
|
||||
};
|
||||
enablePlugin: (id: string) => Promise<void>;
|
||||
disablePlugin: (id: string) => Promise<void>;
|
||||
};
|
||||
}
|
||||
interface Setting {
|
||||
createList: (list: ListComponent | ((list: ListComponent) => void)) => ListComponent;
|
||||
}
|
||||
|
|
|
|||
98
src/main.ts
98
src/main.ts
|
|
@ -15,7 +15,7 @@ import './functions/ListComponent';
|
|||
import { handleDelete } from './events/handleDelete';
|
||||
import { addCSSClassToTitleEL, getEl, loadFileClasses, removeCSSClassFromEL } from './functions/styleFunctions';
|
||||
import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
|
||||
import { ClipBoardManager } from 'obsidian-typings'
|
||||
import { ClipBoardManager, FileExplorerView, InternalPlugin, InternalPluginName, InternalPlugins } from 'obsidian-typings'
|
||||
export default class FolderNotesPlugin extends Plugin {
|
||||
observer: MutationObserver;
|
||||
settings: FolderNotesSettings;
|
||||
|
|
@ -28,6 +28,10 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
hoverLinkTriggered = false;
|
||||
tabManager: TabManager;
|
||||
settingsOpened = false;
|
||||
|
||||
private fileExplorerPlugin!: InternalPlugin;
|
||||
private fileExplorerView!: FileExplorerView;
|
||||
|
||||
async onload() {
|
||||
console.log('loading folder notes plugin');
|
||||
await this.loadSettings();
|
||||
|
|
@ -51,43 +55,7 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
|
||||
new Commands(this.app, this).registerCommands();
|
||||
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
if (this.settings.frontMatterTitle.enabled) {
|
||||
this.fmtpHandler = new FrontMatterTitlePluginHandler(this);
|
||||
}
|
||||
this.tabManager = new TabManager(this);
|
||||
this.tabManager.updateTabs();
|
||||
|
||||
const view = this.app.workspace.getLeavesOfType('markdown')[0]?.view;
|
||||
if (!view) { return; }
|
||||
const plugin = this;
|
||||
// @ts-ignore
|
||||
const originalHandleDragOver = view.editMode.clipboardManager.constructor.prototype.handleDragOver;
|
||||
// @ts-ignore
|
||||
view.editMode.clipboardManager.constructor.prototype.handleDragOver = function (evt, ...args) {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder && getFolderNote(plugin, draggable.file.path)) {
|
||||
plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.insert-link-here"));
|
||||
} else {
|
||||
originalHandleDragOver.call(this, evt, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const originalHandleDrop = view.editMode.clipboardManager.constructor.prototype.handleDrop;
|
||||
// @ts-ignore
|
||||
view.editMode.clipboardManager.constructor.prototype.handleDrop = function (evt, ...args) {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder && getFolderNote(plugin, draggable.file.path)) {
|
||||
const folderNote = getFolderNote(plugin, draggable.file.path);
|
||||
if (draggable?.type === "folder" && draggable.file instanceof TFolder && folderNote) {
|
||||
draggable.file = folderNote;
|
||||
draggable.type = "file";
|
||||
}
|
||||
}
|
||||
return originalHandleDrop.call(this, evt, ...args);
|
||||
}
|
||||
});
|
||||
this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
|
||||
|
||||
await addObserver(this);
|
||||
|
||||
|
|
@ -166,6 +134,48 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
onLayoutReady() {
|
||||
if (this.settings.frontMatterTitle.enabled) {
|
||||
this.fmtpHandler = new FrontMatterTitlePluginHandler(this);
|
||||
}
|
||||
this.tabManager = new TabManager(this);
|
||||
this.tabManager.updateTabs();
|
||||
|
||||
const view = this.app.workspace.getLeavesOfType('markdown')[0]?.view;
|
||||
if (!view) { return; }
|
||||
const plugin = this;
|
||||
// @ts-ignore
|
||||
const originalHandleDragOver = view.editMode.clipboardManager.constructor.prototype.handleDragOver;
|
||||
// @ts-ignore
|
||||
view.editMode.clipboardManager.constructor.prototype.handleDragOver = function (evt, ...args) {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder && getFolderNote(plugin, draggable.file.path)) {
|
||||
plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.insert-link-here"));
|
||||
} else {
|
||||
originalHandleDragOver.call(this, evt, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const originalHandleDrop = view.editMode.clipboardManager.constructor.prototype.handleDrop;
|
||||
// @ts-ignore
|
||||
view.editMode.clipboardManager.constructor.prototype.handleDrop = function (evt, ...args) {
|
||||
const { draggable } = plugin.app.dragManager;
|
||||
if (draggable && draggable.file instanceof TFolder && getFolderNote(plugin, draggable.file.path)) {
|
||||
const folderNote = getFolderNote(plugin, draggable.file.path);
|
||||
if (draggable?.type === "folder" && draggable.file instanceof TFolder && folderNote) {
|
||||
draggable.file = folderNote;
|
||||
draggable.type = "file";
|
||||
}
|
||||
}
|
||||
return originalHandleDrop.call(this, evt, ...args);
|
||||
}
|
||||
|
||||
const fileExplorerPluginInstance = this.app.internalPlugins.getEnabledPluginById(InternalPluginName.FileExplorer);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
|
||||
const observer = new MutationObserver(() => {
|
||||
|
|
@ -184,8 +194,16 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
subtree: true,
|
||||
});
|
||||
try {
|
||||
const folderOverview = new FolderOverview(this, ctx, source, el);
|
||||
folderOverview.create(this, parseYaml(source), el, ctx);
|
||||
if (this.app.workspace.layoutReady) {
|
||||
const folderOverview = new FolderOverview(this, ctx, source, el);
|
||||
folderOverview.create(this, parseYaml(source), el, ctx);
|
||||
} else {
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
console.log('layout ready');
|
||||
const folderOverview = new FolderOverview(this, ctx, source, el);
|
||||
folderOverview.create(this, parseYaml(source), el, ctx);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
new Notice('Error creating folder overview (folder notes plugin) - check console for more details');
|
||||
console.error(e);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default class ConfirmationModal extends Modal {
|
|||
const folders = this.app.vault.getAllLoadedFiles().filter((file) => file.parent instanceof TFolder);
|
||||
for (const folder of folders) {
|
||||
if (folder instanceof TFolder) {
|
||||
const excludedFolder = getExcludedFolder(this.plugin, folder.path, true);
|
||||
const excludedFolder = await getExcludedFolder(this.plugin, folder.path, true);
|
||||
if (excludedFolder) continue;
|
||||
if (folder.path === templateFolderPath) continue;
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ export default class NewFolderNameModal extends Modal {
|
|||
|
||||
this.modalEl.classList.add('mod-file-rename')
|
||||
const modalTitle = this.modalEl.querySelector('div.modal-title')
|
||||
console.log(modalTitle)
|
||||
if (modalTitle) {
|
||||
modalTitle.textContent = 'Folder title'
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue