├── docs └── CollapseAll.gif ├── main.ts ├── .prettierrc ├── src ├── interfaces.ts ├── provider │ ├── index.ts │ ├── bookmarks.ts │ ├── file-explorer.ts │ ├── tag-pane.ts │ ├── search.ts │ ├── global.ts │ └── base.ts ├── constants.ts ├── types.ts ├── settings.ts └── plugin.ts ├── .gitignore ├── versions.json ├── manifest.json ├── tsconfig.json ├── README.md ├── package.json ├── .eslintrc.js ├── LICENSE ├── esbuild.config.mjs └── CHANGELOG.md /docs/CollapseAll.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathonius/obsidian-collapse-all/HEAD/docs/CollapseAll.gif -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { CollapseAllPlugin } from './src/plugin'; 2 | 3 | export default CollapseAllPlugin; 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "always", 4 | "semi": true, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { ProviderType } from './constants'; 2 | 3 | export interface Settings { 4 | commands: Record; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | *.iml 3 | .idea 4 | 5 | # npm 6 | node_modules 7 | 8 | # build 9 | main.js 10 | *.js.map 11 | 12 | # obsidian 13 | data.json 14 | -------------------------------------------------------------------------------- /src/provider/index.ts: -------------------------------------------------------------------------------- 1 | export { FileExplorerProvider } from './file-explorer'; 2 | export { TagPaneProvider } from './tag-pane'; 3 | export { GlobalProvider } from './global'; 4 | export { SearchProvider } from './search'; 5 | export { BookmarksProvider } from './bookmarks'; 6 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "2.1.0": "1.5.11", 3 | "2.0.0": "1.0.0", 4 | "1.5.3": "0.12.11", 5 | "1.5.2": "0.12.11", 6 | "1.5.1": "0.12.11", 7 | "1.5.0": "0.12.11", 8 | "1.4.0": "0.12.11", 9 | "1.3.0": "0.12.11", 10 | "1.2.0": "0.12.10", 11 | "1.1.1": "0.9.12", 12 | "1.0.1": "0.9.12", 13 | "1.0.0": "0.9.7" 14 | } 15 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-collapse-all-plugin", 3 | "name": "Collapse All", 4 | "version": "2.1.0", 5 | "minAppVersion": "1.5.11", 6 | "description": "Extends collapse/expand all with commands that can be bound to hotkeys.", 7 | "author": "Nathonius", 8 | "authorUrl": "https://nathan-smith.org/", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "es6", 8 | "allowJs": false, 9 | "noImplicitAny": true, 10 | "strict": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "lib": ["dom", "es5", "scripthost", "es2015"] 14 | }, 15 | "include": ["**/*.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import { Settings } from './interfaces'; 2 | 3 | export const DEFAULT_SETTINGS: Settings = { 4 | commands: { 5 | Global: false, 6 | FileExplorer: true, 7 | TagPane: true, 8 | Search: false, 9 | Bookmarks: false 10 | } 11 | }; 12 | 13 | export enum ProviderType { 14 | FileExplorer = 'FileExplorer', 15 | TagPane = 'TagPane', 16 | Global = 'Global', 17 | Search = 'Search', 18 | Bookmarks = 'Bookmarks' 19 | } 20 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 2 | import type { View } from 'obsidian'; 3 | declare module 'obsidian' { 4 | interface View { 5 | tree?: { 6 | toggleCollapseAll?: () => void; 7 | setCollapseAll?: (collapse: boolean) => void; 8 | isAllCollapsed?: boolean; 9 | }; 10 | toggleCollapseAll?: () => void; 11 | setCollapseAll?: (collapse: boolean) => void; 12 | isAllCollapsed?: boolean; 13 | collapseOrExpandAllEl?: HTMLDivElement; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/provider/bookmarks.ts: -------------------------------------------------------------------------------- 1 | import { ProviderType } from '../constants'; 2 | import { ProviderBase } from './base'; 3 | 4 | export class BookmarksProvider extends ProviderBase { 5 | public readonly providerType = ProviderType.Bookmarks; 6 | public readonly displayName = 'Bookmarks'; 7 | protected readonly leafType = 'bookmarks'; 8 | protected readonly toggleCommandName = 'Toggle collapse in bookmarks view'; 9 | protected readonly collapseCommandName = 'Collapse all in bookmarks view'; 10 | protected readonly expandCommandName = 'Expand all in bookmarks view'; 11 | } 12 | -------------------------------------------------------------------------------- /src/provider/file-explorer.ts: -------------------------------------------------------------------------------- 1 | import { ProviderType } from '../constants'; 2 | import { ProviderBase } from './base'; 3 | 4 | export class FileExplorerProvider extends ProviderBase { 5 | providerType: ProviderType = ProviderType.FileExplorer; 6 | displayName = 'File explorer'; 7 | protected leafType = 'file-explorer'; 8 | protected readonly collapseCommandName = 9 | 'Collapse open folders in all file explorers'; 10 | protected readonly expandCommandName = 11 | 'Expand closed folders in all file explorers'; 12 | protected readonly toggleCommandName = 13 | 'Toggle collapse in all file explorers'; 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collapse All Plugin 2 | 3 | Extends Obsidian's collapse / expand all functions with bindable commands. 4 | 5 | ## Usage 6 | 7 | Commands can be toggled on or off in settings. Each set of commands has a collapse, expand, and toggle that can be called from the command palette or bound to a hotkey. 8 | 9 | ### Supported Views 10 | 11 | - File Explorer 12 | - Tag Pane 13 | - Search 14 | - Does not support collapse toggle, only expand and collapse 15 | - Bookmarks 16 | - Only supports toggle, may be a bit finnicky 17 | - Global (all supported views) 18 | 19 | ## Changelog 20 | 21 | See [Changelog](CHANGELOG.md). 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-collapse-all-plugin", 3 | "version": "2.1.0", 4 | "description": "This plugin adds a button to collapse all folders in the file explorer.", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production" 9 | }, 10 | "keywords": [], 11 | "author": { 12 | "name": "Nathonius", 13 | "url": "https://nathan-smith.org/" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@types/node": "^16.11.6", 18 | "@typescript-eslint/eslint-plugin": "^7.3.1", 19 | "@typescript-eslint/parser": "^7.3.1", 20 | "builtin-modules": "3.3.0", 21 | "esbuild": "0.17.3", 22 | "eslint": "^8.57.0", 23 | "obsidian": "latest", 24 | "tslib": "2.4.0", 25 | "typescript": "4.7.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es2021: true, 4 | node: true 5 | }, 6 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 7 | overrides: [ 8 | { 9 | env: { 10 | node: true 11 | }, 12 | files: ['.eslintrc.{js,cjs}'], 13 | parserOptions: { 14 | sourceType: 'script' 15 | } 16 | } 17 | ], 18 | parser: '@typescript-eslint/parser', 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module' 22 | }, 23 | plugins: ['@typescript-eslint'], 24 | rules: { 25 | '@typescript-eslint/no-unused-vars': [ 26 | 'error', 27 | { 28 | args: 'all', 29 | argsIgnorePattern: '^_', 30 | caughtErrors: 'all', 31 | caughtErrorsIgnorePattern: '^_', 32 | destructuredArrayIgnorePattern: '^_', 33 | varsIgnorePattern: '^_', 34 | ignoreRestSiblings: true 35 | } 36 | ] 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nathan Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === "production"); 13 | 14 | const context = await esbuild.context({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ["main.ts"], 19 | bundle: true, 20 | external: [ 21 | "obsidian", 22 | "electron", 23 | "@codemirror/autocomplete", 24 | "@codemirror/collab", 25 | "@codemirror/commands", 26 | "@codemirror/language", 27 | "@codemirror/lint", 28 | "@codemirror/search", 29 | "@codemirror/state", 30 | "@codemirror/view", 31 | "@lezer/common", 32 | "@lezer/highlight", 33 | "@lezer/lr", 34 | ...builtins], 35 | format: "cjs", 36 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: prod ? false : "inline", 39 | treeShaking: true, 40 | outfile: "main.js", 41 | }); 42 | 43 | if (prod) { 44 | await context.rebuild(); 45 | process.exit(0); 46 | } else { 47 | await context.watch(); 48 | } -------------------------------------------------------------------------------- /src/provider/tag-pane.ts: -------------------------------------------------------------------------------- 1 | import { Command, WorkspaceLeaf } from 'obsidian'; 2 | import { ProviderType } from '../constants'; 3 | import { ProviderBase } from './base'; 4 | 5 | export class TagPaneProvider extends ProviderBase { 6 | providerType: ProviderType = ProviderType.TagPane; 7 | displayName = 'Tag pane'; 8 | protected leafType = 'tag'; 9 | protected readonly collapseCommandName = 'Not available'; 10 | protected readonly expandCommandName = 'Not available'; 11 | protected readonly toggleCommandName = 'Toggle collapse in all tag explorers'; 12 | 13 | protected override get commands(): Command[] { 14 | return [this.toggleCommand]; 15 | } 16 | 17 | public override toggleCollapse(singleLeaf?: WorkspaceLeaf): void { 18 | const leaves = singleLeaf ? [singleLeaf] : this.leaves; 19 | for (const leaf of leaves) { 20 | if (!leaf.view.collapseOrExpandAllEl) { 21 | console.error(`No collapse element found on ${this.leafType} view.`); 22 | return; 23 | } 24 | leaf.view.collapseOrExpandAllEl.click(); 25 | } 26 | } 27 | 28 | public override collapseAll(_?: WorkspaceLeaf | null): void { 29 | // Not available 30 | } 31 | 32 | public override expandAll(_?: WorkspaceLeaf | null): void { 33 | // Not available 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | import { App, PluginSettingTab, Setting } from 'obsidian'; 2 | import { CollapseAllPlugin } from './plugin'; 3 | 4 | export class CollapseAllPluginSettings extends PluginSettingTab { 5 | constructor(app: App, readonly plugin: CollapseAllPlugin) { 6 | super(app, plugin); 7 | } 8 | 9 | display() { 10 | this.containerEl.empty(); 11 | this.containerEl.createEl('h3', { text: 'Command settings' }); 12 | this.containerEl.createEl('p', { 13 | text: 'Each toggle controls whether commands should be added to collapse and expand that view, or global which operates on all available views.' 14 | }); 15 | 16 | // Add individual toggles 17 | this.plugin.allProviders.forEach((provider) => { 18 | new Setting(this.containerEl) 19 | .setName(provider.displayName) 20 | .addToggle((toggle) => { 21 | toggle 22 | .setTooltip(provider.displayName) 23 | .setValue(this.plugin.settings.commands[provider.providerType]) 24 | .onChange(async (value) => { 25 | this.plugin.settings.commands[provider.providerType] = value; 26 | if (value === true) { 27 | provider.register(); 28 | } 29 | await this.plugin.saveSettings(); 30 | }); 31 | }); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/provider/search.ts: -------------------------------------------------------------------------------- 1 | import { Command, WorkspaceLeaf } from 'obsidian'; 2 | import { ProviderType } from '../constants'; 3 | import { ProviderBase } from './base'; 4 | 5 | export class SearchProvider extends ProviderBase { 6 | public readonly providerType = ProviderType.Search; 7 | public readonly displayName = 'Search'; 8 | protected readonly leafType = 'search'; 9 | protected readonly collapseCommandName = 'Collapse all in search views'; 10 | protected readonly expandCommandName = 'Expand all in search views'; 11 | protected readonly toggleCommandName = 'Not available'; 12 | 13 | protected override get commands(): Command[] { 14 | return [this.collapseCommand, this.expandCommand]; 15 | } 16 | 17 | public override toggleCollapse(): void { 18 | // Not available 19 | } 20 | 21 | /** 22 | * Collapse or expand all items for the given leaf 23 | * @argument collapsed if not provided, will toggle the state 24 | */ 25 | protected collapseOrExpandAll( 26 | leaf: WorkspaceLeaf, 27 | collapsed?: boolean 28 | ): void { 29 | if (collapsed === undefined) { 30 | // Not availabble 31 | } else { 32 | if (!leaf.view.setCollapseAll) { 33 | console.error(`No collapse function found on ${this.leafType} view.`); 34 | return; 35 | } 36 | leaf.view.setCollapseAll(collapsed); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/provider/global.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceLeaf } from 'obsidian'; 2 | import { ProviderType } from '../constants'; 3 | import { ProviderBase } from './base'; 4 | 5 | export class GlobalProvider extends ProviderBase { 6 | public readonly providerType: ProviderType = ProviderType.Global; 7 | public readonly displayName = 'All supported explorers (global)'; 8 | protected readonly leafType = ''; 9 | protected readonly toggleCommandName = 10 | 'Toggle collapse state in all supported explorers'; 11 | protected readonly collapseCommandName = 12 | 'Collapse open items in all supported explorers'; 13 | protected readonly expandCommandName = 14 | 'Expand closed items in all supported explorers'; 15 | 16 | private get providers() { 17 | return this.plugin.allProviders.filter( 18 | (p) => p.providerType !== ProviderType.Global 19 | ); 20 | } 21 | 22 | public override allCollapsed(): boolean { 23 | return this.providers.every((p) => p.allCollapsed()); 24 | } 25 | 26 | public override toggleCollapse(_: WorkspaceLeaf | null = null): void { 27 | for (const provider of this.providers) { 28 | provider.toggleCollapse(); 29 | } 30 | } 31 | 32 | public override collapseAll(_: WorkspaceLeaf | null = null): void { 33 | for (const provider of this.providers) { 34 | provider.collapseAll(); 35 | } 36 | } 37 | 38 | public override expandAll(_: WorkspaceLeaf | null = null): void { 39 | for (const provider of this.providers) { 40 | provider.expandAll(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'obsidian'; 2 | import { DEFAULT_SETTINGS, ProviderType } from './constants'; 3 | import { Settings } from './interfaces'; 4 | import { 5 | BookmarksProvider, 6 | FileExplorerProvider, 7 | GlobalProvider, 8 | SearchProvider, 9 | TagPaneProvider 10 | } from './provider'; 11 | import { ProviderBase } from './provider/base'; 12 | import { CollapseAllPluginSettings } from './settings'; 13 | 14 | export class CollapseAllPlugin extends Plugin { 15 | settings: Settings = DEFAULT_SETTINGS; 16 | 17 | providers: Record = { 18 | [ProviderType.Global]: new GlobalProvider(this), 19 | [ProviderType.FileExplorer]: new FileExplorerProvider(this), 20 | [ProviderType.TagPane]: new TagPaneProvider(this), 21 | [ProviderType.Search]: new SearchProvider(this), 22 | [ProviderType.Bookmarks]: new BookmarksProvider(this) 23 | }; 24 | 25 | get allProviders(): ProviderBase[] { 26 | return Object.values(this.providers); 27 | } 28 | 29 | async onload(): Promise { 30 | // Load settings 31 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 32 | 33 | // Add commands for each provider 34 | this.allProviders.forEach((provider) => { 35 | if (this.settings.commands[provider.providerType]) { 36 | provider.register(); 37 | } 38 | }); 39 | 40 | // Add settings tab 41 | this.addSettingTab(new CollapseAllPluginSettings(this.app, this)); 42 | } 43 | 44 | saveSettings(): Promise { 45 | return this.saveData(this.settings); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.1.0 2 | 3 | ## Features 4 | 5 | - Bookmarks view supports all three command options 6 | 7 | ## Fixes 8 | 9 | - Restored functionality of all providers 10 | - Tag pane no longer supports collapse and expand commands, only toggle 11 | - This was a bug previously, the collapse and expand commands were not reliable. 12 | 13 | # 2.0.0 14 | 15 | ## Breaking Changes 16 | 17 | - The plugin has been largely re-written. It now uses Obsidian's internal collapse / expand logic. 18 | - Collapse / expand via file explorer context menu is removed. 19 | - Settings and hotkeys will likely need to be reset. 20 | 21 | ## Features 22 | 23 | - Each set of commands now includes a "toggle" command as well as the collapse and expand commands. 24 | - Support for search view 25 | - Support for bookmarks view 26 | 27 | ## Fixes 28 | 29 | - Commands will be added immediately when toggled on in settings. 30 | 31 | # 1.5.3 32 | 33 | ## Fixes 34 | 35 | - Collapse button alignment corrected for v0.16.0+ 36 | 37 | # 1.5.2 38 | 39 | ## Fixes 40 | 41 | - Tag pane provider fixed for v0.15.0+ 42 | 43 | # 1.5.1 44 | 45 | ## Fixes 46 | 47 | - Include the selected folder when collapsing or expanding a specific folder 48 | 49 | # 1.5.0 50 | 51 | ## Features 52 | 53 | - Attachment folders are not expanded by default. 54 | - New commands are added to the file explorer right click menu to collapse or expand an individual folder and its children. 55 | 56 | # 1.4.0 57 | 58 | ## Features 59 | 60 | - Added an option to split into two separate collapse and expand buttons 61 | - Added commands to collapse only the file explorers or only the tag panes. These commands can be enabled in settings. 62 | 63 | ## Fixes 64 | 65 | - Fixed inconsistent Obsidian app version requirements 66 | 67 | # 1.3.0 68 | 69 | ## Features 70 | 71 | - Expanded functionality to tag pane. (@ebullient) 72 | 73 | # 1.2.0 74 | 75 | ## Features 76 | 77 | - Enabled for mobile app. 78 | 79 | # 1.1.0 80 | 81 | ## Features 82 | 83 | - Added "expand all" functionality to compliment collapsing. 84 | 85 | ## Fixes 86 | 87 | - Collapse all button now uses sentence case rather than title case for its label to match the other buttons in the file explorer. 88 | 89 | # 1.0.0 90 | 91 | - Initial release. 92 | -------------------------------------------------------------------------------- /src/provider/base.ts: -------------------------------------------------------------------------------- 1 | import { Command, WorkspaceLeaf } from 'obsidian'; 2 | import { ProviderType } from '../constants'; 3 | import { CollapseAllPlugin } from '../plugin'; 4 | 5 | export abstract class ProviderBase { 6 | /** 7 | * ProviderType, used for configuration 8 | */ 9 | abstract readonly providerType: ProviderType; 10 | 11 | /** 12 | * Used when adding UI 13 | */ 14 | abstract readonly displayName: string; 15 | 16 | /** 17 | * The leaf type for getLeavesOfType 18 | */ 19 | protected abstract readonly leafType: string; 20 | 21 | /** 22 | * Name for the toggle command 23 | */ 24 | protected abstract readonly toggleCommandName: string; 25 | 26 | /** 27 | * Name for the collapse command 28 | */ 29 | protected abstract readonly collapseCommandName: string; 30 | 31 | /** 32 | * Name for the expand command 33 | */ 34 | protected abstract readonly expandCommandName: string; 35 | 36 | private registered = false; 37 | 38 | constructor(protected plugin: CollapseAllPlugin) {} 39 | 40 | /** 41 | * Collapse command config 42 | */ 43 | protected get collapseCommand(): Command { 44 | return { 45 | id: `collapse-${this.leafType}`, 46 | name: this.collapseCommandName, 47 | icon: 'double-up-arrow-glyph', 48 | callback: () => { 49 | this.collapseAll(); 50 | } 51 | }; 52 | } 53 | 54 | /** 55 | * Expand command config 56 | */ 57 | protected get expandCommand(): Command { 58 | return { 59 | id: `expand-${this.leafType}`, 60 | name: this.expandCommandName, 61 | icon: 'double-down-arrow-glyph', 62 | callback: () => { 63 | this.expandAll(); 64 | } 65 | }; 66 | } 67 | 68 | /** 69 | * Toggle command config 70 | */ 71 | protected get toggleCommand(): Command { 72 | return { 73 | id: `toggle-${this.leafType}`, 74 | name: this.toggleCommandName, 75 | icon: 'double-down-arrow-glyph', 76 | callback: () => { 77 | this.toggleCollapse(); 78 | } 79 | }; 80 | } 81 | 82 | protected get commands(): Command[] { 83 | return [this.collapseCommand, this.expandCommand, this.toggleCommand]; 84 | } 85 | 86 | public register(): void { 87 | if (this.registered) { 88 | return; 89 | } 90 | for (const command of this.commands) { 91 | this.plugin.addCommand(command); 92 | } 93 | this.registered = true; 94 | } 95 | 96 | /** 97 | * Collapse or expand all items for the given leaf 98 | * @argument collapsed if not provided, will toggle the state 99 | */ 100 | protected collapseOrExpandAll( 101 | leaf: WorkspaceLeaf, 102 | collapsed?: boolean 103 | ): void { 104 | if (collapsed === undefined) { 105 | if (!leaf.view.tree?.toggleCollapseAll) { 106 | console.error( 107 | `No toggle collapse function found on ${this.leafType} view.` 108 | ); 109 | return; 110 | } 111 | leaf.view.tree?.toggleCollapseAll(); 112 | } else { 113 | if (!leaf.view.tree?.setCollapseAll) { 114 | console.error(`No collapse function found on ${this.leafType} view.`); 115 | return; 116 | } 117 | leaf.view.tree.setCollapseAll(collapsed); 118 | } 119 | } 120 | 121 | /** 122 | * Returns true if every item in the given leaf is collapsed 123 | */ 124 | public allCollapsed(singleLeaf: WorkspaceLeaf | null = null): boolean { 125 | const leaves = singleLeaf ? [singleLeaf] : this.leaves; 126 | let collapsed = true; 127 | for (const leaf of leaves) { 128 | if (leaf.view.tree?.isAllCollapsed === undefined) { 129 | console.error('No collapsed state found on view.'); 130 | collapsed = false; 131 | } 132 | } 133 | return collapsed; 134 | } 135 | 136 | public toggleCollapse(singleLeaf: WorkspaceLeaf | null = null): void { 137 | const leaves = singleLeaf ? [singleLeaf] : this.leaves; 138 | for (const leaf of leaves) { 139 | this.collapseOrExpandAll(leaf); 140 | } 141 | } 142 | 143 | /** 144 | * Collapse all open items in the given leaf or all leaves 145 | */ 146 | public collapseAll(singleLeaf: WorkspaceLeaf | null = null): void { 147 | const leaves = singleLeaf ? [singleLeaf] : this.leaves; 148 | for (const leaf of leaves) { 149 | this.collapseOrExpandAll(leaf, true); 150 | } 151 | } 152 | 153 | /** 154 | * Expand all collapsed items in the given leaf or all leaves 155 | */ 156 | public expandAll(singleLeaf: WorkspaceLeaf | null = null): void { 157 | const leaves = singleLeaf ? [singleLeaf] : this.leaves; 158 | for (const leaf of leaves) { 159 | this.collapseOrExpandAll(leaf, false); 160 | } 161 | } 162 | 163 | /** 164 | * Returns all loaded leaves of the class leafType 165 | */ 166 | protected get leaves(): WorkspaceLeaf[] { 167 | return this.plugin.app.workspace.getLeavesOfType(this.leafType); 168 | } 169 | } 170 | --------------------------------------------------------------------------------