mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-13 22:14:59 -05:00
refactor(ui): even more better focus handling
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { Box, type BoxProps, type SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { type FocusRegionName, useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
|
||||
import type { IDockviewPanelProps, IGridviewPanelProps } from 'dockview';
|
||||
import { selectSystemShouldEnableHighlightFocusedRegions } from 'features/system/store/systemSlice';
|
||||
import type { PanelParameters } from 'features/ui/layouts/auto-layout-context';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { memo, useMemo, useRef } from 'react';
|
||||
|
||||
interface FocusRegionWrapperProps extends BoxProps {
|
||||
@@ -9,8 +12,11 @@ interface FocusRegionWrapperProps extends BoxProps {
|
||||
focusOnMount?: boolean;
|
||||
}
|
||||
|
||||
const BASE_FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
export const BASE_FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
p: 2,
|
||||
'&[data-highlighted="true"]::after': {
|
||||
borderColor: 'invokeBlue.300',
|
||||
},
|
||||
@@ -23,7 +29,6 @@ const BASE_FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
border: '1px solid',
|
||||
borderColor: 'transparent',
|
||||
pointerEvents: 'none',
|
||||
// transition: 'border-color 0.1s ease-in-out',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -50,3 +55,26 @@ export const FocusRegionWrapper = memo((props: FocusRegionWrapperProps) => {
|
||||
});
|
||||
|
||||
FocusRegionWrapper.displayName = 'FocusRegionWrapper';
|
||||
|
||||
export const AutoLayoutPanelContainer = memo(
|
||||
(
|
||||
props:
|
||||
| PropsWithChildren<IDockviewPanelProps<PanelParameters>>
|
||||
| PropsWithChildren<IGridviewPanelProps<PanelParameters>>
|
||||
) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const shouldHighlightFocusedRegions = useAppSelector(selectSystemShouldEnableHighlightFocusedRegions);
|
||||
|
||||
useFocusRegion(props.params.focusRegion, ref);
|
||||
|
||||
const isFocused = useIsRegionFocused(props.params.focusRegion);
|
||||
const isHighlighted = isFocused && shouldHighlightFocusedRegions;
|
||||
|
||||
return (
|
||||
<Box ref={ref} tabIndex={-1} sx={BASE_FOCUS_REGION_STYLES} data-highlighted={isHighlighted}>
|
||||
{props.children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
);
|
||||
AutoLayoutPanelContainer.displayName = 'AutoLayoutPanelContainer';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons';
|
||||
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList';
|
||||
import { EntityListSelectedEntityActionBar } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar';
|
||||
@@ -15,14 +14,14 @@ export const CanvasLayersPanel = memo(() => {
|
||||
|
||||
return (
|
||||
<CanvasManagerProviderGate>
|
||||
<FocusRegionWrapper region="layers" as={Flex} flexDir="column" gap={2} w="full" h="full" p={2}>
|
||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||
<EntityListSelectedEntityActionBar />
|
||||
<Divider py={0} />
|
||||
<ParamDenoisingStrength />
|
||||
<Divider py={0} />
|
||||
{!hasEntities && <CanvasAddEntityButtons />}
|
||||
{hasEntities && <CanvasEntityList />}
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Button, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import { WORKSPACE_PANEL_ID } from 'features/ui/layouts/shared';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -18,7 +17,7 @@ export const CanvasLaunchpadPanel = memo(() => {
|
||||
ctx.focusPanel(WORKSPACE_PANEL_ID);
|
||||
}, [ctx]);
|
||||
return (
|
||||
<FocusRegionWrapper region="launchpad" as={Flex} flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
|
||||
<Heading mb={4}>{t('ui.launchpad.canvasTitle')}</Heading>
|
||||
<Flex flexDir="column" gap={8}>
|
||||
@@ -44,7 +43,7 @@ export const CanvasLaunchpadPanel = memo(() => {
|
||||
<LaunchpadUseALayoutImageButton extraAction={focusCanvas} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CanvasLaunchpadPanel.displayName = 'CanvasLaunchpadPanel';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Alert, Button, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { InitialStateMainModelPicker } from 'features/controlLayers/components/SimpleSession/InitialStateMainModelPicker';
|
||||
import { LaunchpadAddStyleReference } from 'features/controlLayers/components/SimpleSession/LaunchpadAddStyleReference';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
@@ -15,7 +14,7 @@ export const GenerateLaunchpadPanel = memo(() => {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="launchpad" as={Flex} flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
|
||||
<Heading mb={4}>Generate images from text prompts.</Heading>
|
||||
<Flex flexDir="column" gap={8}>
|
||||
@@ -47,7 +46,7 @@ export const GenerateLaunchpadPanel = memo(() => {
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
GenerateLaunchpadPanel.displayName = 'GenerateLaunchpad';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Button, Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
|
||||
import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
|
||||
import { useNewWorkflow } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog';
|
||||
@@ -46,7 +45,7 @@ export const WorkflowsLaunchpadPanel = memo(() => {
|
||||
});
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="launchpad" as={Flex} flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
|
||||
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
|
||||
<Heading>{t('ui.launchpad.workflowsTitle')}</Heading>
|
||||
|
||||
@@ -102,7 +101,7 @@ export const WorkflowsLaunchpadPanel = memo(() => {
|
||||
</LaunchpadButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Box, Button, Collapse, Divider, Flex, IconButton } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { useDisclosure } from 'common/hooks/useBoolean';
|
||||
import { BoardsListWrapper } from 'features/gallery/components/Boards/BoardsList/BoardsListWrapper';
|
||||
import { BoardsSearch } from 'features/gallery/components/Boards/BoardsList/BoardsSearch';
|
||||
@@ -46,7 +45,7 @@ export const BoardsPanel = memo(() => {
|
||||
}, [boardSearchText.length, searchDisclosure, collapsibleApi, dispatch]);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="boards" as={Flex} flexDir="column" w="full" h="full" p={2}>
|
||||
<Flex flexDir="column" w="full" h="full">
|
||||
<Flex alignItems="center" justifyContent="space-between" w="full">
|
||||
<Flex flexGrow={1} flexBasis={0}>
|
||||
<Button
|
||||
@@ -82,7 +81,7 @@ export const BoardsPanel = memo(() => {
|
||||
</Collapse>
|
||||
<Divider pt={2} />
|
||||
<BoardsListWrapper />
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
BoardsPanel.displayName = 'BoardsPanel';
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Box, Button, ButtonGroup, Collapse, Divider, Flex, IconButton, Spacer }
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { useDisclosure } from 'common/hooks/useBoolean';
|
||||
import { useGallerySearchTerm } from 'features/gallery/components/ImageGrid/useGallerySearchTerm';
|
||||
import { selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
|
||||
@@ -68,17 +67,7 @@ export const GalleryPanel = memo(() => {
|
||||
const boardName = useBoardName(selectedBoardId);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper
|
||||
region="gallery"
|
||||
as={Flex}
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
h="full"
|
||||
w="full"
|
||||
minH={0}
|
||||
p={2}
|
||||
>
|
||||
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full" minH={0}>
|
||||
<Flex gap={2} fontSize="sm" alignItems="center" w="full">
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -134,7 +123,7 @@ export const GalleryPanel = memo(() => {
|
||||
<Flex w="full" h="full" pt={2}>
|
||||
<NewGallery />
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
GalleryPanel.displayName = 'Gallery';
|
||||
GalleryPanel.displayName = 'GalleryPanel';
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { ProgressIndicator } from './ProgressIndicator';
|
||||
|
||||
export const GenerationProgressPanel = memo(() => (
|
||||
<FocusRegionWrapper
|
||||
region="progress"
|
||||
as={Flex}
|
||||
position="relative"
|
||||
flexDir="column"
|
||||
w="full"
|
||||
h="full"
|
||||
overflow="hidden"
|
||||
p={2}
|
||||
>
|
||||
<ProgressImage />
|
||||
<ProgressIndicator position="absolute" top={6} right={6} size={8} />
|
||||
</FocusRegionWrapper>
|
||||
));
|
||||
export const GenerationProgressPanel = memo(() => {
|
||||
return (
|
||||
<Flex position="relative" flexDir="column" w="full" h="full" overflow="hidden">
|
||||
<ProgressImage />
|
||||
<ProgressIndicator position="absolute" top={6} right={6} size={8} />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
GenerationProgressPanel.displayName = 'GenerationProgressPanel';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
|
||||
import { memo } from 'react';
|
||||
@@ -9,11 +8,11 @@ import { ImageViewerContextProvider } from './context';
|
||||
export const ImageViewerPanel = memo(() => {
|
||||
return (
|
||||
<ImageViewerContextProvider>
|
||||
<FocusRegionWrapper region="viewer" as={Flex} flexDir="column" w="full" h="full" overflow="hidden" p={2} gap={2}>
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" gap={2}>
|
||||
<ViewerToolbar />
|
||||
<Divider />
|
||||
<ImageViewer />
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
</ImageViewerContextProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { ReactFlowProvider } from '@xyflow/react';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { AddNodeCmdk } from 'features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk';
|
||||
import { TopCenterPanel } from 'features/nodes/components/flow/panels/TopPanel/TopCenterPanel';
|
||||
@@ -22,9 +21,7 @@ const NodeEditor = () => {
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<FocusRegionWrapper
|
||||
region="workflows"
|
||||
as={Flex}
|
||||
<Flex
|
||||
bg="base.900"
|
||||
display="flex"
|
||||
position="relative"
|
||||
@@ -46,7 +43,7 @@ const NodeEditor = () => {
|
||||
)}
|
||||
<WorkflowEditorSettings />
|
||||
{isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={PiFlowArrowBold} />}
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { EditModeLeftPanelContent } from 'features/nodes/components/sidePanel/EditModeLeftPanelContent';
|
||||
import { PublishedWorkflowPanelContent } from 'features/nodes/components/sidePanel/PublishedWorkflowPanelContent';
|
||||
import { $isInPublishFlow, useIsWorkflowPublished } from 'features/nodes/components/sidePanel/workflow/publish';
|
||||
@@ -20,7 +19,7 @@ const WorkflowsTabLeftPanel = () => {
|
||||
const isInPublishFlow = useStore($isInPublishFlow);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="settings" as={Flex} flexDir="column" w="full" h="full" gap={2} p={2}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Flex w="full" h="full" gap={2} flexDir="column">
|
||||
{isInPublishFlow && <PublishWorkflowPanelContent />}
|
||||
@@ -30,7 +29,7 @@ const WorkflowsTabLeftPanel = () => {
|
||||
{!isInPublishFlow && !isPublished && mode === 'edit' && <EditModeLeftPanelContent />}
|
||||
{isPublished && <PublishedWorkflowPanelContent />}
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { ParametersPanelCanvas } from 'features/ui/components/ParametersPanels/ParametersPanelCanvas';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const CanvasTabLeftPanel = memo(() => {
|
||||
return (
|
||||
<FocusRegionWrapper region="settings" as={Flex} flexDir="column" w="full" h="full" gap={2} p={2}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ParametersPanelCanvas />
|
||||
</Box>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CanvasTabLeftPanel.displayName = 'CanvasTabLeftPanel';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ContextMenu, Divider, Flex, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { CanvasAlertsInvocationProgress } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsInvocationProgress';
|
||||
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
|
||||
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
|
||||
@@ -56,9 +55,7 @@ export const CanvasWorkspacePanel = memo(() => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper
|
||||
region="canvas"
|
||||
as={Flex}
|
||||
<Flex
|
||||
borderRadius="base"
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
@@ -68,7 +65,6 @@ export const CanvasWorkspacePanel = memo(() => {
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
overflow="hidden"
|
||||
p={2}
|
||||
>
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasToolbar />
|
||||
@@ -136,7 +132,7 @@ export const CanvasWorkspacePanel = memo(() => {
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasDropArea />
|
||||
</CanvasManagerProviderGate>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CanvasWorkspacePanel.displayName = 'CanvasPanel';
|
||||
CanvasWorkspacePanel.displayName = 'CanvasWorkspacePanel';
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { ParametersPanelGenerate } from 'features/ui/components/ParametersPanels/ParametersPanelGenerate';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const GenerateTabLeftPanel = memo(() => {
|
||||
return (
|
||||
<FocusRegionWrapper region="settings" as={Flex} flexDir="column" w="full" h="full" gap={2} p={2}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ParametersPanelGenerate />
|
||||
</Box>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
GenerateTabLeftPanel.displayName = 'GenerateTabLeftPanel';
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { setFocusedRegion } from 'common/hooks/focus';
|
||||
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
|
||||
import type { IDockviewPanelHeaderProps } from 'dockview';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
|
||||
export const TabWithoutCloseButton = memo((props: IDockviewPanelHeaderProps) => {
|
||||
import type { PanelParameters } from './auto-layout-context';
|
||||
|
||||
export const TabWithoutCloseButton = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const setActive = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
@@ -13,8 +16,12 @@ export const TabWithoutCloseButton = memo((props: IDockviewPanelHeaderProps) =>
|
||||
|
||||
useCallbackOnDragEnter(setActive, ref, 300);
|
||||
|
||||
const onPointerDown = useCallback(() => {
|
||||
setFocusedRegion(props.params.focusRegion);
|
||||
}, [props.params.focusRegion]);
|
||||
|
||||
return (
|
||||
<Flex ref={ref} alignItems="center" h="full">
|
||||
<Flex ref={ref} alignItems="center" h="full" onPointerDown={onPointerDown}>
|
||||
<Text userSelect="none" px={4}>
|
||||
{props.api.title ?? props.api.id}
|
||||
</Text>
|
||||
|
||||
@@ -1,31 +1,40 @@
|
||||
import { Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { setFocusedRegion } from 'common/hooks/focus';
|
||||
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
|
||||
import type { IDockviewPanelHeaderProps } from 'dockview';
|
||||
import ProgressBar from 'features/system/components/ProgressBar';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useIsGenerationInProgress } from 'services/api/endpoints/queue';
|
||||
|
||||
export const TabWithoutCloseButtonAndWithProgressIndicator = memo((props: IDockviewPanelHeaderProps) => {
|
||||
const isGenerationInProgress = useIsGenerationInProgress();
|
||||
import type { PanelParameters } from './auto-layout-context';
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const setActive = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
props.api.setActive();
|
||||
}
|
||||
}, [props.api]);
|
||||
export const TabWithoutCloseButtonAndWithProgressIndicator = memo(
|
||||
(props: IDockviewPanelHeaderProps<PanelParameters>) => {
|
||||
const isGenerationInProgress = useIsGenerationInProgress();
|
||||
|
||||
useCallbackOnDragEnter(setActive, ref, 300);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const setActive = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
props.api.setActive();
|
||||
}
|
||||
}, [props.api]);
|
||||
|
||||
return (
|
||||
<Flex ref={ref} position="relative" alignItems="center" h="full">
|
||||
<Text userSelect="none" px={4}>
|
||||
{props.api.title ?? props.api.id}
|
||||
</Text>
|
||||
{isGenerationInProgress && (
|
||||
<ProgressBar position="absolute" bottom={0} left={0} right={0} h={1} borderRadius="none" />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
useCallbackOnDragEnter(setActive, ref, 300);
|
||||
|
||||
const onPointerDown = useCallback(() => {
|
||||
setFocusedRegion(props.params.focusRegion);
|
||||
}, [props.params.focusRegion]);
|
||||
|
||||
return (
|
||||
<Flex ref={ref} position="relative" alignItems="center" h="full" onPointerDown={onPointerDown}>
|
||||
<Text userSelect="none" px={4}>
|
||||
{props.api.title ?? props.api.id}
|
||||
</Text>
|
||||
{isGenerationInProgress && (
|
||||
<ProgressBar position="absolute" bottom={0} left={0} right={0} h={1} borderRadius="none" />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
);
|
||||
TabWithoutCloseButtonAndWithProgressIndicator.displayName = 'TabWithoutCloseButtonAndWithProgressIndicator';
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { ParametersPanelUpscale } from 'features/ui/components/ParametersPanels/ParametersPanelUpscale';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const UpscalingTabLeftPanel = memo(() => {
|
||||
return (
|
||||
<FocusRegionWrapper region="settings" as={Flex} flexDir="column" w="full" h="full" gap={2} p={2}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ParametersPanelUpscale />
|
||||
</Box>
|
||||
</FocusRegionWrapper>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
UpscalingTabLeftPanel.displayName = 'UpscalingTabLeftPanel';
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { DockviewApi, GridviewApi } from 'dockview';
|
||||
import { AutoLayoutPanelContainer } from 'common/components/FocusRegionWrapper';
|
||||
import type { FocusRegionName } from 'common/hooks/focus';
|
||||
import type { DockviewApi, GridviewApi, IDockviewPanelProps, IGridviewPanelProps } from 'dockview';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import type { WritableAtom } from 'nanostores';
|
||||
import { atom } from 'nanostores';
|
||||
import type { PropsWithChildren, RefObject } from 'react';
|
||||
import type { FunctionComponent, PropsWithChildren, RefObject } from 'react';
|
||||
import { createContext, memo, useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
import { LEFT_PANEL_ID, LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX } from './shared';
|
||||
@@ -208,3 +210,21 @@ export const PanelHotkeysLogical = memo(() => {
|
||||
return null;
|
||||
});
|
||||
PanelHotkeysLogical.displayName = 'PanelHotkeysLogical';
|
||||
|
||||
export type PanelParameters = {
|
||||
focusRegion: FocusRegionName;
|
||||
};
|
||||
|
||||
export type AutoLayoutGridviewComponents = Record<string, FunctionComponent<IGridviewPanelProps<PanelParameters>>>;
|
||||
export type AutoLayoutDockviewComponents = Record<string, FunctionComponent<IDockviewPanelProps<PanelParameters>>>;
|
||||
export type RootLayoutGridviewComponents = Record<string, FunctionComponent<IGridviewPanelProps<PanelParameters>>>;
|
||||
export type PanelProps = IDockviewPanelProps<PanelParameters> | IGridviewPanelProps<PanelParameters>;
|
||||
|
||||
export const withPanelContainer = (Component: FunctionComponent) =>
|
||||
memo((props: PanelProps) => {
|
||||
return (
|
||||
<AutoLayoutPanelContainer {...props}>
|
||||
<Component />
|
||||
</AutoLayoutPanelContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,18 @@ import { GenerationProgressPanel } from 'features/gallery/components/ImageViewer
|
||||
import { ImageViewerPanel } from 'features/gallery/components/ImageViewer/ImageViewerPanel';
|
||||
import { FloatingCanvasLeftPanelButtons } from 'features/ui/components/FloatingLeftPanelButtons';
|
||||
import { FloatingRightPanelButtons } from 'features/ui/components/FloatingRightPanelButtons';
|
||||
import { AutoLayoutProvider, PanelHotkeysLogical, useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import type {
|
||||
AutoLayoutDockviewComponents,
|
||||
AutoLayoutGridviewComponents,
|
||||
PanelParameters,
|
||||
RootLayoutGridviewComponents,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import {
|
||||
AutoLayoutProvider,
|
||||
PanelHotkeysLogical,
|
||||
useAutoLayoutContext,
|
||||
withPanelContainer,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
@@ -23,7 +34,6 @@ import { memo, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { CanvasTabLeftPanel } from './CanvasTabLeftPanel';
|
||||
import { CanvasWorkspacePanel } from './CanvasWorkspacePanel';
|
||||
import { registerFocusListener } from './layout-focus-bridge';
|
||||
import {
|
||||
BOARD_PANEL_DEFAULT_HEIGHT_PX,
|
||||
BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
@@ -56,45 +66,51 @@ const tabComponents = {
|
||||
[TAB_WITH_LAUNCHPAD_ICON_ID]: TabWithLaunchpadIcon,
|
||||
};
|
||||
|
||||
const centerPanelComponents: IDockviewReactProps['components'] = {
|
||||
[LAUNCHPAD_PANEL_ID]: CanvasLaunchpadPanel,
|
||||
[WORKSPACE_PANEL_ID]: CanvasWorkspacePanel,
|
||||
[VIEWER_PANEL_ID]: ImageViewerPanel,
|
||||
[PROGRESS_PANEL_ID]: GenerationProgressPanel,
|
||||
const centerPanelComponents: AutoLayoutDockviewComponents = {
|
||||
[LAUNCHPAD_PANEL_ID]: withPanelContainer(CanvasLaunchpadPanel),
|
||||
[WORKSPACE_PANEL_ID]: withPanelContainer(CanvasWorkspacePanel),
|
||||
[VIEWER_PANEL_ID]: withPanelContainer(ImageViewerPanel),
|
||||
[PROGRESS_PANEL_ID]: withPanelContainer(GenerationProgressPanel),
|
||||
};
|
||||
|
||||
const initializeCenterPanelLayout = (api: DockviewApi) => {
|
||||
const launchpadPanel = api.addPanel({
|
||||
const launchpadPanel = api.addPanel<PanelParameters>({
|
||||
id: LAUNCHPAD_PANEL_ID,
|
||||
component: LAUNCHPAD_PANEL_ID,
|
||||
title: 'Launchpad',
|
||||
tabComponent: TAB_WITH_LAUNCHPAD_ICON_ID,
|
||||
params: {
|
||||
focusRegion: 'launchpad',
|
||||
},
|
||||
});
|
||||
registerFocusListener(launchpadPanel, 'launchpad');
|
||||
|
||||
const workspacePanel = api.addPanel({
|
||||
const workspacePanel = api.addPanel<PanelParameters>({
|
||||
id: WORKSPACE_PANEL_ID,
|
||||
component: WORKSPACE_PANEL_ID,
|
||||
title: 'Canvas',
|
||||
tabComponent: DEFAULT_TAB_ID,
|
||||
params: {
|
||||
focusRegion: 'canvas',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(workspacePanel, 'canvas');
|
||||
|
||||
const viewerPanel = api.addPanel({
|
||||
const viewerPanel = api.addPanel<PanelParameters>({
|
||||
id: VIEWER_PANEL_ID,
|
||||
component: VIEWER_PANEL_ID,
|
||||
title: 'Image Viewer',
|
||||
tabComponent: DEFAULT_TAB_ID,
|
||||
params: {
|
||||
focusRegion: 'viewer',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(viewerPanel, 'viewer');
|
||||
|
||||
return { launchpadPanel, workspacePanel, viewerPanel } satisfies Record<string, IDockviewPanel>;
|
||||
};
|
||||
@@ -145,42 +161,48 @@ const CenterPanel = memo(() => {
|
||||
});
|
||||
CenterPanel.displayName = 'CenterPanel';
|
||||
|
||||
const rightPanelComponents: IGridviewReactProps['components'] = {
|
||||
[BOARDS_PANEL_ID]: BoardsPanel,
|
||||
[GALLERY_PANEL_ID]: GalleryPanel,
|
||||
[LAYERS_PANEL_ID]: CanvasLayersPanel,
|
||||
const rightPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[BOARDS_PANEL_ID]: withPanelContainer(BoardsPanel),
|
||||
[GALLERY_PANEL_ID]: withPanelContainer(GalleryPanel),
|
||||
[LAYERS_PANEL_ID]: withPanelContainer(CanvasLayersPanel),
|
||||
};
|
||||
|
||||
export const initializeRightPanelLayout = (api: GridviewApi) => {
|
||||
const galleryPanel = api.addPanel({
|
||||
const galleryPanel = api.addPanel<PanelParameters>({
|
||||
id: GALLERY_PANEL_ID,
|
||||
component: GALLERY_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'gallery',
|
||||
},
|
||||
});
|
||||
registerFocusListener(galleryPanel, 'gallery');
|
||||
|
||||
const layersPanel = api.addPanel({
|
||||
const layersPanel = api.addPanel<PanelParameters>({
|
||||
id: LAYERS_PANEL_ID,
|
||||
component: LAYERS_PANEL_ID,
|
||||
minimumHeight: LAYERS_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'layers',
|
||||
},
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: galleryPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(layersPanel, 'layers');
|
||||
|
||||
const boardsPanel = api.addPanel({
|
||||
const boardsPanel = api.addPanel<PanelParameters>({
|
||||
id: BOARDS_PANEL_ID,
|
||||
component: BOARDS_PANEL_ID,
|
||||
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'boards',
|
||||
},
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: galleryPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(boardsPanel, 'boards');
|
||||
|
||||
boardsPanel.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
|
||||
return { galleryPanel, layersPanel, boardsPanel } satisfies Record<string, IGridviewPanel>;
|
||||
@@ -208,16 +230,18 @@ const RightPanel = memo(() => {
|
||||
});
|
||||
RightPanel.displayName = 'RightPanel';
|
||||
|
||||
const leftPanelComponents: IGridviewReactProps['components'] = {
|
||||
[SETTINGS_PANEL_ID]: CanvasTabLeftPanel,
|
||||
const leftPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[SETTINGS_PANEL_ID]: withPanelContainer(CanvasTabLeftPanel),
|
||||
};
|
||||
|
||||
export const initializeLeftPanelLayout = (api: GridviewApi) => {
|
||||
const settingsPanel = api.addPanel({
|
||||
const settingsPanel = api.addPanel<PanelParameters>({
|
||||
id: SETTINGS_PANEL_ID,
|
||||
component: SETTINGS_PANEL_ID,
|
||||
params: {
|
||||
focusRegion: 'settings',
|
||||
},
|
||||
});
|
||||
registerFocusListener(settingsPanel, 'settings');
|
||||
|
||||
return { settingsPanel } satisfies Record<string, IGridviewPanel>;
|
||||
};
|
||||
@@ -244,7 +268,7 @@ const LeftPanel = memo(() => {
|
||||
});
|
||||
LeftPanel.displayName = 'LeftPanel';
|
||||
|
||||
export const rootPanelComponents: IGridviewReactProps['components'] = {
|
||||
export const rootPanelComponents: RootLayoutGridviewComponents = {
|
||||
[LEFT_PANEL_ID]: LeftPanel,
|
||||
[MAIN_PANEL_ID]: CenterPanel,
|
||||
[RIGHT_PANEL_ID]: RightPanel,
|
||||
|
||||
@@ -14,7 +14,18 @@ import { GenerationProgressPanel } from 'features/gallery/components/ImageViewer
|
||||
import { ImageViewerPanel } from 'features/gallery/components/ImageViewer/ImageViewerPanel';
|
||||
import { FloatingLeftPanelButtons } from 'features/ui/components/FloatingLeftPanelButtons';
|
||||
import { FloatingRightPanelButtons } from 'features/ui/components/FloatingRightPanelButtons';
|
||||
import { AutoLayoutProvider, PanelHotkeysLogical, useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import type {
|
||||
AutoLayoutDockviewComponents,
|
||||
AutoLayoutGridviewComponents,
|
||||
PanelParameters,
|
||||
RootLayoutGridviewComponents,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import {
|
||||
AutoLayoutProvider,
|
||||
PanelHotkeysLogical,
|
||||
useAutoLayoutContext,
|
||||
withPanelContainer,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
@@ -51,26 +62,32 @@ const tabComponents = {
|
||||
[TAB_WITH_LAUNCHPAD_ICON_ID]: TabWithLaunchpadIcon,
|
||||
};
|
||||
|
||||
const centerPanelComponents: IDockviewReactProps['components'] = {
|
||||
[LAUNCHPAD_PANEL_ID]: GenerateLaunchpadPanel,
|
||||
[VIEWER_PANEL_ID]: ImageViewerPanel,
|
||||
[PROGRESS_PANEL_ID]: GenerationProgressPanel,
|
||||
const centerPanelComponents: AutoLayoutDockviewComponents = {
|
||||
[LAUNCHPAD_PANEL_ID]: withPanelContainer(GenerateLaunchpadPanel),
|
||||
[VIEWER_PANEL_ID]: withPanelContainer(ImageViewerPanel),
|
||||
[PROGRESS_PANEL_ID]: withPanelContainer(GenerationProgressPanel),
|
||||
};
|
||||
|
||||
const initializeCenterPanelLayout = (api: DockviewApi) => {
|
||||
const launchpadPanel = api.addPanel({
|
||||
const launchpadPanel = api.addPanel<PanelParameters>({
|
||||
id: LAUNCHPAD_PANEL_ID,
|
||||
component: LAUNCHPAD_PANEL_ID,
|
||||
title: 'Launchpad',
|
||||
tabComponent: TAB_WITH_LAUNCHPAD_ICON_ID,
|
||||
params: {
|
||||
focusRegion: 'launchpad',
|
||||
},
|
||||
});
|
||||
registerFocusListener(launchpadPanel, 'launchpad');
|
||||
|
||||
const viewerPanel = api.addPanel({
|
||||
const viewerPanel = api.addPanel<PanelParameters>({
|
||||
id: VIEWER_PANEL_ID,
|
||||
component: VIEWER_PANEL_ID,
|
||||
title: 'Image Viewer',
|
||||
tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID,
|
||||
params: {
|
||||
focusRegion: 'viewer',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
@@ -127,24 +144,30 @@ const CenterPanel = memo(() => {
|
||||
});
|
||||
CenterPanel.displayName = 'CenterPanel';
|
||||
|
||||
const rightPanelComponents: IGridviewReactProps['components'] = {
|
||||
[BOARDS_PANEL_ID]: BoardsPanel,
|
||||
[GALLERY_PANEL_ID]: GalleryPanel,
|
||||
const rightPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[BOARDS_PANEL_ID]: withPanelContainer(BoardsPanel),
|
||||
[GALLERY_PANEL_ID]: withPanelContainer(GalleryPanel),
|
||||
};
|
||||
|
||||
export const initializeRightPanelLayout = (api: GridviewApi) => {
|
||||
const galleryPanel = api.addPanel({
|
||||
const galleryPanel = api.addPanel<PanelParameters>({
|
||||
id: GALLERY_PANEL_ID,
|
||||
component: GALLERY_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'gallery',
|
||||
},
|
||||
});
|
||||
registerFocusListener(galleryPanel, 'gallery');
|
||||
|
||||
const boardsPanel = api.addPanel({
|
||||
const boardsPanel = api.addPanel<PanelParameters>({
|
||||
id: BOARDS_PANEL_ID,
|
||||
component: BOARDS_PANEL_ID,
|
||||
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'boards',
|
||||
},
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: galleryPanel.id,
|
||||
@@ -179,14 +202,17 @@ const RightPanel = memo(() => {
|
||||
});
|
||||
RightPanel.displayName = 'RightPanel';
|
||||
|
||||
const leftPanelComponents: IGridviewReactProps['components'] = {
|
||||
[SETTINGS_PANEL_ID]: GenerateTabLeftPanel,
|
||||
const leftPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[SETTINGS_PANEL_ID]: withPanelContainer(GenerateTabLeftPanel),
|
||||
};
|
||||
|
||||
export const initializeLeftPanelLayout = (api: GridviewApi) => {
|
||||
const settingsPanel = api.addPanel({
|
||||
const settingsPanel = api.addPanel<PanelParameters>({
|
||||
id: SETTINGS_PANEL_ID,
|
||||
component: SETTINGS_PANEL_ID,
|
||||
params: {
|
||||
focusRegion: 'settings',
|
||||
},
|
||||
});
|
||||
registerFocusListener(settingsPanel, 'settings');
|
||||
|
||||
@@ -215,20 +241,20 @@ const LeftPanel = memo(() => {
|
||||
});
|
||||
LeftPanel.displayName = 'LeftPanel';
|
||||
|
||||
export const rootPanelComponents: IGridviewReactProps['components'] = {
|
||||
export const rootPanelComponents: RootLayoutGridviewComponents = {
|
||||
[LEFT_PANEL_ID]: LeftPanel,
|
||||
[MAIN_PANEL_ID]: CenterPanel,
|
||||
[RIGHT_PANEL_ID]: RightPanel,
|
||||
};
|
||||
|
||||
export const initializeRootPanelLayout = (layoutApi: GridviewApi) => {
|
||||
const mainPanel = layoutApi.addPanel({
|
||||
const mainPanel = layoutApi.addPanel<PanelParameters>({
|
||||
id: MAIN_PANEL_ID,
|
||||
component: MAIN_PANEL_ID,
|
||||
priority: LayoutPriority.High,
|
||||
});
|
||||
|
||||
const leftPanel = layoutApi.addPanel({
|
||||
const leftPanel = layoutApi.addPanel<PanelParameters>({
|
||||
id: LEFT_PANEL_ID,
|
||||
component: LEFT_PANEL_ID,
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
@@ -238,7 +264,7 @@ export const initializeRootPanelLayout = (layoutApi: GridviewApi) => {
|
||||
},
|
||||
});
|
||||
|
||||
const rightPanel = layoutApi.addPanel({
|
||||
const rightPanel = layoutApi.addPanel<PanelParameters>({
|
||||
id: RIGHT_PANEL_ID,
|
||||
component: RIGHT_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
|
||||
@@ -14,13 +14,23 @@ import { GenerationProgressPanel } from 'features/gallery/components/ImageViewer
|
||||
import { ImageViewerPanel } from 'features/gallery/components/ImageViewer/ImageViewerPanel';
|
||||
import { FloatingLeftPanelButtons } from 'features/ui/components/FloatingLeftPanelButtons';
|
||||
import { FloatingRightPanelButtons } from 'features/ui/components/FloatingRightPanelButtons';
|
||||
import { AutoLayoutProvider, PanelHotkeysLogical, useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import type {
|
||||
AutoLayoutDockviewComponents,
|
||||
AutoLayoutGridviewComponents,
|
||||
PanelParameters,
|
||||
RootLayoutGridviewComponents,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import {
|
||||
AutoLayoutProvider,
|
||||
PanelHotkeysLogical,
|
||||
useAutoLayoutContext,
|
||||
withPanelContainer,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { registerFocusListener } from './layout-focus-bridge';
|
||||
import {
|
||||
BOARD_PANEL_DEFAULT_HEIGHT_PX,
|
||||
BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
@@ -51,32 +61,36 @@ const tabComponents = {
|
||||
[TAB_WITH_LAUNCHPAD_ICON_ID]: TabWithLaunchpadIcon,
|
||||
};
|
||||
|
||||
const centerComponents: IDockviewReactProps['components'] = {
|
||||
[LAUNCHPAD_PANEL_ID]: UpscalingLaunchpadPanel,
|
||||
[VIEWER_PANEL_ID]: ImageViewerPanel,
|
||||
[PROGRESS_PANEL_ID]: GenerationProgressPanel,
|
||||
const centerComponents: AutoLayoutDockviewComponents = {
|
||||
[LAUNCHPAD_PANEL_ID]: withPanelContainer(UpscalingLaunchpadPanel),
|
||||
[VIEWER_PANEL_ID]: withPanelContainer(ImageViewerPanel),
|
||||
[PROGRESS_PANEL_ID]: withPanelContainer(GenerationProgressPanel),
|
||||
};
|
||||
|
||||
const initializeCenterPanelLayout = (api: DockviewApi) => {
|
||||
const launchpadPanel = api.addPanel({
|
||||
const launchpadPanel = api.addPanel<PanelParameters>({
|
||||
id: LAUNCHPAD_PANEL_ID,
|
||||
component: LAUNCHPAD_PANEL_ID,
|
||||
title: 'Launchpad',
|
||||
tabComponent: TAB_WITH_LAUNCHPAD_ICON_ID,
|
||||
params: {
|
||||
focusRegion: 'launchpad',
|
||||
},
|
||||
});
|
||||
registerFocusListener(launchpadPanel, 'launchpad');
|
||||
|
||||
const viewerPanel = api.addPanel({
|
||||
const viewerPanel = api.addPanel<PanelParameters>({
|
||||
id: VIEWER_PANEL_ID,
|
||||
component: VIEWER_PANEL_ID,
|
||||
title: 'Image Viewer',
|
||||
tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID,
|
||||
params: {
|
||||
focusRegion: 'viewer',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(viewerPanel, 'viewer');
|
||||
|
||||
return { launchpadPanel, viewerPanel } satisfies Record<string, IDockviewPanel>;
|
||||
};
|
||||
@@ -127,30 +141,34 @@ const CenterPanel = memo(() => {
|
||||
});
|
||||
CenterPanel.displayName = 'CenterPanel';
|
||||
|
||||
const rightPanelComponents: IGridviewReactProps['components'] = {
|
||||
[BOARDS_PANEL_ID]: BoardsPanel,
|
||||
[GALLERY_PANEL_ID]: GalleryPanel,
|
||||
const rightPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[BOARDS_PANEL_ID]: withPanelContainer(BoardsPanel),
|
||||
[GALLERY_PANEL_ID]: withPanelContainer(GalleryPanel),
|
||||
};
|
||||
|
||||
export const initializeRightPanelLayout = (api: GridviewApi) => {
|
||||
const galleryPanel = api.addPanel({
|
||||
const galleryPanel = api.addPanel<PanelParameters>({
|
||||
id: GALLERY_PANEL_ID,
|
||||
component: GALLERY_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'gallery',
|
||||
},
|
||||
});
|
||||
registerFocusListener(galleryPanel, 'gallery');
|
||||
|
||||
const boardsPanel = api.addPanel({
|
||||
const boardsPanel = api.addPanel<PanelParameters>({
|
||||
id: BOARDS_PANEL_ID,
|
||||
component: BOARDS_PANEL_ID,
|
||||
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'boards',
|
||||
},
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: galleryPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(boardsPanel, 'boards');
|
||||
|
||||
boardsPanel.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
|
||||
|
||||
@@ -175,16 +193,18 @@ const RightPanel = memo(() => {
|
||||
});
|
||||
RightPanel.displayName = 'RightPanel';
|
||||
|
||||
const leftPanelComponents: IGridviewReactProps['components'] = {
|
||||
[SETTINGS_PANEL_ID]: UpscalingTabLeftPanel,
|
||||
const leftPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[SETTINGS_PANEL_ID]: withPanelContainer(UpscalingTabLeftPanel),
|
||||
};
|
||||
|
||||
export const initializeLeftPanelLayout = (api: GridviewApi) => {
|
||||
const settingsPanel = api.addPanel({
|
||||
const settingsPanel = api.addPanel<PanelParameters>({
|
||||
id: SETTINGS_PANEL_ID,
|
||||
component: SETTINGS_PANEL_ID,
|
||||
params: {
|
||||
focusRegion: 'settings',
|
||||
},
|
||||
});
|
||||
registerFocusListener(settingsPanel, 'settings');
|
||||
|
||||
return { settingsPanel } satisfies Record<string, IGridviewPanel>;
|
||||
};
|
||||
@@ -211,7 +231,7 @@ const LeftPanel = memo(() => {
|
||||
});
|
||||
LeftPanel.displayName = 'LeftPanel';
|
||||
|
||||
export const rootPanelComponents: IGridviewReactProps['components'] = {
|
||||
export const rootPanelComponents: RootLayoutGridviewComponents = {
|
||||
[LEFT_PANEL_ID]: LeftPanel,
|
||||
[MAIN_PANEL_ID]: CenterPanel,
|
||||
[RIGHT_PANEL_ID]: RightPanel,
|
||||
|
||||
@@ -16,7 +16,18 @@ import NodeEditor from 'features/nodes/components/NodeEditor';
|
||||
import WorkflowsTabLeftPanel from 'features/nodes/components/sidePanel/WorkflowsTabLeftPanel';
|
||||
import { FloatingLeftPanelButtons } from 'features/ui/components/FloatingLeftPanelButtons';
|
||||
import { FloatingRightPanelButtons } from 'features/ui/components/FloatingRightPanelButtons';
|
||||
import { AutoLayoutProvider, PanelHotkeysLogical, useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import type {
|
||||
AutoLayoutDockviewComponents,
|
||||
AutoLayoutGridviewComponents,
|
||||
PanelParameters,
|
||||
RootLayoutGridviewComponents,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import {
|
||||
AutoLayoutProvider,
|
||||
PanelHotkeysLogical,
|
||||
useAutoLayoutContext,
|
||||
withPanelContainer,
|
||||
} from 'features/ui/layouts/auto-layout-context';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
@@ -53,45 +64,51 @@ const tabComponents = {
|
||||
[TAB_WITH_LAUNCHPAD_ICON_ID]: TabWithLaunchpadIcon,
|
||||
};
|
||||
|
||||
const centerPanelComponents: IDockviewReactProps['components'] = {
|
||||
[LAUNCHPAD_PANEL_ID]: WorkflowsLaunchpadPanel,
|
||||
[WORKSPACE_PANEL_ID]: NodeEditor,
|
||||
[VIEWER_PANEL_ID]: ImageViewerPanel,
|
||||
[PROGRESS_PANEL_ID]: GenerationProgressPanel,
|
||||
const centerPanelComponents: AutoLayoutDockviewComponents = {
|
||||
[LAUNCHPAD_PANEL_ID]: withPanelContainer(WorkflowsLaunchpadPanel),
|
||||
[WORKSPACE_PANEL_ID]: withPanelContainer(NodeEditor),
|
||||
[VIEWER_PANEL_ID]: withPanelContainer(ImageViewerPanel),
|
||||
[PROGRESS_PANEL_ID]: withPanelContainer(GenerationProgressPanel),
|
||||
};
|
||||
|
||||
const initializeCenterPanelLayout = (api: DockviewApi) => {
|
||||
const launchpadPanel = api.addPanel({
|
||||
const launchpadPanel = api.addPanel<PanelParameters>({
|
||||
id: LAUNCHPAD_PANEL_ID,
|
||||
component: LAUNCHPAD_PANEL_ID,
|
||||
title: 'Launchpad',
|
||||
tabComponent: TAB_WITH_LAUNCHPAD_ICON_ID,
|
||||
params: {
|
||||
focusRegion: 'launchpad',
|
||||
},
|
||||
});
|
||||
registerFocusListener(launchpadPanel, 'launchpad');
|
||||
|
||||
const workspacePanel = api.addPanel({
|
||||
const workspacePanel = api.addPanel<PanelParameters>({
|
||||
id: WORKSPACE_PANEL_ID,
|
||||
component: WORKSPACE_PANEL_ID,
|
||||
title: 'Workflow Editor',
|
||||
tabComponent: DEFAULT_TAB_ID,
|
||||
params: {
|
||||
focusRegion: 'workflows',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(workspacePanel, 'workflows');
|
||||
|
||||
const viewerPanel = api.addPanel({
|
||||
const viewerPanel = api.addPanel<PanelParameters>({
|
||||
id: VIEWER_PANEL_ID,
|
||||
component: VIEWER_PANEL_ID,
|
||||
title: 'Image Viewer',
|
||||
tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID,
|
||||
params: {
|
||||
focusRegion: 'viewer',
|
||||
},
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: launchpadPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(viewerPanel, 'viewer');
|
||||
|
||||
return { launchpadPanel, workspacePanel, viewerPanel } satisfies Record<string, IDockviewPanel>;
|
||||
};
|
||||
@@ -142,30 +159,34 @@ const CenterPanel = memo(() => {
|
||||
});
|
||||
CenterPanel.displayName = 'CenterPanel';
|
||||
|
||||
const rightPanelComponents: IGridviewReactProps['components'] = {
|
||||
[BOARDS_PANEL_ID]: BoardsPanel,
|
||||
[GALLERY_PANEL_ID]: GalleryPanel,
|
||||
const rightPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[BOARDS_PANEL_ID]: withPanelContainer(BoardsPanel),
|
||||
[GALLERY_PANEL_ID]: withPanelContainer(GalleryPanel),
|
||||
};
|
||||
|
||||
export const initializeRightPanelLayout = (api: GridviewApi) => {
|
||||
const galleryPanel = api.addPanel({
|
||||
const galleryPanel = api.addPanel<PanelParameters>({
|
||||
id: GALLERY_PANEL_ID,
|
||||
component: GALLERY_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'gallery',
|
||||
},
|
||||
});
|
||||
registerFocusListener(galleryPanel, 'gallery');
|
||||
|
||||
const boardsPanel = api.addPanel({
|
||||
const boardsPanel = api.addPanel<PanelParameters>({
|
||||
id: BOARDS_PANEL_ID,
|
||||
component: BOARDS_PANEL_ID,
|
||||
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
|
||||
params: {
|
||||
focusRegion: 'boards',
|
||||
},
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: galleryPanel.id,
|
||||
},
|
||||
});
|
||||
registerFocusListener(boardsPanel, 'boards');
|
||||
|
||||
boardsPanel.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
|
||||
|
||||
@@ -194,14 +215,17 @@ const RightPanel = memo(() => {
|
||||
});
|
||||
RightPanel.displayName = 'RightPanel';
|
||||
|
||||
const leftPanelComponents: IGridviewReactProps['components'] = {
|
||||
[SETTINGS_PANEL_ID]: WorkflowsTabLeftPanel,
|
||||
const leftPanelComponents: AutoLayoutGridviewComponents = {
|
||||
[SETTINGS_PANEL_ID]: withPanelContainer(WorkflowsTabLeftPanel),
|
||||
};
|
||||
|
||||
export const initializeLeftPanelLayout = (api: GridviewApi) => {
|
||||
const settingsPanel = api.addPanel({
|
||||
const settingsPanel = api.addPanel<PanelParameters>({
|
||||
id: SETTINGS_PANEL_ID,
|
||||
component: SETTINGS_PANEL_ID,
|
||||
params: {
|
||||
focusRegion: 'settings',
|
||||
},
|
||||
});
|
||||
registerFocusListener(settingsPanel, 'settings');
|
||||
|
||||
@@ -230,7 +254,7 @@ const LeftPanel = memo(() => {
|
||||
});
|
||||
LeftPanel.displayName = 'LeftPanel';
|
||||
|
||||
export const rootPanelComponents: IGridviewReactProps['components'] = {
|
||||
export const rootPanelComponents: RootLayoutGridviewComponents = {
|
||||
[LEFT_PANEL_ID]: LeftPanel,
|
||||
[MAIN_PANEL_ID]: CenterPanel,
|
||||
[RIGHT_PANEL_ID]: RightPanel,
|
||||
|
||||
Reference in New Issue
Block a user