mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): use new ui_order
to sort fields; connection-only fields in grid
This commit is contained in:
parent
cd73085eb9
commit
38b2dedc1d
@ -1,12 +1,14 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { Flex, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import InvocationNodeFooter from './InvocationNodeFooter';
|
||||
import InvocationNodeHeader from './InvocationNodeHeader';
|
||||
import NodeWrapper from '../common/NodeWrapper';
|
||||
import OutputField from './fields/OutputField';
|
||||
import InputField from './fields/InputField';
|
||||
import { useFieldNames } from 'features/nodes/hooks/useFieldNames';
|
||||
import { useOutputFieldNames } from 'features/nodes/hooks/useOutputFieldNames';
|
||||
import { useWithFooter } from 'features/nodes/hooks/useWithFooter';
|
||||
import { useConnectionInputFieldNames } from 'features/nodes/hooks/useConnectionInputFieldNames';
|
||||
import { useAnyOrDirectInputFieldNames } from 'features/nodes/hooks/useAnyOrDirectInputFieldNames';
|
||||
|
||||
type Props = {
|
||||
nodeId: string;
|
||||
@ -17,8 +19,9 @@ type Props = {
|
||||
};
|
||||
|
||||
const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => {
|
||||
const inputFieldNames = useFieldNames(nodeId, 'input');
|
||||
const outputFieldNames = useFieldNames(nodeId, 'output');
|
||||
const inputConnectionFieldNames = useConnectionInputFieldNames(nodeId);
|
||||
const inputAnyOrDirectFieldNames = useAnyOrDirectInputFieldNames(nodeId);
|
||||
const outputFieldNames = useOutputFieldNames(nodeId);
|
||||
const withFooter = useWithFooter(nodeId);
|
||||
|
||||
return (
|
||||
@ -44,14 +47,27 @@ const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => {
|
||||
}}
|
||||
>
|
||||
<Flex sx={{ flexDir: 'column', px: 2, w: 'full', h: 'full' }}>
|
||||
{outputFieldNames.map((fieldName) => (
|
||||
<OutputField
|
||||
key={`${nodeId}.${fieldName}.output-field`}
|
||||
nodeId={nodeId}
|
||||
fieldName={fieldName}
|
||||
/>
|
||||
))}
|
||||
{inputFieldNames.map((fieldName) => (
|
||||
<Grid gridTemplateColumns="1fr auto" gridAutoRows="1fr">
|
||||
{inputConnectionFieldNames.map((fieldName, i) => (
|
||||
<GridItem
|
||||
gridColumnStart={1}
|
||||
gridRowStart={i + 1}
|
||||
key={`${nodeId}.${fieldName}.input-field`}
|
||||
>
|
||||
<InputField nodeId={nodeId} fieldName={fieldName} />
|
||||
</GridItem>
|
||||
))}
|
||||
{outputFieldNames.map((fieldName, i) => (
|
||||
<GridItem
|
||||
gridColumnStart={2}
|
||||
gridRowStart={i + 1}
|
||||
key={`${nodeId}.${fieldName}.output-field`}
|
||||
>
|
||||
<OutputField nodeId={nodeId} fieldName={fieldName} />
|
||||
</GridItem>
|
||||
))}
|
||||
</Grid>
|
||||
{inputAnyOrDirectFieldNames.map((fieldName) => (
|
||||
<InputField
|
||||
key={`${nodeId}.${fieldName}.input-field`}
|
||||
nodeId={nodeId}
|
||||
|
@ -31,7 +31,7 @@ const FieldTitle = forwardRef((props: Props, ref) => {
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (newTitle: string) => {
|
||||
if (newTitle === label || newTitle === fieldTemplateTitle) {
|
||||
if (newTitle && (newTitle === label || newTitle === fieldTemplateTitle)) {
|
||||
return;
|
||||
}
|
||||
setLocalTitle(newTitle || fieldTemplateTitle || 'Unknown Field');
|
||||
|
@ -1,10 +1,4 @@
|
||||
import {
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Spacer,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { Flex, FormControl, FormLabel, Tooltip } from '@chakra-ui/react';
|
||||
import { useConnectionState } from 'features/nodes/hooks/useConnectionState';
|
||||
import { useFieldTemplate } from 'features/nodes/hooks/useFieldTemplate';
|
||||
import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';
|
||||
@ -42,7 +36,6 @@ const OutputField = ({ nodeId, fieldName }: Props) => {
|
||||
|
||||
return (
|
||||
<OutputFieldWrapper shouldDim={shouldDim}>
|
||||
<Spacer />
|
||||
<Tooltip
|
||||
label={
|
||||
<FieldTooltipContent
|
||||
@ -90,6 +83,7 @@ const OutputFieldWrapper = memo(
|
||||
opacity: shouldDim ? 0.5 : 1,
|
||||
transitionProperty: 'opacity',
|
||||
transitionDuration: '0.1s',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { map } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { isInvocationNode } from '../types/types';
|
||||
|
||||
export const useAnyOrDirectInputFieldNames = (nodeId: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ nodes }) => {
|
||||
const node = nodes.nodes.find((node) => node.id === nodeId);
|
||||
if (!isInvocationNode(node)) {
|
||||
return [];
|
||||
}
|
||||
const nodeTemplate = nodes.nodeTemplates[node.data.type];
|
||||
if (!nodeTemplate) {
|
||||
return [];
|
||||
}
|
||||
return map(nodeTemplate.inputs)
|
||||
.filter((field) => ['any', 'direct'].includes(field.input))
|
||||
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0))
|
||||
.map((field) => field.name)
|
||||
.filter((fieldName) => fieldName !== 'is_intermediate');
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[nodeId]
|
||||
);
|
||||
|
||||
const fieldNames = useAppSelector(selector);
|
||||
return fieldNames;
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { map } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { isInvocationNode } from '../types/types';
|
||||
|
||||
export const useConnectionInputFieldNames = (nodeId: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ nodes }) => {
|
||||
const node = nodes.nodes.find((node) => node.id === nodeId);
|
||||
if (!isInvocationNode(node)) {
|
||||
return [];
|
||||
}
|
||||
const nodeTemplate = nodes.nodeTemplates[node.data.type];
|
||||
if (!nodeTemplate) {
|
||||
return [];
|
||||
}
|
||||
return map(nodeTemplate.inputs)
|
||||
.filter((field) => field.input === 'connection')
|
||||
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0))
|
||||
.map((field) => field.name)
|
||||
.filter((fieldName) => fieldName !== 'is_intermediate');
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[nodeId]
|
||||
);
|
||||
|
||||
const fieldNames = useAppSelector(selector);
|
||||
return fieldNames;
|
||||
};
|
@ -4,10 +4,9 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { map } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { KIND_MAP } from '../types/constants';
|
||||
import { isInvocationNode } from '../types/types';
|
||||
|
||||
export const useFieldNames = (nodeId: string, kind: 'input' | 'output') => {
|
||||
export const useOutputFieldNames = (nodeId: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
@ -17,13 +16,18 @@ export const useFieldNames = (nodeId: string, kind: 'input' | 'output') => {
|
||||
if (!isInvocationNode(node)) {
|
||||
return [];
|
||||
}
|
||||
return map(node.data[KIND_MAP[kind]], (field) => field.name).filter(
|
||||
(fieldName) => fieldName !== 'is_intermediate'
|
||||
);
|
||||
const nodeTemplate = nodes.nodeTemplates[node.data.type];
|
||||
if (!nodeTemplate) {
|
||||
return [];
|
||||
}
|
||||
return map(nodeTemplate.outputs)
|
||||
.sort((a, b) => (a.ui_order ?? 0) - (b.ui_order ?? 0))
|
||||
.map((field) => field.name)
|
||||
.filter((fieldName) => fieldName !== 'is_intermediate');
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[kind, nodeId]
|
||||
[nodeId]
|
||||
);
|
||||
|
||||
const fieldNames = useAppSelector(selector);
|
@ -203,7 +203,7 @@ export type OutputFieldTemplate = {
|
||||
type: FieldType;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
} & _OutputField;
|
||||
|
||||
/**
|
||||
* Indicates the kind of input(s) this field may have.
|
||||
|
@ -467,7 +467,7 @@ export const buildInputFieldTemplate = (
|
||||
const fieldType = getFieldType(fieldSchema);
|
||||
// console.log('input fieldType', fieldType);
|
||||
|
||||
const { input, ui_hidden, ui_component, ui_type } = fieldSchema;
|
||||
const { input, ui_hidden, ui_component, ui_type, ui_order } = fieldSchema;
|
||||
|
||||
const extra = {
|
||||
input,
|
||||
@ -475,6 +475,7 @@ export const buildInputFieldTemplate = (
|
||||
ui_component,
|
||||
ui_type,
|
||||
required: nodeSchema.required?.includes(name) ?? false,
|
||||
ui_order,
|
||||
};
|
||||
|
||||
const baseField = {
|
||||
|
@ -142,6 +142,9 @@ export const parseSchema = (
|
||||
title: property.title ?? '',
|
||||
description: property.description ?? '',
|
||||
type: fieldType,
|
||||
ui_hidden: property.ui_hidden ?? false,
|
||||
ui_type: property.ui_type,
|
||||
ui_order: property.ui_order,
|
||||
};
|
||||
|
||||
return outputsAccumulator;
|
||||
|
Loading…
Reference in New Issue
Block a user