support search node
This commit is contained in:
1 parent
7dbea171bb
commit
82d1d89311
6 files changed
+112
-8
No files matched your search
+104
-2
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user