diff --git a/src/SettingTab.tsx b/src/SettingTab.tsx index 94e5e20..636d0a2 100644 --- a/src/SettingTab.tsx +++ b/src/SettingTab.tsx @@ -125,6 +125,7 @@ export class SettingTab extends PluginSettingTab { this.createHeader2(this.headersCopy.folderAndTagBehavior); this._initToggleSetting("showFolderHierarchyLines"); this._initToggleSetting("showFilesCount"); + this._initDropdownSetting("expandNodeOnClick") this._initToggleSetting("openDestinationFolderAfterMove"); this._initToggleSetting("revealFileInExplorer"); } diff --git a/src/components/ExpandIcon.tsx b/src/components/ExpandIcon.tsx index 17c708a..50f830a 100644 --- a/src/components/ExpandIcon.tsx +++ b/src/components/ExpandIcon.tsx @@ -5,13 +5,15 @@ import { ChevronRight } from "src/assets/icons"; type Props = { isExpanded: boolean; hideIcon: boolean; + onToggle: () => void; }; -const ExpandIcon = ({ isExpanded, hideIcon }: Props) => { +const ExpandIcon = ({ isExpanded, hideIcon, onToggle }: Props) => { return (
{hideIcon ? null : ( diff --git a/src/components/Folder/Content.tsx b/src/components/Folder/Content.tsx index e6d16b3..34f01cd 100644 --- a/src/components/Folder/Content.tsx +++ b/src/components/Folder/Content.tsx @@ -1,10 +1,12 @@ import classNames from "classnames"; import { Menu, TFolder } from "obsidian"; -import { useEffect, useRef } from "react"; +import { MouseEvent, useEffect, useRef } from "react"; import { useShallow } from "zustand/react/shallow"; import { useExplorer } from "src/hooks/useExplorer"; +import { useExpandNodeByClick } from "src/hooks/useSettingsHandler"; import { FOLDER_OPERATION_COPY } from "src/locales"; +import { EXPAND_NODE_ON_CLICK } from "src/settings"; import { ExplorerStore } from "src/store"; import { addCreateFileMenuItem, @@ -22,7 +24,8 @@ import { FolderListModal } from "../FolderListModal"; import FolderContentInner from "./ContentInner"; -export type FolderInnerContentRef = { +export type FolderNameRef = { + isFocusing: boolean; setIsFocusing: (isFocusing: boolean) => void; onStartEditingName: Noop; }; @@ -33,40 +36,42 @@ export type FolderProps = { type Props = FolderProps; const FolderContent = ({ folder }: Props) => { const { useExplorerStore, plugin } = useExplorer(); - const { language } = plugin; + const { language, settings } = plugin; const { focusedFolder, changeFocusedFolder, - createNewFolder, - createFileWithDefaultName, + createNewFolderAndFocus, + createNewFileAndFocus, pinFolder, unpinFolder, isFolderPinned, trashFolder, - expandFolder, - isFolderExpanded + toggleFolder, } = useExplorerStore( useShallow((store: ExplorerStore) => ({ focusedFolder: store.focusedFolder, changeFocusedFolder: store.changeFocusedFolder, - createNewFolder: store.createNewFolder, - createFileWithDefaultName: store.createFileWithDefaultName, + createNewFolderAndFocus: store.createNewFolderAndFocus, + createNewFileAndFocus: store.createNewFileAndFocus, pinFolder: store.pinFolder, unpinFolder: store.unpinFolder, isFolderPinned: store.isFolderPinned, trashFolder: store.trashFolder, - expandFolder: store.expandFolder, - isFolderExpanded: store.isFolderExpanded + toggleFolder: store.toggleFolder, })) ); - const isFocused = folder.path == focusedFolder?.path; + const { expandNodeByClick } = useExpandNodeByClick( + settings.expandNodeOnClick + ); const folderRef = useRef(null); - const innerContentRef = useRef(null); + const nameRef = useRef(null); useEffect(() => { + nameRef.current?.setIsFocusing(false) + if (focusedFolder?.path !== folder.path) return; setTimeout(() => { folderRef.current?.scrollIntoView({ @@ -90,28 +95,11 @@ const FolderContent = ({ folder }: Props) => { modal.open(); }; - const onCreateNewFile = async () => { - if (folder.path !== focusedFolder?.path) { - await changeFocusedFolder(folder); - } - await createFileWithDefaultName(folder); - }; - - const onCreateNewFolder = async () => { - const newFolder = await createNewFolder(folder); - if (!isFolderExpanded(folder)) { - expandFolder(folder); - } - if (newFolder) { - await changeFocusedFolder(newFolder); - } - }; - const addCreateFolderMenuItem = (menu: Menu) => { addMenuItem(menu, { title: FOLDER_OPERATION_COPY.createFolder[language], icon: "folder-open", - action: onCreateNewFolder, + action: async () => await createNewFolderAndFocus(folder), }); }; @@ -125,7 +113,10 @@ const FolderContent = ({ folder }: Props) => { addPinInMenu(menu); menu.addSeparator(); - addCreateFileMenuItem(menu, onCreateNewFile); + addCreateFileMenuItem( + menu, + async () => await createNewFileAndFocus(folder) + ); addCreateFolderMenuItem(menu); menu.addSeparator(); @@ -134,7 +125,7 @@ const FolderContent = ({ folder }: Props) => { addRenameMenuItem( menu, - innerContentRef?.current?.onStartEditingName ?? noop, + nameRef?.current?.onStartEditingName ?? noop, isRootFolder ); addDeleteMenuItem(menu, () => trashFolder(folder)); @@ -142,18 +133,29 @@ const FolderContent = ({ folder }: Props) => { triggerMenu(plugin, menu, "folder-context-menu")(e); }; + const onClickFolderContent = async (e: MouseEvent) => { + e.stopPropagation() + e.preventDefault() + await changeFocusedFolder(folder); + if (expandNodeByClick === EXPAND_NODE_ON_CLICK.LABEL) { + toggleFolder(folder); + } else if (expandNodeByClick === EXPAND_NODE_ON_CLICK.SELECTED_LABEL) { + folderRef.current?.focus(); + if (nameRef.current?.isFocusing) { + toggleFolder(folder); + } else { + nameRef.current?.setIsFocusing(true); + } + } + }; + return (
{ - if (isFocused) { - folderRef.current?.focus(); - innerContentRef.current?.setIsFocusing(true); - } - }} + onClick={onClickFolderContent} > - +
); }; diff --git a/src/components/Folder/ContentInner.tsx b/src/components/Folder/ContentInner.tsx index 101949d..ee940aa 100644 --- a/src/components/Folder/ContentInner.tsx +++ b/src/components/Folder/ContentInner.tsx @@ -1,23 +1,23 @@ import { TFolder } from "obsidian"; -import { ForwardedRef, forwardRef, useRef } from "react"; +import { ForwardedRef, forwardRef, Ref } from "react"; -import { FolderInnerContentRef } from "./Content"; +import { FolderNameRef } from "./Content"; import FolderFilesCount from "./FilesCount"; import FolderIcon from "./Icon"; import FolderName from "./Name"; export type FolderProps = { folder: TFolder; + nameRef: Ref }; type Props = FolderProps; const FolderContentInner = forwardRef( - ({ folder }: Props, ref: ForwardedRef) => { - const folderRef = useRef(null); + ({ folder, nameRef }: Props, ref: ForwardedRef) => { return ( -
+
- +
); diff --git a/src/components/Folder/ExpandIcon.tsx b/src/components/Folder/ExpandIcon.tsx index dec15de..b64f213 100644 --- a/src/components/Folder/ExpandIcon.tsx +++ b/src/components/Folder/ExpandIcon.tsx @@ -12,10 +12,11 @@ type Props = { const FolderExpandIcon = ({ folder }: Props) => { const { useExplorerStore } = useExplorer(); - const { hasSubFolder, isFolderExpanded } = useExplorerStore( + const { hasSubFolder, isFolderExpanded, toggleFolder } = useExplorerStore( useShallow((store: ExplorerStore) => ({ hasSubFolder: store.hasSubFolder, isFolderExpanded: store.isFolderExpanded, + toggleFolder: store.toggleFolder, })) ); @@ -23,6 +24,7 @@ const FolderExpandIcon = ({ folder }: Props) => { toggleFolder(folder)} /> ); }; diff --git a/src/components/Folder/Name.tsx b/src/components/Folder/Name.tsx index dbfbaba..0a4fe44 100644 --- a/src/components/Folder/Name.tsx +++ b/src/components/Folder/Name.tsx @@ -13,43 +13,39 @@ import { useExplorer } from "src/hooks/useExplorer"; import useRenderEditableName from "src/hooks/useRenderEditableName"; import { ExplorerStore } from "src/store"; -import { FolderInnerContentRef } from "./Content"; +import { FolderNameRef } from "./Content"; export type FolderProps = { folder: TFolder; }; type Props = FolderProps; const FolderName = forwardRef( - ({ folder }: Props, ref: ForwardedRef) => { + ({ folder }: Props, ref: ForwardedRef) => { const { useExplorerStore } = useExplorer(); - const { - renameFolder, - getNameOfFolder, - isLastCreatedFolder, - } = useExplorerStore( - useShallow((store: ExplorerStore) => ({ - renameFolder: store.renameFolder, - getNameOfFolder: store.getNameOfFolder, - isLastCreatedFolder: store.isLastCreatedFolder, - })) - ); + const { renameFolder, getNameOfFolder, isLastCreatedFolder } = + useExplorerStore( + useShallow((store: ExplorerStore) => ({ + renameFolder: store.renameFolder, + getNameOfFolder: store.getNameOfFolder, + isLastCreatedFolder: store.isLastCreatedFolder, + })) + ); const onSaveName = (name: string) => renameFolder(folder, name); const folderName = getNameOfFolder(folder); - const { - renderEditableName: renderFolderName, - onStartEditingName, - } = useRenderEditableName(folderName, onSaveName, { - className: "ffs__folder-name", - }); + const { renderEditableName: renderFolderName, onStartEditingName } = + useRenderEditableName(folderName, onSaveName, { + className: "ffs__folder-name", + }); const folderRef = useRef(null); const [isFocusing, setIsFocusing] = useState(false); useImperativeHandle(ref, () => ({ setIsFocusing, - onStartEditingName + onStartEditingName, + isFocusing, })); const onClickOutside = (event: MouseEvent) => { @@ -63,7 +59,7 @@ const FolderName = forwardRef( const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Enter" && isFocusing) { - onStartEditingName() + onStartEditingName(); } }; diff --git a/src/components/Folder/index.tsx b/src/components/Folder/index.tsx index fb0b51b..6b8bd64 100644 --- a/src/components/Folder/index.tsx +++ b/src/components/Folder/index.tsx @@ -27,9 +27,7 @@ const Folder = ({ const { useExplorerStore } = useExplorer(); const { - changeFocusedFolder, expandFolder, - toggleFolder, focusedFolder, hasSubFolder, getFilesinFolder, @@ -37,10 +35,8 @@ const Folder = ({ getFolderLevel, } = useExplorerStore( useShallow((store: ExplorerStore) => ({ - changeFocusedFolder: store.changeFocusedFolder, hasSubFolder: store.hasSubFolder, expandFolder: store.expandFolder, - toggleFolder: store.toggleFolder, focusedFolder: store.focusedFolder, getFilesinFolder: store.getFilesInFolder, getSubFolders: store.getSubFolders, @@ -97,10 +93,6 @@ const Folder = ({
{ - toggleFolder(folder); - await changeFocusedFolder(folder); - }} style={getIndentStyle(getFolderLevel(folder), disableHoverIndent)} data-tooltip-position="right" aria-label={getAriaLabel()} diff --git a/src/components/FolderAndTagTree/index.tsx b/src/components/FolderAndTagTree/index.tsx index 6898214..55c0800 100644 --- a/src/components/FolderAndTagTree/index.tsx +++ b/src/components/FolderAndTagTree/index.tsx @@ -40,9 +40,9 @@ const FolderAndTagTree = () => { if (!showFolderView) return; const sortedFolders = sortFolders(folders); - return sortedFolders.map((folder) => { - return ; - }); + return sortedFolders.map((folder) => ( + + )); }; const renderTags = (tags: TagNode[]) => { diff --git a/src/components/Tag/ExpandIcon.tsx b/src/components/Tag/ExpandIcon.tsx index d7b85ef..6958831 100644 --- a/src/components/Tag/ExpandIcon.tsx +++ b/src/components/Tag/ExpandIcon.tsx @@ -12,10 +12,11 @@ type Props = { const TagExpandIcon = ({ tag }: Props) => { const { useExplorerStore } = useExplorer(); - const { hasSubTag, isTagExpanded } = useExplorerStore( + const { hasSubTag, isTagExpanded, toggleTag } = useExplorerStore( useShallow((store: ExplorerStore) => ({ hasSubTag: store.hasSubTag, isTagExpanded: store.isTagExpanded, + toggleTag: store.toggleTag, })) ); @@ -23,6 +24,7 @@ const TagExpandIcon = ({ tag }: Props) => { toggleTag(tag)} /> ); }; diff --git a/src/hooks/useSettingsHandler/index.tsx b/src/hooks/useSettingsHandler/index.tsx index 918e27f..b13b253 100644 --- a/src/hooks/useSettingsHandler/index.tsx +++ b/src/hooks/useSettingsHandler/index.tsx @@ -1,5 +1,6 @@ import { useAutoHideActionBar } from "./useAutoHideActionBar"; import { useBoldFileTitle } from "./useBoldFileTitle"; +import { useExpandNodeByClick } from "./useExpandNodeByClick"; import { useFileCreationDateFormat } from "./useFileCreationDateFormat"; import { useFileItemSpacing } from "./useFileItemSpacing"; import { useHideRootFolder } from "./useHideRootFolder"; @@ -42,4 +43,5 @@ export { useIncludeSubTagFiles, useShowTagIcon, useShowFilesCount, + useExpandNodeByClick }; diff --git a/src/hooks/useSettingsHandler/useExpandNodeByClick.tsx b/src/hooks/useSettingsHandler/useExpandNodeByClick.tsx new file mode 100644 index 0000000..8acfcb6 --- /dev/null +++ b/src/hooks/useSettingsHandler/useExpandNodeByClick.tsx @@ -0,0 +1,23 @@ +import { useState } from "react"; + +import { ExpandNodeOnClick } from "src/settings"; + +import { useWatchSettingsChange } from "./useWatchSettingsChange"; + +export const useExpandNodeByClick = ( + defaultExpandNodeByClick: ExpandNodeOnClick +): { expandNodeByClick: ExpandNodeOnClick } => { + const [expandNodeByClick, setExpandNodeByClick] = + useState(defaultExpandNodeByClick); + + const onChangeExpand = (event: CustomEvent) => { + const { changeKey, changeValue } = event.detail; + if (changeKey == "expandNodeByClick") { + setExpandNodeByClick(changeValue); + } + }; + + useWatchSettingsChange(onChangeExpand); + + return { expandNodeByClick }; +}; diff --git a/src/locales/settings.ts b/src/locales/settings.ts index 4f7fd9b..74665bd 100644 --- a/src/locales/settings.ts +++ b/src/locales/settings.ts @@ -1,4 +1,5 @@ import { + EXPAND_NODE_ON_CLICK, FILE_ITEM_SPACING, FileDetailSettings, FileDisplaySettings, @@ -145,6 +146,44 @@ export const FOLDER_AND_TAG_BEHAVIOR_SETTINGS_COPY: SettingsLocaleResource): Partial { + const updated = { ...raw }; + + const legacyExpandNodeOnClick = raw.expandFolderByClickingOn; + if (legacyExpandNodeOnClick && !raw.expandNodeOnClick) { + const valueMap: Record< + ExpandFolderByClickingOnElement, + ExpandNodeOnClick + > = { + icon: "icon", + folder: "selected_label", + }; + + updated.expandNodeOnClick = valueMap[legacyExpandNodeOnClick]; + } + + return updated; + } + + removeLegacySettings( + raw: Partial + ): FolderFileSplitterPluginSettings { + return Object.fromEntries( + Object.entries(raw).filter(([key]) => key in DEFAULT_SETTINGS) + ) as FolderFileSplitterPluginSettings; + } + async loadSettings() { const rawData = (await this.loadData()) ?? {}; - const filteredData = Object.fromEntries( - Object.entries(rawData).filter(([key]) => key in DEFAULT_SETTINGS) - ); + const migratedData = this.migrateLegacySettings(rawData); + const filteredData = this.removeLegacySettings(migratedData); this.settings = Object.assign({}, DEFAULT_SETTINGS, filteredData); } diff --git a/src/settings.ts b/src/settings.ts index d7aec1b..0cea89d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,5 +1,12 @@ export type ValueOf = T[keyof T]; +export const EXPAND_NODE_ON_CLICK = { + ICON: "icon", + LABEL: "label", + SELECTED_LABEL: "selected_label", +} as const; +export type ExpandNodeOnClick = ValueOf; + export const LAYOUT_MODE = { HORIZONTAL_SPLIT: "Horizontal split", VERTICAL_SPLIT: "Vertical split", @@ -42,6 +49,7 @@ export type LayoutSettings = { export type FolderAndTagBehaviorSettings = { showFolderHierarchyLines: boolean; showFilesCount: boolean; + expandNodeOnClick: ExpandNodeOnClick; openDestinationFolderAfterMove: boolean; revealFileInExplorer: boolean; }; @@ -96,6 +104,7 @@ export const LAYOUT_SETTINGS: LayoutSettings = { export const FOLDER_AND_TAG_BEHAVIOR_SETTINGS: FolderAndTagBehaviorSettings = { showFolderHierarchyLines: false, showFilesCount: true, + expandNodeOnClick: EXPAND_NODE_ON_CLICK.SELECTED_LABEL, openDestinationFolderAfterMove: false, revealFileInExplorer: false, }; @@ -144,3 +153,21 @@ export const DEFAULT_SETTINGS: FolderFileSplitterPluginSettings = { ...FILE_DISPLAY_SETTINGS, ...FOLDER_NOTE_SETTINGS, }; + +// legacy constant and type +const EXPAND_FOLDER_BY_CLICKING_ELEMENT = { + ICON: "icon", + FOLDER: "folder", +} as const; +export type ExpandFolderByClickingOnElement = ValueOf< + typeof EXPAND_FOLDER_BY_CLICKING_ELEMENT +>; + +export type LegacySettings = { + expandFolderByClickingOn: ExpandFolderByClickingOnElement; +}; +export const LEGACY_SETTINGS = { + expandFolderByClickingOn: EXPAND_FOLDER_BY_CLICKING_ELEMENT.FOLDER, +}; + +export type AllSettings = FolderFileSplitterPluginSettings & LegacySettings; diff --git a/src/store/folder/actions.ts b/src/store/folder/actions.ts index 52886df..aa65367 100644 --- a/src/store/folder/actions.ts +++ b/src/store/folder/actions.ts @@ -16,6 +16,7 @@ export interface FolderActionsSlice { createNewFolder: (parentFolder: TFolder) => Promise; createNewFolderAndFocus: (parentFolder: TFolder) => Promise; + createNewFileAndFocus: (parentFolder: TFolder) => Promise; moveFolder: (folder: TFolder, newPath: string) => Promise; renameFolder: (folder: TFolder, newName: string) => Promise; @@ -76,6 +77,11 @@ export const createFolderActionsSlice = await changeFocusedFolder(newFolder); } }, + createNewFileAndFocus: async (parentFolder: TFolder) => { + const { createFileWithDefaultName, changeFocusedFolder } = get(); + await createFileWithDefaultName(parentFolder); + await changeFocusedFolder(parentFolder); + }, trashFolder: async (folder: TFolder) => { const { focusedFolder, isAnscestorOf, setFocusedFolderAndSave } = get(); diff --git a/src/store/tag/toggle.ts b/src/store/tag/toggle.ts index 9dd533f..6586ea1 100644 --- a/src/store/tag/toggle.ts +++ b/src/store/tag/toggle.ts @@ -19,6 +19,7 @@ export interface ToggleTagSlice { expandTag: (tag: TagNode) => void; expandAllTags: () => void; collapseTag: (tag: TagNode) => void; + toggleTag: (tag: TagNode) => void; collapseAllTags: () => void; restoreExpandedTagPaths: () => void; @@ -72,6 +73,14 @@ export const createToggleTagSlice = removeItemFromArray(expandedTagPaths, tag.fullPath) ); }, + toggleTag: (tag: TagNode) => { + const { isTagExpanded, expandTag, collapseTag } = get(); + if (isTagExpanded(tag)) { + collapseTag(tag); + } else { + expandTag(tag); + } + }, collapseAllTags: () => { get().changeExpandedTagPaths([]); },