feat(ui): add loading state for builder

This commit is contained in:
psychedelicious
2025-02-21 10:54:06 +10:00
parent 012054acaa
commit 3f18bfed4e
4 changed files with 37 additions and 11 deletions

View File

@@ -1004,6 +1004,7 @@
"unknownOutput": "Unknown output: {{name}}",
"updateNode": "Update Node",
"updateApp": "Update App",
"loadingTemplates": "Loading {{name}}",
"updateAllNodes": "Update Nodes",
"allNodesUpdated": "All Nodes Updated",
"unableToUpdateNodes_one": "Unable to update {{count}} node",

View File

@@ -2,12 +2,15 @@ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Button, Flex, Spacer } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { firefoxDndFix } from 'features/dnd/util';
import { FormElementComponent } from 'features/nodes/components/sidePanel/builder/ContainerElementComponent';
import { buildFormElementDndData, useBuilderDndMonitor } from 'features/nodes/components/sidePanel/builder/dnd-hooks';
import { WorkflowBuilderEditMenu } from 'features/nodes/components/sidePanel/builder/WorkflowBuilderMenu';
import { $hasTemplates } from 'features/nodes/store/nodesSlice';
import { selectFormRootElementId, selectIsFormEmpty } from 'features/nodes/store/workflowSlice';
import type { FormElement } from 'features/nodes/types/workflow';
import { buildContainer, buildDivider, buildHeading, buildText } from 'features/nodes/types/workflow';
@@ -15,6 +18,7 @@ import { startCase } from 'lodash-es';
import type { RefObject } from 'react';
import { memo, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetOpenAPISchemaQuery } from 'services/api/endpoints/appInfo';
import { assert } from 'tsafe';
const sx: SystemStyleObject = {
@@ -28,8 +32,7 @@ const sx: SystemStyleObject = {
export const WorkflowBuilder = memo(() => {
const { t } = useTranslation();
const rootElementId = useAppSelector(selectFormRootElementId);
const isFormEmpty = useAppSelector(selectIsFormEmpty);
useBuilderDndMonitor();
return (
@@ -47,9 +50,7 @@ export const WorkflowBuilder = memo(() => {
<WorkflowBuilderEditMenu />
</Flex>
<ScrollableContent>
<Flex sx={sx} data-is-empty={isFormEmpty}>
<FormElementComponent id={rootElementId} />
</Flex>
<WorkflowBuilderContent />
</ScrollableContent>
</Flex>
</Flex>
@@ -57,6 +58,25 @@ export const WorkflowBuilder = memo(() => {
});
WorkflowBuilder.displayName = 'WorkflowBuilder';
const WorkflowBuilderContent = memo(() => {
const { t } = useTranslation();
const rootElementId = useAppSelector(selectFormRootElementId);
const isFormEmpty = useAppSelector(selectIsFormEmpty);
const openApiSchemaQuery = useGetOpenAPISchemaQuery();
const loadedTemplates = useStore($hasTemplates);
if (openApiSchemaQuery.isLoading || !loadedTemplates) {
return <IAINoContentFallback label={t('nodes.loadingNodes')} icon={null} />;
}
return (
<Flex sx={sx} data-is-empty={isFormEmpty}>
<FormElementComponent id={rootElementId} />
</Flex>
);
});
WorkflowBuilderContent.displayName = 'WorkflowBuilderContent';
const useAddFormElementDnd = (
type: Exclude<FormElement['type'], 'node-field'>,
draggableRef: RefObject<HTMLElement>

View File

@@ -10,7 +10,7 @@ export const EmptyState = () => {
const isCleanEditor = useAppSelector(selectCleanEditor);
return (
<Flex w="full" userSelect="none" justifyContent="center">
<Flex w="full" h="full" userSelect="none" justifyContent="center">
<Flex
alignItems="center"
justifyContent="center"

View File

@@ -1,9 +1,11 @@
import { Box, Flex } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { FormElementComponent } from 'features/nodes/components/sidePanel/builder/ContainerElementComponent';
import { EmptyState } from 'features/nodes/components/sidePanel/viewMode/EmptyState';
import { $hasTemplates } from 'features/nodes/store/nodesSlice';
import { selectFormRootElementId, selectIsFormEmpty } from 'features/nodes/store/workflowSlice';
import { t } from 'i18next';
import { memo } from 'react';
@@ -13,9 +15,7 @@ export const ViewModeLeftPanelContent = memo(() => {
return (
<Box position="relative" w="full" h="full">
<ScrollableContent>
<Flex flexDir="column" w="full" maxW="768px">
<ViewModeLeftPanelContentInner />
</Flex>
<ViewModeLeftPanelContentInner />
</ScrollableContent>
</Box>
);
@@ -24,10 +24,11 @@ ViewModeLeftPanelContent.displayName = 'ViewModeLeftPanelContent';
const ViewModeLeftPanelContentInner = memo(() => {
const { isLoading } = useGetOpenAPISchemaQuery();
const loadedTemplates = useStore($hasTemplates);
const rootElementId = useAppSelector(selectFormRootElementId);
const isFormEmpty = useAppSelector(selectIsFormEmpty);
if (isLoading) {
if (isLoading || !loadedTemplates) {
return <IAINoContentFallback label={t('nodes.loadingNodes')} icon={null} />;
}
@@ -35,6 +36,10 @@ const ViewModeLeftPanelContentInner = memo(() => {
return <EmptyState />;
}
return <FormElementComponent id={rootElementId} />;
return (
<Flex flexDir="column" w="full" maxW="768px">
<FormElementComponent id={rootElementId} />
</Flex>
);
});
ViewModeLeftPanelContentInner.displayName = ' ViewModeLeftPanelContentInner';