mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-01 21:55:10 -05:00
feat(ui): builder empty state (WIP)
This commit is contained in:
@@ -52,17 +52,15 @@ export const FormElementEditModeHeader = memo(
|
||||
<Spacer />
|
||||
{isContainerElement(element) && <ContainerElementSettings element={element} />}
|
||||
{isNodeFieldElement(element) && <NodeFieldElementSettings element={element} />}
|
||||
{element.parentId && (
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={removeElement}
|
||||
icon={<PiXBold />}
|
||||
variant="link"
|
||||
size="sm"
|
||||
alignSelf="stretch"
|
||||
colorScheme="error"
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={removeElement}
|
||||
icon={<PiXBold />}
|
||||
variant="link"
|
||||
size="sm"
|
||||
alignSelf="stretch"
|
||||
colorScheme="error"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export const FormElementEditModeWrapper = memo(({ element, children }: PropsWith
|
||||
data-layout={containerCtx?.layout}
|
||||
>
|
||||
<FormElementEditModeHeader ref={dragHandleRef} element={element} />
|
||||
<Flex w="full" h="full" p={4} alignItems="center" gap={4}>
|
||||
<Flex w="full" h="full" p={4} gap={4}>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
||||
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { Button, ButtonGroup, Flex } from '@invoke-ai/ui-library';
|
||||
import { Button, ButtonGroup, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { firefoxDndFix } from 'features/dnd/util';
|
||||
@@ -24,7 +24,7 @@ export const WorkflowBuilder = memo(() => {
|
||||
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<Flex w="full" justifyContent="center">
|
||||
<Flex justifyContent="center">
|
||||
<Flex flexDir="column" w={mode === 'view' ? '512px' : 'min-content'} minW="512px" gap={2}>
|
||||
<ButtonGroup isAttached={false} justifyContent="center">
|
||||
<ToggleModeButton />
|
||||
@@ -35,14 +35,43 @@ export const WorkflowBuilder = memo(() => {
|
||||
<AddFormElementDndButton type="text" />
|
||||
</ButtonGroup>
|
||||
{rootElementId && <FormElementComponent id={rootElementId} />}
|
||||
{!rootElementId && <EmptyState />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
);
|
||||
});
|
||||
|
||||
WorkflowBuilder.displayName = 'WorkflowBuilder';
|
||||
|
||||
const EmptyState = memo(() => {
|
||||
const mode = useAppSelector(selectWorkflowFormMode);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const toggleMode = useCallback(() => {
|
||||
dispatch(formModeToggled());
|
||||
}, [dispatch]);
|
||||
|
||||
const addContainer = useCallback(() => {}, []);
|
||||
|
||||
if (mode === 'view') {
|
||||
return (
|
||||
<Flex flexDir="column" gap={4} w="full" h="full" justifyContent="center" alignItems="center">
|
||||
<Text variant="subtext" fontSize="md">
|
||||
Click Edit to build a form for this workflow.
|
||||
</Text>
|
||||
<Button onClick={toggleMode}>Edit</Button>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
<Flex flexDir="column" gap={4} w="full" h="full" justifyContent="center" alignItems="center">
|
||||
<Text variant="subtext" fontSize="md">
|
||||
No form elements added. Click a button above to add a form element.
|
||||
</Text>
|
||||
</Flex>;
|
||||
});
|
||||
EmptyState.displayName = 'EmptyState';
|
||||
|
||||
const ToggleModeButton = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const mode = useAppSelector(selectWorkflowFormMode);
|
||||
@@ -57,7 +86,8 @@ ToggleModeButton.displayName = 'ToggleModeButton';
|
||||
|
||||
const useAddFormElementDnd = (
|
||||
type: Exclude<FormElement['type'], 'node-field' | 'container'> | 'row' | 'column',
|
||||
draggableRef: RefObject<HTMLElement>
|
||||
draggableRef: RefObject<HTMLElement>,
|
||||
isEnabled = true
|
||||
) => {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
@@ -70,6 +100,7 @@ const useAddFormElementDnd = (
|
||||
firefoxDndFix(draggableElement),
|
||||
draggable({
|
||||
element: draggableElement,
|
||||
canDrag: () => isEnabled,
|
||||
getInitialData: () => {
|
||||
if (type === 'row') {
|
||||
const element = buildContainer('row', []);
|
||||
@@ -101,17 +132,29 @@ const useAddFormElementDnd = (
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [draggableRef, type]);
|
||||
}, [draggableRef, isEnabled, type]);
|
||||
|
||||
return isDragging;
|
||||
};
|
||||
|
||||
const AddFormElementDndButton = ({ type }: { type: Parameters<typeof useAddFormElementDnd>[0] }) => {
|
||||
const draggableRef = useRef<HTMLButtonElement>(null);
|
||||
const draggableRef = useRef<HTMLDivElement>(null);
|
||||
const rootElementId = useAppSelector(selectRootElementId);
|
||||
const isDragging = useAddFormElementDnd(type, draggableRef);
|
||||
|
||||
return (
|
||||
<Button ref={draggableRef} variant="ghost" pointerEvents="auto" opacity={isDragging ? 0.3 : 1}>
|
||||
<Button
|
||||
ref={draggableRef}
|
||||
variant="unstyled"
|
||||
borderWidth={2}
|
||||
borderStyle="dashed"
|
||||
borderRadius="base"
|
||||
px={4}
|
||||
py={1}
|
||||
cursor="grab"
|
||||
_hover={{ bg: 'base.800' }}
|
||||
isDisabled={isDragging || (type !== 'row' && type !== 'column' && !rootElementId)}
|
||||
>
|
||||
{startCase(type)}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,6 @@ import type {
|
||||
WorkflowV3,
|
||||
} from 'features/nodes/types/workflow';
|
||||
import {
|
||||
buildContainer,
|
||||
isContainerElement,
|
||||
isHeadingElement,
|
||||
isNodeFieldElement,
|
||||
@@ -53,7 +52,6 @@ const formElementDataChangedReducer = <T extends FormElement>(
|
||||
};
|
||||
|
||||
const getBlankWorkflow = (): Omit<WorkflowV3, 'nodes' | 'edges'> => {
|
||||
const rootElement = buildContainer('column', []);
|
||||
return {
|
||||
name: '',
|
||||
author: '',
|
||||
@@ -65,12 +63,7 @@ const getBlankWorkflow = (): Omit<WorkflowV3, 'nodes' | 'edges'> => {
|
||||
exposedFields: [],
|
||||
meta: { version: '3.0.0', category: 'user' },
|
||||
id: undefined,
|
||||
form: {
|
||||
elements: {
|
||||
[rootElement.id]: rootElement,
|
||||
},
|
||||
rootElementId: rootElement.id,
|
||||
},
|
||||
form: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -194,7 +187,11 @@ export const workflowSlice = createSlice({
|
||||
return;
|
||||
}
|
||||
const { id } = action.payload;
|
||||
recursivelyRemoveElement({ id, formState: state.form });
|
||||
if (id === state.form.rootElementId) {
|
||||
state.form = undefined;
|
||||
} else {
|
||||
recursivelyRemoveElement({ id, formState: state.form });
|
||||
}
|
||||
},
|
||||
formElementMoved: (state, action: PayloadAction<{ id: string; containerId: string; index?: number }>) => {
|
||||
if (!state.form) {
|
||||
|
||||
Reference in New Issue
Block a user