mirror of
https://github.com/xuquan-nikkkki/FolderFile-Splitter-Plugin.git
synced 2026-07-22 12:00:27 +00:00
feat: support pin folders
This commit is contained in:
parent
a689e0ff51
commit
e59cd6489d
7 changed files with 152 additions and 12 deletions
|
|
@ -1,14 +1,11 @@
|
|||
import { TAbstractFile } from "obsidian";
|
||||
|
||||
export const FFS_FOCUSED_FOLDER_PATH_KEY =
|
||||
"FocusedFolderPath";
|
||||
export const FFS_EXPANDED_FOLDER_PATHS_KEY =
|
||||
"ExpandedFolderPaths";
|
||||
export const FFS_FOCUSED_FILE_PATH_KEY =
|
||||
"FocusedFilePath";
|
||||
export const FFS_FOLDER_SORT_RULE_KEY =
|
||||
"FolderSortRule";
|
||||
export const FFS_FOCUSED_FOLDER_PATH_KEY = "FocusedFolderPath";
|
||||
export const FFS_EXPANDED_FOLDER_PATHS_KEY = "ExpandedFolderPaths";
|
||||
export const FFS_FOCUSED_FILE_PATH_KEY = "FocusedFilePath";
|
||||
export const FFS_FOLDER_SORT_RULE_KEY = "FolderSortRule";
|
||||
export const FFS_FILE_SORT_RULE_KEY = "FileSortRule";
|
||||
export const FFS_PINNED_FOLDER_PATHS_KEY = "PinnedFolderPaths";
|
||||
|
||||
export const FFS_FOLDER_PANE_WIDTH_KEY =
|
||||
"FolderFileSplitterPlugin-FolderPaneWidth";
|
||||
|
|
|
|||
16
src/assets/icons/PinIcon.tsx
Normal file
16
src/assets/icons/PinIcon.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const PinIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="ffs-icon ffs-pin-icon"
|
||||
>
|
||||
<path d="M12 17v5" />
|
||||
<path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default PinIcon
|
||||
|
|
@ -57,6 +57,9 @@ const Folder = ({
|
|||
createFile,
|
||||
folders,
|
||||
focusedFile,
|
||||
pinFolder,
|
||||
unpinFolder,
|
||||
isFolderPinned,
|
||||
} = useFileTreeStore(
|
||||
useShallow((store: FileTreeStore) => ({
|
||||
hasFolderChildren: store.hasFolderChildren,
|
||||
|
|
@ -68,6 +71,9 @@ const Folder = ({
|
|||
createFile: store.createFile,
|
||||
folders: store.folders,
|
||||
focusedFile: store.focusedFile,
|
||||
pinFolder: store.pinFolder,
|
||||
unpinFolder: store.unpinFolder,
|
||||
isFolderPinned: store.isFolderPinned,
|
||||
}))
|
||||
);
|
||||
|
||||
|
|
@ -157,6 +163,19 @@ const Folder = ({
|
|||
e.stopPropagation();
|
||||
|
||||
const menu = new Menu();
|
||||
menu.addItem((item) => {
|
||||
const isPinned = isFolderPinned(folder);
|
||||
const title = isPinned ? "Unpin folder" : "Pin folder";
|
||||
item.setTitle(title);
|
||||
item.onClick(() => {
|
||||
if (isPinned) {
|
||||
unpinFolder(folder);
|
||||
} else {
|
||||
pinFolder(folder);
|
||||
}
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
menu.addItem((item) => {
|
||||
item.setTitle("New note");
|
||||
item.onClick(() => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { FileTreeStore } from "src/store";
|
|||
import Folder from "./Folder";
|
||||
import { useShowHierarchyLines } from "src/hooks/useSettingsHandler";
|
||||
import { useChangeFolder } from "src/hooks/useVaultChangeHandler";
|
||||
import PinIcon from "src/assets/icons/PinIcon";
|
||||
|
||||
type Props = {
|
||||
useFileTreeStore: UseBoundStore<StoreApi<FileTreeStore>>;
|
||||
|
|
@ -31,6 +32,7 @@ const Folders = ({ useFileTreeStore, plugin }: Props) => {
|
|||
sortFolders: store.sortFolders,
|
||||
expandedFolderPaths: store.expandedFolderPaths,
|
||||
focusedFolder: store.focusedFolder,
|
||||
pinnedFolders: store.pinnedFolderPaths,
|
||||
}))
|
||||
);
|
||||
|
||||
|
|
@ -66,6 +68,7 @@ const Folders = ({ useFileTreeStore, plugin }: Props) => {
|
|||
|
||||
const renderFolder = (folder: TFolder, isRoot?: boolean) => (
|
||||
<Folder
|
||||
key={folder.path}
|
||||
folder={folder}
|
||||
useFileTreeStore={useFileTreeStore}
|
||||
plugin={plugin}
|
||||
|
|
@ -110,8 +113,28 @@ const Folders = ({ useFileTreeStore, plugin }: Props) => {
|
|||
);
|
||||
};
|
||||
|
||||
const renderPinnedFolders = () => {
|
||||
const pinnedFolderPaths = useFileTreeStore.getState().pinnedFolderPaths;
|
||||
if (!pinnedFolderPaths.length) return null;
|
||||
return (
|
||||
<div className="ffs-pinned-folders-section">
|
||||
<span className="ffs-pinned-title">
|
||||
<PinIcon />
|
||||
Pin
|
||||
</span>
|
||||
<div className="ffs-pinned-folders">
|
||||
{pinnedFolderPaths.map((path) => {
|
||||
const folder = plugin.app.vault.getFolderByPath(path);
|
||||
return folder ? renderFolder(folder) : null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={foldersRef}>
|
||||
{renderPinnedFolders()}
|
||||
{renderRootFolder()}
|
||||
{renderFolders(topFolders)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ const Loading = ({ width = 30, height = 30 }: Props) => {
|
|||
<div className="loading-icon">
|
||||
<LoadingIcon />
|
||||
</div>
|
||||
<p>Loading...</p>
|
||||
<style jsx>
|
||||
<p>Loading...</p>
|
||||
<style>
|
||||
{`
|
||||
.loading {
|
||||
height: 100%;
|
||||
|
|
|
|||
60
src/store.ts
60
src/store.ts
|
|
@ -2,13 +2,20 @@ import { create } from "zustand";
|
|||
import { TFile, TFolder } from "obsidian";
|
||||
|
||||
import FolderFileSplitterPlugin from "./main";
|
||||
import { isFile, isFolder } from "./utils";
|
||||
import {
|
||||
addItem,
|
||||
isAbstractFileIncluded,
|
||||
isFile,
|
||||
isFolder,
|
||||
removeItem,
|
||||
} from "./utils";
|
||||
import {
|
||||
FFS_EXPANDED_FOLDER_PATHS_KEY,
|
||||
FFS_FILE_SORT_RULE_KEY,
|
||||
FFS_FOCUSED_FILE_PATH_KEY,
|
||||
FFS_FOCUSED_FOLDER_PATH_KEY,
|
||||
FFS_FOLDER_SORT_RULE_KEY,
|
||||
FFS_PINNED_FOLDER_PATHS_KEY,
|
||||
} from "./assets/constants";
|
||||
|
||||
export type FolderSortRule =
|
||||
|
|
@ -30,6 +37,7 @@ export type FileTreeStore = {
|
|||
folders: TFolder[];
|
||||
rootFolder: TFolder | null;
|
||||
focusedFolder: TFolder | null;
|
||||
pinnedFolderPaths: string[];
|
||||
focusedFile: TFile | null;
|
||||
folderSortRule: FolderSortRule;
|
||||
fileSortRule: FileSortRule;
|
||||
|
|
@ -63,6 +71,10 @@ export type FileTreeStore = {
|
|||
changeExpandedFolderPaths: (folderNames: string[]) => Promise<void>;
|
||||
restoreExpandedFolderPaths: () => Promise<void>;
|
||||
restoreLastFocusedFolder: () => Promise<void>;
|
||||
pinFolder: (folder: TFolder) => Promise<void>;
|
||||
unpinFolder: (folder: TFolder) => Promise<void>;
|
||||
isFolderPinned: (folder: TFolder) => boolean;
|
||||
restorePinnedFolders: () => Promise<void>;
|
||||
|
||||
// Files related
|
||||
findFileByPath: (path: string) => TFile | null;
|
||||
|
|
@ -84,6 +96,7 @@ export const createFileTreeStore = (plugin: FolderFileSplitterPlugin) =>
|
|||
folders: plugin.app.vault.getAllFolders() || [],
|
||||
rootFolder: plugin.app.vault.getRoot() || null,
|
||||
focusedFolder: null,
|
||||
pinnedFolderPaths: [],
|
||||
focusedFile: null,
|
||||
folderSortRule: DEFAULT_FOLDER_SORT_RULE,
|
||||
fileSortRule: DEFAULT_FILE_SORT_RULE,
|
||||
|
|
@ -98,7 +111,6 @@ export const createFileTreeStore = (plugin: FolderFileSplitterPlugin) =>
|
|||
},
|
||||
getData: async <T>(key: string): Promise<T | undefined> => {
|
||||
const data = await plugin.loadData();
|
||||
|
||||
return data[key];
|
||||
},
|
||||
restoreData: async () => {
|
||||
|
|
@ -108,6 +120,7 @@ export const createFileTreeStore = (plugin: FolderFileSplitterPlugin) =>
|
|||
restoreFolderSortRule,
|
||||
restoreFileSortRule,
|
||||
restoreExpandedFolderPaths,
|
||||
restorePinnedFolders,
|
||||
} = get();
|
||||
await Promise.all([
|
||||
restoreLastFocusedFolder(),
|
||||
|
|
@ -115,6 +128,7 @@ export const createFileTreeStore = (plugin: FolderFileSplitterPlugin) =>
|
|||
restoreFolderSortRule(),
|
||||
restoreFileSortRule(),
|
||||
restoreExpandedFolderPaths(),
|
||||
restorePinnedFolders(),
|
||||
]);
|
||||
},
|
||||
|
||||
|
|
@ -299,6 +313,48 @@ export const createFileTreeStore = (plugin: FolderFileSplitterPlugin) =>
|
|||
_setFocusedFolder(rootFolder);
|
||||
}
|
||||
},
|
||||
isFolderPinned: (folder: TFolder) => {
|
||||
const { pinnedFolderPaths } = get();
|
||||
return pinnedFolderPaths.includes(folder.path);
|
||||
},
|
||||
pinFolder: async (folder: TFolder) => {
|
||||
const { pinnedFolderPaths, saveData } = get();
|
||||
const folderPaths = [...pinnedFolderPaths, folder.path];
|
||||
set({
|
||||
pinnedFolderPaths: folderPaths,
|
||||
});
|
||||
await saveData({
|
||||
[FFS_PINNED_FOLDER_PATHS_KEY]: JSON.stringify(folderPaths),
|
||||
});
|
||||
},
|
||||
unpinFolder: async (folder: TFolder) => {
|
||||
const { pinnedFolderPaths, saveData } = get();
|
||||
const folderPaths = pinnedFolderPaths.filter(
|
||||
(path) => path !== folder.path
|
||||
);
|
||||
set({
|
||||
pinnedFolderPaths: folderPaths,
|
||||
});
|
||||
await saveData({
|
||||
[FFS_PINNED_FOLDER_PATHS_KEY]: JSON.stringify(folderPaths),
|
||||
});
|
||||
},
|
||||
restorePinnedFolders: async () => {
|
||||
const { getData } = get();
|
||||
const pinnedFolderPaths = await getData<string>(
|
||||
FFS_PINNED_FOLDER_PATHS_KEY
|
||||
);
|
||||
if (pinnedFolderPaths) {
|
||||
try {
|
||||
const folderPaths: string[] = JSON.parse(pinnedFolderPaths);
|
||||
set({
|
||||
pinnedFolderPaths: folderPaths,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Invalid Json format: ", error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Files related
|
||||
findFileByPath: (path: string): TFile | null => {
|
||||
|
|
|
|||
29
styles.css
29
styles.css
|
|
@ -324,3 +324,32 @@ If your plugin does not need CSS, delete this file.
|
|||
.ffs-actions-icon-wrapper:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.ffs-pinned-folders-section {
|
||||
margin: 8px 0 4px;
|
||||
padding: 6px 4px 8px;
|
||||
background-color: var(--ffs-actions-background-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ffs-pinned-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
color: var(--ffs-actions-icon-color);
|
||||
margin-bottom: 4px;
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ffs-pinned-folders-section .ffs-pin-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 2px;
|
||||
fill: var(--ffs-actions-icon-color);
|
||||
color: var(--ffs-actions-icon-color);
|
||||
}
|
||||
|
||||
.ffs-pinned-folders .ffs-folder-arrow-icon-wrapper {
|
||||
width: 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue