mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Reorder exposed fields in workflow tab (#5711)
## What type of PR is this? (check all applicable) - [ ] Refactor - [x] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Community Node Submission ## Have you discussed this change with the InvokeAI team? - [x] Yes - [ ] No, because: ## Have you updated all relevant documentation? - [ ] Yes - [ ] No ## Description ## Related Tickets & Documents <!-- For pull requests that relate or close an issue, please include them below. For example having the text: "closes #1234" would connect the current pull request to issue 1234. And when we merge the pull request, Github will automatically close the issue. --> - Related Issue # - Closes # ## QA Instructions, Screenshots, Recordings <!-- Please provide steps on how to test changes, any hardware or software specifications as well as any other pertinent information. --> ## Merge Plan <!-- A merge plan describes how this PR should be handled after it is approved. Example merge plans: - "This PR can be merged when approved" - "This must be squash-merged when approved" - "DO NOT MERGE - I will rebase and tidy commits before merging" - "#dev-chat on discord needs to be advised of this change when it is merged" A merge plan is particularly important for large PRs or PRs that touch the database in any way. --> ## Added/updated tests? - [ ] Yes - [ ] No : _please replace this line with details on why tests have not been included_ ## [optional] Are there any post deployment tasks we need to perform?
This commit is contained in:
commit
5e77f0d93b
@ -52,6 +52,7 @@
|
||||
"@chakra-ui/react-use-size": "^2.1.0",
|
||||
"@dagrejs/graphlib": "^2.1.13",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@fontsource-variable/inter": "^5.0.16",
|
||||
"@invoke-ai/ui-library": "^0.0.18",
|
||||
|
@ -22,6 +22,9 @@ dependencies:
|
||||
'@dnd-kit/core':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@dnd-kit/sortable':
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0)
|
||||
'@dnd-kit/utilities':
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2(react@18.2.0)
|
||||
@ -2884,6 +2887,18 @@ packages:
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
|
||||
peerDependencies:
|
||||
'@dnd-kit/core': ^6.1.0
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
'@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@dnd-kit/utilities': 3.2.2(react@18.2.0)
|
||||
react: 18.2.0
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@dnd-kit/utilities@3.2.2(react@18.2.0):
|
||||
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
|
||||
peerDependencies:
|
||||
|
@ -1000,6 +1000,7 @@
|
||||
"resetToDefaultValue": "Reset to default value",
|
||||
"reloadNodeTemplates": "Reload Node Templates",
|
||||
"removeLinearView": "Remove from Linear View",
|
||||
"reorderLinearView": "Reorder Linear View",
|
||||
"newWorkflow": "New Workflow",
|
||||
"newWorkflowDesc": "Create a new workflow?",
|
||||
"newWorkflowDesc2": "Your current workflow has unsaved changes.",
|
||||
|
@ -0,0 +1,23 @@
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { DndContextTypesafe } from './DndContextTypesafe';
|
||||
|
||||
type Props = PropsWithChildren & {
|
||||
items: string[];
|
||||
onDragEnd(event: DragEndEvent): void;
|
||||
};
|
||||
|
||||
const DndSortable = (props: Props) => {
|
||||
return (
|
||||
<DndContextTypesafe onDragEnd={props.onDragEnd}>
|
||||
<SortableContext items={props.items} strategy={verticalListSortingStrategy}>
|
||||
{props.children}
|
||||
</SortableContext>
|
||||
</DndContextTypesafe>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(DndSortable);
|
@ -1,3 +1,5 @@
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Flex, Icon, IconButton, Spacer, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay';
|
||||
@ -7,7 +9,7 @@ import { workflowExposedFieldRemoved } from 'features/nodes/store/workflowSlice'
|
||||
import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowCounterClockwiseBold, PiInfoBold, PiTrashSimpleBold } from 'react-icons/pi';
|
||||
import { PiArrowCounterClockwiseBold, PiDotsSixVerticalBold, PiInfoBold, PiTrashSimpleBold } from 'react-icons/pi';
|
||||
|
||||
import EditableFieldTitle from './EditableFieldTitle';
|
||||
import FieldTooltipContent from './FieldTooltipContent';
|
||||
@ -28,18 +30,38 @@ const LinearViewField = ({ nodeId, fieldName }: Props) => {
|
||||
dispatch(workflowExposedFieldRemoved({ nodeId, fieldName }));
|
||||
}, [dispatch, fieldName, nodeId]);
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: `${nodeId}.${fieldName}` });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
onMouseEnter={handleMouseOver}
|
||||
onMouseLeave={handleMouseOut}
|
||||
layerStyle="second"
|
||||
alignItems="center"
|
||||
position="relative"
|
||||
borderRadius="base"
|
||||
w="full"
|
||||
p={4}
|
||||
flexDir="column"
|
||||
paddingLeft={0}
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
>
|
||||
<Flex>
|
||||
<IconButton
|
||||
aria-label={t('nodes.reorderLinearView')}
|
||||
variant="ghost"
|
||||
icon={<PiDotsSixVerticalBold />}
|
||||
{...listeners}
|
||||
{...attributes}
|
||||
mx={2}
|
||||
height="full"
|
||||
/>
|
||||
<Flex flexDir="column" w="full">
|
||||
<Flex alignItems="center">
|
||||
<EditableFieldTitle nodeId={nodeId} fieldName={fieldName} kind="input" />
|
||||
<Spacer />
|
||||
{isValueChanged && (
|
||||
@ -73,6 +95,7 @@ const LinearViewField = ({ nodeId, fieldName }: Props) => {
|
||||
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />
|
||||
<NodeSelectionOverlay isSelected={false} isHovered={isMouseOverNode} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,15 @@
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import DndSortable from 'features/dnd/components/DndSortable';
|
||||
import type { DragEndEvent } from 'features/dnd/types';
|
||||
import LinearViewField from 'features/nodes/components/flow/nodes/Invocation/fields/LinearViewField';
|
||||
import { selectWorkflowSlice } from 'features/nodes/store/workflowSlice';
|
||||
import { memo } from 'react';
|
||||
import { selectWorkflowSlice, workflowExposedFieldsReordered } from 'features/nodes/store/workflowSlice';
|
||||
import type { FieldIdentifier } from 'features/nodes/types/field';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetOpenAPISchemaQuery } from 'services/api/endpoints/appInfo';
|
||||
|
||||
@ -15,10 +19,31 @@ const WorkflowLinearTab = () => {
|
||||
const fields = useAppSelector(selector);
|
||||
const { isLoading } = useGetOpenAPISchemaQuery();
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleDragEnd = useCallback(
|
||||
(event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
const fieldsStrings = fields.map((field) => `${field.nodeId}.${field.fieldName}`);
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = fieldsStrings.indexOf(active.id as string);
|
||||
const newIndex = fieldsStrings.indexOf(over.id as string);
|
||||
|
||||
const newFields = arrayMove(fieldsStrings, oldIndex, newIndex)
|
||||
.map((field) => fields.find((obj) => `${obj.nodeId}.${obj.fieldName}` === field))
|
||||
.filter((field) => field) as FieldIdentifier[];
|
||||
|
||||
dispatch(workflowExposedFieldsReordered(newFields));
|
||||
}
|
||||
},
|
||||
[dispatch, fields]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ScrollableContent>
|
||||
<DndSortable onDragEnd={handleDragEnd} items={fields.map((field) => `${field.nodeId}.${field.fieldName}`)}>
|
||||
<Flex position="relative" flexDir="column" alignItems="flex-start" p={1} gap={2} h="full" w="full">
|
||||
{isLoading ? (
|
||||
<IAINoContentFallback label={t('nodes.loadingNodes')} icon={null} />
|
||||
@ -30,6 +55,7 @@ const WorkflowLinearTab = () => {
|
||||
<IAINoContentFallback label={t('nodes.noFieldsLinearview')} icon={null} />
|
||||
)}
|
||||
</Flex>
|
||||
</DndSortable>
|
||||
</ScrollableContent>
|
||||
</Box>
|
||||
);
|
||||
|
@ -59,6 +59,10 @@ export const workflowSlice = createSlice({
|
||||
);
|
||||
state.isTouched = true;
|
||||
},
|
||||
workflowExposedFieldsReordered: (state, action: PayloadAction<FieldIdentifier[]>) => {
|
||||
state.exposedFields = action.payload;
|
||||
state.isTouched = true;
|
||||
},
|
||||
workflowNameChanged: (state, action: PayloadAction<string>) => {
|
||||
state.name = action.payload;
|
||||
state.isTouched = true;
|
||||
@ -175,6 +179,7 @@ export const {
|
||||
workflowModeChanged,
|
||||
workflowExposedFieldAdded,
|
||||
workflowExposedFieldRemoved,
|
||||
workflowExposedFieldsReordered,
|
||||
workflowNameChanged,
|
||||
workflowCategoryChanged,
|
||||
workflowDescriptionChanged,
|
||||
|
Loading…
Reference in New Issue
Block a user