mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix selection on dropdowns
Mantine's multiselect does not let you edit the search box with mouse, paste into it, etc. Normal select is fine. I can't remember why I made Lora etc multiselects, but everything seems to work with normal selects, so I've change to that.
This commit is contained in:
parent
81817532f8
commit
be06d4c0af
@ -59,6 +59,7 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
searchable={searchable}
|
searchable={searchable}
|
||||||
|
maxDropdownHeight={300}
|
||||||
styles={() => ({
|
styles={() => ({
|
||||||
label: {
|
label: {
|
||||||
color: mode(base700, base300)(colorMode),
|
color: mode(base700, base300)(colorMode),
|
||||||
|
@ -3,7 +3,7 @@ import { Select, SelectProps } from '@mantine/core';
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens';
|
import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens';
|
||||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||||
import { KeyboardEvent, memo, useCallback } from 'react';
|
import { KeyboardEvent, RefObject, memo, useCallback, useState } from 'react';
|
||||||
import { mode } from 'theme/util/mode';
|
import { mode } from 'theme/util/mode';
|
||||||
|
|
||||||
export type IAISelectDataType = {
|
export type IAISelectDataType = {
|
||||||
@ -14,10 +14,11 @@ export type IAISelectDataType = {
|
|||||||
|
|
||||||
type IAISelectProps = SelectProps & {
|
type IAISelectProps = SelectProps & {
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IAIMantineSelect = (props: IAISelectProps) => {
|
const IAIMantineSelect = (props: IAISelectProps) => {
|
||||||
const { searchable = true, tooltip, ...rest } = props;
|
const { searchable = true, tooltip, inputRef, onChange, ...rest } = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const {
|
||||||
base50,
|
base50,
|
||||||
@ -38,7 +39,9 @@ const IAIMantineSelect = (props: IAISelectProps) => {
|
|||||||
} = useChakraThemeTokens();
|
} = useChakraThemeTokens();
|
||||||
|
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
|
const [searchValue, setSearchValue] = useState('');
|
||||||
|
|
||||||
|
// we want to capture shift keypressed even when an input is focused
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
@ -57,14 +60,33 @@ const IAIMantineSelect = (props: IAISelectProps) => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// wrap onChange to clear search value on select
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(v: string | null) => {
|
||||||
|
setSearchValue('');
|
||||||
|
|
||||||
|
if (!onChange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(v);
|
||||||
|
},
|
||||||
|
[onChange]
|
||||||
|
);
|
||||||
|
|
||||||
const [boxShadow] = useToken('shadows', ['dark-lg']);
|
const [boxShadow] = useToken('shadows', ['dark-lg']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||||
<Select
|
<Select
|
||||||
|
ref={inputRef}
|
||||||
|
searchValue={searchValue}
|
||||||
|
onSearchChange={setSearchValue}
|
||||||
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
searchable={searchable}
|
searchable={searchable}
|
||||||
|
maxDropdownHeight={300}
|
||||||
styles={() => ({
|
styles={() => ({
|
||||||
label: {
|
label: {
|
||||||
color: mode(base700, base300)(colorMode),
|
color: mode(base700, base300)(colorMode),
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
import { SelectItem } from '@mantine/core';
|
import { SelectItem } from '@mantine/core';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
|
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
|
||||||
import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect';
|
import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect';
|
||||||
import { forEach } from 'lodash-es';
|
import { forEach } from 'lodash-es';
|
||||||
@ -61,12 +61,12 @@ const ParamEmbeddingPopover = (props: Props) => {
|
|||||||
}, [embeddingQueryData, currentMainModel?.base_model]);
|
}, [embeddingQueryData, currentMainModel?.base_model]);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(v: string[]) => {
|
(v: string | null) => {
|
||||||
if (v.length === 0) {
|
if (!v) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelect(v[0]);
|
onSelect(v);
|
||||||
},
|
},
|
||||||
[onSelect]
|
[onSelect]
|
||||||
);
|
);
|
||||||
@ -106,16 +106,16 @@ const ParamEmbeddingPopover = (props: Props) => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<IAIMantineMultiSelect
|
<IAIMantineSelect
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
|
autoFocus
|
||||||
placeholder={'Add Embedding'}
|
placeholder={'Add Embedding'}
|
||||||
value={[]}
|
value={null}
|
||||||
data={data}
|
data={data}
|
||||||
maxDropdownHeight={400}
|
nothingFound="No matching Embeddings"
|
||||||
nothingFound="No Matching Embeddings"
|
|
||||||
itemComponent={IAIMantineSelectItemWithTooltip}
|
itemComponent={IAIMantineSelectItemWithTooltip}
|
||||||
disabled={data.length === 0}
|
disabled={data.length === 0}
|
||||||
filter={(value, selected, item: SelectItem) =>
|
filter={(value, item: SelectItem) =>
|
||||||
item.label
|
item.label
|
||||||
?.toLowerCase()
|
?.toLowerCase()
|
||||||
.includes(value.toLowerCase().trim()) ||
|
.includes(value.toLowerCase().trim()) ||
|
||||||
|
@ -4,7 +4,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { RootState, stateSelector } from 'app/store/store';
|
import { RootState, stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
|
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
|
||||||
import { loraAdded } from 'features/lora/store/loraSlice';
|
import { loraAdded } from 'features/lora/store/loraSlice';
|
||||||
import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect';
|
import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect';
|
||||||
@ -58,12 +58,15 @@ const ParamLoraSelect = () => {
|
|||||||
}, [loras, lorasQueryData, currentMainModel?.base_model]);
|
}, [loras, lorasQueryData, currentMainModel?.base_model]);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(v: string[]) => {
|
(v: string | null | undefined) => {
|
||||||
const loraEntity = lorasQueryData?.entities[v[0]];
|
if (!v) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const loraEntity = lorasQueryData?.entities[v];
|
||||||
if (!loraEntity) {
|
if (!loraEntity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
v[0] && dispatch(loraAdded(loraEntity));
|
dispatch(loraAdded(loraEntity));
|
||||||
},
|
},
|
||||||
[dispatch, lorasQueryData?.entities]
|
[dispatch, lorasQueryData?.entities]
|
||||||
);
|
);
|
||||||
@ -79,15 +82,14 @@ const ParamLoraSelect = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIMantineMultiSelect
|
<IAIMantineSelect
|
||||||
placeholder={data.length === 0 ? 'All LoRAs added' : 'Add LoRA'}
|
placeholder={data.length === 0 ? 'All LoRAs added' : 'Add LoRA'}
|
||||||
value={[]}
|
value={null}
|
||||||
data={data}
|
data={data}
|
||||||
maxDropdownHeight={400}
|
|
||||||
nothingFound="No matching LoRAs"
|
nothingFound="No matching LoRAs"
|
||||||
itemComponent={IAIMantineSelectItemWithTooltip}
|
itemComponent={IAIMantineSelectItemWithTooltip}
|
||||||
disabled={data.length === 0}
|
disabled={data.length === 0}
|
||||||
filter={(value, selected, item: SelectItem) =>
|
filter={(value, item: SelectItem) =>
|
||||||
item.label?.toLowerCase().includes(value.toLowerCase().trim()) ||
|
item.label?.toLowerCase().includes(value.toLowerCase().trim()) ||
|
||||||
item.value.toLowerCase().includes(value.toLowerCase().trim())
|
item.value.toLowerCase().includes(value.toLowerCase().trim())
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import 'reactflow/dist/style.css';
|
|
||||||
import { useCallback, forwardRef } from 'react';
|
|
||||||
import { Flex, Text } from '@chakra-ui/react';
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { nodeAdded, nodesSelector } from '../store/nodesSlice';
|
|
||||||
import { map } from 'lodash-es';
|
|
||||||
import { useBuildInvocation } from '../hooks/useBuildInvocation';
|
|
||||||
import { AnyInvocationType } from 'services/events/types';
|
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
|
import { map } from 'lodash-es';
|
||||||
|
import { forwardRef, useCallback } from 'react';
|
||||||
|
import 'reactflow/dist/style.css';
|
||||||
|
import { AnyInvocationType } from 'services/events/types';
|
||||||
|
import { useBuildInvocation } from '../hooks/useBuildInvocation';
|
||||||
|
import { nodeAdded, nodesSelector } from '../store/nodesSlice';
|
||||||
|
|
||||||
type NodeTemplate = {
|
type NodeTemplate = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -58,24 +58,33 @@ const AddNodeMenu = () => {
|
|||||||
[dispatch, buildInvocation, toaster]
|
[dispatch, buildInvocation, toaster]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(v: string | null) => {
|
||||||
|
if (!v) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addNode(v as AnyInvocationType);
|
||||||
|
},
|
||||||
|
[addNode]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex sx={{ gap: 2, alignItems: 'center' }}>
|
<Flex sx={{ gap: 2, alignItems: 'center' }}>
|
||||||
<IAIMantineMultiSelect
|
<IAIMantineSelect
|
||||||
selectOnBlur={false}
|
selectOnBlur={false}
|
||||||
placeholder="Add Node"
|
placeholder="Add Node"
|
||||||
value={[]}
|
value={null}
|
||||||
data={data}
|
data={data}
|
||||||
maxDropdownHeight={400}
|
maxDropdownHeight={400}
|
||||||
nothingFound="No matching nodes"
|
nothingFound="No matching nodes"
|
||||||
itemComponent={SelectItem}
|
itemComponent={SelectItem}
|
||||||
filter={(value, selected, item: NodeTemplate) =>
|
filter={(value, item: NodeTemplate) =>
|
||||||
item.label.toLowerCase().includes(value.toLowerCase().trim()) ||
|
item.label.toLowerCase().includes(value.toLowerCase().trim()) ||
|
||||||
item.value.toLowerCase().includes(value.toLowerCase().trim()) ||
|
item.value.toLowerCase().includes(value.toLowerCase().trim()) ||
|
||||||
item.description.toLowerCase().includes(value.toLowerCase().trim())
|
item.description.toLowerCase().includes(value.toLowerCase().trim())
|
||||||
}
|
}
|
||||||
onChange={(v) => {
|
onChange={handleChange}
|
||||||
v[0] && addNode(v[0] as AnyInvocationType);
|
|
||||||
}}
|
|
||||||
sx={{
|
sx={{
|
||||||
width: '18rem',
|
width: '18rem',
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user