feat(ui): revised focus handling (attempt 1)

This commit is contained in:
psychedelicious
2024-09-26 16:30:13 +10:00
parent ea0dc09c64
commit 7631d55c2a
19 changed files with 140 additions and 228 deletions

View File

@@ -1,24 +1,24 @@
import { Divider, Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useFocusRegion } from 'common/hooks/interactionScopes';
import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons';
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList';
import { EntityListSelectedEntityActionBar } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { selectHasEntities } from 'features/controlLayers/store/selectors';
import { memo } from 'react';
import { memo, useRef } from 'react';
export const CanvasLayersPanelContent = memo(() => {
const hasEntities = useAppSelector(selectHasEntities);
const layersPanelFocusRef = useRef<HTMLDivElement>(null);
useFocusRegion('layersPanel', layersPanelFocusRef);
return (
<CanvasManagerProviderGate>
<Flex flexDir="column" gap={2} w="full" h="full">
<EntityListSelectedEntityActionBar />
<Divider py={0} />
{!hasEntities && <CanvasAddEntityButtons />}
{hasEntities && <CanvasEntityList />}
</Flex>
</CanvasManagerProviderGate>
<Flex ref={layersPanelFocusRef} flexDir="column" gap={2} w="full" h="full">
<EntityListSelectedEntityActionBar />
<Divider py={0} />
{!hasEntities && <CanvasAddEntityButtons />}
{hasEntities && <CanvasEntityList />}
</Flex>
);
});

View File

@@ -1,6 +1,6 @@
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { useFocusRegion } from 'common/hooks/interactionScopes';
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
import { CanvasAlertsSendingToGallery } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo';
@@ -35,7 +35,7 @@ export const CanvasMainPanelContent = memo(() => {
);
}, []);
useScopeOnFocus('canvas', ref);
useFocusRegion('canvas', ref);
return (
<Flex

View File

@@ -2,8 +2,8 @@ import { useDndContext } from '@dnd-kit/core';
import { Box, Button, Spacer, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import {
$canvasRightPanelTabIndex,
selectCanvasRightPanelGalleryTab,
@@ -18,9 +18,7 @@ import { useTranslation } from 'react-i18next';
export const CanvasRightPanel = memo(() => {
const { t } = useTranslation();
const ref = useRef<HTMLDivElement>(null);
const tabIndex = useStore($canvasRightPanelTabIndex);
useScopeOnFocus('gallery', ref);
const imageViewer = useImageViewer();
const onClickViewerToggleButton = useCallback(() => {
if ($canvasRightPanelTabIndex.get() !== 1) {
@@ -46,7 +44,9 @@ export const CanvasRightPanel = memo(() => {
</TabList>
<TabPanels w="full" h="full">
<TabPanel w="full" h="full" p={0} pt={3}>
<CanvasLayersPanelContent />
<CanvasManagerProviderGate>
<CanvasLayersPanelContent />
</CanvasManagerProviderGate>
</TabPanel>
<TabPanel w="full" h="full" p={0} pt={3}>
<GalleryPanelContent />

View File

@@ -1,5 +1,5 @@
import { ButtonGroup } from '@invoke-ai/ui-library';
import { useScopeOnMount } from 'common/hooks/interactionScopes';
import { useFocusRegionOnMount } from 'common/hooks/interactionScopes';
import { StagingAreaToolbarAcceptButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton';
import { StagingAreaToolbarDiscardAllButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton';
import { StagingAreaToolbarDiscardSelectedButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton';
@@ -11,7 +11,7 @@ import { StagingAreaToolbarToggleShowResultsButton } from 'features/controlLayer
import { memo } from 'react';
export const StagingAreaToolbar = memo(() => {
useScopeOnMount('stagingArea');
useFocusRegionOnMount('stagingArea');
return (
<>

View File

@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice';
import {
@@ -25,7 +25,7 @@ export const StagingAreaToolbarAcceptButton = memo(() => {
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
const imageCount = useAppSelector(selectImageCount);
const shouldShowStagedImage = useStore(canvasManager.stagingArea.$shouldShowStagedImage);
const isCanvasActive = useStore(INTERACTION_SCOPES.canvas.$isActive);
const isCanvasActive = useStore(FOCUS_REGIONS.canvas.$isFocused);
const { t } = useTranslation();

View File

@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import {
selectImageCount,
@@ -17,7 +17,7 @@ export const StagingAreaToolbarNextButton = memo(() => {
const canvasManager = useCanvasManager();
const imageCount = useAppSelector(selectImageCount);
const shouldShowStagedImage = useStore(canvasManager.stagingArea.$shouldShowStagedImage);
const isCanvasActive = useStore(INTERACTION_SCOPES.canvas.$isActive);
const isCanvasActive = useStore(FOCUS_REGIONS.canvas.$isFocused);
const { t } = useTranslation();

View File

@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import {
selectImageCount,
@@ -17,7 +17,7 @@ export const StagingAreaToolbarPrevButton = memo(() => {
const canvasManager = useCanvasManager();
const imageCount = useAppSelector(selectImageCount);
const shouldShowStagedImage = useStore(canvasManager.stagingArea.$shouldShowStagedImage);
const isCanvasActive = useStore(INTERACTION_SCOPES.canvas.$isActive);
const isCanvasActive = useStore(FOCUS_REGIONS.canvas.$isFocused);
const { t } = useTranslation();

View File

@@ -1,6 +1,6 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
@@ -11,7 +11,7 @@ import { PiArrowsOutBold } from 'react-icons/pi';
export const CanvasToolbarResetViewButton = memo(() => {
const { t } = useTranslation();
const canvasManager = useCanvasManager();
const isCanvasActive = useStore(INTERACTION_SCOPES.canvas.$isActive);
const isCanvasActive = useStore(FOCUS_REGIONS.canvas.$isFocused);
const imageViewer = useImageViewer();
useRegisteredHotkeys({

View File

@@ -1,6 +1,6 @@
import { Box, Button, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { useFocusRegion } from 'common/hooks/interactionScopes';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
import { selectBoardSearchText } from 'features/gallery/store/gallerySelectors';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
@@ -26,8 +26,8 @@ const GalleryPanelContent = () => {
const dispatch = useAppDispatch();
const boardSearchDisclosure = useDisclosure({ defaultIsOpen: !!boardSearchText.length });
const imperativePanelGroupRef = useRef<ImperativePanelGroupHandle>(null);
const ref = useRef<HTMLDivElement>(null);
useScopeOnFocus('gallery', ref);
const galleryPanelFocusRef = useRef<HTMLDivElement>(null);
useFocusRegion('galleryPanel', galleryPanelFocusRef);
const boardsListPanelOptions = useMemo<UsePanelOptions>(
() => ({
@@ -50,7 +50,7 @@ const GalleryPanelContent = () => {
}, [boardSearchText.length, boardSearchDisclosure, boardsListPanel, dispatch]);
return (
<Flex ref={ref} position="relative" flexDirection="column" h="full" w="full" tabIndex={-1}>
<Flex ref={galleryPanelFocusRef} position="relative" flexDirection="column" h="full" w="full" tabIndex={-1}>
<Flex alignItems="center" w="full">
<Flex w="25%">
<Button

View File

@@ -1,25 +1,19 @@
import { Tag, TagCloseButton, TagLabel } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { $activeScopes } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useGalleryImages } from 'features/gallery/hooks/useGalleryImages';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { $isRightPanelOpen } from 'features/ui/store/uiSlice';
import { computed } from 'nanostores';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const $isSelectAllEnabled = computed([$activeScopes, $isRightPanelOpen], (activeScopes, isGalleryPanelOpen) => {
return activeScopes.has('gallery') && !activeScopes.has('workflows') && isGalleryPanelOpen;
});
export const GallerySelectionCountTag = () => {
const dispatch = useAppDispatch();
const { selection } = useAppSelector((s) => s.gallery);
const { t } = useTranslation();
const { imageDTOs } = useGalleryImages();
const isSelectAllEnabled = useStore($isSelectAllEnabled);
const isFocusedOnGallery = useStore(FOCUS_REGIONS.galleryPanel.$isFocused);
const onClearSelection = useCallback(() => {
const firstImage = selection[0];
@@ -36,16 +30,16 @@ export const GallerySelectionCountTag = () => {
id: 'selectAllOnPage',
category: 'gallery',
callback: onSelectPage,
options: { preventDefault: true, enabled: isSelectAllEnabled },
dependencies: [onSelectPage, isSelectAllEnabled],
options: { preventDefault: true, enabled: isFocusedOnGallery },
dependencies: [onSelectPage, isFocusedOnGallery],
});
useRegisteredHotkeys({
id: 'clearSelection',
category: 'gallery',
callback: onClearSelection,
options: { enabled: selection.length > 0 && isSelectAllEnabled },
dependencies: [onClearSelection, selection, isSelectAllEnabled],
options: { enabled: selection.length > 0 && isFocusedOnGallery },
dependencies: [onClearSelection, selection, isFocusedOnGallery],
});
if (selection.length <= 1) {

View File

@@ -3,7 +3,7 @@ import { useStore } from '@nanostores/react';
import { skipToken } from '@reduxjs/toolkit/query';
import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
@@ -45,7 +45,7 @@ const CurrentImageButtons = () => {
const isUpscalingEnabled = useFeatureStatus('upscaling');
const isQueueMutationInProgress = useIsQueueMutationInProgress();
const { t } = useTranslation();
const isImageViewerActive = useStore(INTERACTION_SCOPES.imageViewer.$isActive);
const isImageViewerActive = useStore(FOCUS_REGIONS.imageViewer.$isFocused);
const { currentData: imageDTO } = useGetImageDTOQuery(lastSelectedImage?.image_name ?? skipToken);
const { recallAll, remix, recallSeed, recallPrompts, hasMetadata, hasSeed, hasPrompts, isLoadingMetadata } =

View File

@@ -1,6 +1,6 @@
import { Box, Flex, IconButton } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus, useScopeOnMount } from 'common/hooks/interactionScopes';
import { useFocusRegion, useFocusRegionOnMount } from 'common/hooks/interactionScopes';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { CanvasAlertsSendingToCanvas } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo';
import { CompareToolbar } from 'features/gallery/components/ImageViewer/CompareToolbar';
@@ -10,7 +10,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, useEffect, useRef } from 'react';
import { memo, useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
@@ -27,12 +27,8 @@ export const ImageViewer = memo(({ closeButton }: Props) => {
const hasImageToCompare = useAppSelector(selectHasImageToCompare);
const [containerRef, containerDims] = useMeasure<HTMLDivElement>();
const ref = useRef<HTMLDivElement>(null);
useScopeOnFocus('imageViewer', ref);
useScopeOnMount('imageViewer');
useEffect(() => {
ref?.current?.focus();
}, []);
useFocusRegion('imageViewer', ref);
useFocusRegionOnMount('imageViewer');
return (
<Flex

View File

@@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { $activeScopes, INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
import { $focusedRegion, FOCUS_REGIONS } from 'common/hooks/interactionScopes';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { $canvasRightPanelTab } from 'features/controlLayers/store/ephemeral';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
@@ -9,21 +9,9 @@ import { useGalleryPagination } from 'features/gallery/hooks/useGalleryPaginatio
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { $isRightPanelOpen } from 'features/ui/store/uiSlice';
import { computed } from 'nanostores';
import { useMemo } from 'react';
import { useListImagesQuery } from 'services/api/endpoints/images';
const $leftRightHotkeysEnabled = computed($activeScopes, (activeScopes) => {
// The left and right hotkeys can be used when the gallery is focused and the canvas is not focused, OR when the image viewer is focused.
return !activeScopes.has('canvas') || activeScopes.has('imageViewer');
});
const $upDownHotkeysEnabled = computed([$activeScopes, $isRightPanelOpen], (activeScopes, isGalleryPanelOpen) => {
// The up and down hotkeys can be used when the gallery is focused and the canvas is not focused, and the gallery panel is open.
return !activeScopes.has('canvas') && isGalleryPanelOpen;
});
/**
* Registers gallery hotkeys. This hook is a singleton.
*/
@@ -34,11 +22,10 @@ export const useGalleryHotkeys = () => {
const selection = useAppSelector((s) => s.gallery.selection);
const queryArgs = useAppSelector(selectListImagesQueryArgs);
const queryResult = useListImagesQuery(queryArgs);
const leftRightHotkeysEnabled = useStore($leftRightHotkeysEnabled);
const upDownHotkeysEnabled = useStore($upDownHotkeysEnabled);
const canvasRightPanelTab = useStore($canvasRightPanelTab);
const appTab = useAppSelector(selectActiveTab);
const isWorkflowsScopeActive = useStore(INTERACTION_SCOPES.workflows.$isActive);
const isWorkflowsScopeActive = useStore(FOCUS_REGIONS.workflows.$isFocused);
const focusedRegion = useStore($focusedRegion);
// When we are on the canvas tab, we need to disable the delete hotkey when the user is focused on the layers tab in
// the right hand panel, because the same hotkey is used to delete layers.
@@ -70,15 +57,8 @@ export const useGalleryHotkeys = () => {
}
handleLeftImage(false);
},
options: { preventDefault: true, enabled: leftRightHotkeysEnabled },
dependencies: [
handleLeftImage,
isOnFirstImageOfView,
goPrev,
isPrevEnabled,
queryResult.isFetching,
leftRightHotkeysEnabled,
],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' || focusedRegion === 'imageViewer' },
dependencies: [handleLeftImage, isOnFirstImageOfView, goPrev, isPrevEnabled, queryResult.isFetching, focusedRegion],
});
useRegisteredHotkeys({
@@ -93,15 +73,8 @@ export const useGalleryHotkeys = () => {
handleRightImage(false);
}
},
options: { preventDefault: true, enabled: leftRightHotkeysEnabled },
dependencies: [
isOnLastImageOfView,
goNext,
isNextEnabled,
queryResult.isFetching,
handleRightImage,
leftRightHotkeysEnabled,
],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' || focusedRegion === 'imageViewer' },
dependencies: [isOnLastImageOfView, goNext, isNextEnabled, queryResult.isFetching, handleRightImage, focusedRegion],
});
useRegisteredHotkeys({
@@ -114,8 +87,8 @@ export const useGalleryHotkeys = () => {
}
handleUpImage(false);
},
options: { preventDefault: true, enabled: upDownHotkeysEnabled },
dependencies: [handleUpImage, isOnFirstRow, goPrev, isPrevEnabled, queryResult.isFetching, upDownHotkeysEnabled],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' },
dependencies: [handleUpImage, isOnFirstRow, goPrev, isPrevEnabled, queryResult.isFetching, focusedRegion],
});
useRegisteredHotkeys({
@@ -128,8 +101,8 @@ export const useGalleryHotkeys = () => {
}
handleDownImage(false);
},
options: { preventDefault: true, enabled: upDownHotkeysEnabled },
dependencies: [isOnLastRow, goNext, isNextEnabled, queryResult.isFetching, handleDownImage, upDownHotkeysEnabled],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' },
dependencies: [isOnLastRow, goNext, isNextEnabled, queryResult.isFetching, handleDownImage, focusedRegion],
});
useRegisteredHotkeys({
@@ -142,15 +115,8 @@ export const useGalleryHotkeys = () => {
}
handleLeftImage(true);
},
options: { preventDefault: true, enabled: leftRightHotkeysEnabled },
dependencies: [
handleLeftImage,
isOnFirstImageOfView,
goPrev,
isPrevEnabled,
queryResult.isFetching,
leftRightHotkeysEnabled,
],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' || focusedRegion === 'imageViewer' },
dependencies: [handleLeftImage, isOnFirstImageOfView, goPrev, isPrevEnabled, queryResult.isFetching, focusedRegion],
});
useRegisteredHotkeys({
@@ -165,15 +131,8 @@ export const useGalleryHotkeys = () => {
handleRightImage(true);
}
},
options: { preventDefault: true, enabled: leftRightHotkeysEnabled },
dependencies: [
isOnLastImageOfView,
goNext,
isNextEnabled,
queryResult.isFetching,
handleRightImage,
leftRightHotkeysEnabled,
],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' || focusedRegion === 'imageViewer' },
dependencies: [isOnLastImageOfView, goNext, isNextEnabled, queryResult.isFetching, handleRightImage, focusedRegion],
});
useRegisteredHotkeys({
@@ -186,8 +145,8 @@ export const useGalleryHotkeys = () => {
}
handleUpImage(true);
},
options: { preventDefault: true, enabled: upDownHotkeysEnabled },
dependencies: [handleUpImage, isOnFirstRow, goPrev, isPrevEnabled, queryResult.isFetching, upDownHotkeysEnabled],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' },
dependencies: [handleUpImage, isOnFirstRow, goPrev, isPrevEnabled, queryResult.isFetching, focusedRegion],
});
useRegisteredHotkeys({
@@ -200,8 +159,8 @@ export const useGalleryHotkeys = () => {
}
handleDownImage(true);
},
options: { preventDefault: true, enabled: upDownHotkeysEnabled },
dependencies: [isOnLastRow, goNext, isNextEnabled, queryResult.isFetching, handleDownImage, upDownHotkeysEnabled],
options: { preventDefault: true, enabled: focusedRegion === 'galleryPanel' },
dependencies: [isOnLastRow, goNext, isNextEnabled, queryResult.isFetching, handleDownImage, focusedRegion],
});
useRegisteredHotkeys({
@@ -213,7 +172,12 @@ export const useGalleryHotkeys = () => {
}
dispatch(imagesToDeleteSelected(selection));
},
options: { enabled: leftRightHotkeysEnabled && isDeleteEnabledByTab && !isWorkflowsScopeActive },
dependencies: [leftRightHotkeysEnabled, isDeleteEnabledByTab, selection, isWorkflowsScopeActive],
options: {
enabled:
(focusedRegion === 'galleryPanel' || focusedRegion === 'imageViewer') &&
isDeleteEnabledByTab &&
!isWorkflowsScopeActive,
},
dependencies: [focusedRegion, isDeleteEnabledByTab, selection, isWorkflowsScopeActive],
});
};

View File

@@ -2,7 +2,7 @@ import 'reactflow/dist/style.css';
import { Flex } from '@invoke-ai/ui-library';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { useFocusRegion } from 'common/hooks/interactionScopes';
import { AddNodeCmdk } from 'features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk';
import TopPanel from 'features/nodes/components/flow/panels/TopPanel/TopPanel';
import WorkflowEditorSettings from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings';
@@ -21,7 +21,7 @@ const NodeEditor = () => {
const { data, isLoading } = useGetOpenAPISchemaQuery();
const { t } = useTranslation();
const ref = useRef<HTMLDivElement>(null);
useScopeOnFocus('workflows', ref);
useFocusRegion('workflows', ref);
return (
<Flex

View File

@@ -1,7 +1,7 @@
import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector, useAppStore } from 'app/store/storeHooks';
import { INTERACTION_SCOPES, useScopeImperativeApi, useScopeOnFocus } from 'common/hooks/interactionScopes';
import { $focusedRegion, FOCUS_REGIONS, useFocusRegion } from 'common/hooks/interactionScopes';
import { useConnection } from 'features/nodes/hooks/useConnection';
import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste';
import { useSyncExecutionState } from 'features/nodes/hooks/useExecutionState';
@@ -89,9 +89,8 @@ export const Flow = memo(() => {
const cancelConnection = useReactFlowStore(selectCancelConnection);
const updateNodeInternals = useUpdateNodeInternals();
const store = useAppStore();
const isWorkflowsActive = useStore(INTERACTION_SCOPES.workflows.$isActive);
const workflowsScopeApi = useScopeImperativeApi('workflows');
useScopeOnFocus('workflows', flowWrapper);
const isWorkflowsActive = useStore(FOCUS_REGIONS.workflows.$isFocused);
useFocusRegion('workflows', flowWrapper);
useWorkflowWatcher();
useSyncExecutionState();
@@ -129,8 +128,8 @@ export const Flow = memo(() => {
const { onCloseGlobal } = useGlobalMenuClose();
const handlePaneClick = useCallback(() => {
onCloseGlobal();
workflowsScopeApi.add();
}, [onCloseGlobal, workflowsScopeApi]);
$focusedRegion.set('workflows');
}, [onCloseGlobal]);
const onInit: OnInit = useCallback((flow) => {
$flow.set(flow);

View File

@@ -1,6 +1,5 @@
import { Box, Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { CanvasMainPanelContent } from 'features/controlLayers/components/CanvasMainPanelContent';
import { CanvasRightPanel } from 'features/controlLayers/components/CanvasRightPanel';
import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent';
@@ -40,9 +39,6 @@ const onLeftPanelCollapse = (isCollapsed: boolean) => $isLeftPanelOpen.set(!isCo
const onRightPanelCollapse = (isCollapsed: boolean) => $isRightPanelOpen.set(!isCollapsed);
export const AppContent = memo(() => {
const ref = useRef<HTMLDivElement>(null);
useScopeOnFocus('gallery', ref);
const imperativePanelGroupRef = useRef<ImperativePanelGroupHandle>(null);
const withLeftPanel = useAppSelector(selectWithLeftPanel);
@@ -119,7 +115,7 @@ export const AppContent = memo(() => {
});
return (
<Flex ref={ref} id="invoke-app-tabs" w="full" h="full" gap={4} p={4}>
<Flex id="invoke-app-tabs" w="full" h="full" gap={4} p={4}>
<VerticalNavBar />
<Flex position="relative" w="full" h="full" gap={4} minW={0}>
<PanelGroup