mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): node buttons and shadow
This commit is contained in:
parent
2ec8fd3dc7
commit
0cb886b915
@ -718,12 +718,12 @@
|
||||
"swapSizes": "Swap Sizes"
|
||||
},
|
||||
"nodes": {
|
||||
"reloadSchema": "Reload Node Templates",
|
||||
"saveGraph": "Save Workflow",
|
||||
"loadGraph": "Load Workflow",
|
||||
"resetGraph": "Reset Workflow",
|
||||
"clearGraph": "Reset Graph",
|
||||
"clearGraphDesc": "Are you sure you want to clear all nodes?",
|
||||
"reloadNodeTemplates": "Reload Node Templates",
|
||||
"saveWorkflow": "Save Workflow",
|
||||
"loadWorkflow": "Load Workflow",
|
||||
"resetWorkflow": "Reset Workflow",
|
||||
"resetWorkflowDesc": "Are you sure you want to reset this workflow?",
|
||||
"resetWorkflowDesc2": "Resetting the workflow will clear all nodes, edges and workflow details.",
|
||||
"zoomInNodes": "Zoom In",
|
||||
"zoomOutNodes": "Zoom Out",
|
||||
"fitViewportNodes": "Fit View",
|
||||
|
@ -24,34 +24,40 @@ type NodeWrapperProps = PropsWithChildren & {
|
||||
const NodeWrapper = (props: NodeWrapperProps) => {
|
||||
const { nodeId, width, children, selected } = props;
|
||||
|
||||
const selectNodeExecutionState = useMemo(
|
||||
const selectIsInProgress = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ nodes }) => nodes.nodeExecutionStates[nodeId]
|
||||
({ nodes }) =>
|
||||
nodes.nodeExecutionStates[nodeId]?.status === NodeStatus.IN_PROGRESS
|
||||
),
|
||||
[nodeId]
|
||||
);
|
||||
|
||||
const nodeExecutionState = useAppSelector(selectNodeExecutionState);
|
||||
const isInProgress = useAppSelector(selectIsInProgress);
|
||||
|
||||
const [
|
||||
nodeSelectedOutlineLight,
|
||||
nodeSelectedOutlineDark,
|
||||
nodeSelectedLight,
|
||||
nodeSelectedDark,
|
||||
nodeInProgressLight,
|
||||
nodeInProgressDark,
|
||||
shadowsXl,
|
||||
shadowsBase,
|
||||
] = useToken('shadows', [
|
||||
'nodeSelectedOutline.light',
|
||||
'nodeSelectedOutline.dark',
|
||||
'nodeSelected.light',
|
||||
'nodeSelected.dark',
|
||||
'nodeInProgress.light',
|
||||
'nodeInProgress.dark',
|
||||
'shadows.xl',
|
||||
'shadows.base',
|
||||
]);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shadow = useColorModeValue(
|
||||
nodeSelectedOutlineLight,
|
||||
nodeSelectedOutlineDark
|
||||
const selectedShadow = useColorModeValue(nodeSelectedLight, nodeSelectedDark);
|
||||
const inProgressShadow = useColorModeValue(
|
||||
nodeInProgressLight,
|
||||
nodeInProgressDark
|
||||
);
|
||||
|
||||
const opacity = useAppSelector((state) => state.nodes.nodeOpacity);
|
||||
@ -71,24 +77,9 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
||||
w: width ?? NODE_WIDTH,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
shadow: selected
|
||||
? nodeExecutionState?.status === NodeStatus.IN_PROGRESS
|
||||
? undefined
|
||||
: shadow
|
||||
: undefined,
|
||||
shadow: selected ? selectedShadow : undefined,
|
||||
cursor: 'grab',
|
||||
opacity,
|
||||
borderWidth: 2,
|
||||
borderColor:
|
||||
nodeExecutionState?.status === NodeStatus.IN_PROGRESS
|
||||
? 'warning.300'
|
||||
: 'base.200',
|
||||
_dark: {
|
||||
borderColor:
|
||||
nodeExecutionState?.status === NodeStatus.IN_PROGRESS
|
||||
? 'warning.500'
|
||||
: 'base.900',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@ -104,6 +95,22 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
borderRadius: 'md',
|
||||
pointerEvents: 'none',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
opacity: 0.7,
|
||||
shadow: isInProgress ? inProgressShadow : undefined,
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
|
@ -1,20 +1,19 @@
|
||||
import { ButtonGroup, Tooltip } from '@chakra-ui/react';
|
||||
import { ButtonGroup } from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
// shouldShowFieldTypeLegendChanged,
|
||||
shouldShowMinimapPanelChanged,
|
||||
} from 'features/nodes/store/nodesSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
FaExpand,
|
||||
// FaInfo,
|
||||
FaMapMarkerAlt,
|
||||
FaMinus,
|
||||
FaPlus,
|
||||
} from 'react-icons/fa';
|
||||
import { FaMagnifyingGlassMinus, FaMagnifyingGlassPlus } from 'react-icons/fa6';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import {
|
||||
// shouldShowFieldTypeLegendChanged,
|
||||
shouldShowMinimapPanelChanged,
|
||||
} from 'features/nodes/store/nodesSlice';
|
||||
|
||||
const ViewportControls = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -49,27 +48,24 @@ const ViewportControls = () => {
|
||||
|
||||
return (
|
||||
<ButtonGroup isAttached orientation="vertical">
|
||||
<Tooltip label={t('nodes.zoomInNodes')}>
|
||||
<IAIIconButton
|
||||
aria-label="Zoom in "
|
||||
tooltip={t('nodes.zoomInNodes')}
|
||||
aria-label={t('nodes.zoomInNodes')}
|
||||
onClick={handleClickedZoomIn}
|
||||
icon={<FaPlus />}
|
||||
icon={<FaMagnifyingGlassPlus />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('nodes.zoomOutNodes')}>
|
||||
<IAIIconButton
|
||||
aria-label="Zoom out"
|
||||
tooltip={t('nodes.zoomOutNodes')}
|
||||
aria-label={t('nodes.zoomOutNodes')}
|
||||
onClick={handleClickedZoomOut}
|
||||
icon={<FaMinus />}
|
||||
icon={<FaMagnifyingGlassMinus />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('nodes.fitViewportNodes')}>
|
||||
<IAIIconButton
|
||||
aria-label="Fit viewport"
|
||||
tooltip={t('nodes.fitViewportNodes')}
|
||||
aria-label={t('nodes.fitViewportNodes')}
|
||||
onClick={handleClickedFitView}
|
||||
icon={<FaExpand />}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/* <Tooltip
|
||||
label={
|
||||
shouldShowFieldTypeLegend
|
||||
@ -84,20 +80,21 @@ const ViewportControls = () => {
|
||||
icon={<FaInfo />}
|
||||
/>
|
||||
</Tooltip> */}
|
||||
<Tooltip
|
||||
label={
|
||||
<IAIIconButton
|
||||
tooltip={
|
||||
shouldShowMinimapPanel
|
||||
? t('nodes.hideMinimapnodes')
|
||||
: t('nodes.showMinimapnodes')
|
||||
}
|
||||
aria-label={
|
||||
shouldShowMinimapPanel
|
||||
? t('nodes.hideMinimapnodes')
|
||||
: t('nodes.showMinimapnodes')
|
||||
}
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label="Toggle minimap"
|
||||
isChecked={shouldShowMinimapPanel}
|
||||
onClick={handleClickedToggleMiniMapPanel}
|
||||
icon={<FaMapMarkerAlt />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
@ -2,9 +2,11 @@ import { FileButton } from '@mantine/core';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { useLoadWorkflowFromFile } from 'features/nodes/hooks/useLoadWorkflowFromFile';
|
||||
import { memo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaUpload } from 'react-icons/fa';
|
||||
|
||||
const LoadWorkflowButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const resetRef = useRef<() => void>(null);
|
||||
const loadWorkflowFromFile = useLoadWorkflowFromFile();
|
||||
return (
|
||||
@ -16,8 +18,8 @@ const LoadWorkflowButton = () => {
|
||||
{(props) => (
|
||||
<IAIIconButton
|
||||
icon={<FaUpload />}
|
||||
tooltip="Load Workflow"
|
||||
aria-label="Load Workflow"
|
||||
tooltip={t('nodes.loadWorkflow')}
|
||||
aria-label={t('nodes.loadWorkflow')}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
|
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FaSyncAlt } from 'react-icons/fa';
|
||||
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
|
||||
|
||||
const ReloadSchemaButton = () => {
|
||||
const ReloadNodeTemplatesButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@ -16,13 +16,13 @@ const ReloadSchemaButton = () => {
|
||||
return (
|
||||
<IAIButton
|
||||
leftIcon={<FaSyncAlt />}
|
||||
tooltip={t('nodes.reloadSchema')}
|
||||
aria-label={t('nodes.reloadSchema')}
|
||||
tooltip={t('nodes.reloadNodeTemplates')}
|
||||
aria-label={t('nodes.reloadNodeTemplates')}
|
||||
onClick={handleReloadSchema}
|
||||
>
|
||||
{t('nodes.reloadSchema')}
|
||||
{t('nodes.reloadNodeTemplates')}
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ReloadSchemaButton);
|
||||
export default memo(ReloadNodeTemplatesButton);
|
||||
|
@ -6,7 +6,10 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
Text,
|
||||
VStack,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
@ -19,7 +22,7 @@ import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
const ClearGraphButton = () => {
|
||||
const ResetWorkflowButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
@ -48,8 +51,8 @@ const ClearGraphButton = () => {
|
||||
<>
|
||||
<IAIIconButton
|
||||
icon={<FaTrash />}
|
||||
tooltip={t('nodes.clearGraph')}
|
||||
aria-label={t('nodes.clearGraph')}
|
||||
tooltip={t('nodes.resetWorkflow')}
|
||||
aria-label={t('nodes.resetWorkflow')}
|
||||
onClick={onOpen}
|
||||
isDisabled={!nodesCount}
|
||||
/>
|
||||
@ -64,18 +67,21 @@ const ClearGraphButton = () => {
|
||||
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{t('nodes.clearGraph')}
|
||||
{t('nodes.resetWorkflow')}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>
|
||||
<Text>{t('nodes.clearGraphDesc')}</Text>
|
||||
<AlertDialogBody py={4}>
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<Text>{t('nodes.resetWorkflowDesc')}</Text>
|
||||
<Text variant="subtext">{t('nodes.resetWorkflowDesc2')}</Text>
|
||||
</Flex>
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button ref={cancelRef} onClick={onClose}>
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button colorScheme="red" ml={3} onClick={handleConfirmClear}>
|
||||
<Button colorScheme="error" ml={3} onClick={handleConfirmClear}>
|
||||
{t('common.accept')}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
@ -85,4 +91,4 @@ const ClearGraphButton = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ClearGraphButton);
|
||||
export default memo(ResetWorkflowButton);
|
@ -0,0 +1,29 @@
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { useWorkflow } from 'features/nodes/hooks/useWorkflow';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
|
||||
const SaveWorkflowButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const workflow = useWorkflow();
|
||||
const handleSave = useCallback(() => {
|
||||
const blob = new Blob([JSON.stringify(workflow, null, 2)]);
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = `${workflow.name || 'My Workflow'}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}, [workflow]);
|
||||
return (
|
||||
<IAIIconButton
|
||||
icon={<FaSave />}
|
||||
tooltip={t('nodes.saveWorkflow')}
|
||||
aria-label={t('nodes.saveWorkflow')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SaveWorkflowButton);
|
@ -1,16 +1,8 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { Panel } from 'reactflow';
|
||||
import WorkflowEditorControls from './WorkflowEditorControls';
|
||||
|
||||
const TopCenterPanel = () => {
|
||||
return (
|
||||
<Panel position="top-center">
|
||||
<Flex gap={2}>
|
||||
<WorkflowEditorControls />
|
||||
</Flex>
|
||||
</Panel>
|
||||
);
|
||||
return <Panel position="top-center">{null}</Panel>;
|
||||
};
|
||||
|
||||
export default memo(TopCenterPanel);
|
||||
|
@ -1,15 +1,17 @@
|
||||
import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton';
|
||||
import InvokeButton from 'features/parameters/components/ProcessButtons/InvokeButton';
|
||||
import { memo } from 'react';
|
||||
import ClearGraphButton from './ClearGraphButton';
|
||||
import ResetWorkflowButton from './ResetWorkflowButton';
|
||||
import LoadWorkflowButton from './LoadWorkflowButton';
|
||||
import SaveWorkflowButton from './SaveWorkflowButton';
|
||||
|
||||
const WorkflowEditorControls = () => {
|
||||
return (
|
||||
<>
|
||||
<InvokeButton />
|
||||
<CancelButton />
|
||||
<ClearGraphButton />
|
||||
<ResetWorkflowButton />
|
||||
<SaveWorkflowButton />
|
||||
<LoadWorkflowButton />
|
||||
</>
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { addNodePopoverOpened } from 'features/nodes/store/nodesSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { FaPlus } from 'react-icons/fa';
|
||||
import { Panel } from 'reactflow';
|
||||
|
||||
const TopLeftPanel = () => {
|
||||
@ -14,15 +14,12 @@ const TopLeftPanel = () => {
|
||||
|
||||
return (
|
||||
<Panel position="top-left">
|
||||
<Tooltip label="Add New Node (Shift+A, Space)">
|
||||
<IAIButton
|
||||
size="sm"
|
||||
<IAIIconButton
|
||||
tooltip="Add Node (Shift+A, Space)"
|
||||
aria-label="Add Node"
|
||||
icon={<FaPlus />}
|
||||
onClick={handleOpenAddNodePopover}
|
||||
>
|
||||
Add Node
|
||||
</IAIButton>
|
||||
</Tooltip>
|
||||
/>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
import { ChangeEvent, memo, useCallback } from 'react';
|
||||
import { FaCog } from 'react-icons/fa';
|
||||
import { SelectionMode } from 'reactflow';
|
||||
import ReloadSchemaButton from '../TopCenterPanel/ReloadSchemaButton';
|
||||
import ReloadNodeTemplatesButton from '../TopCenterPanel/ReloadSchemaButton';
|
||||
|
||||
const formLabelProps: FormLabelProps = {
|
||||
fontWeight: 600,
|
||||
@ -163,7 +163,7 @@ const WorkflowEditorSettings = () => {
|
||||
label="Validate Connections and Graph"
|
||||
helperText="Prevent invalid connections from being made, and invalid graphs from being invoked"
|
||||
/>
|
||||
<ReloadSchemaButton />
|
||||
<ReloadNodeTemplatesButton />
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
|
@ -1,26 +1,10 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||
import { buildWorkflow } from 'features/nodes/util/buildWorkflow';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
|
||||
const useWatchWorkflow = () => {
|
||||
const nodes = useAppSelector((state: RootState) => state.nodes);
|
||||
const [debouncedNodes] = useDebounce(nodes, 300);
|
||||
const workflow = useMemo(
|
||||
() => buildWorkflow(debouncedNodes),
|
||||
[debouncedNodes]
|
||||
);
|
||||
|
||||
return {
|
||||
workflow,
|
||||
};
|
||||
};
|
||||
import { useWorkflow } from 'features/nodes/hooks/useWorkflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
const WorkflowJSONTab = () => {
|
||||
const { workflow } = useWatchWorkflow();
|
||||
const workflow = useWorkflow();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
@ -10,6 +10,7 @@ import { memo } from 'react';
|
||||
import WorkflowGeneralTab from './WorkflowGeneralTab';
|
||||
import WorkflowLinearTab from './WorkflowLinearTab';
|
||||
import WorkflowJSONTab from './WorkflowJSONTab';
|
||||
import WorkflowEditorControls from '../../flow/panels/TopCenterPanel/WorkflowEditorControls';
|
||||
|
||||
const WorkflowPanel = () => {
|
||||
return (
|
||||
@ -21,8 +22,12 @@ const WorkflowPanel = () => {
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
p: 4,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<WorkflowEditorControls />
|
||||
</Flex>
|
||||
<Tabs
|
||||
variant="line"
|
||||
sx={{ display: 'flex', flexDir: 'column', w: 'full', h: 'full' }}
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { buildWorkflow } from 'features/nodes/util/buildWorkflow';
|
||||
import { useMemo } from 'react';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
|
||||
export const useWorkflow = () => {
|
||||
const nodes = useAppSelector((state: RootState) => state.nodes);
|
||||
const [debouncedNodes] = useDebounce(nodes, 300);
|
||||
const workflow = useMemo(
|
||||
() => buildWorkflow(debouncedNodes),
|
||||
[debouncedNodes]
|
||||
);
|
||||
|
||||
return workflow;
|
||||
};
|
@ -600,9 +600,9 @@ const nodesSlice = createSlice({
|
||||
state.workflow.contact = action.payload;
|
||||
},
|
||||
workflowLoaded: (state, action: PayloadAction<Workflow>) => {
|
||||
// TODO: validation
|
||||
const { nodes, edges, ...workflow } = action.payload;
|
||||
state.workflow = workflow;
|
||||
|
||||
state.nodes = applyNodeChanges(
|
||||
nodes.map((node) => ({
|
||||
item: { ...node, dragHandle: `.${DRAG_HANDLE_CLASSNAME}` },
|
||||
@ -614,6 +614,16 @@ const nodesSlice = createSlice({
|
||||
edges.map((edge) => ({ item: edge, type: 'add' })),
|
||||
[]
|
||||
);
|
||||
|
||||
state.nodeExecutionStates = nodes.reduce<
|
||||
Record<string, NodeExecutionState>
|
||||
>((acc, node) => {
|
||||
acc[node.id] = {
|
||||
nodeId: node.id,
|
||||
...initialNodeExecutionState,
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
workflowReset: (state) => {
|
||||
state.workflow = cloneDeep(initialWorkflow);
|
||||
|
@ -807,7 +807,7 @@ export const zSemVer = z.string().refine((val) => {
|
||||
export type SemVer = z.infer<typeof zSemVer>;
|
||||
|
||||
export const zWorkflow = z.object({
|
||||
name: z.string().trim().min(1),
|
||||
name: z.string(),
|
||||
author: z.string(),
|
||||
description: z.string(),
|
||||
version: z.string(),
|
||||
|
@ -27,6 +27,7 @@ import { MdCancel, MdCancelScheduleSend } from 'react-icons/md';
|
||||
|
||||
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||
import { sessionCanceled } from 'services/api/thunks/session';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
||||
const cancelButtonSelector = createSelector(
|
||||
systemSelector,
|
||||
@ -49,15 +50,14 @@ const cancelButtonSelector = createSelector(
|
||||
}
|
||||
);
|
||||
|
||||
interface CancelButtonProps {
|
||||
type Props = Omit<ButtonProps, 'aria-label'> & {
|
||||
btnGroupWidth?: string | number;
|
||||
}
|
||||
asIconButton?: boolean;
|
||||
};
|
||||
|
||||
const CancelButton = (
|
||||
props: CancelButtonProps & Omit<ButtonProps, 'aria-label'>
|
||||
) => {
|
||||
const CancelButton = (props: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { btnGroupWidth = 'auto', ...rest } = props;
|
||||
const { btnGroupWidth = 'auto', asIconButton = false, ...rest } = props;
|
||||
const {
|
||||
isProcessing,
|
||||
isConnected,
|
||||
@ -124,6 +124,7 @@ const CancelButton = (
|
||||
|
||||
return (
|
||||
<ButtonGroup isAttached width={btnGroupWidth}>
|
||||
{asIconButton ? (
|
||||
<IAIIconButton
|
||||
icon={cancelIcon}
|
||||
tooltip={cancelLabel}
|
||||
@ -134,6 +135,20 @@ const CancelButton = (
|
||||
id="cancel-button"
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<IAIButton
|
||||
leftIcon={cancelIcon}
|
||||
tooltip={cancelLabel}
|
||||
aria-label={cancelLabel}
|
||||
isDisabled={!isConnected || !isProcessing || !isCancelable}
|
||||
onClick={handleClickCancel}
|
||||
colorScheme="error"
|
||||
id="cancel-button"
|
||||
{...rest}
|
||||
>
|
||||
Cancel
|
||||
</IAIButton>
|
||||
)}
|
||||
<Menu closeOnSelect={false}>
|
||||
<MenuButton
|
||||
as={IAIIconButton}
|
||||
|
@ -56,7 +56,7 @@ const FloatingSidePanelButtons = ({
|
||||
icon={<FaSlidersH />}
|
||||
/>
|
||||
<InvokeButton asIconButton sx={floatingButtonStyles} />
|
||||
<CancelButton sx={floatingButtonStyles} />
|
||||
<CancelButton sx={floatingButtonStyles} asIconButton />
|
||||
</Flex>
|
||||
</Portal>
|
||||
);
|
||||
|
@ -107,10 +107,15 @@ export const theme: ThemeOverride = {
|
||||
'0px 0px 0px 1px var(--invokeai-colors-base-150), 0px 0px 0px 3px var(--invokeai-colors-accent-500)',
|
||||
dark: '0px 0px 0px 1px var(--invokeai-colors-base-900), 0px 0px 0px 3px var(--invokeai-colors-accent-400)',
|
||||
},
|
||||
nodeSelectedOutline: {
|
||||
nodeSelected: {
|
||||
light: `0 0 0 2px var(--invokeai-colors-accent-400)`,
|
||||
dark: `0 0 0 2px var(--invokeai-colors-accent-500)`,
|
||||
},
|
||||
nodeInProgress: {
|
||||
light:
|
||||
'0 0 4px 2px var(--invokeai-colors-accent-500), 0 0 15px 4px var(--invokeai-colors-accent-600)',
|
||||
dark: '0 0 4px 2px var(--invokeai-colors-accent-400), 0 0 15px 4px var(--invokeai-colors-accent-400)',
|
||||
},
|
||||
},
|
||||
colors: InvokeAIColors,
|
||||
components: {
|
||||
|
Loading…
Reference in New Issue
Block a user