diff --git a/src/components/editor.tsx b/src/components/editor.tsx index 8c443be..7b1e84f 100644 --- a/src/components/editor.tsx +++ b/src/components/editor.tsx @@ -34,6 +34,14 @@ export interface EditorProps extends React.HTMLAttributes { onUpdate: () => void; } +interface FilterOption { + results: string[]; + index: number; + filterStr: string; + filterCase: boolean; + filterFocus: boolean; +} + const createMenu = () => { const t = i18n.t; const MenuItem: FC = (itemProps) => { @@ -129,24 +137,27 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const { t } = useTranslation(); const menuItems = useMemo(() => createMenu(), [t]); - const [results, setResults] = useState([]); - const [resultIndex, setResultIndex] = useState(0); - const [searchString, setSearchString] = useState(""); const [showingSearch, setShowingSearch] = useState(false); - const [filterCase, setFilterCase] = useState(false); - const [filterFocus, setFilterFocus] = useState(false); + const [filterOption, setFilterOption] = useState({ + results: [], + index: 0, + filterStr: "", + filterCase: false, + filterFocus: false, + }); - const onSearchChange = useDebounceCallback((filter: string) => { - const ids = filterNodes(filter, findDataById("1")); - setSearchString(filter); - setResults(ids); - setResultIndex(0); - if (ids.length > 0) { - editor.graph.focusItem(ids[0]); - selectNode(ids[0]); - } - if (filter.length == 0) { - restoreViewport(); + const onSearchChange = useDebounceCallback((option: FilterOption) => { + option.results.length = 0; + filterNodes(option, findDataById("1")); + setFilterOption({ + ...option, + index: 0, + }); + if (option.results.length > 0) { + editor.graph.focusItem(option.results[0]); + selectNode(option.results[0]); + } else { + selectNode(null); } }, 200); @@ -202,39 +213,48 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . return editor.graph.findDataById(id) as TreeGraphData; }; - const includeString = (content: string | undefined, search: string) => { + const includeString = (content: string | undefined, option: FilterOption) => { if (!content) { return false; - } else if (filterCase) { - return content.includes(search); + } else if (option.filterCase) { + return content.includes(option.filterStr); } else { - return content.toLowerCase().includes(search.toLowerCase()); + return content.toLowerCase().includes(option.filterStr.toLowerCase()); } }; - const filterNodes = (search: string, node: TreeGraphData | null, ids?: string[]): string[] => { - ids ||= []; - if (node && search) { - if (includeString(node.name, search) || includeString(node.desc || node.def.desc, search)) { - ids.push(node.id); + const filterNodes = (option: FilterOption, node: TreeGraphData | null) => { + if (node && option.filterStr) { + if (includeString(node.name, option) || includeString(node.desc || node.def.desc, option)) { + option.results.push(node.id); } else if (node.input) { for (const str of node.input) { - if (includeString(str, search)) { - ids.push(node.id); + if (includeString(str, option)) { + 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; } } } else if (node.output) { for (const str of node.output) { - if (includeString(str, search)) { - ids.push(node.id); + if (includeString(str, option)) { + option.results.push(node.id); 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 => { @@ -1025,21 +1045,22 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . }, [t]); const nextResult = () => { + const { results, index } = filterOption; if (results.length > 0) { - const idx = (resultIndex + 1) % results.length; - setResultIndex(idx); + const idx = (index + 1) % results.length; editor.graph.focusItem(results[idx]); selectNode(results[idx]); + setFilterOption({ ...filterOption, index: idx }); } }; const prevResult = () => { + const { results, index } = filterOption; if (results.length > 0) { - const idx = (resultIndex + results.length - 1) % results.length; - setResultIndex(idx); - console.log(idx, results[idx]); + const idx = (index + results.length - 1) % results.length; editor.graph.focusItem(results[idx]); selectNode(results[idx]); + setFilterOption({ ...filterOption, index: idx }); } }; @@ -1080,7 +1101,12 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . paddingBottom: "1px", paddingRight: "2px", }} - onChange={(e) => onSearchChange(e.currentTarget.value)} + onChange={(e) => + onSearchChange({ + ...filterOption, + filterStr: e.currentTarget.value, + }) + } onKeyDown={(e) => e.code === Hotkey.Enter && nextResult()} suffix={ @@ -1089,36 +1115,45 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . size="small" className={mergeClassNames( "b3-editor-button-filter", - filterCase && "b3-editor-button-filter-selected" + filterOption.filterCase && "b3-editor-button-filter-selected" )} icon={} - onClick={() => { - setFilterCase(!filterCase); - onSearchChange(searchString); - }} + onClick={() => + onSearchChange({ + ...filterOption, + filterCase: !filterOption.filterCase, + }) + } />