show all files in explore

This commit is contained in:
codetypes committed 2024-05-19 22:27:58 +08:00
1 parent fa5644fda7
commit 9e71989ff5
3 files changed
+216 -187

No files matched your search

+207 -180
View File
@@ -2,6 +2,7 @@ import { FileTreeType, useWorkspace } from "@/contexts/workspace-context";
import { NodeDef, getNodeType } from "@/misc/b3type"; import { NodeDef, getNodeType } from "@/misc/b3type";
import * as b3util from "@/misc/b3util"; import * as b3util from "@/misc/b3util";
import { modal } from "@/misc/hooks"; import { modal } from "@/misc/hooks";
import i18n from "@/misc/i18n";
import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys"; import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys";
import Path from "@/misc/path"; import Path from "@/misc/path";
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
@@ -115,6 +116,160 @@ const renameFile = (oldPath: string, newPath: string) => {
return false; return false;
}; };
// file context menu
const createFileContextMenu = (node: FileTreeType) => {
const isTreeFile = b3util.isTreeFile(node.path);
const MenuItem: FC<FlexProps> = (itemProps) => {
return (
<Flex
gap="50px"
style={{ minWidth: "200px", justifyContent: "space-between", alignItems: "center" }}
{...itemProps}
></Flex>
);
};
const arr: MenuProps["items"] = [
{
disabled: !isTreeFile,
label: (
<MenuItem>
<div>{i18n.t("open")}</div>
</MenuItem>
),
key: "open",
},
{
disabled: !isTreeFile,
label: (
<MenuItem>
<div>{i18n.t("copy")}</div>
<div>{isMacos ? "⌘ C" : "Ctrl+C"}</div>
</MenuItem>
),
key: "copy",
},
{
disabled: !isTreeFile,
label: (
<MenuItem>
<div>{i18n.t("duplicate")}</div>
<div>{isMacos ? "⌘ D" : "Ctrl+D"}</div>
</MenuItem>
),
key: "duplicate",
},
{
label: (
<MenuItem>
<div>{isMacos ? i18n.t("revealFileOnMac") : i18n.t("revealFileOnWindows")}</div>
</MenuItem>
),
key: "revealFile",
},
{
label: (
<MenuItem>
<div>{i18n.t("rename")}</div>
{isMacos && <IoMdReturnLeft />}
{!isMacos && <div>F2</div>}
</MenuItem>
),
key: "rename",
},
{
label: (
<MenuItem>
<div>{i18n.t("delete")}</div>
{isMacos && (
<Space size={6}>
<FiCommand />
<FiDelete />
</Space>
)}
</MenuItem>
),
key: "delete",
},
];
return arr;
};
// folder context menu
const createFolderContextMenu = (copiedPath: string) => {
const MenuItem: FC<FlexProps> = (itemProps) => {
return (
<Flex
gap="50px"
style={{ minWidth: "200px", justifyContent: "space-between", alignItems: "center" }}
{...itemProps}
></Flex>
);
};
const arr: MenuProps["items"] = [
{
label: (
<MenuItem>
<div>{i18n.t("newFile")}</div>
</MenuItem>
),
key: "newFile",
},
{
label: (
<MenuItem>
<div>{i18n.t("newFolder")}</div>
</MenuItem>
),
key: "newFolder",
},
{
label: (
<MenuItem>
<div>{isMacos ? i18n.t("revealFileOnMac") : i18n.t("revealFileOnWindows")}</div>
</MenuItem>
),
key: "revealFile",
},
{
disabled: !copiedPath,
label: (
<MenuItem>
<div>{i18n.t("paste")}</div>
<div>{isMacos ? "⌘ V" : "Ctrl+V"}</div>
</MenuItem>
),
key: "paste",
},
{
label: (
<MenuItem>
<div>{i18n.t("rename")}</div>
{isMacos && <IoMdReturnLeft />}
{!isMacos && <div>F2</div>}
</MenuItem>
),
key: "rename",
},
{
label: (
<MenuItem>
<div>{i18n.t("delete")}</div>
{isMacos && (
<Space size={6}>
<FiCommand />
<FiDelete />
</Space>
)}
</MenuItem>
),
key: "delete",
},
];
return arr;
};
export const Explorer: FC = () => { export const Explorer: FC = () => {
const workspace = { const workspace = {
close: useWorkspace((state) => state.close), close: useWorkspace((state) => state.close),
@@ -237,8 +392,7 @@ export const Explorer: FC = () => {
const node = findFile(selectedKeys[0], workspace.fileTree!); const node = findFile(selectedKeys[0], workspace.fileTree!);
if (isHotkeyPressed(Hotkey.F2) || (isMacos && isHotkeyPressed(Hotkey.Enter))) { if (isHotkeyPressed(Hotkey.F2) || (isMacos && isHotkeyPressed(Hotkey.Enter))) {
if (node && node !== workspace.fileTree) { if (node && node !== workspace.fileTree) {
node.editing = true; dispatch("rename", node);
setNewName("");
} }
} else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.MacDelete)) { } else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.MacDelete)) {
if (node && node !== workspace.fileTree) { if (node && node !== workspace.fileTree) {
@@ -283,12 +437,12 @@ export const Explorer: FC = () => {
node.title = Path.basename(node.path); node.title = Path.basename(node.path);
fs.mkdirSync(node.path); fs.mkdirSync(node.path);
} else if (node.path.endsWith("/.json")) { } 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); node.title = Path.basename(node.path);
fs.writeFileSync(node.path, JSON.stringify(b3util.createNewTree(node.title), null, 2)); fs.writeFileSync(node.path, JSON.stringify(b3util.createNewTree(node.title), null, 2));
workspace.open(node.path); workspace.open(node.path);
} else { } else {
const newpath = Path.dirname(node.path) + "/" + newName + Path.extname(node.path); const newpath = Path.dirname(node.path) + "/" + newName;
if (renameFile(node.path, newpath)) { if (renameFile(node.path, newpath)) {
setSelectedKeys([newpath]); setSelectedKeys([newpath]);
} }
@@ -300,7 +454,9 @@ export const Explorer: FC = () => {
const dispatch = (event: MenuEvent, node: FileTreeType, dest?: FileTreeType) => { const dispatch = (event: MenuEvent, node: FileTreeType, dest?: FileTreeType) => {
switch (event) { switch (event) {
case "open": { case "open": {
workspace.open(node.path); if (b3util.isTreeFile(node.path)) {
workspace.open(node.path);
}
break; break;
} }
case "newFolder": { case "newFolder": {
@@ -320,7 +476,7 @@ export const Explorer: FC = () => {
case "newFile": { case "newFile": {
const folderNode: FileTreeType = { const folderNode: FileTreeType = {
path: node.path + "/.json", path: node.path + "/.json",
title: "", title: ".json",
isLeaf: true, isLeaf: true,
editing: true, editing: true,
}; };
@@ -385,17 +541,21 @@ export const Explorer: FC = () => {
break; break;
} }
case "copy": { case "copy": {
setCopyFile(node.path); if (b3util.isTreeFile(node.path)) {
setCopyFile(node.path);
}
break; break;
} }
case "duplicate": { case "duplicate": {
for (let i = 1; ; i++) { if (b3util.isTreeFile(node.path)) {
const dupName = Path.basenameWithoutExt(node.path) + " " + i + ".json"; for (let i = 1; ; i++) {
const dupPath = Path.dirname(node.path) + "/" + dupName; const dupName = Path.basenameWithoutExt(node.path) + " " + i + ".json";
if (!fs.existsSync(dupPath)) { const dupPath = Path.dirname(node.path) + "/" + dupName;
fs.copyFileSync(node.path, dupPath); if (!fs.existsSync(dupPath)) {
setSelectedKeys([dupPath]); fs.copyFileSync(node.path, dupPath);
break; setSelectedKeys([dupPath]);
break;
}
} }
} }
break; break;
@@ -491,12 +651,16 @@ export const Explorer: FC = () => {
} }
case "move": { case "move": {
try { 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 = () => { const doMove = () => {
fs.renameSync(node.path, newPath); fs.renameSync(node.path, newPath);
for (const editor of workspace.editors) { for (const editor of workspace.editors) {
if (editor.path.startsWith(node.path)) { 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); console.log("editor move", editor.path === newPath, editor.path, newPath);
if (editor.path.startsWith(newPath)) { if (editor.path.startsWith(newPath)) {
@@ -554,161 +718,15 @@ export const Explorer: FC = () => {
ipcRenderer.invoke("showItemInFolder", node.path); ipcRenderer.invoke("showItemInFolder", node.path);
break; break;
case "rename": { case "rename": {
node.editing = true; if (b3util.isTreeFile(node.path)) {
setNewName(""); node.editing = true;
setNewName("");
}
break; break;
} }
} }
}; };
// context menu
const fileContextMenu = useMemo(() => {
const MenuItem: FC<FlexProps> = (itemProps) => {
return (
<Flex
gap="50px"
style={{ minWidth: "200px", justifyContent: "space-between", alignItems: "center" }}
{...itemProps}
></Flex>
);
};
const arr: MenuProps["items"] = [
{
label: (
<MenuItem>
<div>{t("open")}</div>
</MenuItem>
),
key: "open",
},
{
label: (
<MenuItem>
<div>{t("copy")}</div>
<div>{isMacos ? "⌘ C" : "Ctrl+C"}</div>
</MenuItem>
),
key: "copy",
},
{
label: (
<MenuItem>
<div>{t("duplicate")}</div>
<div>{isMacos ? "⌘ D" : "Ctrl+D"}</div>
</MenuItem>
),
key: "duplicate",
},
{
label: (
<MenuItem>
<div>{isMacos ? t("revealFileOnMac") : t("revealFileOnWindows")}</div>
</MenuItem>
),
key: "revealFile",
},
{
label: (
<MenuItem>
<div>{t("rename")}</div>
{isMacos && <IoMdReturnLeft />}
{!isMacos && <div>F2</div>}
</MenuItem>
),
key: "rename",
},
{
label: (
<MenuItem>
<div>{t("delete")}</div>
{isMacos && (
<Space size={6}>
<FiCommand />
<FiDelete />
</Space>
)}
</MenuItem>
),
key: "delete",
},
];
return arr;
}, [t]);
const directoryContextMenu = useMemo(() => {
const MenuItem: FC<FlexProps> = (itemProps) => {
return (
<Flex
gap="50px"
style={{ minWidth: "200px", justifyContent: "space-between", alignItems: "center" }}
{...itemProps}
></Flex>
);
};
const arr: MenuProps["items"] = [
{
label: (
<MenuItem>
<div>{t("newFile")}</div>
</MenuItem>
),
key: "newFile",
},
{
label: (
<MenuItem>
<div>{t("newFolder")}</div>
</MenuItem>
),
key: "newFolder",
},
{
label: (
<MenuItem>
<div>{isMacos ? t("revealFileOnMac") : t("revealFileOnWindows")}</div>
</MenuItem>
),
key: "revealFile",
},
{
label: (
<MenuItem>
<div style={{ color: copyFile ? "inherit" : "gray" }}>{t("paste")}</div>
<div style={{ color: copyFile ? "inherit" : "gray" }}>{isMacos ? "⌘ V" : "Ctrl+V"}</div>
</MenuItem>
),
key: "paste",
},
{
label: (
<MenuItem>
<div>{t("rename")}</div>
{isMacos && <IoMdReturnLeft />}
{!isMacos && <div>F2</div>}
</MenuItem>
),
key: "rename",
},
{
label: (
<MenuItem>
<div>{t("delete")}</div>
{isMacos && (
<Space size={6}>
<FiCommand />
<FiDelete />
</Space>
)}
</MenuItem>
),
key: "delete",
},
];
return arr;
}, [t, copyFile]);
const onClick = (info: MenuInfo) => { const onClick = (info: MenuInfo) => {
const node = findFile(selectedKeys[0], workspace.fileTree!) ?? workspace.fileTree; const node = findFile(selectedKeys[0], workspace.fileTree!) ?? workspace.fileTree;
if (node) { if (node) {
@@ -728,7 +746,7 @@ export const Explorer: FC = () => {
tabIndex={-1} tabIndex={-1}
style={{ height: "100%" }} style={{ height: "100%" }}
onContextMenuCapture={() => { onContextMenuCapture={() => {
setContextMenu(directoryContextMenu); setContextMenu(createFolderContextMenu(copyFile));
}} }}
> >
<div style={{ padding: "12px 24px" }}> <div style={{ padding: "12px 24px" }}>
@@ -750,34 +768,36 @@ export const Explorer: FC = () => {
}} }}
onRightClick={(info) => { onRightClick={(info) => {
if (info.node.isLeaf) { if (info.node.isLeaf) {
setContextMenu(fileContextMenu); setContextMenu(createFileContextMenu(info.node));
} }
setSelectedKeys([info.node.path]); setSelectedKeys([info.node.path]);
}} }}
onSelect={(_, info) => { onSelect={(_, info) => {
const node = info.selectedNodes.at(0); const node = info.selectedNodes.at(0);
if (node) { if (node) {
if (!node.children) { dispatch("open", node);
workspace.open(node.path);
}
setSelectedKeys([node.path]); setSelectedKeys([node.path]);
} }
if (node && !node.children) {
workspace.open(node.path);
}
}} }}
onDrop={(info) => { onDrop={(info) => {
dispatch("move", info.dragNode, info.node); dispatch("move", info.dragNode, info.node);
}} }}
titleRender={(node) => { titleRender={(node) => {
const value = Path.basename(node.title);
if (node.editing) { if (node.editing) {
return ( return (
<div style={{ display: "inline-flex" }}> <div style={{ display: "inline-flex" }}>
<Input <Input
defaultValue={Path.basenameWithoutExt(node.title)} defaultValue={value}
autoFocus autoFocus
style={{ padding: "0px 0px", borderRadius: "2px" }} style={{ padding: "0px 0px", borderRadius: "2px" }}
onFocus={(e) => 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)} onChange={(e) => setNewName(e.target.value)}
onPressEnter={() => { onPressEnter={() => {
if (newName) { if (newName) {
@@ -817,14 +837,21 @@ export const Explorer: FC = () => {
); );
} }
}} }}
allowDrop={(options) => {
return !options.dropNode.isLeaf;
}}
onDragStart={(e) => { onDragStart={(e) => {
e.event.dataTransfer.setData("explore-file", e.node.path); e.event.dataTransfer.setData("explore-file", e.node.path);
}} }}
defaultExpandedKeys={[workspace.fileTree.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} expandedKeys={expandedKeys}
switcherIcon={<DownOutlined />} switcherIcon={<DownOutlined />}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
+3 -5
View File
@@ -161,14 +161,12 @@ export type WorkspaceStore = {
const loadFileTree = (workdir: string, filename: string) => { const loadFileTree = (workdir: string, filename: string) => {
const fullpath = fs.realpathSync(`${workdir}/${filename}`); const fullpath = fs.realpathSync(`${workdir}/${filename}`);
if (!fs.existsSync(fullpath)) {
if (!fs.existsSync(fullpath) || filename.endsWith(".DS_Store")) {
return; return;
} }
const stat = fs.statSync(fullpath); const stat = fs.statSync(fullpath);
if (!(stat.isDirectory() || fullpath.endsWith(".json"))) {
return;
}
const data: FileTreeType = { const data: FileTreeType = {
path: fullpath.replaceAll(Path.sep, "/"), path: fullpath.replaceAll(Path.sep, "/"),
@@ -492,7 +490,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
let updated = false; let updated = false;
allFiles.forEach((file) => (file.exists = false)); allFiles.forEach((file) => (file.exists = false));
const collect = (fileNode?: FileTreeType) => { const collect = (fileNode?: FileTreeType) => {
if (fileNode?.isLeaf) { if (fileNode?.isLeaf && b3util.isTreeFile(fileNode.path)) {
const path = workspace.relative(fileNode.path); const path = workspace.relative(fileNode.path);
let fileMeta = allFiles.get(path); let fileMeta = allFiles.get(path);
if (!fileMeta) { if (!fileMeta) {
+6 -2
View File
@@ -364,9 +364,9 @@ export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) =>
return nodeData; return nodeData;
}; };
export const createNewTree = (filename: string) => { export const createNewTree = (path: string) => {
const tree: TreeModel = { const tree: TreeModel = {
name: Path.basenameWithoutExt(filename), name: Path.basenameWithoutExt(path),
root: { root: {
id: 1, id: 1,
name: "Sequence", name: "Sequence",
@@ -375,6 +375,10 @@ export const createNewTree = (filename: string) => {
return tree; return tree;
}; };
export const isTreeFile = (path: string) => {
return path.toLocaleLowerCase().endsWith(".json");
};
const isAsciiChar = (c: number) => { const isAsciiChar = (c: number) => {
return (c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f); return (c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f);
}; };