Skip to content

Commit e78b36a

Browse files
feat(ui): render input components for polymorphic fields
Polymorphic fields now render the appropriate input component for their base type. For example, float polymorphics will render the number input box. You no longer need to specify ui_type to force it to display. TODO: The UI *may* break if a list is provided as the default value for a polymorphic field.
1 parent 144ede0 commit e78b36a

File tree

9 files changed

+97
-16
lines changed

9 files changed

+97
-16
lines changed

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
3030
return <Box p={2}>Output field in input: {field?.type}</Box>;
3131
}
3232

33-
if (field?.type === 'string' && fieldTemplate?.type === 'string') {
33+
if (
34+
(field?.type === 'string' && fieldTemplate?.type === 'string') ||
35+
(field?.type === 'StringPolymorphic' &&
36+
fieldTemplate?.type === 'StringPolymorphic')
37+
) {
3438
return (
3539
<StringInputField
3640
nodeId={nodeId}
@@ -40,7 +44,11 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
4044
);
4145
}
4246

43-
if (field?.type === 'boolean' && fieldTemplate?.type === 'boolean') {
47+
if (
48+
(field?.type === 'boolean' && fieldTemplate?.type === 'boolean') ||
49+
(field?.type === 'BooleanPolymorphic' &&
50+
fieldTemplate?.type === 'BooleanPolymorphic')
51+
) {
4452
return (
4553
<BooleanInputField
4654
nodeId={nodeId}
@@ -52,7 +60,11 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
5260

5361
if (
5462
(field?.type === 'integer' && fieldTemplate?.type === 'integer') ||
55-
(field?.type === 'float' && fieldTemplate?.type === 'float')
63+
(field?.type === 'float' && fieldTemplate?.type === 'float') ||
64+
(field?.type === 'FloatPolymorphic' &&
65+
fieldTemplate?.type === 'FloatPolymorphic') ||
66+
(field?.type === 'IntegerPolymorphic' &&
67+
fieldTemplate?.type === 'IntegerPolymorphic')
5668
) {
5769
return (
5870
<NumberInputField
@@ -73,7 +85,11 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
7385
);
7486
}
7587

76-
if (field?.type === 'ImageField' && fieldTemplate?.type === 'ImageField') {
88+
if (
89+
(field?.type === 'ImageField' && fieldTemplate?.type === 'ImageField') ||
90+
(field?.type === 'ImagePolymorphic' &&
91+
fieldTemplate?.type === 'ImagePolymorphic')
92+
) {
7793
return (
7894
<ImageInputField
7995
nodeId={nodeId}

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BooleanInputField.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ import { fieldBooleanValueChanged } from 'features/nodes/store/nodesSlice';
44
import {
55
BooleanInputFieldTemplate,
66
BooleanInputFieldValue,
7+
BooleanPolymorphicInputFieldTemplate,
8+
BooleanPolymorphicInputFieldValue,
79
FieldComponentProps,
810
} from 'features/nodes/types/types';
911
import { ChangeEvent, memo, useCallback } from 'react';
1012

1113
const BooleanInputFieldComponent = (
12-
props: FieldComponentProps<BooleanInputFieldValue, BooleanInputFieldTemplate>
14+
props: FieldComponentProps<
15+
BooleanInputFieldValue | BooleanPolymorphicInputFieldValue,
16+
BooleanInputFieldTemplate | BooleanPolymorphicInputFieldTemplate
17+
>
1318
) => {
1419
const { nodeId, field } = props;
1520

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageInputField.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ import {
1212
FieldComponentProps,
1313
ImageInputFieldTemplate,
1414
ImageInputFieldValue,
15+
ImagePolymorphicInputFieldTemplate,
16+
ImagePolymorphicInputFieldValue,
1517
} from 'features/nodes/types/types';
1618
import { memo, useCallback, useMemo } from 'react';
1719
import { FaUndo } from 'react-icons/fa';
1820
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
1921
import { PostUploadAction } from 'services/api/types';
2022

2123
const ImageInputFieldComponent = (
22-
props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate>
24+
props: FieldComponentProps<
25+
ImageInputFieldValue | ImagePolymorphicInputFieldValue,
26+
ImageInputFieldTemplate | ImagePolymorphicInputFieldTemplate
27+
>
2328
) => {
2429
const { nodeId, field } = props;
2530
const dispatch = useAppDispatch();

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/NumberInputField.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@ import {
1212
FieldComponentProps,
1313
FloatInputFieldTemplate,
1414
FloatInputFieldValue,
15+
FloatPolymorphicInputFieldTemplate,
16+
FloatPolymorphicInputFieldValue,
1517
IntegerInputFieldTemplate,
1618
IntegerInputFieldValue,
19+
IntegerPolymorphicInputFieldTemplate,
20+
IntegerPolymorphicInputFieldValue,
1721
} from 'features/nodes/types/types';
1822
import { memo, useEffect, useMemo, useState } from 'react';
1923

2024
const NumberInputFieldComponent = (
2125
props: FieldComponentProps<
22-
IntegerInputFieldValue | FloatInputFieldValue,
23-
IntegerInputFieldTemplate | FloatInputFieldTemplate
26+
| IntegerInputFieldValue
27+
| IntegerPolymorphicInputFieldValue
28+
| FloatInputFieldValue
29+
| FloatPolymorphicInputFieldValue,
30+
| IntegerInputFieldTemplate
31+
| IntegerPolymorphicInputFieldTemplate
32+
| FloatInputFieldTemplate
33+
| FloatPolymorphicInputFieldTemplate
2434
>
2535
) => {
2636
const { nodeId, field, fieldTemplate } = props;

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringInputField.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ import {
66
StringInputFieldTemplate,
77
StringInputFieldValue,
88
FieldComponentProps,
9+
StringPolymorphicInputFieldValue,
10+
StringPolymorphicInputFieldTemplate,
911
} from 'features/nodes/types/types';
1012
import { ChangeEvent, memo, useCallback } from 'react';
1113

1214
const StringInputFieldComponent = (
13-
props: FieldComponentProps<StringInputFieldValue, StringInputFieldTemplate>
15+
props: FieldComponentProps<
16+
StringInputFieldValue | StringPolymorphicInputFieldValue,
17+
StringInputFieldTemplate | StringPolymorphicInputFieldTemplate
18+
>
1419
) => {
1520
const { nodeId, field, fieldTemplate } = props;
1621
const dispatch = useAppDispatch();

invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
55
import { map } from 'lodash-es';
66
import { useMemo } from 'react';
77
import { isInvocationNode } from '../types/types';
8+
import {
9+
POLYMORPHIC_TYPES,
10+
TYPES_WITH_INPUT_COMPONENTS,
11+
} from '../types/constants';
812

913
export const useAnyOrDirectInputFieldNames = (nodeId: string) => {
1014
const selector = useMemo(
@@ -21,7 +25,12 @@ export const useAnyOrDirectInputFieldNames = (nodeId: string) => {
2125
return [];
2226
}
2327
return map(nodeTemplate.inputs)
24-
.filter((field) => ['any', 'direct'].includes(field.input))
28+
.filter(
29+
(field) =>
30+
(['any', 'direct'].includes(field.input) ||
31+
POLYMORPHIC_TYPES.includes(field.type)) &&
32+
TYPES_WITH_INPUT_COMPONENTS.includes(field.type)
33+
)
2534
.filter((field) => !field.ui_hidden)
2635
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0))
2736
.map((field) => field.name)

invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { useAppSelector } from 'app/store/storeHooks';
44
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
55
import { map } from 'lodash-es';
66
import { useMemo } from 'react';
7+
import {
8+
POLYMORPHIC_TYPES,
9+
TYPES_WITH_INPUT_COMPONENTS,
10+
} from '../types/constants';
711
import { isInvocationNode } from '../types/types';
812

913
export const useConnectionInputFieldNames = (nodeId: string) => {
@@ -21,7 +25,12 @@ export const useConnectionInputFieldNames = (nodeId: string) => {
2125
return [];
2226
}
2327
return map(nodeTemplate.inputs)
24-
.filter((field) => field.input === 'connection')
28+
.filter(
29+
(field) =>
30+
(field.input === 'connection' &&
31+
!POLYMORPHIC_TYPES.includes(field.type)) ||
32+
!TYPES_WITH_INPUT_COMPONENTS.includes(field.type)
33+
)
2534
.filter((field) => !field.ui_hidden)
2635
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0))
2736
.map((field) => field.name)

invokeai/frontend/web/src/features/nodes/types/constants.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,28 @@ export const POLYMORPHIC_TO_SINGLE_MAP = {
9696
ColorPolymorphic: 'ColorField',
9797
};
9898

99+
export const TYPES_WITH_INPUT_COMPONENTS = [
100+
'string',
101+
'StringPolymorphic',
102+
'boolean',
103+
'BooleanPolymorphic',
104+
'integer',
105+
'float',
106+
'FloatPolymorphic',
107+
'IntegerPolymorphic',
108+
'enum',
109+
'ImageField',
110+
'ImagePolymorphic',
111+
'MainModelField',
112+
'SDXLRefinerModelField',
113+
'VaeModelField',
114+
'LoRAModelField',
115+
'ControlNetModelField',
116+
'ColorField',
117+
'SDXLMainModelField',
118+
'Scheduler',
119+
];
120+
99121
export const isPolymorphicItemType = (
100122
itemType: string | undefined
101123
): itemType is keyof typeof SINGLE_TO_POLYMORPHIC_MAP =>

invokeai/frontend/web/src/features/nodes/types/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export type IntegerCollectionInputFieldValue = z.infer<
220220

221221
export const zIntegerPolymorphicInputFieldValue = zInputFieldValueBase.extend({
222222
type: z.literal('IntegerPolymorphic'),
223-
value: z.union([z.number().int(), z.array(z.number().int())]).optional(),
223+
value: z.number().int().optional(),
224224
});
225225
export type IntegerPolymorphicInputFieldValue = z.infer<
226226
typeof zIntegerPolymorphicInputFieldValue
@@ -242,7 +242,7 @@ export type FloatCollectionInputFieldValue = z.infer<
242242

243243
export const zFloatPolymorphicInputFieldValue = zInputFieldValueBase.extend({
244244
type: z.literal('FloatPolymorphic'),
245-
value: z.union([z.number(), z.array(z.number())]).optional(),
245+
value: z.number().optional(),
246246
});
247247
export type FloatPolymorphicInputFieldValue = z.infer<
248248
typeof zFloatPolymorphicInputFieldValue
@@ -264,7 +264,7 @@ export type StringCollectionInputFieldValue = z.infer<
264264

265265
export const zStringPolymorphicInputFieldValue = zInputFieldValueBase.extend({
266266
type: z.literal('StringPolymorphic'),
267-
value: z.union([z.string(), z.array(z.string())]).optional(),
267+
value: z.string().optional(),
268268
});
269269
export type StringPolymorphicInputFieldValue = z.infer<
270270
typeof zStringPolymorphicInputFieldValue
@@ -286,7 +286,7 @@ export type BooleanCollectionInputFieldValue = z.infer<
286286

287287
export const zBooleanPolymorphicInputFieldValue = zInputFieldValueBase.extend({
288288
type: z.literal('BooleanPolymorphic'),
289-
value: z.union([z.boolean(), z.array(z.boolean())]).optional(),
289+
value: z.boolean().optional(),
290290
});
291291
export type BooleanPolymorphicInputFieldValue = z.infer<
292292
typeof zBooleanPolymorphicInputFieldValue
@@ -496,7 +496,7 @@ export type ImageInputFieldValue = z.infer<typeof zImageInputFieldValue>;
496496

497497
export const zImagePolymorphicInputFieldValue = zInputFieldValueBase.extend({
498498
type: z.literal('ImagePolymorphic'),
499-
value: z.union([zImageField, z.array(zImageField)]).optional(),
499+
value: zImageField.optional(),
500500
});
501501
export type ImagePolymorphicInputFieldValue = z.infer<
502502
typeof zImagePolymorphicInputFieldValue

0 commit comments

Comments
 (0)