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