From 9e71989ff5191db66d84e8bbab26dfd947b9a7fb Mon Sep 17 00:00:00 2001 From: codetypes Date: Sun, 19 May 2024 22:27:58 +0800 Subject: [PATCH] show all files in explore --- src/components/explorer.tsx | 387 ++++++++++++++++-------------- src/contexts/workspace-context.ts | 8 +- src/misc/b3util.ts | 8 +- 3 files changed, 216 insertions(+), 187 deletions(-) diff --git a/src/components/explorer.tsx b/src/components/explorer.tsx index 224a974..736de6d 100644 --- a/src/components/explorer.tsx +++ b/src/components/explorer.tsx @@ -2,6 +2,7 @@ import { FileTreeType, useWorkspace } from "@/contexts/workspace-context"; import { NodeDef, getNodeType } from "@/misc/b3type"; import * as b3util from "@/misc/b3util"; import { modal } from "@/misc/hooks"; +import i18n from "@/misc/i18n"; import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys"; import Path from "@/misc/path"; import { DownOutlined } from "@ant-design/icons"; @@ -115,6 +116,160 @@ const renameFile = (oldPath: string, newPath: string) => { return false; }; +// file context menu +const createFileContextMenu = (node: FileTreeType) => { + const isTreeFile = b3util.isTreeFile(node.path); + const MenuItem: FC = (itemProps) => { + return ( + + ); + }; + + const arr: MenuProps["items"] = [ + { + disabled: !isTreeFile, + label: ( + +
{i18n.t("open")}
+
+ ), + key: "open", + }, + { + disabled: !isTreeFile, + label: ( + +
{i18n.t("copy")}
+
{isMacos ? "⌘ C" : "Ctrl+C"}
+
+ ), + key: "copy", + }, + { + disabled: !isTreeFile, + label: ( + +
{i18n.t("duplicate")}
+
{isMacos ? "⌘ D" : "Ctrl+D"}
+
+ ), + key: "duplicate", + }, + { + label: ( + +
{isMacos ? i18n.t("revealFileOnMac") : i18n.t("revealFileOnWindows")}
+
+ ), + key: "revealFile", + }, + { + label: ( + +
{i18n.t("rename")}
+ {isMacos && } + {!isMacos &&
F2
} +
+ ), + key: "rename", + }, + { + label: ( + +
{i18n.t("delete")}
+ {isMacos && ( + + + + + )} +
+ ), + key: "delete", + }, + ]; + return arr; +}; + +// folder context menu +const createFolderContextMenu = (copiedPath: string) => { + const MenuItem: FC = (itemProps) => { + return ( + + ); + }; + + const arr: MenuProps["items"] = [ + { + label: ( + +
{i18n.t("newFile")}
+
+ ), + key: "newFile", + }, + { + label: ( + +
{i18n.t("newFolder")}
+
+ ), + key: "newFolder", + }, + { + label: ( + +
{isMacos ? i18n.t("revealFileOnMac") : i18n.t("revealFileOnWindows")}
+
+ ), + key: "revealFile", + }, + { + disabled: !copiedPath, + label: ( + +
{i18n.t("paste")}
+
{isMacos ? "⌘ V" : "Ctrl+V"}
+
+ ), + key: "paste", + }, + { + label: ( + +
{i18n.t("rename")}
+ {isMacos && } + {!isMacos &&
F2
} +
+ ), + key: "rename", + }, + { + label: ( + +
{i18n.t("delete")}
+ {isMacos && ( + + + + + )} +
+ ), + key: "delete", + }, + ]; + return arr; +}; + export const Explorer: FC = () => { const workspace = { close: useWorkspace((state) => state.close), @@ -237,8 +392,7 @@ export const Explorer: FC = () => { const node = findFile(selectedKeys[0], workspace.fileTree!); if (isHotkeyPressed(Hotkey.F2) || (isMacos && isHotkeyPressed(Hotkey.Enter))) { if (node && node !== workspace.fileTree) { - node.editing = true; - setNewName(""); + dispatch("rename", node); } } else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.MacDelete)) { if (node && node !== workspace.fileTree) { @@ -283,12 +437,12 @@ export const Explorer: FC = () => { node.title = Path.basename(node.path); fs.mkdirSync(node.path); } else if (node.path.endsWith("/.json")) { - node.path = Path.dirname(node.path) + "/" + newName.replace(/[^\w. _-]+/g, "") + ".json"; + node.path = Path.dirname(node.path) + "/" + newName.replace(/[^\w. _-]+/g, ""); node.title = Path.basename(node.path); fs.writeFileSync(node.path, JSON.stringify(b3util.createNewTree(node.title), null, 2)); workspace.open(node.path); } else { - const newpath = Path.dirname(node.path) + "/" + newName + Path.extname(node.path); + const newpath = Path.dirname(node.path) + "/" + newName; if (renameFile(node.path, newpath)) { setSelectedKeys([newpath]); } @@ -300,7 +454,9 @@ export const Explorer: FC = () => { const dispatch = (event: MenuEvent, node: FileTreeType, dest?: FileTreeType) => { switch (event) { case "open": { - workspace.open(node.path); + if (b3util.isTreeFile(node.path)) { + workspace.open(node.path); + } break; } case "newFolder": { @@ -320,7 +476,7 @@ export const Explorer: FC = () => { case "newFile": { const folderNode: FileTreeType = { path: node.path + "/.json", - title: "", + title: ".json", isLeaf: true, editing: true, }; @@ -385,17 +541,21 @@ export const Explorer: FC = () => { break; } case "copy": { - setCopyFile(node.path); + if (b3util.isTreeFile(node.path)) { + setCopyFile(node.path); + } break; } case "duplicate": { - for (let i = 1; ; i++) { - const dupName = Path.basenameWithoutExt(node.path) + " " + i + ".json"; - const dupPath = Path.dirname(node.path) + "/" + dupName; - if (!fs.existsSync(dupPath)) { - fs.copyFileSync(node.path, dupPath); - setSelectedKeys([dupPath]); - break; + if (b3util.isTreeFile(node.path)) { + for (let i = 1; ; i++) { + const dupName = Path.basenameWithoutExt(node.path) + " " + i + ".json"; + const dupPath = Path.dirname(node.path) + "/" + dupName; + if (!fs.existsSync(dupPath)) { + fs.copyFileSync(node.path, dupPath); + setSelectedKeys([dupPath]); + break; + } } } break; @@ -491,12 +651,16 @@ export const Explorer: FC = () => { } case "move": { try { - const newPath = dest!.path + "/" + Path.basename(node.path); + const destDir = dest?.children ? dest.path : Path.dirname(dest!.path); + if (destDir === Path.dirname(node.path)) { + return; + } + const newPath = destDir + "/" + Path.basename(node.path); const doMove = () => { fs.renameSync(node.path, newPath); for (const editor of workspace.editors) { if (editor.path.startsWith(node.path)) { - editor.dispatch("rename", dest!.path + "/" + Path.basename(editor.path)); + editor.dispatch("rename", destDir + "/" + Path.basename(editor.path)); } console.log("editor move", editor.path === newPath, editor.path, newPath); if (editor.path.startsWith(newPath)) { @@ -554,161 +718,15 @@ export const Explorer: FC = () => { ipcRenderer.invoke("showItemInFolder", node.path); break; case "rename": { - node.editing = true; - setNewName(""); + if (b3util.isTreeFile(node.path)) { + node.editing = true; + setNewName(""); + } break; } } }; - // context menu - const fileContextMenu = useMemo(() => { - const MenuItem: FC = (itemProps) => { - return ( - - ); - }; - - const arr: MenuProps["items"] = [ - { - label: ( - -
{t("open")}
-
- ), - key: "open", - }, - { - label: ( - -
{t("copy")}
-
{isMacos ? "⌘ C" : "Ctrl+C"}
-
- ), - key: "copy", - }, - { - label: ( - -
{t("duplicate")}
-
{isMacos ? "⌘ D" : "Ctrl+D"}
-
- ), - key: "duplicate", - }, - { - label: ( - -
{isMacos ? t("revealFileOnMac") : t("revealFileOnWindows")}
-
- ), - key: "revealFile", - }, - { - label: ( - -
{t("rename")}
- {isMacos && } - {!isMacos &&
F2
} -
- ), - key: "rename", - }, - { - label: ( - -
{t("delete")}
- {isMacos && ( - - - - - )} -
- ), - key: "delete", - }, - ]; - return arr; - }, [t]); - - const directoryContextMenu = useMemo(() => { - const MenuItem: FC = (itemProps) => { - return ( - - ); - }; - - const arr: MenuProps["items"] = [ - { - label: ( - -
{t("newFile")}
-
- ), - key: "newFile", - }, - { - label: ( - -
{t("newFolder")}
-
- ), - key: "newFolder", - }, - { - label: ( - -
{isMacos ? t("revealFileOnMac") : t("revealFileOnWindows")}
-
- ), - key: "revealFile", - }, - { - label: ( - -
{t("paste")}
-
{isMacos ? "⌘ V" : "Ctrl+V"}
-
- ), - key: "paste", - }, - { - label: ( - -
{t("rename")}
- {isMacos && } - {!isMacos &&
F2
} -
- ), - key: "rename", - }, - { - label: ( - -
{t("delete")}
- {isMacos && ( - - - - - )} -
- ), - key: "delete", - }, - ]; - return arr; - }, [t, copyFile]); - const onClick = (info: MenuInfo) => { const node = findFile(selectedKeys[0], workspace.fileTree!) ?? workspace.fileTree; if (node) { @@ -728,7 +746,7 @@ export const Explorer: FC = () => { tabIndex={-1} style={{ height: "100%" }} onContextMenuCapture={() => { - setContextMenu(directoryContextMenu); + setContextMenu(createFolderContextMenu(copyFile)); }} >
@@ -750,34 +768,36 @@ export const Explorer: FC = () => { }} onRightClick={(info) => { if (info.node.isLeaf) { - setContextMenu(fileContextMenu); + setContextMenu(createFileContextMenu(info.node)); } setSelectedKeys([info.node.path]); }} onSelect={(_, info) => { const node = info.selectedNodes.at(0); if (node) { - if (!node.children) { - workspace.open(node.path); - } + dispatch("open", node); setSelectedKeys([node.path]); } - if (node && !node.children) { - workspace.open(node.path); - } }} onDrop={(info) => { dispatch("move", info.dragNode, info.node); }} titleRender={(node) => { + const value = Path.basename(node.title); if (node.editing) { return (
e.target.select()} + onFocus={(e) => { + if (value.startsWith(".")) { + e.target.setSelectionRange(0, 0); + } else { + e.target.setSelectionRange(0, value.lastIndexOf(".")); + } + }} onChange={(e) => setNewName(e.target.value)} onPressEnter={() => { if (newName) { @@ -817,14 +837,21 @@ export const Explorer: FC = () => { ); } }} - allowDrop={(options) => { - return !options.dropNode.isLeaf; - }} onDragStart={(e) => { e.event.dataTransfer.setData("explore-file", e.node.path); }} defaultExpandedKeys={[workspace.fileTree.path]} - draggable={newName !== null ? false : { icon: false }} + draggable={ + newName !== null + ? false + : { + icon: false, + nodeDraggable: (node) => { + const fileNode = node as unknown as FileTreeType; + return !!fileNode.children || b3util.isTreeFile(fileNode.path); + }, + } + } expandedKeys={expandedKeys} switcherIcon={} selectedKeys={selectedKeys} diff --git a/src/contexts/workspace-context.ts b/src/contexts/workspace-context.ts index 4a681ed..a5dfd22 100644 --- a/src/contexts/workspace-context.ts +++ b/src/contexts/workspace-context.ts @@ -161,14 +161,12 @@ export type WorkspaceStore = { const loadFileTree = (workdir: string, filename: string) => { const fullpath = fs.realpathSync(`${workdir}/${filename}`); - if (!fs.existsSync(fullpath)) { + + if (!fs.existsSync(fullpath) || filename.endsWith(".DS_Store")) { return; } const stat = fs.statSync(fullpath); - if (!(stat.isDirectory() || fullpath.endsWith(".json"))) { - return; - } const data: FileTreeType = { path: fullpath.replaceAll(Path.sep, "/"), @@ -492,7 +490,7 @@ export const useWorkspace = create((set, get) => ({ let updated = false; allFiles.forEach((file) => (file.exists = false)); const collect = (fileNode?: FileTreeType) => { - if (fileNode?.isLeaf) { + if (fileNode?.isLeaf && b3util.isTreeFile(fileNode.path)) { const path = workspace.relative(fileNode.path); let fileMeta = allFiles.get(path); if (!fileMeta) { diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts index bec3031..b75ac99 100644 --- a/src/misc/b3util.ts +++ b/src/misc/b3util.ts @@ -364,9 +364,9 @@ export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) => return nodeData; }; -export const createNewTree = (filename: string) => { +export const createNewTree = (path: string) => { const tree: TreeModel = { - name: Path.basenameWithoutExt(filename), + name: Path.basenameWithoutExt(path), root: { id: 1, name: "Sequence", @@ -375,6 +375,10 @@ export const createNewTree = (filename: string) => { return tree; }; +export const isTreeFile = (path: string) => { + return path.toLocaleLowerCase().endsWith(".json"); +}; + const isAsciiChar = (c: number) => { return (c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f); };