Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 44 additions & 26 deletions invokeai/frontend/web/src/common/components/IAICustomSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { CheckIcon, ChevronUpIcon } from '@chakra-ui/icons';
import {
Box,
Flex,
FlexProps,
FormControl,
FormControlProps,
FormLabel,
Expand All @@ -16,22 +15,27 @@ import {
} from '@chakra-ui/react';
import { autoUpdate, offset, shift, useFloating } from '@floating-ui/react-dom';
import { useSelect } from 'downshift';
import { isString } from 'lodash-es';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';

import { memo, useMemo } from 'react';
import { getInputOutlineStyles } from 'theme/util/getInputOutlineStyles';

export type ItemTooltips = { [key: string]: string };

export type IAICustomSelectOption = {
value: string;
label: string;
tooltip?: string;
};

type IAICustomSelectProps = {
label?: string;
items: string[];
itemTooltips?: ItemTooltips;
selectedItem: string;
setSelectedItem: (v: string | null | undefined) => void;
value: string;
data: IAICustomSelectOption[] | string[];
onChange: (v: string) => void;
withCheckIcon?: boolean;
formControlProps?: FormControlProps;
buttonProps?: FlexProps;
tooltip?: string;
tooltipProps?: Omit<TooltipProps, 'children'>;
ellipsisPosition?: 'start' | 'end';
Expand All @@ -40,18 +44,33 @@ type IAICustomSelectProps = {
const IAICustomSelect = (props: IAICustomSelectProps) => {
const {
label,
items,
itemTooltips,
setSelectedItem,
selectedItem,
withCheckIcon,
formControlProps,
tooltip,
buttonProps,
tooltipProps,
ellipsisPosition = 'end',
data,
value,
onChange,
} = props;

const values = useMemo(() => {
return data.map<IAICustomSelectOption>((v) => {
if (isString(v)) {
return { value: v, label: v };
}
return v;
});
}, [data]);

const stringValues = useMemo(() => {
return values.map((v) => v.value);
}, [values]);

const valueData = useMemo(() => {
return values.find((v) => v.value === value);
}, [values, value]);

const {
isOpen,
getToggleButtonProps,
Expand All @@ -60,10 +79,11 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
highlightedIndex,
getItemProps,
} = useSelect({
items,
selectedItem,
onSelectedItemChange: ({ selectedItem: newSelectedItem }) =>
setSelectedItem(newSelectedItem),
items: stringValues,
selectedItem: value,
onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {
newSelectedItem && onChange(newSelectedItem);
},
});

const { refs, floatingStyles } = useFloating<HTMLButtonElement>({
Expand Down Expand Up @@ -94,7 +114,6 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
<Tooltip label={tooltip} {...tooltipProps}>
<Flex
{...getToggleButtonProps({ ref: refs.setReference })}
{...buttonProps}
sx={{
alignItems: 'center',
userSelect: 'none',
Expand All @@ -119,7 +138,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
direction: labelTextDirection,
}}
>
{selectedItem}
{valueData?.label}
</Text>
<ChevronUpIcon
sx={{
Expand Down Expand Up @@ -155,8 +174,8 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
}}
>
<OverlayScrollbarsComponent>
{items.map((item, index) => {
const isSelected = selectedItem === item;
{values.map((v, index) => {
const isSelected = value === v.value;
const isHighlighted = highlightedIndex === index;
const fontWeight = isSelected ? 700 : 500;
const bg = isHighlighted
Expand All @@ -166,9 +185,9 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
: undefined;
return (
<Tooltip
isDisabled={!itemTooltips}
key={`${item}${index}`}
label={itemTooltips?.[item]}
isDisabled={!v.tooltip}
key={`${v.value}${index}`}
label={v.tooltip}
hasArrow
placement="right"
>
Expand All @@ -182,8 +201,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
transitionProperty: 'common',
transitionDuration: '0.15s',
}}
key={`${item}${index}`}
{...getItemProps({ item, index })}
{...getItemProps({ item: v.value, index })}
>
{withCheckIcon ? (
<Grid gridTemplateColumns="1.25rem auto">
Expand All @@ -198,7 +216,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
fontWeight,
}}
>
{item}
{v.label}
</Text>
</GridItem>
</Grid>
Expand All @@ -210,7 +228,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
fontWeight,
}}
>
{item}
{v.label}
</Text>
)}
</ListItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAICustomSelect from 'common/components/IAICustomSelect';
import IAICustomSelect, {
IAICustomSelectOption,
} from 'common/components/IAICustomSelect';
import {
CONTROLNET_MODELS,
ControlNetModel,
ControlNetModelName,
} from 'features/controlNet/store/constants';
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
import { map } from 'lodash-es';
import { memo, useCallback } from 'react';

type ParamControlNetModelProps = {
controlNetId: string;
model: ControlNetModel;
model: ControlNetModelName;
};

const DATA: IAICustomSelectOption[] = map(CONTROLNET_MODELS, (m) => ({
value: m.type,
label: m.label,
tooltip: m.type,
}));

const ParamControlNetModel = (props: ParamControlNetModelProps) => {
const { controlNetId, model } = props;
const dispatch = useAppDispatch();

const handleModelChanged = useCallback(
(val: string | null | undefined) => {
// TODO: do not cast
const model = val as ControlNetModel;
const model = val as ControlNetModelName;
dispatch(controlNetModelChanged({ controlNetId, model }));
},
[controlNetId, dispatch]
Expand All @@ -29,9 +38,9 @@ const ParamControlNetModel = (props: ParamControlNetModelProps) => {
<IAICustomSelect
tooltip={model}
tooltipProps={{ placement: 'top', hasArrow: true }}
items={CONTROLNET_MODELS}
selectedItem={model}
setSelectedItem={handleModelChanged}
data={DATA}
value={model}
onChange={handleModelChanged}
ellipsisPosition="start"
withCheckIcon
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import IAICustomSelect from 'common/components/IAICustomSelect';
import IAICustomSelect, {
IAICustomSelectOption,
} from 'common/components/IAICustomSelect';
import { memo, useCallback } from 'react';
import {
ControlNetProcessorNode,
Expand All @@ -7,15 +9,28 @@ import {
import { controlNetProcessorTypeChanged } from '../../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
import { CONTROLNET_PROCESSORS } from '../../store/constants';
import { map } from 'lodash-es';

type ParamControlNetProcessorSelectProps = {
controlNetId: string;
processorNode: ControlNetProcessorNode;
};

const CONTROLNET_PROCESSOR_TYPES = Object.keys(
CONTROLNET_PROCESSORS
) as ControlNetProcessorType[];
const CONTROLNET_PROCESSOR_TYPES: IAICustomSelectOption[] = map(
CONTROLNET_PROCESSORS,
(p) => ({
value: p.type,
label: p.label,
tooltip: p.description,
})
).sort((a, b) =>
// sort 'none' to the top
a.value === 'none'
? -1
: b.value === 'none'
? 1
: a.label.localeCompare(b.label)
);

const ParamControlNetProcessorSelect = (
props: ParamControlNetProcessorSelectProps
Expand All @@ -36,9 +51,9 @@ const ParamControlNetProcessorSelect = (
return (
<IAICustomSelect
label="Processor"
items={CONTROLNET_PROCESSOR_TYPES}
selectedItem={processorNode.type ?? 'canny_image_processor'}
setSelectedItem={handleProcessorTypeChanged}
value={processorNode.type ?? 'canny_image_processor'}
data={CONTROLNET_PROCESSOR_TYPES}
onChange={handleProcessorTypeChanged}
withCheckIcon
/>
);
Expand Down
Loading