search in path or args

This commit is contained in:
codetypes committed 2024-04-28 22:24:25 +08:00
1 parent f09fd55343
commit 098894b63f
1 file changed
+83 -49
+83 -49
View File
@@ -34,6 +34,14 @@ export interface EditorProps extends React.HTMLAttributes<HTMLElement> {
onUpdate: () => void; onUpdate: () => void;
} }
interface FilterOption {
results: string[];
index: number;
filterStr: string;
filterCase: boolean;
filterFocus: boolean;
}
const createMenu = () => { const createMenu = () => {
const t = i18n.t; const t = i18n.t;
const MenuItem: FC<FlexProps> = (itemProps) => { const MenuItem: FC<FlexProps> = (itemProps) => {
@@ -129,24 +137,27 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
const { t } = useTranslation(); const { t } = useTranslation();
const menuItems = useMemo(() => createMenu(), [t]); const menuItems = useMemo(() => createMenu(), [t]);
const [results, setResults] = useState<string[]>([]);
const [resultIndex, setResultIndex] = useState(0);
const [searchString, setSearchString] = useState("");
const [showingSearch, setShowingSearch] = useState(false); const [showingSearch, setShowingSearch] = useState(false);
const [filterCase, setFilterCase] = useState(false); const [filterOption, setFilterOption] = useState<FilterOption>({
const [filterFocus, setFilterFocus] = useState(false); results: [],
index: 0,
filterStr: "",
filterCase: false,
filterFocus: false,
});
const onSearchChange = useDebounceCallback((filter: string) => { const onSearchChange = useDebounceCallback((option: FilterOption) => {
const ids = filterNodes(filter, findDataById("1")); option.results.length = 0;
setSearchString(filter); filterNodes(option, findDataById("1"));
setResults(ids); setFilterOption({
setResultIndex(0); ...option,
if (ids.length > 0) { index: 0,
editor.graph.focusItem(ids[0]); });
selectNode(ids[0]); if (option.results.length > 0) {
} editor.graph.focusItem(option.results[0]);
if (filter.length == 0) { selectNode(option.results[0]);
restoreViewport(); } else {
selectNode(null);
} }
}, 200); }, 200);
@@ -202,39 +213,48 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
return editor.graph.findDataById(id) as TreeGraphData; return editor.graph.findDataById(id) as TreeGraphData;
}; };
const includeString = (content: string | undefined, search: string) => { const includeString = (content: string | undefined, option: FilterOption) => {
if (!content) { if (!content) {
return false; return false;
} else if (filterCase) { } else if (option.filterCase) {
return content.includes(search); return content.includes(option.filterStr);
} else { } else {
return content.toLowerCase().includes(search.toLowerCase()); return content.toLowerCase().includes(option.filterStr.toLowerCase());
} }
}; };
const filterNodes = (search: string, node: TreeGraphData | null, ids?: string[]): string[] => { const filterNodes = (option: FilterOption, node: TreeGraphData | null) => {
ids ||= []; if (node && option.filterStr) {
if (node && search) { if (includeString(node.name, option) || includeString(node.desc || node.def.desc, option)) {
if (includeString(node.name, search) || includeString(node.desc || node.def.desc, search)) { option.results.push(node.id);
ids.push(node.id);
} else if (node.input) { } else if (node.input) {
for (const str of node.input) { for (const str of node.input) {
if (includeString(str, search)) { if (includeString(str, option)) {
ids.push(node.id); option.results.push(node.id);
break;
}
}
} else if (node.args) {
for (const str in node.args) {
if (includeString(str, option)) {
option.results.push(node.id);
break; break;
} }
} }
} else if (node.output) { } else if (node.output) {
for (const str of node.output) { for (const str of node.output) {
if (includeString(str, search)) { if (includeString(str, option)) {
ids.push(node.id); option.results.push(node.id);
break; break;
} }
} }
} else if (node.path) {
if (includeString(node.path, option)) {
option.results.push(node.id);
}
} }
node.children?.forEach((child: TreeGraphData) => filterNodes(search, child, ids)); node.children?.forEach((child: TreeGraphData) => filterNodes(option, child));
} }
return ids;
}; };
const isAncestor = (ancestor: TreeGraphData, node: TreeGraphData): boolean => { const isAncestor = (ancestor: TreeGraphData, node: TreeGraphData): boolean => {
@@ -1025,21 +1045,22 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
}, [t]); }, [t]);
const nextResult = () => { const nextResult = () => {
const { results, index } = filterOption;
if (results.length > 0) { if (results.length > 0) {
const idx = (resultIndex + 1) % results.length; const idx = (index + 1) % results.length;
setResultIndex(idx);
editor.graph.focusItem(results[idx]); editor.graph.focusItem(results[idx]);
selectNode(results[idx]); selectNode(results[idx]);
setFilterOption({ ...filterOption, index: idx });
} }
}; };
const prevResult = () => { const prevResult = () => {
const { results, index } = filterOption;
if (results.length > 0) { if (results.length > 0) {
const idx = (resultIndex + results.length - 1) % results.length; const idx = (index + results.length - 1) % results.length;
setResultIndex(idx);
console.log(idx, results[idx]);
editor.graph.focusItem(results[idx]); editor.graph.focusItem(results[idx]);
selectNode(results[idx]); selectNode(results[idx]);
setFilterOption({ ...filterOption, index: idx });
} }
}; };
@@ -1080,7 +1101,12 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
paddingBottom: "1px", paddingBottom: "1px",
paddingRight: "2px", paddingRight: "2px",
}} }}
onChange={(e) => onSearchChange(e.currentTarget.value)} onChange={(e) =>
onSearchChange({
...filterOption,
filterStr: e.currentTarget.value,
})
}
onKeyDown={(e) => e.code === Hotkey.Enter && nextResult()} onKeyDown={(e) => e.code === Hotkey.Enter && nextResult()}
suffix={ suffix={
<Flex gap="2px" style={{ alignItems: "center" }}> <Flex gap="2px" style={{ alignItems: "center" }}>
@@ -1089,36 +1115,45 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
size="small" size="small"
className={mergeClassNames( className={mergeClassNames(
"b3-editor-button-filter", "b3-editor-button-filter",
filterCase && "b3-editor-button-filter-selected" filterOption.filterCase && "b3-editor-button-filter-selected"
)} )}
icon={<VscCaseSensitive style={{ width: "18px", height: "18px" }} />} icon={<VscCaseSensitive style={{ width: "18px", height: "18px" }} />}
onClick={() => { onClick={() =>
setFilterCase(!filterCase); onSearchChange({
onSearchChange(searchString); ...filterOption,
}} filterCase: !filterOption.filterCase,
})
}
/> />
<Button <Button
type="text" type="text"
size="small" size="small"
className={mergeClassNames( className={mergeClassNames(
"b3-editor-button-filter", "b3-editor-button-filter",
filterFocus && "b3-editor-button-filter-selected" filterOption.filterFocus && "b3-editor-button-filter-selected"
)} )}
icon={<RiFocus3Line />} icon={<RiFocus3Line />}
onClick={() => setFilterFocus(!filterFocus)} onClick={() =>
onSearchChange({
...filterOption,
filterFocus: !filterOption.filterFocus,
})
}
/> />
</Flex> </Flex>
} }
/> />
<div style={{ padding: "0 10px 0 5px", minWidth: "40px" }}> <div style={{ padding: "0 10px 0 5px", minWidth: "40px" }}>
{results.length ? `${resultIndex + 1}/${results.length}` : ""} {filterOption.results.length
? `${filterOption.index + 1}/${filterOption.results.length}`
: ""}
</div> </div>
<Button <Button
icon={<ArrowDownOutlined />} icon={<ArrowDownOutlined />}
type="text" type="text"
size="small" size="small"
style={{ width: "30px" }} style={{ width: "30px" }}
disabled={results.length == 0} disabled={filterOption.results.length == 0}
onClick={nextResult} onClick={nextResult}
/> />
<Button <Button
@@ -1126,7 +1161,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
type="text" type="text"
size="small" size="small"
style={{ width: "30px" }} style={{ width: "30px" }}
disabled={results.length == 0} disabled={filterOption.results.length == 0}
onClick={prevResult} onClick={prevResult}
/> />
<Button <Button
@@ -1136,8 +1171,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
style={{ width: "30px" }} style={{ width: "30px" }}
onClick={() => { onClick={() => {
setShowingSearch(false); setShowingSearch(false);
setResults([]); setFilterOption({ ...filterOption, results: [], index: 0 });
setResultIndex(0);
keysRef.current?.focus(); keysRef.current?.focus();
}} }}
/> />