search node def in title bar

This commit is contained in:
codetypes committed 2024-08-03 00:12:22 +08:00
1 parent 27bf5b98cc
commit d582fadf6a
5 files changed
+90 -34

No files matched your search

+43 -16
View File
@@ -35,6 +35,7 @@ type MenuEvent =
export type NodeTreeType = { export type NodeTreeType = {
title: string; title: string;
path: string;
def?: NodeDef; def?: NodeDef;
icon?: React.ReactNode; icon?: React.ReactNode;
isLeaf?: boolean; isLeaf?: boolean;
@@ -72,7 +73,7 @@ const findParent = (node: FileTreeType, parent?: FileTreeType): FileTreeType | u
} }
}; };
const resolveKeys = (path: string, node: FileTreeType, keys: React.Key[]) => { const resolveKeys = (path: string, node: FileTreeType | NodeTreeType, keys: React.Key[]) => {
if (node.path === path) { if (node.path === path) {
return true; return true;
} }
@@ -274,6 +275,7 @@ export const Explorer: FC = () => {
const workspace = { const workspace = {
close: useWorkspace((state) => state.close), close: useWorkspace((state) => state.close),
editing: useWorkspace((state) => state.editing), editing: useWorkspace((state) => state.editing),
editingNodeDef: useWorkspace((state) => state.editingNodeDef),
editors: useWorkspace((state) => state.editors), editors: useWorkspace((state) => state.editors),
fileTree: useWorkspace((state) => state.fileTree), fileTree: useWorkspace((state) => state.fileTree),
nodeDefs: useWorkspace((state) => state.nodeDefs), nodeDefs: useWorkspace((state) => state.nodeDefs),
@@ -290,6 +292,10 @@ export const Explorer: FC = () => {
const [newName, setNewName] = useState<string | null>(null); const [newName, setNewName] = useState<string | null>(null);
const [contextMenu, setContextMenu] = useState<ItemType[]>([]); const [contextMenu, setContextMenu] = useState<ItemType[]>([]);
const rootNodedefName = "nodeTree.root";
const [selectedNodedefKeys, setSelectedNodedefKeys] = useState<string[]>([]);
const [expandedNodedefKeys, setExpandedNodedefKeys] = useState<React.Key[]>([rootNodedefName]);
if (workspace.fileTree) { if (workspace.fileTree) {
workspace.fileTree.icon = ( workspace.fileTree.icon = (
<Flex justify="center" align="center" style={{ height: "100%" }}> <Flex justify="center" align="center" style={{ height: "100%" }}>
@@ -301,6 +307,7 @@ export const Explorer: FC = () => {
const nodeTree = useMemo(() => { const nodeTree = useMemo(() => {
const data: NodeTreeType = { const data: NodeTreeType = {
title: t("nodeDefinition"), title: t("nodeDefinition"),
path: rootNodedefName,
icon: ( icon: (
<Flex justify="center" align="center" style={{ height: "100%" }}> <Flex justify="center" align="center" style={{ height: "100%" }}>
<PiTreeStructureFill size={19} /> <PiTreeStructureFill size={19} />
@@ -318,6 +325,7 @@ export const Explorer: FC = () => {
const type = getNodeType(nodeDef); const type = getNodeType(nodeDef);
catalog = { catalog = {
title: nodeDef.type, title: nodeDef.type,
path: `nodeTree.catalog.${nodeDef.type}`,
children: [], children: [],
icon: ( icon: (
<Flex justify="center" align="center" style={{ height: "100%" }}> <Flex justify="center" align="center" style={{ height: "100%" }}>
@@ -335,17 +343,14 @@ export const Explorer: FC = () => {
title: `${nodeDef.name}(${nodeDef.desc})`, title: `${nodeDef.name}(${nodeDef.desc})`,
isLeaf: true, isLeaf: true,
def: nodeDef, def: nodeDef,
path: `${nodeDef.name}`,
icon: nodeDef.icon ? ( icon: nodeDef.icon ? (
<Flex justify="center" align="center" style={{ height: "100%" }}> <Flex justify="center" align="center" style={{ height: "100%" }}>
<img <img
className="b3-node-icon" className="b3-node-icon"
key={catalog.title} key={catalog.title}
style={{ width: "13px", height: "13px", color: "white" }} style={{ width: "13px", height: "13px", color: "white" }}
src={ src={`file:///${workspace.workdir}/${nodeDef.icon}`}
process.env.VITE_DEV_SERVER_URL && isMacos
? `${Path.basename(workspace.workdir)}/${nodeDef.icon}`
: `${workspace.workdir}/${nodeDef.icon}`
}
/> />
</Flex> </Flex>
) : ( ) : (
@@ -362,6 +367,7 @@ export const Explorer: FC = () => {
return data; return data;
}, [t, workspace.nodeDefs]); }, [t, workspace.nodeDefs]);
// expand the selected tree
useEffect(() => { useEffect(() => {
if (workspace.editing) { if (workspace.editing) {
const keys: React.Key[] = []; const keys: React.Key[] = [];
@@ -376,6 +382,21 @@ export const Explorer: FC = () => {
} }
}, [workspace.editing]); }, [workspace.editing]);
// expand the selected node def
useEffect(() => {
if (workspace.editingNodeDef) {
const keys: React.Key[] = [];
resolveKeys(workspace.editingNodeDef.data.name, nodeTree, keys);
for (const k of expandedNodedefKeys) {
if (keys.indexOf(k) === -1) {
keys.push(k);
}
}
setExpandedNodedefKeys(keys);
setSelectedNodedefKeys([workspace.editingNodeDef.data.name]);
}
}, [t, workspace.editingNodeDef]);
const keysRef = useHotkeys<HTMLDivElement>( const keysRef = useHotkeys<HTMLDivElement>(
[ [
Hotkey.F2, Hotkey.F2,
@@ -763,6 +784,8 @@ export const Explorer: FC = () => {
tabIndex={-1} tabIndex={-1}
treeData={workspace.fileTree ? [workspace.fileTree] : []} treeData={workspace.fileTree ? [workspace.fileTree] : []}
fieldNames={{ key: "path" }} fieldNames={{ key: "path" }}
expandedKeys={expandedKeys}
selectedKeys={selectedKeys}
onExpand={(keys) => { onExpand={(keys) => {
setExpandedKeys(keys); setExpandedKeys(keys);
}} }}
@@ -840,7 +863,6 @@ export const Explorer: FC = () => {
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]}
draggable={ draggable={
newName !== null newName !== null
? false ? false
@@ -852,17 +874,19 @@ export const Explorer: FC = () => {
}, },
} }
} }
expandedKeys={expandedKeys}
switcherIcon={<DownOutlined />} switcherIcon={<DownOutlined />}
selectedKeys={selectedKeys}
/> />
</div> </div>
</Dropdown> </Dropdown>
<DirectoryTree <DirectoryTree
tabIndex={-1} tabIndex={-1}
fieldNames={{ key: "title" }} fieldNames={{ key: "path" }}
treeData={nodeTree ? [nodeTree] : []} treeData={[nodeTree]}
defaultExpandedKeys={[nodeTree.title]} expandedKeys={expandedNodedefKeys}
selectedKeys={selectedNodedefKeys}
onExpand={(keys) => {
setExpandedNodedefKeys(keys);
}}
draggable={{ icon: false, nodeDraggable: (node) => !!node.isLeaf }} draggable={{ icon: false, nodeDraggable: (node) => !!node.isLeaf }}
titleRender={(node) => ( titleRender={(node) => (
<div style={{ flex: 1, width: 0, minWidth: 0 }}> <div style={{ flex: 1, width: 0, minWidth: 0 }}>
@@ -879,10 +903,13 @@ export const Explorer: FC = () => {
)} )}
onSelect={(_, info) => { onSelect={(_, info) => {
const node = info.node; const node = info.node;
if (node && node.isLeaf) { if (node) {
workspace.onEditingNodeDef({ if (node.isLeaf) {
data: node.def!, workspace.onEditingNodeDef({
}); data: node.def!,
});
}
setSelectedNodedefKeys([node.path]);
} }
}} }}
onDragStart={(e) => { onDragStart={(e) => {
+7 -6
View File
@@ -2,6 +2,7 @@ import { EditNode, EditTree, useWorkspace } from "@/contexts/workspace-context";
import { NodeArg, NodeModel, TreeGraphData } from "@/misc/b3type"; import { NodeArg, NodeModel, TreeGraphData } from "@/misc/b3type";
import { checkOneof } from "@/misc/b3util"; import { checkOneof } from "@/misc/b3util";
import { Hotkey, isMacos } from "@/misc/keys"; import { Hotkey, isMacos } from "@/misc/keys";
import { mergeClassNames } from "@/misc/util";
import { EditOutlined } from "@ant-design/icons"; import { EditOutlined } from "@ant-design/icons";
import { import {
AutoComplete, AutoComplete,
@@ -57,8 +58,8 @@ const TreeInspector: FC = () => {
<span style={{ fontSize: "18px", fontWeight: "600" }}>{t("tree.overview")}</span> <span style={{ fontSize: "18px", fontWeight: "600" }}>{t("tree.overview")}</span>
</div> </div>
<div <div
className={isMacos ? undefined : "b3-overflow"} className={mergeClassNames("b3-inspector-content", isMacos ? "" : "b3-overflow")}
style={{ overflow: "auto", height: "100%", padding: "24px" }} style={{ overflow: "auto", height: "100%" }}
> >
<Form form={form} labelCol={{ span: 8 }} onFinish={finish}> <Form form={form} labelCol={{ span: 8 }} onFinish={finish}>
<Form.Item name="name" label={t("tree.name")}> <Form.Item name="name" label={t("tree.name")}>
@@ -263,8 +264,8 @@ const NodeInspector: FC = () => {
<span style={{ fontSize: "18px", fontWeight: "600" }}>{def.desc}</span> <span style={{ fontSize: "18px", fontWeight: "600" }}>{def.desc}</span>
</div> </div>
<div <div
className={isMacos ? undefined : "b3-overflow"} className={mergeClassNames("b3-inspector-content", isMacos ? "" : "b3-overflow")}
style={{ overflow: "auto", height: "100%", padding: "24px" }} style={{ overflow: "auto", height: "100%" }}
> >
<Form <Form
form={form} form={form}
@@ -549,8 +550,8 @@ const NodeDefInspector: FC = () => {
<span style={{ fontSize: "18px", fontWeight: "600" }}>{t("nodeDefinition")}</span> <span style={{ fontSize: "18px", fontWeight: "600" }}>{t("nodeDefinition")}</span>
</div> </div>
<div <div
className={isMacos ? undefined : "b3-overflow"} className={mergeClassNames("b3-inspector-content", isMacos ? "" : "b3-overflow")}
style={{ overflow: "auto", height: "100%", padding: "24px" }} style={{ overflow: "auto", height: "100%" }}
> >
<Form <Form
form={form} form={form}
+1 -3
View File
@@ -177,9 +177,7 @@ G6.registerNode(
// icon // icon
const img = nodeDef.icon const img = nodeDef.icon
? process.env.VITE_DEV_SERVER_URL && isMacos ? `file:///${workspace.workdir}/${nodeDef.icon}`
? `${Path.basename(workspace.workdir)}/${nodeDef.icon}`
: `${workspace.workdir}/${nodeDef.icon}`
: `./icons/${classify}.svg`; : `./icons/${classify}.svg`;
addShape("image", { addShape("image", {
attrs: { attrs: {
+34 -8
View File
@@ -5,19 +5,23 @@ import { SearchOutlined } from "@ant-design/icons";
import { Button, Flex, LayoutProps, Select } from "antd"; import { Button, Flex, LayoutProps, Select } from "antd";
import React, { FC, useMemo } from "react"; import React, { FC, useMemo } from "react";
import { Menu } from "./menu"; import { Menu } from "./menu";
import { NodeDef } from "@/misc/b3type";
interface OptionType { interface OptionType {
label: string | React.ReactNode; label: string | React.ReactNode;
value: string; value: string;
path: string; path?: string;
nodeDef?: NodeDef;
} }
export const TitleBar: FC<LayoutProps> = () => { export const TitleBar: FC<LayoutProps> = () => {
const workspace = { const workspace = {
allFiles: useWorkspace((state) => state.allFiles), allFiles: useWorkspace((state) => state.allFiles),
fileTree: useWorkspace((state) => state.fileTree), fileTree: useWorkspace((state) => state.fileTree),
nodeDefs: useWorkspace((state) => state.nodeDefs),
isShowingSearch: useWorkspace((state) => state.isShowingSearch), isShowingSearch: useWorkspace((state) => state.isShowingSearch),
onShowingSearch: useWorkspace((state) => state.onShowingSearch), onShowingSearch: useWorkspace((state) => state.onShowingSearch),
onEditingNodeDef: useWorkspace((state) => state.onEditingNodeDef),
open: useWorkspace((state) => state.open), open: useWorkspace((state) => state.open),
name: useWorkspace((state) => state.path), name: useWorkspace((state) => state.path),
relative: useWorkspace((state) => state.relative), relative: useWorkspace((state) => state.relative),
@@ -41,7 +45,23 @@ export const TitleBar: FC<LayoutProps> = () => {
path: file.path, path: file.path,
}); });
}); });
options.sort((a, b) => a.path.localeCompare(b.path)); workspace.nodeDefs.forEach((def) => {
const value = def.name;
const desc = def.desc ?? "";
options.push({
label: (
<div>
{value}
<span> </span>
<span style={{ color: "gray", fontSize: "12px" }}>
{value} {desc}
</span>
</div>
),
value: `${value.toLocaleLowerCase()} ${desc.toLocaleLowerCase()}`,
nodeDef: def,
});
});
return options; return options;
}, [workspace.allFiles, workspace.fileTree]); }, [workspace.allFiles, workspace.fileTree]);
return ( return (
@@ -103,17 +123,23 @@ export const TitleBar: FC<LayoutProps> = () => {
onChange={(_, option) => { onChange={(_, option) => {
if (!(option instanceof Array)) { if (!(option instanceof Array)) {
workspace.onShowingSearch(false); workspace.onShowingSearch(false);
workspace.open(option.path); if (option.path) {
workspace.open(option.path);
} else if (option.nodeDef) {
workspace.onEditingNodeDef({
data: option.nodeDef,
});
}
} }
}} }}
filterOption={(input, option) => filterOption={(input, option) =>
(option?.value ?? "").includes(input.toLocaleLowerCase()) (option?.value ?? "").includes(input.toLocaleLowerCase())
} }
filterSort={(optionA, optionB) => // filterSort={(optionA, optionB) =>
(optionA?.value ?? "") // (optionA?.value ?? "")
.toLowerCase() // .toLowerCase()
.localeCompare((optionB?.value ?? "").toLowerCase()) // .localeCompare((optionB?.value ?? "").toLowerCase())
} // }
options={searchOptions} options={searchOptions}
/> />
)} )}
+5 -1
View File
@@ -107,8 +107,12 @@ body {
width: 12px !important; width: 12px !important;
} }
.b3-inspector-content {
padding: 20px;
}
.b3-inspector .ant-form-item { .b3-inspector .ant-form-item {
margin-bottom: 16px; margin-bottom: 10px;
} }
.b3-inspector .ant-form-item-label { .b3-inspector .ant-form-item-label {