feat: simplify click to expand folder/tag logic

This commit is contained in:
Xu Quan 2025-05-22 18:39:44 +08:00
parent 0080382154
commit 949b0b351b
15 changed files with 64 additions and 161 deletions

View file

@ -136,7 +136,6 @@ export class SettingTab extends PluginSettingTab {
initFolderAndFileBehaviorSettings() {
this.createHeader2(this.headersCopy.folderAndFileBehavior);
this._initDropdownSetting("expandFolderByClickingOn");
this._initToggleSetting("hideRootFolder");
this._initToggleSetting("showFolderHierarchyLines");
this._initToggleSetting("openDestinationFolderAfterMove");

View file

@ -6,9 +6,8 @@ import { ChevronRight } from "src/assets/icons";
type Props = {
isExpanded: boolean;
hideIcon: boolean;
onClickExpandIcon: (e: React.MouseEvent<HTMLDivElement>) => void;
};
const ExpandIcon = ({ isExpanded, hideIcon, onClickExpandIcon }: Props) => {
const ExpandIcon = ({ isExpanded, hideIcon }: Props) => {
let content: ReactNode;
if (hideIcon) {
content = null;
@ -23,7 +22,6 @@ const ExpandIcon = ({ isExpanded, hideIcon, onClickExpandIcon }: Props) => {
className={classNames("tree-item-icon collapse-icon", {
"is-collapsed": !isExpanded,
})}
onClick={onClickExpandIcon}
>
{content}
</div>

View file

@ -4,10 +4,7 @@ import { useEffect, useRef, useState } from "react";
import { ExplorerStore } from "src/store";
import { FolderListModal } from "../FolderListModal";
import {
useShowFolderIcon,
useExpandFolderByClickingOnElement,
} from "src/hooks/useSettingsHandler";
import { useShowFolderIcon } from "src/hooks/useSettingsHandler";
import FilesCount from "./FilesCount";
import useRenderEditableName from "src/hooks/useRenderEditableName";
import { FOLDER_OPERATION_COPY } from "src/locales";
@ -38,7 +35,7 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
renameFolder,
latestCreatedFolder,
latestFolderCreatedTime,
getNameOfFolder
getNameOfFolder,
} = useExplorerStore(
useShallow((store: ExplorerStore) => ({
focusedFolder: store.focusedFolder,
@ -53,7 +50,7 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
renameFolder: store.renameFolder,
latestCreatedFolder: store.latestCreatedFolder,
latestFolderCreatedTime: store.latestFolderCreatedTime,
getNameOfFolder: store.getNameOfFolder
getNameOfFolder: store.getNameOfFolder,
}))
);
@ -62,9 +59,7 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
const { settings } = plugin;
const { showFolderIcon } = useShowFolderIcon(settings.showFolderIcon);
const { expandFolderByClickingOn } = useExpandFolderByClickingOnElement(
settings.expandFolderByClickingOn
);
const isFocused = folder.path == focusedFolder?.path;
const onSaveName = (name: string) => renameFolder(folder, name);
@ -210,15 +205,6 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
menu.showAtPosition({ x: e.clientX, y: e.clientY });
};
const onClickFolderName = (e: React.MouseEvent<HTMLDivElement>): void => {
e.stopPropagation();
if (focusedFolder?.path !== folder.path) {
setFocusedFolder(folder);
} else if (expandFolderByClickingOn === "folder") {
onToggleExpandState();
}
};
const maybeRenderFolderIcon = () => {
if (!showFolderIcon) return null;
const className = classNames("ffs__folder-icon", {
@ -237,11 +223,7 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
};
const renderTitleContent = () => (
<div
ref={folderRef}
className="ffs__folder-content--main"
onClick={onClickFolderName}
>
<div ref={folderRef} className="ffs__folder-content--main">
{maybeRenderFolderIcon()}
{renderFolderName()}
<FilesCount folder={folder} />
@ -254,7 +236,6 @@ const FolderContent = ({ folder, onToggleExpandState }: Props) => {
onContextMenu={onShowContextMenu}
onClick={(e) => {
if (isFocused) {
e.stopPropagation();
folderRef.current?.focus();
setIsFocusing(true);
}

View file

@ -2,56 +2,28 @@ import { TFolder } from "obsidian";
import { useShallow } from "zustand/react/shallow";
import { ExplorerStore } from "src/store";
import { useExpandFolderByClickingOnElement } from "src/hooks/useSettingsHandler";
import { useExplorer } from "src/hooks/useExplorer";
import ExpandIcon from "../ExpandIcon";
import { uniq } from "src/utils";
type Props = {
folder: TFolder;
};
const FolderExpandIcon = ({ folder }: Props) => {
const { useExplorerStore, plugin } = useExplorer();
const { settings } = plugin;
const { useExplorerStore } = useExplorer();
const {
hasFolderChildren,
expandedFolderPaths,
changeExpandedFolderPaths,
} = useExplorerStore(
const { hasFolderChildren, expandedFolderPaths } = useExplorerStore(
useShallow((store: ExplorerStore) => ({
hasFolderChildren: store.hasFolderChildren,
expandedFolderPaths: store.expandedFolderPaths,
changeExpandedFolderPaths: store.changeExpandedFolderPaths,
}))
);
const { expandFolderByClickingOn } = useExpandFolderByClickingOnElement(
settings.expandFolderByClickingOn
);
const isFolderExpanded = expandedFolderPaths.includes(folder.path);
const onToggleExpandState = (): void => {
if (hasFolderChildren(folder)) {
const folderPaths = isFolderExpanded
? expandedFolderPaths.filter((path) => path !== folder.path)
: uniq([...expandedFolderPaths, folder.path]);
changeExpandedFolderPaths(folderPaths);
}
};
const onClickExpandIcon = (e: React.MouseEvent<HTMLDivElement>): void => {
if (expandFolderByClickingOn !== "icon") return;
e.stopPropagation();
onToggleExpandState();
};
return (
<ExpandIcon
isExpanded={isFolderExpanded}
hideIcon={!hasFolderChildren(folder)}
onClickExpandIcon={onClickExpandIcon}
/>
);
};

View file

@ -140,7 +140,10 @@ const Folder = ({
<div
className={getClassNames()}
ref={setDropRef}
onClick={() => setFocusedFolder(folder)}
onClick={() => {
onToggleExpandState();
setFocusedFolder(folder);
}}
style={getIndentStyle()}
data-tooltip-position="right"
aria-label={getAriaLabel()}

View file

@ -132,7 +132,6 @@ const TagContent = ({ tag }: Props) => {
onContextMenu={onShowContextMenu}
onClick={(e) => {
if (isFocused) {
e.stopPropagation();
tagRef.current?.focus();
setIsFocusing(true);
}

View file

@ -1,54 +1,29 @@
import { useShallow } from "zustand/react/shallow";
import { ExplorerStore } from "src/store";
import { useExpandFolderByClickingOnElement } from "src/hooks/useSettingsHandler";
import { useExplorer } from "src/hooks/useExplorer";
import ExpandIcon from "../ExpandIcon";
import { TagNode } from "src/store/tag";
import { uniq } from "src/utils";
type Props = {
tag: TagNode;
};
const TagExpandIcon = ({ tag }: Props) => {
const { useExplorerStore, plugin } = useExplorer();
const { settings } = plugin;
const { useExplorerStore } = useExplorer();
const { hasTagChildren, expandedTagPaths, changeExpandedTagPaths } =
useExplorerStore(
useShallow((store: ExplorerStore) => ({
hasTagChildren: store.hasTagChildren,
expandedTagPaths: store.expandedTagPaths,
changeExpandedTagPaths: store.changeExpandedTagPaths,
}))
);
const { expandFolderByClickingOn } = useExpandFolderByClickingOnElement(
settings.expandFolderByClickingOn
const { hasTagChildren, expandedTagPaths } = useExplorerStore(
useShallow((store: ExplorerStore) => ({
hasTagChildren: store.hasTagChildren,
expandedTagPaths: store.expandedTagPaths,
}))
);
const isTagExpanded = expandedTagPaths.includes(tag.fullPath);
const onToggleExpandState = (): void => {
if (hasTagChildren(tag)) {
const tagPaths = isTagExpanded
? expandedTagPaths.filter((path) => path !== tag.fullPath)
: uniq([...expandedTagPaths, tag.fullPath]);
changeExpandedTagPaths(tagPaths);
}
};
const onClickExpandIcon = (e: React.MouseEvent<HTMLDivElement>): void => {
if (expandFolderByClickingOn !== "icon") return;
e.stopPropagation();
onToggleExpandState();
};
return (
<ExpandIcon
isExpanded={isTagExpanded}
hideIcon={!hasTagChildren(tag)}
onClickExpandIcon={onClickExpandIcon}
/>
);
};

View file

@ -27,6 +27,9 @@ const Tag = ({
focusedTag,
getFilesInTag,
getTagsByParent,
expandedTagPaths,
collapseTag,
expandTag,
} = useExplorerStore(
useShallow((store: ExplorerStore) => ({
hasTagChildren: store.hasTagChildren,
@ -34,8 +37,21 @@ const Tag = ({
focusedTag: store.focusedTag,
getTagsByParent: store.getTagsByParent,
getFilesInTag: store.getFilesInTag,
expandedTagPaths: store.expandedTagPaths,
collapseTag: store.collapseTag,
expandTag: store.expandTag,
}))
);
const onToggleExpandState = (): void => {
const isFolderExpanded = expandedTagPaths.includes(tag.fullPath);
if (isFolderExpanded) {
collapseTag(tag);
} else {
expandTag(tag);
}
};
const isFocused = tag.fullPath == focusedTag?.fullPath;
const getAriaLabel = () => {
@ -75,7 +91,10 @@ const Tag = ({
<div
className={getClassNames()}
style={getIndentStyle()}
onClick={() => setFocusedTag(tag)}
onClick={() => {
setFocusedTag(tag);
onToggleExpandState();
}}
data-tooltip-position="right"
aria-label={getAriaLabel()}
>

View file

@ -1,5 +1,4 @@
import { useShowFolderIcon } from "./useShowFolderIcon";
import { useExpandFolderByClickingOnElement } from "./useExpandFolderByClickingOnEle";
import { useIncludeSubfolderFiles } from "./useIncludeSubfolderFiles";
import { useShowFileDetail } from "./useShowFileDetail";
import { useShowHierarchyLines } from "./useShowHierarchyLines";
@ -22,7 +21,6 @@ import { useShowTagIcon } from "./useShowTagIcon";
export {
useShowFolderIcon,
useExpandFolderByClickingOnElement,
useIncludeSubfolderFiles,
useShowFileDetail,
useShowHierarchyLines,

View file

@ -1,23 +0,0 @@
import { useState } from "react";
import { ExpandFolderByClickingOnElement } from "src/settings";
import { useWatchSettingsChange } from "./useWatchSettingsChange";
export const useExpandFolderByClickingOnElement = (
defaultClickingOnElement: ExpandFolderByClickingOnElement
): { expandFolderByClickingOn: ExpandFolderByClickingOnElement } => {
const [expandFolderByClickingOn, setExpandFolderByClickingOn] = useState(
defaultClickingOnElement
);
const onChangeShowFolderIcon = (event: CustomEvent) => {
const { changeKey, changeValue } = event.detail;
if (changeKey == "expandFolderByClickingOn") {
setExpandFolderByClickingOn(changeValue);
}
};
useWatchSettingsChange(onChangeShowFolderIcon);
return { expandFolderByClickingOn };
};

View file

@ -123,20 +123,6 @@ export const EN_SETTINGS: SettingsLocaleResource = {
name: "Auto-hide action bar",
desc: "When enabled, the top action bar will be hidden by default and only appear when hovering over it.",
},
expandFolderByClickingOn: {
name: "Expand folder/tag on click",
desc: "Choose whether to expand a folder/tag by clicking on the toggle icon (▶/▼) or the folder/tag name.",
options: [
{
value: EXPAND_FOLDER_BY_CLICKING_ELEMENT.ICON,
text: "Toggle Icon",
},
{
value: EXPAND_FOLDER_BY_CLICKING_ELEMENT.FOLDER,
text: "Folder/Tag Name",
},
],
},
includeSubfolderFiles: {
name: "Include subfolder files",
desc: "When enabled, files inside subfolders will be included in the file list and their count will be reflected in the folders file count.",
@ -293,20 +279,6 @@ export const ZH_SETTINGS: SettingsLocaleResource = {
name: "自动隐藏操作栏",
desc: "启用后,顶部操作栏默认隐藏,鼠标悬停时才会显示。",
},
expandFolderByClickingOn: {
name: "点击展开文件夹/标签",
desc: "选择通过点击切换图标(▶/▼)或文件夹/标签名称来展开文件夹/标签。",
options: [
{
value: EXPAND_FOLDER_BY_CLICKING_ELEMENT.ICON,
text: "切换图标",
},
{
value: EXPAND_FOLDER_BY_CLICKING_ELEMENT.FOLDER,
text: "文件夹/标签名称",
},
],
},
includeSubfolderFiles: {
name: "包含子文件夹文件",
desc: "启用后,子文件夹中的文件将会显示在文件列表中,并计入所属文件夹的文件数量。",

View file

@ -38,6 +38,7 @@ export default class FolderFileSplitterPlugin extends Plugin {
);
await this.loadSettings();
console.log("settings", this.settings.expandFolderByClickingOn)
this.addCommand({
id: "open-file-tree-view",

View file

@ -1,13 +1,5 @@
type ValueOf<T> = T[keyof T];
export const EXPAND_FOLDER_BY_CLICKING_ELEMENT = {
ICON: "icon",
FOLDER: "folder",
} as const;
export type ExpandFolderByClickingOnElement = ValueOf<
typeof EXPAND_FOLDER_BY_CLICKING_ELEMENT
>;
export const LAYOUT_MODE = {
HORIZONTAL_SPLIT: "Horizontal split",
VERTICAL_SPLIT: "Vertical split",
@ -40,7 +32,6 @@ export type FolderNoteMissingBehavior = ValueOf<
export const DEFAULT_FILE_CREATION_DATE_FORMAT = "YYYY/MM/DD";
export interface FolderFileSplitterPluginSettings {
expandFolderByClickingOn: ExpandFolderByClickingOnElement;
includeSubfolderFiles: boolean;
showFolderHierarchyLines: boolean;
showFolderIcon: boolean;
@ -70,7 +61,6 @@ export interface FolderFileSplitterPluginSettings {
}
export const DEFAULT_SETTINGS: FolderFileSplitterPluginSettings = {
expandFolderByClickingOn: EXPAND_FOLDER_BY_CLICKING_ELEMENT.FOLDER,
includeSubfolderFiles: false,
showFolderHierarchyLines: false,
showFolderIcon: true,

View file

@ -95,8 +95,8 @@ export type FolderExplorerStore = {
) => Promise<void>;
moveFolder: (folder: TFolder, newPath: string) => Promise<void>;
renameFolder: (folder: TFolder, newName: string) => Promise<void>;
expandFolder: (folder: TFolder) => void;
collapseFolder: (folder: TFolder) => void;
expandFolder: (folder: TFolder) => Promise<void>;
collapseFolder: (folder: TFolder) => Promise<void>;
updateFolderPinState: (oldPath: string, newPath: string) => Promise<void>;
getNameOfFolder: (folder: TFolder) => string;
};
@ -609,25 +609,25 @@ export const createFolderExplorerStore =
const newPath = folder.path.replace(folder.name, newName);
await moveFolder(folder, newPath);
},
expandFolder: (folder: TFolder) => {
expandFolder: async (folder: TFolder) => {
const {
changeExpandedFolderPaths,
expandedFolderPaths,
hasFolderChildren,
} = get();
if (!hasFolderChildren(folder) || folder.isRoot()) return;
changeExpandedFolderPaths(
await changeExpandedFolderPaths(
uniq([...expandedFolderPaths, folder.path])
);
},
collapseFolder: (folder: TFolder) => {
collapseFolder: async(folder: TFolder) => {
const {
changeExpandedFolderPaths,
hasFolderChildren,
expandedFolderPaths,
} = get();
if (!hasFolderChildren(folder) || folder.isRoot()) return;
changeExpandedFolderPaths(
await changeExpandedFolderPaths(
expandedFolderPaths.filter((path) => path !== folder.path)
);
},

View file

@ -8,6 +8,7 @@ import {
FFS_FOCUSED_TAG_PATH_KEY,
FFS_PINNED_TAG_PATHS_KEY,
} from "src/assets/constants";
import { uniq } from "src/utils";
type FolderPath = string;
type ChildrenPaths = string[];
@ -50,6 +51,8 @@ export type TagExplorerStore = {
changeExpandedTagPaths: (paths: string[]) => Promise<void>;
restoreExpandedTagPaths: () => Promise<void>;
expandTag: (tag: TagNode) => Promise<void>;
collapseTag: (tag: TagNode) => Promise<void>;
_setFocusedTag: (tag: TagNode | null) => void;
setFocusedTag: (folder: TagNode | null) => Promise<void>;
@ -230,6 +233,22 @@ export const createTagExplorerStore =
);
},
expandTag: async (tag: TagNode) => {
const { changeExpandedTagPaths, expandedTagPaths, hasTagChildren } =
get();
if (!hasTagChildren(tag)) return;
await changeExpandedTagPaths(uniq([...expandedTagPaths, tag.fullPath]));
},
collapseTag: async (tag: TagNode) => {
const { changeExpandedTagPaths, hasTagChildren, expandedTagPaths } =
get();
if (!hasTagChildren(tag)) return;
await changeExpandedTagPaths(
expandedTagPaths.filter((path) => path !== tag.fullPath)
);
},
restoreExpandedTagPaths: async () => {
const { getDataFromLocalStorage, hasTagChildren, tagTree } = get();
const lastExpandedTagPaths = getDataFromLocalStorage(