mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
wip
This commit is contained in:
@@ -4,16 +4,19 @@ import {
|
||||
MenuButton,
|
||||
MenuGroup,
|
||||
MenuItem,
|
||||
MenuItemOption,
|
||||
MenuList,
|
||||
MenuOptionGroup,
|
||||
Portal,
|
||||
useDisclosure,
|
||||
useGlobalMenuClose,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import AboutModal from 'features/system/components/AboutModal/AboutModal';
|
||||
import HotkeysModal from 'features/system/components/HotkeysModal/HotkeysModal';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { discordLink, githubLink } from 'features/system/store/constants';
|
||||
import { $panels } from 'features/ui/components/AppContent';
|
||||
import { $advancedLayout, $panels, toggleAdvancedLayout } from 'features/ui/components/AppContent';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -38,6 +41,7 @@ const SettingsMenu = () => {
|
||||
const isBugLinkEnabled = useFeatureStatus('bugLink');
|
||||
const isDiscordLinkEnabled = useFeatureStatus('discordLink');
|
||||
const isGithubLinkEnabled = useFeatureStatus('githubLink');
|
||||
const advancedLayout = useStore($advancedLayout);
|
||||
|
||||
const resetLayout = useCallback(() => {
|
||||
$panels.get()?.resetLayout();
|
||||
@@ -79,6 +83,15 @@ const SettingsMenu = () => {
|
||||
<MenuItem as="button" onClick={resetLayout} icon={<PiLayoutBold />}>
|
||||
Reset Layout
|
||||
</MenuItem>
|
||||
<MenuOptionGroup
|
||||
value={advancedLayout ? 'advanced' : 'simple'}
|
||||
onChange={toggleAdvancedLayout}
|
||||
title="Layout"
|
||||
type="radio"
|
||||
>
|
||||
<MenuItemOption value="simple">Simple</MenuItemOption>
|
||||
<MenuItemOption value="advanced">Advanced</MenuItemOption>
|
||||
</MenuOptionGroup>
|
||||
<MenuGroup title={t('common.settingsLabel')}>
|
||||
<HotkeysModal>
|
||||
<MenuItem as="button" icon={<PiKeyboardBold />}>
|
||||
|
||||
@@ -1,451 +1,86 @@
|
||||
import 'dockview/dist/styles/dockview.css';
|
||||
import './dockview_theme_invoke.css';
|
||||
|
||||
import { Divider, Flex, IconButton } from '@invoke-ai/ui-library';
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type {
|
||||
DockviewApi,
|
||||
DockviewTheme,
|
||||
IDockviewHeaderActionsProps,
|
||||
IDockviewPanelHeaderProps,
|
||||
IDockviewReactProps,
|
||||
} from 'dockview';
|
||||
import { DockviewDefaultTab, DockviewReact } from 'dockview';
|
||||
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import type { DockviewApi } from 'dockview';
|
||||
import { useDndMonitor } from 'features/dnd/useDndMonitor';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import ProgressImage from 'features/gallery/components/ImageViewer/ProgressImage';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
|
||||
import { CompositingSettingsAccordion } from 'features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion';
|
||||
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
|
||||
import { ImageSettingsAccordion } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion';
|
||||
import { RefinerSettingsAccordion } from 'features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { MainPanelContent } from 'features/ui/components/MainPanelContent';
|
||||
import { GridviewWrapper } from 'features/ui/components/GridviewWrapper';
|
||||
import { VerticalNavBar } from 'features/ui/components/VerticalNavBar';
|
||||
import type { UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||
import { usePanel } from 'features/ui/hooks/usePanel';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import {
|
||||
$isLeftPanelOpen,
|
||||
$isRightPanelOpen,
|
||||
LEFT_PANEL_MIN_SIZE_PX,
|
||||
RIGHT_PANEL_MIN_SIZE_PX,
|
||||
selectWithLeftPanel,
|
||||
selectWithRightPanel,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { atom } from 'nanostores';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { PiArrowSquareOutBold, PiCornersInBold, PiCornersOutBold } from 'react-icons/pi';
|
||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||
|
||||
const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%', minWidth: 0 };
|
||||
|
||||
const onLeftPanelCollapse = (isCollapsed: boolean) => $isLeftPanelOpen.set(!isCollapsed);
|
||||
const onRightPanelCollapse = (isCollapsed: boolean) => $isRightPanelOpen.set(!isCollapsed);
|
||||
|
||||
const MyCustomTab = (props: IDockviewPanelHeaderProps) => {
|
||||
const onDragEnter = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
props.api.setActive();
|
||||
}
|
||||
}, [props.api]);
|
||||
|
||||
return <DockviewDefaultTab hideClose {...props} onDragEnter={onDragEnter} />;
|
||||
};
|
||||
|
||||
const RightHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||
const [isMaximized, setIsMaximized] = useState(false);
|
||||
const popOutToFloating = useCallback(() => {
|
||||
props.containerApi.addFloatingGroup(props.group);
|
||||
}, [props.containerApi, props.group]);
|
||||
const maximize = useCallback(() => {
|
||||
if (!props.group.activePanel) {
|
||||
props.group.panels.at(0)?.api.setActive();
|
||||
}
|
||||
if (!props.group.activePanel) {
|
||||
return;
|
||||
}
|
||||
props.containerApi.maximizeGroup(props.group.activePanel);
|
||||
}, [props.containerApi, props.group]);
|
||||
const exitMaximized = useCallback(() => {
|
||||
if (!props.group.activePanel) {
|
||||
props.group.panels.at(0)?.api.setActive();
|
||||
}
|
||||
if (!props.group.activePanel) {
|
||||
return;
|
||||
}
|
||||
props.containerApi.exitMaximizedGroup();
|
||||
}, [props.containerApi, props.group]);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = props.containerApi.onDidMaximizedGroupChange((e) => {
|
||||
if (e.group.id === props.group.id) {
|
||||
setIsMaximized(e.isMaximized);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
subscription.dispose();
|
||||
};
|
||||
}, [props.containerApi, props.group.id]);
|
||||
|
||||
return (
|
||||
<Flex h="full" alignItems="center" pe={1}>
|
||||
{!isMaximized && (
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiCornersOutBold />}
|
||||
aria-label="Maximize Panel"
|
||||
tooltip="Maximize Panel"
|
||||
onClick={maximize}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
{isMaximized && (
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiCornersInBold />}
|
||||
aria-label="Maximize Panel"
|
||||
tooltip="Maximize Panel"
|
||||
onClick={exitMaximized}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
{props.group.api.location.type !== 'floating' && (
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiArrowSquareOutBold />}
|
||||
aria-label="Pop out Panel"
|
||||
tooltip="Pop out Panel"
|
||||
onClick={popOutToFloating}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const components: IDockviewReactProps['components'] = {
|
||||
main: MainPanelContent,
|
||||
boards: BoardsListPanelContent,
|
||||
gallery: Gallery,
|
||||
layers: () => (
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasLayersPanelContent />
|
||||
</CanvasManagerProviderGate>
|
||||
),
|
||||
queueControls: QueueControls,
|
||||
prompts: Prompts,
|
||||
imageSettings: ImageSettingsAccordion,
|
||||
generationSettings: GenerationSettingsAccordion,
|
||||
compositingSettings: CompositingSettingsAccordion,
|
||||
advancedSettings: AdvancedSettingsAccordion,
|
||||
refinerSettings: RefinerSettingsAccordion,
|
||||
viewer: () => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2} gap={2}>
|
||||
<ViewerToolbar />
|
||||
<Divider />
|
||||
<ImageViewer />
|
||||
</Flex>
|
||||
),
|
||||
progress: () => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2}>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
const theme: DockviewTheme = {
|
||||
className: 'dockview-theme-invoke',
|
||||
name: 'Invoke',
|
||||
};
|
||||
import { memo } from 'react';
|
||||
|
||||
export const $panels = atom<{ api: DockviewApi; resetLayout: () => void } | null>(null);
|
||||
|
||||
const canvasLayout = (api: DockviewApi) => {
|
||||
api.clear();
|
||||
const mainPanel = api.addPanel({
|
||||
id: 'main',
|
||||
component: 'main',
|
||||
title: 'Workspace',
|
||||
minimumWidth: 200,
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'viewer',
|
||||
component: 'viewer',
|
||||
title: 'Viewer',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'progress',
|
||||
component: 'progress',
|
||||
title: 'Progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const queueControls = api.addPanel({
|
||||
id: 'queue-controls',
|
||||
title: 'Queue',
|
||||
component: 'queueControls',
|
||||
// floating: true,
|
||||
// initialHeight: 48 + 24,
|
||||
initialHeight: 48,
|
||||
maximumHeight: 48,
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
initialWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const promptsPanel = api.addPanel({
|
||||
id: 'prompts',
|
||||
title: 'Prompts',
|
||||
component: 'prompts',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: queueControls,
|
||||
},
|
||||
});
|
||||
const imagePanel = api.addPanel({
|
||||
id: 'imageSettings',
|
||||
title: 'Image',
|
||||
component: 'imageSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: promptsPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'generationSettings',
|
||||
title: 'Generation',
|
||||
component: 'generationSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: imagePanel,
|
||||
},
|
||||
});
|
||||
const compPanel = api.addPanel({
|
||||
id: 'compositingSettings',
|
||||
title: 'Compositing',
|
||||
component: 'compositingSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: imagePanel,
|
||||
},
|
||||
});
|
||||
const advancedPanel = api.addPanel({
|
||||
id: 'advancedSettings',
|
||||
title: 'Advanced',
|
||||
component: 'advancedSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: compPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'refinerSettings',
|
||||
title: 'Refiner',
|
||||
component: 'refinerSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: advancedPanel,
|
||||
},
|
||||
});
|
||||
const boardsPanel = api.addPanel({
|
||||
id: 'boards',
|
||||
component: 'boards',
|
||||
title: 'Boards',
|
||||
initialWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const galleryPanel = api.addPanel({
|
||||
id: 'gallery',
|
||||
component: 'gallery',
|
||||
title: 'Gallery',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: boardsPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'layers',
|
||||
component: 'layers',
|
||||
title: 'Layers',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: galleryPanel,
|
||||
},
|
||||
});
|
||||
mainPanel.api.setActive();
|
||||
};
|
||||
|
||||
const galleryLayout = (api: DockviewApi) => {
|
||||
api.clear();
|
||||
const viewer = api.addPanel({
|
||||
id: 'gallery-viewer',
|
||||
title: 'Viewer',
|
||||
component: 'viewer',
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'gallery-progress',
|
||||
title: 'Progress',
|
||||
component: 'progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: viewer,
|
||||
},
|
||||
});
|
||||
const gallery = api.addPanel({
|
||||
id: 'gallery-gallery',
|
||||
title: 'Gallery',
|
||||
component: 'gallery',
|
||||
initialWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: viewer,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'gallery-boards',
|
||||
title: 'Boards',
|
||||
component: 'boards',
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: gallery,
|
||||
},
|
||||
});
|
||||
viewer.api.setActive();
|
||||
export const $advancedLayout = atom<boolean>(false);
|
||||
export const toggleAdvancedLayout = () => {
|
||||
$advancedLayout.set(!$advancedLayout.get());
|
||||
};
|
||||
|
||||
export const AppContent = memo(() => {
|
||||
const imperativePanelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
useDndMonitor();
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const advancedLayout = useStore($advancedLayout);
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'toggleLeftPanel',
|
||||
// category: 'app',
|
||||
// callback: leftPanel.toggle,
|
||||
// options: { enabled: withLeftPanel },
|
||||
// dependencies: [leftPanel.toggle, withLeftPanel],
|
||||
// });
|
||||
|
||||
const withLeftPanel = useAppSelector(selectWithLeftPanel);
|
||||
const leftPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
() => ({
|
||||
id: 'left-panel',
|
||||
minSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
defaultSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
imperativePanelGroupRef,
|
||||
panelGroupDirection: 'horizontal',
|
||||
onCollapse: onLeftPanelCollapse,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
const leftPanel = usePanel(leftPanelUsePanelOptions);
|
||||
useRegisteredHotkeys({
|
||||
id: 'toggleLeftPanel',
|
||||
category: 'app',
|
||||
callback: leftPanel.toggle,
|
||||
options: { enabled: withLeftPanel },
|
||||
dependencies: [leftPanel.toggle, withLeftPanel],
|
||||
});
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'toggleRightPanel',
|
||||
// category: 'app',
|
||||
// callback: rightPanel.toggle,
|
||||
// options: { enabled: withRightPanel },
|
||||
// dependencies: [rightPanel.toggle, withRightPanel],
|
||||
// });
|
||||
|
||||
const withRightPanel = useAppSelector(selectWithRightPanel);
|
||||
const rightPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
() => ({
|
||||
id: 'right-panel',
|
||||
minSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
defaultSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
imperativePanelGroupRef,
|
||||
panelGroupDirection: 'horizontal',
|
||||
onCollapse: onRightPanelCollapse,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
const rightPanel = usePanel(rightPanelUsePanelOptions);
|
||||
useRegisteredHotkeys({
|
||||
id: 'toggleRightPanel',
|
||||
category: 'app',
|
||||
callback: rightPanel.toggle,
|
||||
options: { enabled: withRightPanel },
|
||||
dependencies: [rightPanel.toggle, withRightPanel],
|
||||
});
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'resetPanelLayout',
|
||||
category: 'app',
|
||||
callback: () => {
|
||||
leftPanel.reset();
|
||||
rightPanel.reset();
|
||||
},
|
||||
dependencies: [leftPanel.reset, rightPanel.reset],
|
||||
});
|
||||
useRegisteredHotkeys({
|
||||
id: 'togglePanels',
|
||||
category: 'app',
|
||||
callback: () => {
|
||||
if (leftPanel.isCollapsed || rightPanel.isCollapsed) {
|
||||
leftPanel.expand();
|
||||
rightPanel.expand();
|
||||
} else {
|
||||
leftPanel.collapse();
|
||||
rightPanel.collapse();
|
||||
}
|
||||
},
|
||||
dependencies: [
|
||||
leftPanel.isCollapsed,
|
||||
rightPanel.isCollapsed,
|
||||
leftPanel.expand,
|
||||
rightPanel.expand,
|
||||
leftPanel.collapse,
|
||||
rightPanel.collapse,
|
||||
],
|
||||
});
|
||||
|
||||
const onReady = useCallback<IDockviewReactProps['onReady']>((event) => {
|
||||
const _resetToDefaults = () => canvasLayout(event.api);
|
||||
$panels.set({ api: event.api, resetLayout: _resetToDefaults });
|
||||
_resetToDefaults();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const panels = $panels.get();
|
||||
if (!panels) {
|
||||
return;
|
||||
}
|
||||
if (tab === 'gallery') {
|
||||
galleryLayout(panels.api);
|
||||
} else {
|
||||
canvasLayout(panels.api);
|
||||
}
|
||||
}, [tab]);
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'resetPanelLayout',
|
||||
// category: 'app',
|
||||
// callback: () => {
|
||||
// leftPanel.reset();
|
||||
// rightPanel.reset();
|
||||
// },
|
||||
// dependencies: [leftPanel.reset, rightPanel.reset],
|
||||
// });
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'togglePanels',
|
||||
// category: 'app',
|
||||
// callback: () => {
|
||||
// if (leftPanel.isCollapsed || rightPanel.isCollapsed) {
|
||||
// leftPanel.expand();
|
||||
// rightPanel.expand();
|
||||
// } else {
|
||||
// leftPanel.collapse();
|
||||
// rightPanel.collapse();
|
||||
// }
|
||||
// },
|
||||
// dependencies: [
|
||||
// leftPanel.isCollapsed,
|
||||
// rightPanel.isCollapsed,
|
||||
// leftPanel.expand,
|
||||
// rightPanel.expand,
|
||||
// leftPanel.collapse,
|
||||
// rightPanel.collapse,
|
||||
// ],
|
||||
// });
|
||||
|
||||
return (
|
||||
<Flex id="invoke-app-tabs" w="full" h="full" overflow="hidden">
|
||||
<Flex id="invoke-app-tabs" w="full" h="full" overflow="hidden" position="relative">
|
||||
<VerticalNavBar />
|
||||
<DockviewReact
|
||||
{/* <DockviewReact
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
theme={theme}
|
||||
defaultTabComponent={MyCustomTab}
|
||||
rightHeaderActionsComponent={RightHeaderActions}
|
||||
/>
|
||||
/> */}
|
||||
<GridviewWrapper />
|
||||
{/* <PanelGroup
|
||||
ref={imperativePanelGroupRef}
|
||||
id="app-panel-group"
|
||||
@@ -479,41 +114,3 @@ export const AppContent = memo(() => {
|
||||
);
|
||||
});
|
||||
AppContent.displayName = 'AppContent';
|
||||
|
||||
const GalleryTab = () => {
|
||||
const onReady = useCallback<IDockviewReactProps['onReady']>((event) => {
|
||||
const viewer = event.api.addPanel({
|
||||
id: 'gallery-viewer',
|
||||
title: 'Viewer',
|
||||
component: 'viewer',
|
||||
});
|
||||
const gallery = event.api.addPanel({
|
||||
id: 'gallery-gallery',
|
||||
title: 'Gallery',
|
||||
component: 'gallery',
|
||||
initialWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: viewer,
|
||||
},
|
||||
});
|
||||
event.api.addPanel({
|
||||
id: 'gallery-boards',
|
||||
title: 'Boards',
|
||||
component: 'boards',
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: gallery,
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<DockviewReact
|
||||
onReady={onReady}
|
||||
components={components}
|
||||
theme={theme}
|
||||
defaultTabComponent={MyCustomTab}
|
||||
rightHeaderActionsComponent={RightHeaderActions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
import 'dockview/dist/styles/dockview.css';
|
||||
import './dockview_theme_invoke.css';
|
||||
|
||||
import { Divider, Flex, IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
type DockviewApi,
|
||||
DockviewDefaultTab,
|
||||
DockviewReact,
|
||||
type DockviewTheme,
|
||||
type IDockviewHeaderActionsProps,
|
||||
type IDockviewPanelHeaderProps,
|
||||
type IDockviewReactProps,
|
||||
} from 'dockview';
|
||||
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import ProgressImage from 'features/gallery/components/ImageViewer/ProgressImage';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
|
||||
import { CompositingSettingsAccordion } from 'features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion';
|
||||
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
|
||||
import { ImageSettingsAccordion } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion';
|
||||
import { RefinerSettingsAccordion } from 'features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion';
|
||||
import { MainPanelContent } from 'features/ui/components/MainPanelContent';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_MIN_SIZE_PX } from 'features/ui/store/uiSlice';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { PiArrowSquareOutBold, PiCornersInBold, PiCornersOutBold } from 'react-icons/pi';
|
||||
|
||||
const MyCustomTab = (props: IDockviewPanelHeaderProps) => {
|
||||
const onDragEnter = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
props.api.setActive();
|
||||
}
|
||||
}, [props.api]);
|
||||
|
||||
return <DockviewDefaultTab hideClose {...props} onDragEnter={onDragEnter} />;
|
||||
};
|
||||
|
||||
const RightHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||
const [isMaximized, setIsMaximized] = useState(false);
|
||||
const popOutToFloating = useCallback(() => {
|
||||
props.containerApi.addFloatingGroup(props.group);
|
||||
}, [props.containerApi, props.group]);
|
||||
const maximize = useCallback(() => {
|
||||
if (!props.group.activePanel) {
|
||||
props.group.panels.at(0)?.api.setActive();
|
||||
}
|
||||
if (!props.group.activePanel) {
|
||||
return;
|
||||
}
|
||||
props.containerApi.maximizeGroup(props.group.activePanel);
|
||||
}, [props.containerApi, props.group]);
|
||||
const exitMaximized = useCallback(() => {
|
||||
if (!props.group.activePanel) {
|
||||
props.group.panels.at(0)?.api.setActive();
|
||||
}
|
||||
if (!props.group.activePanel) {
|
||||
return;
|
||||
}
|
||||
props.containerApi.exitMaximizedGroup();
|
||||
}, [props.containerApi, props.group]);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = props.containerApi.onDidMaximizedGroupChange((e) => {
|
||||
if (e.group.id === props.group.id) {
|
||||
setIsMaximized(e.isMaximized);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
subscription.dispose();
|
||||
};
|
||||
}, [props.containerApi, props.group.id]);
|
||||
|
||||
return (
|
||||
<Flex h="full" alignItems="center" pe={1}>
|
||||
{!isMaximized && (
|
||||
<IconButton
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiCornersOutBold />}
|
||||
aria-label="Maximize Panel"
|
||||
tooltip="Maximize Panel"
|
||||
onClick={maximize}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
{isMaximized && (
|
||||
<IconButton
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiCornersInBold />}
|
||||
aria-label="Maximize Panel"
|
||||
tooltip="Maximize Panel"
|
||||
onClick={exitMaximized}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
{props.group.api.location.type !== 'floating' && (
|
||||
<IconButton
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
icon={<PiArrowSquareOutBold />}
|
||||
aria-label="Pop out Panel"
|
||||
tooltip="Pop out Panel"
|
||||
onClick={popOutToFloating}
|
||||
opacity={0.7}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const LayersPanelContent = memo(() => (
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasLayersPanelContent />
|
||||
</CanvasManagerProviderGate>
|
||||
));
|
||||
LayersPanelContent.displayName = 'LayersPanelContent';
|
||||
|
||||
const ViewerPanelContent = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2} gap={2}>
|
||||
<ViewerToolbar />
|
||||
<Divider />
|
||||
<ImageViewer />
|
||||
</Flex>
|
||||
));
|
||||
ViewerPanelContent.displayName = 'ViewerPanelContent';
|
||||
|
||||
const ProgressPanelContent = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2}>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
));
|
||||
ProgressPanelContent.displayName = 'ProgressPanelContent';
|
||||
|
||||
const components: IDockviewReactProps['components'] = {
|
||||
main: MainPanelContent,
|
||||
boards: BoardsListPanelContent,
|
||||
gallery: Gallery,
|
||||
layers: LayersPanelContent,
|
||||
queueControls: QueueControls,
|
||||
prompts: Prompts,
|
||||
imageSettings: ImageSettingsAccordion,
|
||||
generationSettings: GenerationSettingsAccordion,
|
||||
compositingSettings: CompositingSettingsAccordion,
|
||||
advancedSettings: AdvancedSettingsAccordion,
|
||||
refinerSettings: RefinerSettingsAccordion,
|
||||
viewer: ViewerPanelContent,
|
||||
progress: ProgressPanelContent,
|
||||
};
|
||||
|
||||
const theme: DockviewTheme = {
|
||||
className: 'dockview-theme-invoke',
|
||||
name: 'Invoke',
|
||||
};
|
||||
|
||||
export const $panels = atom<{ api: DockviewApi; resetLayout: () => void } | null>(null);
|
||||
|
||||
const canvasLayout = (api: DockviewApi) => {
|
||||
api.clear();
|
||||
const mainPanel = api.addPanel({
|
||||
id: 'main',
|
||||
component: 'main',
|
||||
title: 'Workspace',
|
||||
minimumWidth: 200,
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'viewer',
|
||||
component: 'viewer',
|
||||
title: 'Viewer',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'progress',
|
||||
component: 'progress',
|
||||
title: 'Progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const queueControls = api.addPanel({
|
||||
id: 'queue-controls',
|
||||
title: 'Queue',
|
||||
component: 'queueControls',
|
||||
// floating: true,
|
||||
// initialHeight: 48 + 24,
|
||||
initialHeight: 48,
|
||||
maximumHeight: 48,
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
initialWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const promptsPanel = api.addPanel({
|
||||
id: 'prompts',
|
||||
title: 'Prompts',
|
||||
component: 'prompts',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: queueControls,
|
||||
},
|
||||
});
|
||||
const imagePanel = api.addPanel({
|
||||
id: 'imageSettings',
|
||||
title: 'Image',
|
||||
component: 'imageSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: promptsPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'generationSettings',
|
||||
title: 'Generation',
|
||||
component: 'generationSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: imagePanel,
|
||||
},
|
||||
});
|
||||
const compPanel = api.addPanel({
|
||||
id: 'compositingSettings',
|
||||
title: 'Compositing',
|
||||
component: 'compositingSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: imagePanel,
|
||||
},
|
||||
});
|
||||
const advancedPanel = api.addPanel({
|
||||
id: 'advancedSettings',
|
||||
title: 'Advanced',
|
||||
component: 'advancedSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: compPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'refinerSettings',
|
||||
title: 'Refiner',
|
||||
component: 'refinerSettings',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: advancedPanel,
|
||||
},
|
||||
});
|
||||
const boardsPanel = api.addPanel({
|
||||
id: 'boards',
|
||||
component: 'boards',
|
||||
title: 'Boards',
|
||||
initialWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: mainPanel,
|
||||
},
|
||||
});
|
||||
const galleryPanel = api.addPanel({
|
||||
id: 'gallery',
|
||||
component: 'gallery',
|
||||
title: 'Gallery',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: boardsPanel,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'layers',
|
||||
component: 'layers',
|
||||
title: 'Layers',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: galleryPanel,
|
||||
},
|
||||
});
|
||||
mainPanel.api.setActive();
|
||||
};
|
||||
|
||||
const galleryLayout = (api: DockviewApi) => {
|
||||
api.clear();
|
||||
const viewer = api.addPanel({
|
||||
id: 'gallery-viewer',
|
||||
title: 'Viewer',
|
||||
component: 'viewer',
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'gallery-progress',
|
||||
title: 'Progress',
|
||||
component: 'progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: viewer,
|
||||
},
|
||||
});
|
||||
const gallery = api.addPanel({
|
||||
id: 'gallery-gallery',
|
||||
title: 'Gallery',
|
||||
component: 'gallery',
|
||||
initialWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: viewer,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'gallery-boards',
|
||||
title: 'Boards',
|
||||
component: 'boards',
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: gallery,
|
||||
},
|
||||
});
|
||||
viewer.api.setActive();
|
||||
};
|
||||
|
||||
export const DockviewWrapper = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
|
||||
const onTabChange = useCallback((tab: TabName, api: DockviewApi) => {
|
||||
if (tab === 'gallery') {
|
||||
galleryLayout(api);
|
||||
} else {
|
||||
canvasLayout(api);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onReady = useCallback<IDockviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
const _resetToDefaults = () => canvasLayout(event.api);
|
||||
$panels.set({ api: event.api, resetLayout: _resetToDefaults });
|
||||
onTabChange(tab, event.api);
|
||||
},
|
||||
[onTabChange, tab]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const panels = $panels.get();
|
||||
if (!panels) {
|
||||
return;
|
||||
}
|
||||
onTabChange(tab, panels.api);
|
||||
}, [onTabChange, tab]);
|
||||
|
||||
return (
|
||||
<DockviewReact
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
theme={theme}
|
||||
defaultTabComponent={MyCustomTab}
|
||||
rightHeaderActionsComponent={RightHeaderActions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
DockviewWrapper.displayName = 'DockviewWrapper';
|
||||
@@ -0,0 +1,242 @@
|
||||
import 'dockview/dist/styles/dockview.css';
|
||||
import './dockview_theme_invoke.css';
|
||||
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { GridviewApi, IGridviewReactProps } from 'dockview';
|
||||
import { GridviewReact, LayoutPriority, Orientation } from 'dockview';
|
||||
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import ProgressImage from 'features/gallery/components/ImageViewer/ProgressImage';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
|
||||
import { CompositingSettingsAccordion } from 'features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion';
|
||||
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
|
||||
import { ImageSettingsAccordion } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion';
|
||||
import { RefinerSettingsAccordion } from 'features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion';
|
||||
import { MainPanelContent } from 'features/ui/components/MainPanelContent';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_MIN_SIZE_PX } from 'features/ui/store/uiSlice';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback, useEffect } from 'react';
|
||||
|
||||
const LayersPanelContent = memo(() => (
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasLayersPanelContent />
|
||||
</CanvasManagerProviderGate>
|
||||
));
|
||||
LayersPanelContent.displayName = 'LayersPanelContent';
|
||||
|
||||
const ViewerPanelContent = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2} gap={2}>
|
||||
<ViewerToolbar />
|
||||
<Divider />
|
||||
<ImageViewer />
|
||||
</Flex>
|
||||
));
|
||||
ViewerPanelContent.displayName = 'ViewerPanelContent';
|
||||
|
||||
const ProgressPanelContent = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2}>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
));
|
||||
ProgressPanelContent.displayName = 'ProgressPanelContent';
|
||||
|
||||
const components: IGridviewReactProps['components'] = {
|
||||
main: MainPanelContent,
|
||||
boards: BoardsListPanelContent,
|
||||
gallery: Gallery,
|
||||
layers: LayersPanelContent,
|
||||
queueControls: QueueControls,
|
||||
prompts: Prompts,
|
||||
imageSettings: ImageSettingsAccordion,
|
||||
generationSettings: GenerationSettingsAccordion,
|
||||
compositingSettings: CompositingSettingsAccordion,
|
||||
advancedSettings: AdvancedSettingsAccordion,
|
||||
refinerSettings: RefinerSettingsAccordion,
|
||||
viewer: ViewerPanelContent,
|
||||
progress: ProgressPanelContent,
|
||||
};
|
||||
|
||||
export const $panels = atom<{ api: GridviewApi; resetLayout: () => void } | null>(null);
|
||||
|
||||
const canvasLayout = (api: GridviewApi) => {
|
||||
api.clear();
|
||||
const mainPanel = api.addPanel({
|
||||
id: 'main',
|
||||
component: 'main',
|
||||
minimumWidth: 200,
|
||||
priority: LayoutPriority.High,
|
||||
});
|
||||
// api.addPanel({
|
||||
// id: 'viewer',
|
||||
// component: 'viewer',
|
||||
// position: {
|
||||
// direction: 'within',
|
||||
// referencePanel: mainPanel.id,
|
||||
// },
|
||||
// });
|
||||
// api.addPanel({
|
||||
// id: 'progress',
|
||||
// component: 'progress',
|
||||
// position: {
|
||||
// direction: 'within',
|
||||
// referencePanel: mainPanel.id,
|
||||
// },
|
||||
// });
|
||||
const queueControls = api.addPanel({
|
||||
id: 'queue-controls',
|
||||
component: 'queueControls',
|
||||
// floating: true,
|
||||
// initialHeight: 48 + 24,
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: mainPanel.id,
|
||||
},
|
||||
priority: LayoutPriority.Low,
|
||||
snap: true,
|
||||
});
|
||||
const promptsPanel = api.addPanel({
|
||||
id: 'prompts',
|
||||
component: 'prompts',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: queueControls.id,
|
||||
},
|
||||
});
|
||||
const imagePanel = api.addPanel({
|
||||
id: 'imageSettings',
|
||||
component: 'imageSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: promptsPanel.id,
|
||||
},
|
||||
});
|
||||
const genPanel = api.addPanel({
|
||||
id: 'generationSettings',
|
||||
component: 'generationSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: imagePanel.id,
|
||||
},
|
||||
});
|
||||
const compPanel = api.addPanel({
|
||||
id: 'compositingSettings',
|
||||
component: 'compositingSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: genPanel.id,
|
||||
},
|
||||
});
|
||||
const advancedPanel = api.addPanel({
|
||||
id: 'advancedSettings',
|
||||
component: 'advancedSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: compPanel.id,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'refinerSettings',
|
||||
component: 'refinerSettings',
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: advancedPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const galleryPanel = api.addPanel({
|
||||
id: 'gallery',
|
||||
component: 'gallery',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: mainPanel.id,
|
||||
},
|
||||
snap: true,
|
||||
});
|
||||
const boardsPanel = api.addPanel({
|
||||
id: 'boards',
|
||||
component: 'boards',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: galleryPanel.id,
|
||||
},
|
||||
priority: LayoutPriority.Low,
|
||||
});
|
||||
mainPanel.api.setActive();
|
||||
galleryPanel.api.setSize({ width: RIGHT_PANEL_MIN_SIZE_PX });
|
||||
boardsPanel.api.setSize({ height: 200 });
|
||||
queueControls.api.setSize({ width: LEFT_PANEL_MIN_SIZE_PX });
|
||||
};
|
||||
|
||||
const galleryLayout = (api: GridviewApi) => {
|
||||
api.clear();
|
||||
const viewer = api.addPanel({
|
||||
id: 'gallery-viewer',
|
||||
component: 'viewer',
|
||||
});
|
||||
const gallery = api.addPanel({
|
||||
id: 'gallery-gallery',
|
||||
component: 'gallery',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: viewer.id,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'gallery-boards',
|
||||
component: 'boards',
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: gallery.id,
|
||||
},
|
||||
});
|
||||
viewer.api.setActive();
|
||||
};
|
||||
|
||||
export const GridviewWrapper = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
|
||||
const onTabChange = useCallback((tab: TabName, api: GridviewApi) => {
|
||||
if (tab === 'gallery') {
|
||||
galleryLayout(api);
|
||||
} else {
|
||||
canvasLayout(api);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>((event) => {
|
||||
const _resetToDefaults = () => canvasLayout(event.api);
|
||||
$panels.set({ api: event.api, resetLayout: _resetToDefaults });
|
||||
_resetToDefaults();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const panels = $panels.get();
|
||||
if (!panels) {
|
||||
return;
|
||||
}
|
||||
onTabChange(tab, panels.api);
|
||||
}, [onTabChange, tab]);
|
||||
|
||||
return (
|
||||
<GridviewReact
|
||||
className="dockview-theme-invoke"
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
orientation={Orientation.VERTICAL}
|
||||
/>
|
||||
);
|
||||
});
|
||||
GridviewWrapper.displayName = 'GridviewWrapper';
|
||||
Reference in New Issue
Block a user