From 53a3dc52bc448e112b73128fe8dda3aeacf42a13 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Fri, 16 May 2025 12:00:39 +1000
Subject: [PATCH] feat(ui): viewer is a modal (wip)
---
.../src/app/components/GlobalHookIsolator.tsx | 8 +++
.../app/components/GlobalModalIsolator.tsx | 2 +
.../components/CanvasDropArea.tsx | 6 --
.../components/CanvasMainPanelContent.tsx | 2 -
.../components/CanvasRightPanel.tsx | 17 +----
.../NewSessionConfirmationAlertDialog.tsx | 9 +--
.../components/Tool/ToolBboxButton.tsx | 6 +-
.../components/Tool/ToolBrushButton.tsx | 6 +-
.../components/Tool/ToolBrushWidth.tsx | 10 ++-
.../components/Tool/ToolColorPickerButton.tsx | 6 +-
.../components/Tool/ToolEraserButton.tsx | 6 +-
.../components/Tool/ToolEraserWidth.tsx | 10 ++-
.../components/Tool/ToolFillColorPicker.tsx | 6 +-
.../components/Tool/ToolMoveButton.tsx | 6 +-
.../components/Tool/ToolRectButton.tsx | 6 +-
.../components/Tool/ToolViewButton.tsx | 6 +-
.../Toolbar/CanvasToolbarResetViewButton.tsx | 26 ++++----
.../hooks/useCanvasDeleteLayerHotkey.ts | 7 +-
.../hooks/useCanvasEntityQuickSwitchHotkey.ts | 5 +-
.../hooks/useCanvasResetLayerHotkey.ts | 6 +-
.../hooks/useCanvasUndoRedoHotkeys.tsx | 10 ++-
.../controlLayers/hooks/useEntityFilter.ts | 5 +-
.../hooks/useEntitySegmentAnything.ts | 5 +-
.../controlLayers/hooks/useEntityTransform.ts | 8 +--
.../web/src/features/dnd/DndImage.tsx | 4 ++
.../components/ImageViewer/ImageViewer.tsx | 65 ++++++++++++-------
.../components/ImageViewer/useImageViewer.ts | 3 +-
.../FloatingParametersPanelButtons.tsx | 4 +-
28 files changed, 114 insertions(+), 146 deletions(-)
diff --git a/invokeai/frontend/web/src/app/components/GlobalHookIsolator.tsx b/invokeai/frontend/web/src/app/components/GlobalHookIsolator.tsx
index 5dfcdcab5b..4be48fcd2c 100644
--- a/invokeai/frontend/web/src/app/components/GlobalHookIsolator.tsx
+++ b/invokeai/frontend/web/src/app/components/GlobalHookIsolator.tsx
@@ -10,9 +10,11 @@ import type { PartialAppConfig } from 'app/types/invokeai';
import { useFocusRegionWatcher } from 'common/hooks/focus';
import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
import { useDynamicPromptsWatcher } from 'features/dynamicPrompts/hooks/useDynamicPromptsWatcher';
+import { toggleImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast';
import { useWorkflowBuilderWatcher } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher';
import { useReadinessWatcher } from 'features/queue/store/readiness';
+import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { configChanged } from 'features/system/store/configSlice';
import { selectLanguage } from 'features/system/store/systemSelectors';
import i18n from 'i18n';
@@ -61,6 +63,12 @@ export const GlobalHookIsolator = memo(
useWorkflowBuilderWatcher();
useDynamicPromptsWatcher();
+ useRegisteredHotkeys({
+ id: 'toggleViewer',
+ category: 'viewer',
+ callback: toggleImageViewer,
+ });
+
return null;
}
);
diff --git a/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx b/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
index da982d105f..ae83f0c5c2 100644
--- a/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
+++ b/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
@@ -11,6 +11,7 @@ import { FullscreenDropzone } from 'features/dnd/FullscreenDropzone';
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { ImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
+import { ImageViewerModal } from 'features/gallery/components/ImageViewer/ImageViewer';
import { ShareWorkflowModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/ShareWorkflowModal';
import { WorkflowLibraryModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal';
import { CancelAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
@@ -58,6 +59,7 @@ export const GlobalModalIsolator = memo(() => {
+
>
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasDropArea.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasDropArea.tsx
index e988ecce68..adf21bd318 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasDropArea.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasDropArea.tsx
@@ -2,7 +2,6 @@ import { Grid, GridItem } from '@invoke-ai/ui-library';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { newCanvasEntityFromImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -19,13 +18,8 @@ const addGlobalReferenceImageFromImageDndTargetData = newCanvasEntityFromImageDn
export const CanvasDropArea = memo(() => {
const { t } = useTranslation();
- const imageViewer = useImageViewer();
const isBusy = useCanvasIsBusy();
- if (imageViewer.isOpen) {
- return null;
- }
-
return (
<>
{
-
);
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasRightPanel.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasRightPanel.tsx
index ac0c669005..c1bca93a26 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasRightPanel.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasRightPanel.tsx
@@ -11,7 +11,6 @@ import { DndDropOverlay } from 'features/dnd/DndDropOverlay';
import type { DndTargetState } from 'features/dnd/types';
import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
-import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { selectActiveTabCanvasRightPanel } from 'features/ui/store/uiSelectors';
import { activeTabCanvasRightPanelChanged } from 'features/ui/store/uiSlice';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -32,11 +31,8 @@ export const CanvasRightPanel = memo(() => {
}, [activeTab]);
const onClickViewerToggleButton = useCallback(() => {
- if (activeTab !== 'gallery') {
- dispatch(activeTabCanvasRightPanelChanged('gallery'));
- }
- imageViewer.toggle();
- }, [imageViewer, activeTab, dispatch]);
+ imageViewer.open();
+ }, [imageViewer]);
const onChangeTab = useCallback(
(index: number) => {
@@ -49,20 +45,13 @@ export const CanvasRightPanel = memo(() => {
[dispatch]
);
- useRegisteredHotkeys({
- id: 'toggleViewer',
- category: 'viewer',
- callback: imageViewer.toggle,
- dependencies: [imageViewer],
- });
-
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx
index b365abb807..f57818964c 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx
@@ -3,7 +3,6 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { buildUseBoolean } from 'common/hooks/useBoolean';
import { newCanvasSessionRequested, newGallerySessionRequested } from 'features/controlLayers/store/actions';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import {
selectSystemShouldConfirmOnNewSession,
shouldConfirmOnNewSessionToggled,
@@ -17,15 +16,13 @@ const [useNewCanvasSessionDialog] = buildUseBoolean(false);
export const useNewGallerySession = () => {
const dispatch = useAppDispatch();
- const imageViewer = useImageViewer();
const shouldConfirmOnNewSession = useAppSelector(selectSystemShouldConfirmOnNewSession);
const newSessionDialog = useNewGallerySessionDialog();
const newGallerySessionImmediate = useCallback(() => {
dispatch(newGallerySessionRequested());
- imageViewer.open();
dispatch(activeTabCanvasRightPanelChanged('gallery'));
- }, [dispatch, imageViewer]);
+ }, [dispatch]);
const newGallerySessionWithDialog = useCallback(() => {
if (shouldConfirmOnNewSession) {
@@ -40,15 +37,13 @@ export const useNewGallerySession = () => {
export const useNewCanvasSession = () => {
const dispatch = useAppDispatch();
- const imageViewer = useImageViewer();
const shouldConfirmOnNewSession = useAppSelector(selectSystemShouldConfirmOnNewSession);
const newSessionDialog = useNewCanvasSessionDialog();
const newCanvasSessionImmediate = useCallback(() => {
dispatch(newCanvasSessionRequested());
- imageViewer.close();
dispatch(activeTabCanvasRightPanelChanged('layers'));
- }, [dispatch, imageViewer]);
+ }, [dispatch]);
const newCanvasSessionWithDialog = useCallback(() => {
if (shouldConfirmOnNewSession) {
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx
index 4796092faf..30f5eb0c38 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolBboxButton = memo(() => {
const { t } = useTranslation();
const selectBbox = useSelectTool('bbox');
const isSelected = useToolIsSelected('bbox');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectBboxTool',
category: 'canvas',
callback: selectBbox,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [selectBbox, isSelected, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [selectBbox, isSelected],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx
index d9afdb18f5..1f3f2a0044 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolBrushButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('brush');
const selectBrush = useSelectTool('brush');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectBrushTool',
category: 'canvas',
callback: selectBrush,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [isSelected, selectBrush, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [isSelected, selectBrush],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushWidth.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushWidth.tsx
index a8dcb12c71..6d14493d50 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushWidth.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushWidth.tsx
@@ -16,7 +16,6 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { selectCanvasSettingsSlice, settingsBrushWidthChanged } from 'features/controlLayers/store/canvasSettingsSlice';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { clamp } from 'lodash-es';
import type { KeyboardEvent } from 'react';
@@ -69,7 +68,6 @@ const sliderDefaultValue = mapRawValueToSliderValue(50);
export const ToolBrushWidth = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
- const imageViewer = useImageViewer();
const isSelected = useToolIsSelected('brush');
const width = useAppSelector(selectBrushWidth);
const [localValue, setLocalValue] = useState(width);
@@ -133,15 +131,15 @@ export const ToolBrushWidth = memo(() => {
id: 'decrementToolWidth',
category: 'canvas',
callback: decrement,
- options: { enabled: isSelected && !imageViewer.isOpen },
- dependencies: [decrement, isSelected, imageViewer.isOpen],
+ options: { enabled: isSelected },
+ dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
- options: { enabled: isSelected && !imageViewer.isOpen },
- dependencies: [increment, isSelected, imageViewer.isOpen],
+ options: { enabled: isSelected },
+ dependencies: [increment, isSelected],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx
index 1f48b2ee72..a8c590163e 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolColorPickerButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('colorPicker');
const selectColorPicker = useSelectTool('colorPicker');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectColorPickerTool',
category: 'canvas',
callback: selectColorPicker,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [selectColorPicker, isSelected, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [selectColorPicker, isSelected],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx
index 8257523367..f9de6cc4aa 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolEraserButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('eraser');
const selectEraser = useSelectTool('eraser');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectEraserTool',
category: 'canvas',
callback: selectEraser,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [isSelected, selectEraser, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [isSelected, selectEraser],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserWidth.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserWidth.tsx
index 1bcdd69297..a0821c8776 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserWidth.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserWidth.tsx
@@ -19,7 +19,6 @@ import {
selectCanvasSettingsSlice,
settingsEraserWidthChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { clamp } from 'lodash-es';
import type { KeyboardEvent } from 'react';
@@ -72,7 +71,6 @@ const sliderDefaultValue = mapRawValueToSliderValue(50);
export const ToolEraserWidth = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
- const imageViewer = useImageViewer();
const isSelected = useToolIsSelected('eraser');
const width = useAppSelector(selectEraserWidth);
const [localValue, setLocalValue] = useState(width);
@@ -136,15 +134,15 @@ export const ToolEraserWidth = memo(() => {
id: 'decrementToolWidth',
category: 'canvas',
callback: decrement,
- options: { enabled: isSelected && !imageViewer.isOpen },
- dependencies: [decrement, isSelected, imageViewer.isOpen],
+ options: { enabled: isSelected },
+ dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
- options: { enabled: isSelected && !imageViewer.isOpen },
- dependencies: [increment, isSelected, imageViewer.isOpen],
+ options: { enabled: isSelected },
+ dependencies: [increment, isSelected],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx
index 7d1b4bdd0e..8b3de75e72 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx
@@ -14,7 +14,6 @@ import RgbaColorPicker from 'common/components/ColorPicker/RgbaColorPicker';
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
import { selectCanvasSettingsSlice, settingsColorChanged } from 'features/controlLayers/store/canvasSettingsSlice';
import type { RgbaColor } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -31,14 +30,13 @@ export const ToolColorPicker = memo(() => {
},
[dispatch]
);
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'setFillToWhite',
category: 'canvas',
callback: () => dispatch(settingsColorChanged({ r: 255, g: 255, b: 255, a: 1 })),
- options: { preventDefault: true, enabled: !imageViewer.isOpen },
- dependencies: [dispatch, imageViewer.isOpen],
+ options: { preventDefault: true },
+ dependencies: [dispatch],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx
index 63cbbbce8f..cd842e64b6 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolMoveButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('move');
const selectMove = useSelectTool('move');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectMoveTool',
category: 'canvas',
callback: selectMove,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [isSelected, selectMove, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [isSelected, selectMove],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx
index 6e9251c1ef..9302939088 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolRectButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('rect');
const selectRect = useSelectTool('rect');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectRectTool',
category: 'canvas',
callback: selectRect,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [isSelected, selectRect, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [isSelected, selectRect],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx
index f58e66b91c..f16c055fcf 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx
@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,14 +9,13 @@ export const ToolViewButton = memo(() => {
const { t } = useTranslation();
const isSelected = useToolIsSelected('view');
const selectView = useSelectTool('view');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'selectViewTool',
category: 'canvas',
callback: selectView,
- options: { enabled: !isSelected && !imageViewer.isOpen },
- dependencies: [selectView, isSelected, imageViewer.isOpen],
+ options: { enabled: !isSelected },
+ dependencies: [selectView, isSelected],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx
index 88aae8a2ba..547575cff2 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx
@@ -1,7 +1,6 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useIsRegionFocused } from 'common/hooks/focus';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -11,49 +10,48 @@ export const CanvasToolbarResetViewButton = memo(() => {
const { t } = useTranslation();
const canvasManager = useCanvasManager();
const isCanvasFocused = useIsRegionFocused('canvas');
- const imageViewer = useImageViewer();
useRegisteredHotkeys({
id: 'fitLayersToCanvas',
category: 'canvas',
callback: canvasManager.stage.fitLayersToStage,
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
useRegisteredHotkeys({
id: 'fitBboxToCanvas',
category: 'canvas',
callback: canvasManager.stage.fitBboxToStage,
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
useRegisteredHotkeys({
id: 'setZoomTo100Percent',
category: 'canvas',
callback: () => canvasManager.stage.setScale(1),
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
useRegisteredHotkeys({
id: 'setZoomTo200Percent',
category: 'canvas',
callback: () => canvasManager.stage.setScale(2),
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
useRegisteredHotkeys({
id: 'setZoomTo400Percent',
category: 'canvas',
callback: () => canvasManager.stage.setScale(4),
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
useRegisteredHotkeys({
id: 'setZoomTo800Percent',
category: 'canvas',
callback: () => canvasManager.stage.setScale(8),
- options: { enabled: isCanvasFocused && !imageViewer.isOpen, preventDefault: true },
- dependencies: [isCanvasFocused, imageViewer.isOpen],
+ options: { enabled: isCanvasFocused, preventDefault: true },
+ dependencies: [isCanvasFocused],
});
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts
index 98d9222bd7..11ac1b6897 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts
@@ -3,7 +3,6 @@ import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { entityDeleted } from 'features/controlLayers/store/canvasSlice';
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { selectActiveTab, selectActiveTabCanvasRightPanel } from 'features/ui/store/uiSelectors';
import { useCallback, useMemo } from 'react';
@@ -16,8 +15,6 @@ export function useCanvasDeleteLayerHotkey() {
const canvasRightPanelTab = useAppSelector(selectActiveTabCanvasRightPanel);
const appTab = useAppSelector(selectActiveTab);
- const imageViewer = useImageViewer();
-
const deleteSelectedLayer = useCallback(() => {
if (selectedEntityIdentifier === null) {
return;
@@ -34,7 +31,7 @@ export function useCanvasDeleteLayerHotkey() {
id: 'deleteSelected',
category: 'canvas',
callback: deleteSelectedLayer,
- options: { enabled: isDeleteEnabled && !imageViewer.isOpen },
- dependencies: [isDeleteEnabled, deleteSelectedLayer, imageViewer.isOpen],
+ options: { enabled: isDeleteEnabled },
+ dependencies: [isDeleteEnabled, deleteSelectedLayer],
});
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts
index 10b51c585f..f8ee66c289 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts
@@ -5,7 +5,6 @@ import {
selectSelectedEntityIdentifier,
} from 'features/controlLayers/store/selectors';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { useCallback, useEffect, useState } from 'react';
@@ -15,7 +14,6 @@ export const useCanvasEntityQuickSwitchHotkey = () => {
const [current, setCurrent] = useState(null);
const selected = useAppSelector(selectSelectedEntityIdentifier);
const bookmarked = useAppSelector(selectBookmarkedEntityIdentifier);
- const imageViewer = useImageViewer();
// Update prev and current when selected entity changes
useEffect(() => {
@@ -49,7 +47,6 @@ export const useCanvasEntityQuickSwitchHotkey = () => {
id: 'quickSwitch',
category: 'canvas',
callback: onQuickSwitch,
- options: { enabled: !imageViewer.isOpen },
- dependencies: [onQuickSwitch, imageViewer.isOpen],
+ dependencies: [onQuickSwitch],
});
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts
index cfa648eb9f..b62d2d7dfd 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts
@@ -6,7 +6,6 @@ import { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocke
import { entityReset } from 'features/controlLayers/store/canvasSlice';
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
import { isMaskEntityIdentifier } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { useCallback, useMemo } from 'react';
@@ -17,7 +16,6 @@ export function useCanvasResetLayerHotkey() {
const isBusy = useCanvasIsBusy();
const adapter = useEntityAdapterSafe(entityIdentifier);
const isLocked = useEntityIsLocked(entityIdentifier);
- const imageViewer = useImageViewer();
const resetSelectedLayer = useCallback(() => {
if (entityIdentifier === null || adapter === null) {
@@ -36,7 +34,7 @@ export function useCanvasResetLayerHotkey() {
id: 'resetSelected',
category: 'canvas',
callback: resetSelectedLayer,
- options: { enabled: isResetAllowed && !isBusy && !isLocked && !imageViewer.isOpen },
- dependencies: [isResetAllowed, isBusy, isLocked, resetSelectedLayer, imageViewer.isOpen],
+ options: { enabled: isResetAllowed && !isBusy && !isLocked },
+ dependencies: [isResetAllowed, isBusy, isLocked, resetSelectedLayer],
});
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx
index c5b1c03023..9e90a197e6 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx
@@ -3,7 +3,6 @@ import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { canvasRedo, canvasUndo } from 'features/controlLayers/store/canvasSlice';
import { selectCanvasMayRedo, selectCanvasMayUndo } from 'features/controlLayers/store/selectors';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
@@ -12,7 +11,6 @@ export const useCanvasUndoRedoHotkeys = () => {
useAssertSingleton('useCanvasUndoRedo');
const dispatch = useDispatch();
const isBusy = useCanvasIsBusy();
- const imageViewer = useImageViewer();
const mayUndo = useAppSelector(selectCanvasMayUndo);
const handleUndo = useCallback(() => {
@@ -22,8 +20,8 @@ export const useCanvasUndoRedoHotkeys = () => {
id: 'undo',
category: 'canvas',
callback: handleUndo,
- options: { enabled: mayUndo && !isBusy && !imageViewer.isOpen, preventDefault: true },
- dependencies: [mayUndo, isBusy, handleUndo, imageViewer.isOpen],
+ options: { enabled: mayUndo && !isBusy, preventDefault: true },
+ dependencies: [mayUndo, isBusy, handleUndo],
});
const mayRedo = useAppSelector(selectCanvasMayRedo);
@@ -34,7 +32,7 @@ export const useCanvasUndoRedoHotkeys = () => {
id: 'redo',
category: 'canvas',
callback: handleRedo,
- options: { enabled: mayRedo && !isBusy && !imageViewer.isOpen, preventDefault: true },
- dependencies: [mayRedo, handleRedo, isBusy, imageViewer.isOpen],
+ options: { enabled: mayRedo && !isBusy, preventDefault: true },
+ dependencies: [mayRedo, handleRedo, isBusy],
});
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts
index c86aa92dc2..e3da064b51 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts
@@ -5,13 +5,11 @@ import { useEntityIsEmpty } from 'features/controlLayers/hooks/useEntityIsEmpty'
import { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocked';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { isFilterableEntityIdentifier } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useCallback, useMemo } from 'react';
export const useEntityFilter = (entityIdentifier: CanvasEntityIdentifier | null) => {
const canvasManager = useCanvasManager();
const adapter = useEntityAdapterSafe(entityIdentifier);
- const imageViewer = useImageViewer();
const isBusy = useCanvasIsBusy();
const isLocked = useEntityIsLocked(entityIdentifier);
const isEmpty = useEntityIsEmpty(entityIdentifier);
@@ -52,9 +50,8 @@ export const useEntityFilter = (entityIdentifier: CanvasEntityIdentifier | null)
if (!adapter) {
return;
}
- imageViewer.close();
adapter.filterer.start();
- }, [isDisabled, entityIdentifier, canvasManager, imageViewer]);
+ }, [isDisabled, entityIdentifier, canvasManager]);
return { isDisabled, start } as const;
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts
index 0893f02119..2959bb9b96 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts
@@ -5,13 +5,11 @@ import { useEntityIsEmpty } from 'features/controlLayers/hooks/useEntityIsEmpty'
import { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocked';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { isSegmentableEntityIdentifier } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useCallback, useMemo } from 'react';
export const useEntitySegmentAnything = (entityIdentifier: CanvasEntityIdentifier | null) => {
const canvasManager = useCanvasManager();
const adapter = useEntityAdapterSafe(entityIdentifier);
- const imageViewer = useImageViewer();
const isBusy = useCanvasIsBusy();
const isLocked = useEntityIsLocked(entityIdentifier);
const isEmpty = useEntityIsEmpty(entityIdentifier);
@@ -52,9 +50,8 @@ export const useEntitySegmentAnything = (entityIdentifier: CanvasEntityIdentifie
if (!adapter) {
return;
}
- imageViewer.close();
adapter.segmentAnything.start();
- }, [isDisabled, entityIdentifier, canvasManager, imageViewer]);
+ }, [isDisabled, entityIdentifier, canvasManager]);
return { isDisabled, start } as const;
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
index 5b7e3fbc74..624cb4395d 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
@@ -5,13 +5,11 @@ import { useEntityIsEmpty } from 'features/controlLayers/hooks/useEntityIsEmpty'
import { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocked';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { isTransformableEntityIdentifier } from 'features/controlLayers/store/types';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useCallback, useMemo } from 'react';
export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | null) => {
const canvasManager = useCanvasManager();
const adapter = useEntityAdapterSafe(entityIdentifier);
- const imageViewer = useImageViewer();
const isBusy = useCanvasIsBusy();
const isLocked = useEntityIsLocked(entityIdentifier);
const isEmpty = useEntityIsEmpty(entityIdentifier);
@@ -52,9 +50,8 @@ export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | nu
if (!adapter) {
return;
}
- imageViewer.close();
await adapter.transformer.startTransform();
- }, [isDisabled, entityIdentifier, canvasManager, imageViewer]);
+ }, [isDisabled, entityIdentifier, canvasManager]);
const fitToBbox = useCallback(async () => {
if (isDisabled) {
@@ -70,11 +67,10 @@ export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | nu
if (!adapter) {
return;
}
- imageViewer.close();
await adapter.transformer.startTransform({ silent: true });
adapter.transformer.fitToBboxContain();
await adapter.transformer.applyTransform();
- }, [canvasManager, entityIdentifier, imageViewer, isDisabled]);
+ }, [canvasManager, entityIdentifier, isDisabled]);
return { isDisabled, start, fitToBbox } as const;
};
diff --git a/invokeai/frontend/web/src/features/dnd/DndImage.tsx b/invokeai/frontend/web/src/features/dnd/DndImage.tsx
index 533bc2bd4c..5e7042cde7 100644
--- a/invokeai/frontend/web/src/features/dnd/DndImage.tsx
+++ b/invokeai/frontend/web/src/features/dnd/DndImage.tsx
@@ -8,6 +8,7 @@ import type { DndDragPreviewSingleImageState } from 'features/dnd/DndDragPreview
import { createSingleImageDragPreview, setSingleImageDragPreview } from 'features/dnd/DndDragPreviewSingleImage';
import { firefoxDndFix } from 'features/dnd/util';
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
+import { $imageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { memo, useEffect, useState } from 'react';
import type { ImageDTO } from 'services/api/types';
@@ -47,6 +48,9 @@ export const DndImage = memo(({ imageDTO, asThumbnail, ...rest }: DndImage.Props
getInitialData: () => singleImageDndSource.getData({ imageDTO }, imageDTO.image_name),
onDragStart: () => {
setIsDragging(true);
+ if ($imageViewer.get()) {
+ $imageViewer.set(false);
+ }
},
onDrop: () => {
setIsDragging(false);
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
index 5f080d1ab6..4264196bca 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
@@ -1,4 +1,4 @@
-import { Box, IconButton, type SystemStyleObject } from '@invoke-ai/ui-library';
+import { Box, IconButton, type SystemStyleObject, useOutsideClick } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
@@ -9,7 +9,7 @@ import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewe
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
import { selectHasImageToCompare } from 'features/gallery/store/gallerySelectors';
import type { ReactNode } from 'react';
-import { memo } from 'react';
+import { memo, useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
@@ -43,7 +43,7 @@ export const ImageViewer = memo(({ closeButton }: Props) => {
const [containerRef, containerDims] = useMeasure();
return (
-
+
{hasImageToCompare && }
{!hasImageToCompare && }
@@ -57,17 +57,50 @@ export const ImageViewer = memo(({ closeButton }: Props) => {
ImageViewer.displayName = 'ImageViewer';
-export const GatedImageViewer = memo(() => {
+const imageViewerContainerSx: SystemStyleObject = {
+ position: 'absolute',
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ transition: 'opacity 0.15s ease',
+ opacity: 1,
+ pointerEvents: 'auto',
+ '&[data-hidden="true"]': {
+ opacity: 0,
+ pointerEvents: 'none',
+ },
+ backdropFilter: 'blur(10px) brightness(70%)',
+};
+
+const imageViewerModalSx: SystemStyleObject = {
+ position: 'absolute',
+ bg: 'base.800',
+ borderRadius: 'base',
+ top: 16,
+ right: 16,
+ bottom: 16,
+ left: 16,
+};
+
+export const ImageViewerModal = memo(() => {
+ const ref = useRef(null);
const imageViewer = useImageViewer();
+ useOutsideClick({
+ ref,
+ handler: imageViewer.close,
+ });
- if (!imageViewer.isOpen) {
- return null;
- }
-
- return } />;
+ return (
+
+
+ } />
+
+
+ );
});
-GatedImageViewer.displayName = 'GatedImageViewer';
+ImageViewerModal.displayName = 'GatedImageViewer';
const ImageViewerCloseButton = memo(() => {
const { t } = useTranslation();
@@ -87,15 +120,3 @@ const ImageViewerCloseButton = memo(() => {
});
ImageViewerCloseButton.displayName = 'ImageViewerCloseButton';
-
-const GatedImageViewerCloseButton = memo(() => {
- const imageViewer = useImageViewer();
-
- if (!imageViewer.isOpen) {
- return null;
- }
-
- return ;
-});
-
-GatedImageViewerCloseButton.displayName = 'GatedImageViewerCloseButton';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useImageViewer.ts b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useImageViewer.ts
index 854326450f..038c5ee607 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useImageViewer.ts
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useImageViewer.ts
@@ -27,9 +27,10 @@ import type { ImageDTO } from 'services/api/types';
* TODO(psyche): Figure out a better way to do handle this...
*/
let didCloseImageViewer = false;
-const api = buildUseBoolean(true);
+const api = buildUseBoolean(false);
const useImageViewerState = api[0];
export const $imageViewer = api[1];
+export const toggleImageViewer = () => $imageViewer.set(!$imageViewer.get());
export const useImageViewer = () => {
const dispatch = useAppDispatch();
diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx
index a731d40ad9..61a118881e 100644
--- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx
+++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx
@@ -2,7 +2,6 @@ import { ButtonGroup, Flex, Icon, IconButton, spinAnimation, Tooltip, useShiftMo
import { useAppSelector } from 'app/store/storeHooks';
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
-import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useCancelAllExceptCurrentQueueItemDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
import { useClearQueueDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { InvokeButtonTooltip } from 'features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip';
@@ -30,12 +29,11 @@ type Props = {
const FloatingSidePanelButtons = ({ togglePanel }: Props) => {
const { t } = useTranslation();
const tab = useAppSelector(selectActiveTab);
- const imageViewer = useImageViewer();
const isCancelAndClearAllEnabled = useFeatureStatus('cancelAndClearAll');
return (
- {tab === 'canvas' && !imageViewer.isOpen && (
+ {tab === 'canvas' && (