mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): Add fusejs to NodeSearch
This commit is contained in:
parent
ff891b1ff2
commit
3f334d9e5e
@ -54,6 +54,7 @@
|
||||
"dateformat": "^5.0.3",
|
||||
"formik": "^2.2.9",
|
||||
"framer-motion": "^9.0.4",
|
||||
"fuse.js": "^6.6.2",
|
||||
"i18next": "^22.4.10",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.1.1",
|
||||
|
@ -5,6 +5,7 @@ import IAIInput from 'common/components/IAIInput';
|
||||
import { Panel } from 'reactflow';
|
||||
import { map } from 'lodash';
|
||||
import {
|
||||
ChangeEvent,
|
||||
FocusEvent,
|
||||
KeyboardEvent,
|
||||
memo,
|
||||
@ -19,6 +20,8 @@ import { useBuildInvocation } from 'features/nodes/hooks/useBuildInvocation';
|
||||
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { nodeAdded } from '../../store/nodesSlice';
|
||||
import Fuse from 'fuse.js';
|
||||
import { InvocationTemplate } from 'features/nodes/types/types';
|
||||
|
||||
interface NodeListItemProps {
|
||||
title: string;
|
||||
@ -55,6 +58,9 @@ const NodeSearch = () => {
|
||||
);
|
||||
|
||||
const nodes = map(invocationTemplates);
|
||||
const [filteredNodes, setFilteredNodes] = useState<
|
||||
Fuse.FuseResult<InvocationTemplate>[]
|
||||
>([]);
|
||||
|
||||
const buildInvocation = useBuildInvocation();
|
||||
const dispatch = useAppDispatch();
|
||||
@ -64,6 +70,21 @@ const NodeSearch = () => {
|
||||
const [focusedIndex, setFocusedIndex] = useState<number>(-1);
|
||||
const nodeSearchRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fuseOptions = {
|
||||
findAllMatches: true,
|
||||
threshold: 0,
|
||||
ignoreLocation: true,
|
||||
keys: ['title', 'type', 'tags'],
|
||||
};
|
||||
|
||||
const fuse = new Fuse(nodes, fuseOptions);
|
||||
|
||||
const findNode = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchText(e.target.value);
|
||||
setFilteredNodes(fuse.search(e.target.value));
|
||||
setShowNodeList(true);
|
||||
};
|
||||
|
||||
const addNode = useCallback(
|
||||
(nodeType: AnyInvocationType) => {
|
||||
const invocation = buildInvocation(nodeType);
|
||||
@ -85,7 +106,9 @@ const NodeSearch = () => {
|
||||
const renderNodeList = () => {
|
||||
const nodeListToRender: ReactNode[] = [];
|
||||
|
||||
nodes.forEach(({ title, description, type }, index) => {
|
||||
if (searchText.length > 0) {
|
||||
filteredNodes.forEach(({ item }, index) => {
|
||||
const { title, description, type } = item;
|
||||
if (title.toLowerCase().includes(searchText)) {
|
||||
nodeListToRender.push(
|
||||
<NodeListItem
|
||||
@ -97,7 +120,11 @@ const NodeSearch = () => {
|
||||
addNode={addNode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
nodes.forEach(({ title, description, type }, index) => {
|
||||
nodeListToRender.push(
|
||||
<NodeListItem
|
||||
key={index}
|
||||
title={title}
|
||||
@ -105,18 +132,13 @@ const NodeSearch = () => {
|
||||
type={type}
|
||||
isSelected={focusedIndex === index}
|
||||
addNode={addNode}
|
||||
/>;
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
background="base.900"
|
||||
borderRadius={6}
|
||||
maxHeight={400}
|
||||
overflowY="scroll"
|
||||
>
|
||||
<Flex flexDirection="column" background="base.900" borderRadius={6}>
|
||||
{nodeListToRender}
|
||||
</Flex>
|
||||
);
|
||||
@ -128,12 +150,21 @@ const NodeSearch = () => {
|
||||
|
||||
if (key === 'ArrowDown') {
|
||||
setShowNodeList(true);
|
||||
if (searchText.length > 0) {
|
||||
nextIndex = (focusedIndex + 1) % filteredNodes.length;
|
||||
} else {
|
||||
nextIndex = (focusedIndex + 1) % nodes.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'ArrowUp') {
|
||||
setShowNodeList(true);
|
||||
nextIndex = (focusedIndex + nodes.length - 1) % nodes.length;
|
||||
if (searchText.length > 0) {
|
||||
nextIndex =
|
||||
(focusedIndex + filteredNodes.length - 1) % filteredNodes.length;
|
||||
} else {
|
||||
nextIndex = (focusedIndex + filteredNodes.length - 1) % nodes.length;
|
||||
}
|
||||
}
|
||||
|
||||
// # TODO Handle Blur
|
||||
@ -141,7 +172,14 @@ const NodeSearch = () => {
|
||||
// }
|
||||
|
||||
if (key === 'Enter') {
|
||||
const selectedNodeType = nodes[focusedIndex].type;
|
||||
let selectedNodeType: AnyInvocationType;
|
||||
|
||||
if (searchText.length > 0) {
|
||||
selectedNodeType = filteredNodes[focusedIndex].item.type;
|
||||
} else {
|
||||
selectedNodeType = nodes[focusedIndex].type;
|
||||
}
|
||||
|
||||
addNode(selectedNodeType);
|
||||
setShowNodeList(false);
|
||||
}
|
||||
@ -163,13 +201,7 @@ const NodeSearch = () => {
|
||||
onBlur={searchInputBlurHandler}
|
||||
ref={nodeSearchRef}
|
||||
>
|
||||
<IAIInput
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
setShowNodeList(true);
|
||||
}}
|
||||
/>
|
||||
<IAIInput value={searchText} onChange={findNode} />
|
||||
{showNodeList && renderNodeList()}
|
||||
</Flex>
|
||||
</Panel>
|
||||
|
@ -3454,6 +3454,11 @@ functions-have-names@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
fuse.js@^6.6.2:
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.6.2.tgz#fe463fed4b98c0226ac3da2856a415576dc9a111"
|
||||
integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==
|
||||
|
||||
get-amd-module-type@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0"
|
||||
|
Loading…
Reference in New Issue
Block a user