support search node

This commit is contained in:
codetypes committed 2024-04-26 09:37:49 +08:00
1 parent 7dbea171bb
commit 82d1d89311
6 files changed
+112 -8

No files matched your search

+104 -2
View File
@@ -10,17 +10,19 @@ import * as b3util from "@/misc/b3util";
import { message } from "@/misc/hooks";
import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys";
import Path from "@/misc/path";
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined } from "@ant-design/icons";
import G6, { G6GraphEvent, Item, TreeGraph } from "@antv/g6";
import { dialog } from "@electron/remote";
import { useSize } from "ahooks";
import { Dropdown, Flex, FlexProps, MenuProps } from "antd";
import { Button, Dropdown, Flex, FlexProps, Input, MenuProps } from "antd";
import { clipboard } from "electron";
import * as fs from "fs";
import React, { FC, useCallback, useEffect, useMemo, useRef } from "react";
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { FiDelete } from "react-icons/fi";
import { IoMdReturnLeft } from "react-icons/io";
import { mergeRefs } from "react-merge-refs";
import { useDebounceCallback } from "usehooks-ts";
import "./register-node";
export interface EditorProps extends React.HTMLAttributes<HTMLElement> {
@@ -36,11 +38,27 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
open: useWorkspace((state) => state.open),
workdir: useWorkspace((state) => state.workdir),
};
const [results, setResults] = useState<string[]>([]);
const [resultIndex, setResultIndex] = useState(0);
const [showingSearch, setShowingSearch] = useState(false);
const graphRef = useRef(null);
const sizeRef = useRef(null);
const editorSize = useSize(sizeRef);
const { t } = useTranslation();
const onSearchChange = useDebounceCallback((filter: string) => {
const ids = filterNodes(filter, findDataById("1"));
setResults(ids);
setResultIndex(0);
if (ids.length > 0) {
editor.graph.focusItem(ids[0]);
selectNode(ids[0]);
}
if (filter.length == 0) {
restoreViewport();
}
}, 200);
const onChange = () => {
if (!editor.unsave) {
editor.unsave = true;
@@ -93,6 +111,17 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
return editor.graph.findDataById(id) as TreeGraphData;
};
const filterNodes = (str: string, node: TreeGraphData | null, ids?: string[]): string[] => {
ids ||= [];
if (node && str) {
if (node.name.toLocaleLowerCase().indexOf(str) >= 0) {
ids.push(node.id);
}
node.children?.forEach((child: TreeGraphData) => filterNodes(str, child, ids));
}
return ids;
};
const isAncestor = (ancestor: TreeGraphData, node: TreeGraphData): boolean => {
if (ancestor.id === node.parent) {
return true;
@@ -761,6 +790,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
Hotkey.Delete,
Hotkey.Enter,
Hotkey.Backspace,
Hotkey.SearchNode,
],
(event) => {
event.preventDefault();
@@ -778,6 +808,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
createNode();
} else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.Backspace)) {
deleteNode();
} else if (isHotkeyPressed(Hotkey.SearchNode)) {
setShowingSearch(true);
}
}
);
@@ -967,6 +999,76 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
tabIndex={-1}
style={{ maxWidth: "inherit", maxHeight: "inherit" }}
>
{showingSearch && (
<Flex
style={{
position: "absolute",
marginTop: "10px",
marginLeft: "10px",
backgroundColor: "#161b22",
padding: "4px 10px 4px 10px",
borderRadius: "4px",
borderLeft: "4px solid #f78166",
boxShadow: "0 0 8px 2px #0000005c",
alignItems: "center",
}}
>
<Input
autoFocus
allowClear
size="small"
style={{ borderRadius: "2px" }}
onChange={(e) => onSearchChange(e.currentTarget.value)}
/>
<div style={{ padding: "0 10px 0 5px" }}>
{results.length ? `${resultIndex + 1}/${results.length}` : ""}
</div>
<Button
icon={<ArrowDownOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
disabled={results.length == 0}
onClick={() => {
if (results.length > 0) {
const idx = (resultIndex + 1) % results.length;
setResultIndex(idx);
editor.graph.focusItem(results[idx]);
selectNode(results[idx]);
}
}}
/>
<Button
icon={<ArrowUpOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
disabled={results.length == 0}
onClick={() => {
if (results.length > 0) {
const idx = (resultIndex + results.length - 1) % results.length;
setResultIndex(idx);
console.log(idx, results[idx]);
editor.graph.focusItem(results[idx]);
selectNode(results[idx]);
}
}}
/>
<Button
icon={<CloseOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
onClick={() => {
setShowingSearch(false);
setResults([]);
setResultIndex(0);
keysRef.current?.focus();
}}
/>
</Flex>
)}
<Dropdown menu={{ items: menuItems, onClick }} trigger={["contextMenu"]}>
<div tabIndex={-1} style={{ width: "100%", height: "100%" }} ref={graphRef} />
</Dropdown>
+2 -2
View File
@@ -44,7 +44,7 @@ export const Workspace: FC = () => {
const { width = 0, height = 0 } = useWindowSize();
const keysRef = useHotkeys<HTMLDivElement>(
[Hotkey.Save, Hotkey.CloseEditor, Hotkey.Search, Hotkey.Build],
[Hotkey.Save, Hotkey.CloseEditor, Hotkey.SearchTree, Hotkey.Build],
(event) => {
if (isHotkeyPressed(Hotkey.Save)) {
event.preventDefault();
@@ -58,7 +58,7 @@ export const Workspace: FC = () => {
workspace.close(workspace.editing.path);
}
}
} else if (isHotkeyPressed(Hotkey.Search)) {
} else if (isHotkeyPressed(Hotkey.SearchTree)) {
event.preventDefault();
workspace.onShowingSearch(true);
} else if (isHotkeyPressed(Hotkey.Build)) {