From 2d922a0a65b203d4ae27c00ba4a5b14d32b9b702 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 4 Jan 2024 19:32:33 +1100 Subject: [PATCH] feat(ui): restore floating options and gallery buttons --- ...x => CancelCurrentQueueItemIconButton.tsx} | 14 +-- .../queue/components/ClearQueueButton.tsx | 45 +++----- .../ClearQueueConfirmationAlertDialog.tsx | 32 ++++++ .../queue/components/ClearQueueIconButton.tsx | 35 ++++++ .../queue/components/QueueControls.tsx | 4 +- .../ui/components/FloatingGalleryButton.tsx | 37 +++--- .../FloatingParametersPanelButtons.tsx | 57 +++++----- .../src/features/ui/components/InvokeTabs.tsx | 106 +++++++++--------- 8 files changed, 187 insertions(+), 143 deletions(-) rename invokeai/frontend/web/src/features/queue/components/{CancelCurrentQueueItemButton.tsx => CancelCurrentQueueItemIconButton.tsx} (69%) create mode 100644 invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx create mode 100644 invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemButton.tsx b/invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx similarity index 69% rename from invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemButton.tsx rename to invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx index 7fc6d89ff3..c921ca7ee6 100644 --- a/invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx @@ -1,27 +1,23 @@ import type { ChakraProps } from '@chakra-ui/react'; +import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaTimes } from 'react-icons/fa'; - -import QueueButton from './common/QueueButton'; - type Props = { - asIconButton?: boolean; sx?: ChakraProps['sx']; }; -const CancelCurrentQueueItemButton = ({ asIconButton, sx }: Props) => { +const CancelCurrentQueueItemIconButton = ({ sx }: Props) => { const { t } = useTranslation(); const { cancelQueueItem, isLoading, isDisabled } = useCancelCurrentQueueItem(); return ( - } onClick={cancelQueueItem} @@ -31,4 +27,4 @@ const CancelCurrentQueueItemButton = ({ asIconButton, sx }: Props) => { ); }; -export default memo(CancelCurrentQueueItemButton); +export default memo(CancelCurrentQueueItemIconButton); diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx index 6c606893e9..e277a45c01 100644 --- a/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx @@ -1,47 +1,34 @@ -import { type ChakraProps, useDisclosure } from '@chakra-ui/react'; -import { InvConfirmationAlertDialog } from 'common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog'; -import { InvText } from 'common/components/InvText/wrapper'; +import { useDisclosure } from '@chakra-ui/react'; +import { InvButton } from 'common/components/InvButton/InvButton'; +import type { InvButtonProps } from 'common/components/InvButton/types'; +import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useClearQueue } from 'features/queue/hooks/useClearQueue'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaTrash } from 'react-icons/fa'; -import QueueButton from './common/QueueButton'; +type Props = InvButtonProps; -type Props = { - asIconButton?: boolean; - sx?: ChakraProps['sx']; -}; - -const ClearQueueButton = ({ asIconButton, sx }: Props) => { +const ClearQueueButton = (props: Props) => { const { t } = useTranslation(); - const { isOpen, onClose, onOpen } = useDisclosure(); - const { clearQueue, isLoading, isDisabled } = useClearQueue(); + const disclosure = useDisclosure(); + const { isLoading, isDisabled } = useClearQueue(); return ( <> - } + leftIcon={} colorScheme="error" - sx={sx} - onClick={onOpen} - /> - - {t('queue.clearQueueAlertDialog')} -
- {t('queue.clearQueueAlertDialog2')} -
+ {t('queue.clear')} + + ); }; diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx new file mode 100644 index 0000000000..abaa35276a --- /dev/null +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx @@ -0,0 +1,32 @@ +import type { + UseDisclosureReturn} from '@chakra-ui/react'; +import { InvConfirmationAlertDialog } from 'common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog'; +import { InvText } from 'common/components/InvText/wrapper'; +import { useClearQueue } from 'features/queue/hooks/useClearQueue'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +type Props = { + disclosure: UseDisclosureReturn; +}; + +const ClearQueueButton = ({ disclosure }: Props) => { + const { t } = useTranslation(); + const { clearQueue } = useClearQueue(); + + return ( + + {t('queue.clearQueueAlertDialog')} +
+ {t('queue.clearQueueAlertDialog2')} +
+ ); +}; + +export default memo(ClearQueueButton); diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx new file mode 100644 index 0000000000..02e795a466 --- /dev/null +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx @@ -0,0 +1,35 @@ +import { useDisclosure } from '@chakra-ui/react'; +import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; +import type { InvIconButtonProps } from 'common/components/InvIconButton/types'; +import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; +import { useClearQueue } from 'features/queue/hooks/useClearQueue'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaTrash } from 'react-icons/fa'; + +type Props = Omit; + +const ClearQueueIconButton = (props: Props) => { + const { t } = useTranslation(); + const disclosure = useDisclosure(); + const { isLoading, isDisabled } = useClearQueue(); + + return ( + <> + } + colorScheme="error" + onClick={disclosure.onOpen} + data-testid={t('queue.clear')} + {...props} + /> + + + ); +}; + +export default memo(ClearQueueIconButton); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx index 61dd0f5473..0d0e707941 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx @@ -1,6 +1,6 @@ import { Flex, Spacer } from '@chakra-ui/react'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; -import ClearQueueButton from 'features/queue/components/ClearQueueButton'; +import ClearQueueIconButton from 'features/queue/components/ClearQueueIconButton'; import QueueFrontButton from 'features/queue/components/QueueFrontButton'; import ProgressBar from 'features/system/components/ProgressBar'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; @@ -28,7 +28,7 @@ const QueueControls = () => { {/* {isResumeEnabled && } {isPauseEnabled && } */} - + diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx index df8087a498..fb5cbebbfb 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx @@ -1,22 +1,20 @@ import { Flex } from '@chakra-ui/layout'; import { Portal } from '@chakra-ui/portal'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; +import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import type { UsePanelReturn } from 'features/ui/hooks/usePanel'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { MdPhotoLibrary } from 'react-icons/md'; type Props = { - isGalleryCollapsed: boolean; - expandGallery: () => void; + panelApi: UsePanelReturn; }; -const FloatingGalleryButton = ({ - isGalleryCollapsed, - expandGallery, -}: Props) => { +const FloatingGalleryButton = (props: Props) => { const { t } = useTranslation(); - if (!isGalleryCollapsed) { + if (!props.panelApi.isCollapsed) { return null; } @@ -27,18 +25,21 @@ const FloatingGalleryButton = ({ transform="translate(0, -50%)" minW={8} top="50%" - insetInlineEnd="1.63rem" + insetInlineEnd={0} > - } - p={0} - px={3} - h={48} - borderEndRadius={0} - /> + + } + p={0} + h={48} + borderEndRadius={0} + /> + ); diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx index 8cd6611cd6..3b99918323 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx @@ -1,40 +1,45 @@ -import type { ChakraProps } from '@chakra-ui/react'; +import { SpinnerIcon } from '@chakra-ui/icons'; +import type { SystemStyleObject } from '@chakra-ui/react'; import { Flex, Portal } from '@chakra-ui/react'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; -import CancelCurrentQueueItemButton from 'features/queue/components/CancelCurrentQueueItemButton'; -import ClearQueueButton from 'features/queue/components/ClearQueueButton'; +import CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton'; +import ClearQueueIconButton from 'features/queue/components/ClearQueueIconButton'; import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip'; import { useQueueBack } from 'features/queue/hooks/useQueueBack'; -import type { RefObject } from 'react'; -import { memo, useCallback } from 'react'; +import type { UsePanelReturn } from 'features/ui/hooks/usePanel'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaSlidersH } from 'react-icons/fa'; import { IoSparkles } from 'react-icons/io5'; -import type { ImperativePanelHandle } from 'react-resizable-panels'; +import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; +import { spinAnimationSlow } from 'theme/animations'; -const floatingButtonStyles: ChakraProps['sx'] = { +const floatingButtonStyles: SystemStyleObject = { borderStartRadius: 0, flexGrow: 1, }; type Props = { - isSidePanelCollapsed: boolean; - sidePanelRef: RefObject; + panelApi: UsePanelReturn; }; -const FloatingSidePanelButtons = ({ - isSidePanelCollapsed, - sidePanelRef, -}: Props) => { +const FloatingSidePanelButtons = (props: Props) => { const { t } = useTranslation(); const { queueBack, isLoading, isDisabled } = useQueueBack(); + const { data: queueStatus } = useGetQueueStatusQuery(); - const handleShowSidePanel = useCallback(() => { - sidePanelRef.current?.expand(); - }, [sidePanelRef]); + const queueButtonIcon = useMemo( + () => + queueStatus?.processor.is_processing ? ( + + ) : ( + + ), + [queueStatus?.processor.is_processing] + ); - if (!isSidePanelCollapsed) { + if (!props.panelApi.isCollapsed) { return null; } @@ -45,7 +50,7 @@ const FloatingSidePanelButtons = ({ transform="translate(0, -50%)" minW={8} top="50%" - insetInlineStart="5.13rem" + insetInlineStart="54px" direction="column" gap={2} h={48} @@ -54,29 +59,23 @@ const FloatingSidePanelButtons = ({ } /> } - variant="solid" - colorScheme="yellow" + icon={queueButtonIcon} + colorScheme="invokeYellow" tooltip={} sx={floatingButtonStyles} /> - + - + ); diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index 07c4167709..3ee22260cf 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -16,6 +16,8 @@ import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditor import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent'; import SettingsMenu from 'features/system/components/SettingsModal/SettingsMenu'; import StatusIndicator from 'features/system/components/StatusIndicator'; +import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton'; +import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons'; import type { UsePanelOptions } from 'features/ui/hooks/usePanel'; import { usePanel } from 'features/ui/hooks/usePanel'; import { usePanelStorage } from 'features/ui/hooks/usePanelStorage'; @@ -99,8 +101,8 @@ const enabledTabsSelector = createMemoizedSelector( } ); -export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue']; -export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue']; +export const NO_GALLERY_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue']; +export const NO_OPTIONS_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue']; const panelStyles: CSSProperties = { height: '100%', width: '100%' }; const GALLERY_MIN_SIZE_PX = 310; const GALLERY_MIN_SIZE_PCT = 20; @@ -121,6 +123,14 @@ const InvokeTabs = () => { e.target.blur(); } }, []); + const shouldShowOptionsPanel = useMemo( + () => !NO_OPTIONS_PANEL_TABS.includes(activeTabName), + [activeTabName] + ); + const shouldShowGalleryPanel = useMemo( + () => !NO_GALLERY_PANEL_TABS.includes(activeTabName), + [activeTabName] + ); const tabs = useMemo( () => @@ -185,60 +195,38 @@ const InvokeTabs = () => { const panelStorage = usePanelStorage(); - const { - ref: optionsPanelRef, - minSize: optionsPanelMinSize, - isCollapsed: isOptionsPanelCollapsed, - onCollapse: onCollapseOptionsPanel, - onExpand: onExpandOptionsPanel, - reset: resetOptionsPanel, - expand: expandOptionsPanel, - collapse: collapseOptionsPanel, - toggle: toggleOptionsPanel, - onDoubleClickHandle: onDoubleClickOptionsPanelHandle, - } = usePanel(optionsPanelUsePanelOptions); + const optionsPanel = usePanel(optionsPanelUsePanelOptions); - const { - ref: galleryPanelRef, - minSize: galleryPanelMinSize, - isCollapsed: isGalleryPanelCollapsed, - onCollapse: onCollapseGalleryPanel, - onExpand: onExpandGalleryPanel, - reset: resetGalleryPanel, - expand: expandGalleryPanel, - collapse: collapseGalleryPanel, - toggle: toggleGalleryPanel, - onDoubleClickHandle: onDoubleClickGalleryPanelHandle, - } = usePanel(galleryPanelUsePanelOptions); + const galleryPanel = usePanel(galleryPanelUsePanelOptions); - useHotkeys('g', toggleGalleryPanel, [toggleGalleryPanel]); - useHotkeys(['t', 'o'], toggleOptionsPanel, [toggleOptionsPanel]); + useHotkeys('g', galleryPanel.toggle, [galleryPanel.toggle]); + useHotkeys(['t', 'o'], optionsPanel.toggle, [optionsPanel.toggle]); useHotkeys( 'shift+r', () => { - resetOptionsPanel(); - resetGalleryPanel(); + optionsPanel.reset(); + galleryPanel.reset(); }, - [resetOptionsPanel, resetGalleryPanel] + [optionsPanel.reset, galleryPanel.reset] ); useHotkeys( 'f', () => { - if (isOptionsPanelCollapsed || isGalleryPanelCollapsed) { - expandOptionsPanel(); - expandGalleryPanel(); + if (optionsPanel.isCollapsed || galleryPanel.isCollapsed) { + optionsPanel.expand(); + galleryPanel.expand(); } else { - collapseOptionsPanel(); - collapseGalleryPanel(); + optionsPanel.collapse(); + galleryPanel.collapse(); } }, [ - isOptionsPanelCollapsed, - isGalleryPanelCollapsed, - expandOptionsPanel, - expandGalleryPanel, - collapseOptionsPanel, - collapseGalleryPanel, + optionsPanel.isCollapsed, + galleryPanel.isCollapsed, + optionsPanel.expand, + galleryPanel.expand, + optionsPanel.collapse, + galleryPanel.collapse, ] ); @@ -272,16 +260,16 @@ const InvokeTabs = () => { style={panelStyles} storage={panelStorage} > - {!NO_SIDE_PANEL_TABS.includes(activeTabName) && ( + {shouldShowOptionsPanel && ( <> {activeTabName === 'nodes' ? ( @@ -292,7 +280,7 @@ const InvokeTabs = () => { @@ -302,21 +290,21 @@ const InvokeTabs = () => { {tabPanels} - {!NO_GALLERY_TABS.includes(activeTabName) && ( + {shouldShowGalleryPanel && ( <> @@ -324,6 +312,12 @@ const InvokeTabs = () => { )} + {shouldShowOptionsPanel && ( + + )} + {shouldShowGalleryPanel && ( + + )} ); };