mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
lru
This commit is contained in:
parent
89b7082bc0
commit
ee2529f3fd
@ -63,7 +63,7 @@ const BoardsList = (props: Props) => {
|
|||||||
<Grid
|
<Grid
|
||||||
className="list-container"
|
className="list-container"
|
||||||
data-testid="boards-list"
|
data-testid="boards-list"
|
||||||
gridTemplateColumns="repeat(auto-fill, minmax(108px, 1fr))"
|
gridTemplateColumns="repeat(auto-fill, minmax(90px, 1fr))"
|
||||||
maxH={346}
|
maxH={346}
|
||||||
>
|
>
|
||||||
<GridItem p={1.5} data-testid="no-board">
|
<GridItem p={1.5} data-testid="no-board">
|
||||||
|
@ -12,7 +12,7 @@ export const initialGalleryState: GalleryState = {
|
|||||||
shouldAutoSwitch: true,
|
shouldAutoSwitch: true,
|
||||||
autoAssignBoardOnClick: true,
|
autoAssignBoardOnClick: true,
|
||||||
autoAddBoardId: 'none',
|
autoAddBoardId: 'none',
|
||||||
galleryImageMinimumWidth: 96,
|
galleryImageMinimumWidth: 90,
|
||||||
selectedBoardId: 'none',
|
selectedBoardId: 'none',
|
||||||
galleryView: 'images',
|
galleryView: 'images',
|
||||||
boardSearchText: '',
|
boardSearchText: '',
|
||||||
|
@ -39,7 +39,7 @@ const NodeEditorPanelGroup = () => {
|
|||||||
<WorkflowPanel />
|
<WorkflowPanel />
|
||||||
</Panel>
|
</Panel>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
direction="vertical"
|
orientation="horizontal"
|
||||||
onDoubleClick={handleDoubleClickHandle}
|
onDoubleClick={handleDoubleClickHandle}
|
||||||
/>
|
/>
|
||||||
<Panel id="inspector" collapsible minSize={25}>
|
<Panel id="inspector" collapsible minSize={25}>
|
||||||
|
@ -29,7 +29,7 @@ const QueueControls = () => {
|
|||||||
{isPauseEnabled && <PauseProcessorButton asIconButton />} */}
|
{isPauseEnabled && <PauseProcessorButton asIconButton />} */}
|
||||||
<ClearQueueButton asIconButton />
|
<ClearQueueButton asIconButton />
|
||||||
</InvButtonGroup>
|
</InvButtonGroup>
|
||||||
<Flex h={1} w="full">
|
<Flex h={2} w="full">
|
||||||
<ProgressBar />
|
<ProgressBar />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Flex, Spacer } from '@chakra-ui/react';
|
import { Spacer } from '@chakra-ui/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
@ -13,6 +13,7 @@ import {
|
|||||||
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
||||||
import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
|
import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
|
||||||
import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup';
|
import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup';
|
||||||
|
import type { UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||||
import { usePanel } from 'features/ui/hooks/usePanel';
|
import { usePanel } from 'features/ui/hooks/usePanel';
|
||||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
||||||
import type { InvokeTabName } from 'features/ui/store/tabMap';
|
import type { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
@ -22,15 +23,15 @@ import {
|
|||||||
} from 'features/ui/store/uiSelectors';
|
} from 'features/ui/store/uiSelectors';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import type { CSSProperties, MouseEvent, ReactElement, ReactNode } from 'react';
|
import type { CSSProperties, MouseEvent, ReactElement, ReactNode } from 'react';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaCube, FaFont, FaImage } from 'react-icons/fa';
|
import { FaCube, FaFont, FaImage } from 'react-icons/fa';
|
||||||
import { FaCircleNodes, FaList } from 'react-icons/fa6';
|
import { FaCircleNodes, FaList } from 'react-icons/fa6';
|
||||||
import { MdGridOn } from 'react-icons/md';
|
import { MdGridOn } from 'react-icons/md';
|
||||||
|
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
|
|
||||||
import FloatingGalleryButton from './FloatingGalleryButton';
|
|
||||||
import ParametersPanel from './ParametersPanel';
|
import ParametersPanel from './ParametersPanel';
|
||||||
import ImageTab from './tabs/ImageToImageTab';
|
import ImageTab from './tabs/ImageToImageTab';
|
||||||
import ModelManagerTab from './tabs/ModelManagerTab';
|
import ModelManagerTab from './tabs/ModelManagerTab';
|
||||||
@ -98,8 +99,24 @@ const enabledTabsSelector = createMemoizedSelector(
|
|||||||
export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
||||||
export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
||||||
const panelStyles: CSSProperties = { height: '100%', width: '100%' };
|
const panelStyles: CSSProperties = { height: '100%', width: '100%' };
|
||||||
|
const GALLERY_MIN_SIZE_PX = 310;
|
||||||
const GALLERY_MIN_SIZE_PCT = 20;
|
const GALLERY_MIN_SIZE_PCT = 20;
|
||||||
const OPTIONS_PANEL_WIDTH = '434px';
|
const OPTIONS_PANEL_MIN_SIZE_PX = 430;
|
||||||
|
const OPTIONS_PANEL_MIN_SIZE_PCT = 20;
|
||||||
|
|
||||||
|
const optionsPanelUsePanelOptions: UsePanelOptions = {
|
||||||
|
unit: 'pixels',
|
||||||
|
minSize: OPTIONS_PANEL_MIN_SIZE_PX,
|
||||||
|
fallbackMinSizePct: OPTIONS_PANEL_MIN_SIZE_PCT,
|
||||||
|
panelGroupID: 'app',
|
||||||
|
};
|
||||||
|
|
||||||
|
const galleryPanelUsePanelOptions: UsePanelOptions = {
|
||||||
|
unit: 'pixels',
|
||||||
|
minSize: GALLERY_MIN_SIZE_PX,
|
||||||
|
fallbackMinSizePct: GALLERY_MIN_SIZE_PCT,
|
||||||
|
panelGroupID: 'app',
|
||||||
|
};
|
||||||
|
|
||||||
const InvokeTabs = () => {
|
const InvokeTabs = () => {
|
||||||
const activeTabIndex = useAppSelector(activeTabIndexSelector);
|
const activeTabIndex = useAppSelector(activeTabIndexSelector);
|
||||||
@ -107,7 +124,7 @@ const InvokeTabs = () => {
|
|||||||
const enabledTabs = useAppSelector(enabledTabsSelector);
|
const enabledTabs = useAppSelector(enabledTabsSelector);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const panelGroupHandleRef = useRef<ImperativePanelGroupHandle>(null);
|
||||||
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
|
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
|
||||||
if (e.target instanceof HTMLElement) {
|
if (e.target instanceof HTMLElement) {
|
||||||
e.target.blur();
|
e.target.blur();
|
||||||
@ -153,6 +170,20 @@ const InvokeTabs = () => {
|
|||||||
[dispatch, enabledTabs]
|
[dispatch, enabledTabs]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const panelStorage = usePanelStorage();
|
||||||
|
|
||||||
|
const {
|
||||||
|
ref: optionsPanelRef,
|
||||||
|
minSize: optionsPanelMinSize,
|
||||||
|
isCollapsed: isOptionsPanelCollapsed,
|
||||||
|
onCollapse: onCollapseOptionsPanel,
|
||||||
|
onExpand: onExpandOptionsPanel,
|
||||||
|
reset: resetOptionsPanel,
|
||||||
|
expand: expandOptionsPanel,
|
||||||
|
collapse: collapseOptionsPanel,
|
||||||
|
toggle: toggleOptionsPanel,
|
||||||
|
} = usePanel(optionsPanelUsePanelOptions);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ref: galleryPanelRef,
|
ref: galleryPanelRef,
|
||||||
minSize: galleryPanelMinSize,
|
minSize: galleryPanelMinSize,
|
||||||
@ -161,12 +192,25 @@ const InvokeTabs = () => {
|
|||||||
onExpand: onExpandGalleryPanel,
|
onExpand: onExpandGalleryPanel,
|
||||||
reset: resetGalleryPanel,
|
reset: resetGalleryPanel,
|
||||||
expand: expandGalleryPanel,
|
expand: expandGalleryPanel,
|
||||||
|
collapse: collapseGalleryPanel,
|
||||||
toggle: toggleGalleryPanel,
|
toggle: toggleGalleryPanel,
|
||||||
} = usePanel(GALLERY_MIN_SIZE_PCT);
|
} = usePanel(galleryPanelUsePanelOptions);
|
||||||
|
|
||||||
useHotkeys('g', toggleGalleryPanel, []);
|
useHotkeys('g', toggleGalleryPanel, []);
|
||||||
|
useHotkeys('t', toggleOptionsPanel, []);
|
||||||
const panelStorage = usePanelStorage();
|
useHotkeys(
|
||||||
|
'f',
|
||||||
|
() => {
|
||||||
|
if (isOptionsPanelCollapsed || isGalleryPanelCollapsed) {
|
||||||
|
expandOptionsPanel();
|
||||||
|
expandGalleryPanel();
|
||||||
|
} else {
|
||||||
|
collapseOptionsPanel();
|
||||||
|
collapseGalleryPanel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[isOptionsPanelCollapsed, isGalleryPanelCollapsed]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvTabs
|
<InvTabs
|
||||||
@ -183,35 +227,52 @@ const InvokeTabs = () => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
</InvTabList>
|
</InvTabList>
|
||||||
<PanelGroup
|
<PanelGroup
|
||||||
|
ref={panelGroupHandleRef}
|
||||||
id="app"
|
id="app"
|
||||||
autoSaveId="app"
|
autoSaveId="app"
|
||||||
direction="horizontal"
|
direction="horizontal"
|
||||||
style={panelStyles}
|
style={panelStyles}
|
||||||
storage={panelStorage}
|
storage={panelStorage}
|
||||||
>
|
>
|
||||||
<Panel id="main" order={0} minSize={50}>
|
|
||||||
<Flex w="full" h="full" gap={4}>
|
|
||||||
{!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
|
{!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
|
||||||
<Flex h="full" w={OPTIONS_PANEL_WIDTH} flexShrink={0}>
|
<>
|
||||||
|
<Panel
|
||||||
|
id="options"
|
||||||
|
ref={optionsPanelRef}
|
||||||
|
order={0}
|
||||||
|
defaultSize={optionsPanelMinSize}
|
||||||
|
minSize={optionsPanelMinSize}
|
||||||
|
onCollapse={onCollapseOptionsPanel}
|
||||||
|
onExpand={onExpandOptionsPanel}
|
||||||
|
collapsible
|
||||||
|
>
|
||||||
{activeTabName === 'nodes' ? (
|
{activeTabName === 'nodes' ? (
|
||||||
<NodeEditorPanelGroup />
|
<NodeEditorPanelGroup />
|
||||||
) : (
|
) : (
|
||||||
<ParametersPanel />
|
<ParametersPanel />
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Panel>
|
||||||
|
<ResizeHandle
|
||||||
|
onDoubleClick={resetOptionsPanel}
|
||||||
|
orientation="vertical"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
<Panel id="main" order={1} minSize={20}>
|
||||||
<InvTabPanels w="full" h="full">
|
<InvTabPanels w="full" h="full">
|
||||||
{tabPanels}
|
{tabPanels}
|
||||||
</InvTabPanels>
|
</InvTabPanels>
|
||||||
</Flex>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
{!NO_GALLERY_TABS.includes(activeTabName) && (
|
{!NO_GALLERY_TABS.includes(activeTabName) && (
|
||||||
<>
|
<>
|
||||||
<ResizeHandle onDoubleClick={resetGalleryPanel} />
|
<ResizeHandle
|
||||||
|
onDoubleClick={resetGalleryPanel}
|
||||||
|
orientation="vertical"
|
||||||
|
/>
|
||||||
<Panel
|
<Panel
|
||||||
id="gallery"
|
id="gallery"
|
||||||
ref={galleryPanelRef}
|
ref={galleryPanelRef}
|
||||||
order={1}
|
order={2}
|
||||||
defaultSize={galleryPanelMinSize}
|
defaultSize={galleryPanelMinSize}
|
||||||
minSize={galleryPanelMinSize}
|
minSize={galleryPanelMinSize}
|
||||||
onCollapse={onCollapseGalleryPanel}
|
onCollapse={onCollapseGalleryPanel}
|
||||||
@ -220,10 +281,6 @@ const InvokeTabs = () => {
|
|||||||
>
|
>
|
||||||
<ImageGalleryContent />
|
<ImageGalleryContent />
|
||||||
</Panel>
|
</Panel>
|
||||||
<FloatingGalleryButton
|
|
||||||
isGalleryCollapsed={isGalleryPanelCollapsed}
|
|
||||||
expandGallery={expandGalleryPanel}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
|
@ -46,7 +46,10 @@ const ImageToImageTab = () => {
|
|||||||
>
|
>
|
||||||
<InitialImageDisplay />
|
<InitialImageDisplay />
|
||||||
</Panel>
|
</Panel>
|
||||||
<ResizeHandle onDoubleClick={handleDoubleClickHandle} />
|
<ResizeHandle
|
||||||
|
orientation="vertical"
|
||||||
|
onDoubleClick={handleDoubleClickHandle}
|
||||||
|
/>
|
||||||
<Panel
|
<Panel
|
||||||
id="imageTab.content.selectedImage"
|
id="imageTab.content.selectedImage"
|
||||||
order={1}
|
order={1}
|
||||||
|
@ -1,103 +1,84 @@
|
|||||||
import type { FlexProps, SystemStyleObject } from '@chakra-ui/react';
|
import { Box, defineStyleConfig, Flex, useStyleConfig } from '@chakra-ui/react';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { memo } from 'react';
|
||||||
import type { CSSProperties } from 'react';
|
import type { PanelResizeHandleProps } from 'react-resizable-panels';
|
||||||
import { memo, useMemo } from 'react';
|
|
||||||
import { PanelResizeHandle } from 'react-resizable-panels';
|
import { PanelResizeHandle } from 'react-resizable-panels';
|
||||||
|
|
||||||
type ResizeHandleProps = Omit<FlexProps, 'direction'> & {
|
type ResizeHandleProps = PanelResizeHandleProps & {
|
||||||
direction?: 'horizontal' | 'vertical';
|
orientation: 'horizontal' | 'vertical';
|
||||||
collapsedDirection?: 'top' | 'bottom' | 'left' | 'right';
|
|
||||||
isCollapsed?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResizeHandle = (props: ResizeHandleProps) => {
|
const ResizeHandle = (props: ResizeHandleProps) => {
|
||||||
const {
|
const { orientation, ...rest } = props;
|
||||||
direction = 'horizontal',
|
const styles = useStyleConfig('ResizeHandle', { orientation });
|
||||||
collapsedDirection,
|
|
||||||
isCollapsed = false,
|
|
||||||
...rest
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const resizeHandleStyles = useMemo<CSSProperties>(() => {
|
|
||||||
if (direction === 'horizontal') {
|
|
||||||
return {
|
|
||||||
visibility: isCollapsed ? 'hidden' : 'visible',
|
|
||||||
width: isCollapsed ? 0 : 'auto',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
visibility: isCollapsed ? 'hidden' : 'visible',
|
|
||||||
width: isCollapsed ? 0 : 'auto',
|
|
||||||
};
|
|
||||||
}, [direction, isCollapsed]);
|
|
||||||
|
|
||||||
const resizeHandleWrapperStyles = useMemo<SystemStyleObject>(() => {
|
|
||||||
if (direction === 'horizontal') {
|
|
||||||
return {
|
|
||||||
w: collapsedDirection ? 2.5 : 4,
|
|
||||||
h: 'full',
|
|
||||||
justifyContent: collapsedDirection
|
|
||||||
? collapsedDirection === 'left'
|
|
||||||
? 'flex-start'
|
|
||||||
: 'flex-end'
|
|
||||||
: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
div: {
|
|
||||||
bg: 'base.850',
|
|
||||||
},
|
|
||||||
_hover: {
|
|
||||||
div: { bg: 'base.700' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
w: 'full',
|
|
||||||
h: collapsedDirection ? 2.5 : 4,
|
|
||||||
alignItems: collapsedDirection
|
|
||||||
? collapsedDirection === 'top'
|
|
||||||
? 'flex-start'
|
|
||||||
: 'flex-end'
|
|
||||||
: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
div: {
|
|
||||||
bg: 'base.850',
|
|
||||||
},
|
|
||||||
_hover: {
|
|
||||||
div: { bg: 'base.700' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, [collapsedDirection, direction]);
|
|
||||||
const resizeInnerStyles = useMemo<SystemStyleObject>(() => {
|
|
||||||
if (direction === 'horizontal') {
|
|
||||||
return {
|
|
||||||
w: 1,
|
|
||||||
h: 'calc(100% - 1rem)',
|
|
||||||
borderRadius: 'base',
|
|
||||||
transitionProperty: 'common',
|
|
||||||
transitionDuration: 'normal',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
h: 1,
|
|
||||||
w: 'calc(100% - 1rem)',
|
|
||||||
borderRadius: 'base',
|
|
||||||
transitionProperty: 'common',
|
|
||||||
transitionDuration: 'normal',
|
|
||||||
};
|
|
||||||
}, [direction]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelResizeHandle style={resizeHandleStyles}>
|
<PanelResizeHandle {...rest}>
|
||||||
<Flex
|
<Flex __css={styles} data-orientation={orientation}>
|
||||||
className="resize-handle-horizontal"
|
<Box className="resize-handle-inner" data-orientation={orientation} />
|
||||||
sx={resizeHandleWrapperStyles}
|
<Box
|
||||||
{...rest}
|
className="resize-handle-drag-handle"
|
||||||
>
|
data-orientation={orientation}
|
||||||
<Box sx={resizeInnerStyles} />
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</PanelResizeHandle>
|
</PanelResizeHandle>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ResizeHandle);
|
export default memo(ResizeHandle);
|
||||||
|
|
||||||
|
export const resizeHandleTheme = defineStyleConfig({
|
||||||
|
// The styles all Cards have in common
|
||||||
|
baseStyle: () => ({
|
||||||
|
display: 'flex',
|
||||||
|
pos: 'relative',
|
||||||
|
'&[data-orientation="horizontal"]': {
|
||||||
|
w: 'full',
|
||||||
|
h: 5,
|
||||||
|
},
|
||||||
|
'&[data-orientation="vertical"]': { w: 5, h: 'full' },
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
div: {
|
||||||
|
bg: 'base.800',
|
||||||
|
},
|
||||||
|
_hover: {
|
||||||
|
div: { bg: 'base.700' },
|
||||||
|
},
|
||||||
|
_active: {
|
||||||
|
div: { bg: 'base.600' },
|
||||||
|
},
|
||||||
|
transitionProperty: 'common',
|
||||||
|
transitionDuration: 'normal',
|
||||||
|
'.resize-handle-inner': {
|
||||||
|
'&[data-orientation="horizontal"]': {
|
||||||
|
w: 'calc(100% - 1rem)',
|
||||||
|
h: '2px',
|
||||||
|
},
|
||||||
|
'&[data-orientation="vertical"]': {
|
||||||
|
w: '2px',
|
||||||
|
h: 'calc(100% - 1rem)',
|
||||||
|
},
|
||||||
|
borderRadius: 'base',
|
||||||
|
transitionProperty: 'inherit',
|
||||||
|
transitionDuration: 'inherit',
|
||||||
|
},
|
||||||
|
'.resize-handle-drag-handle': {
|
||||||
|
pos: 'absolute',
|
||||||
|
borderRadius: '2px',
|
||||||
|
transitionProperty: 'inherit',
|
||||||
|
transitionDuration: 'inherit',
|
||||||
|
'&[data-orientation="horizontal"]': {
|
||||||
|
w: '20px',
|
||||||
|
h: '6px',
|
||||||
|
insetInlineStart: '50%',
|
||||||
|
transform: 'translate(-50%, 0)',
|
||||||
|
},
|
||||||
|
'&[data-orientation="vertical"]': {
|
||||||
|
w: '6px',
|
||||||
|
h: '20px',
|
||||||
|
insetBlockStart: '50%',
|
||||||
|
transform: 'translate(0, -50%)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
@ -1,16 +1,64 @@
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { flushSync } from 'react-dom';
|
|
||||||
import type {
|
import type {
|
||||||
ImperativePanelHandle,
|
ImperativePanelHandle,
|
||||||
PanelOnCollapse,
|
PanelOnCollapse,
|
||||||
PanelOnExpand,
|
PanelOnExpand,
|
||||||
} from 'react-resizable-panels';
|
} from 'react-resizable-panels';
|
||||||
|
|
||||||
export const usePanel = (minSize: number) => {
|
export type UsePanelOptions =
|
||||||
const ref = useRef<ImperativePanelHandle>(null);
|
| { minSize: number; unit: 'percentages' }
|
||||||
|
| {
|
||||||
|
minSize: number;
|
||||||
|
unit: 'pixels';
|
||||||
|
fallbackMinSizePct: number;
|
||||||
|
panelGroupID: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePanel = (arg: UsePanelOptions) => {
|
||||||
|
const panelHandleRef = useRef<ImperativePanelHandle>(null);
|
||||||
|
const newMinSizeRef = useRef<number>(0);
|
||||||
|
const currentSizeRef = useRef<number>(0);
|
||||||
|
const [_minSize, _setMinSize] = useState<number>(
|
||||||
|
arg.unit === 'percentages' ? arg.minSize : arg.fallbackMinSizePct
|
||||||
|
);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (arg.unit === 'percentages') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const panelGroupElement = document.querySelector(
|
||||||
|
`[data-panel-group][data-panel-group-id="${arg.panelGroupID}"]`
|
||||||
|
);
|
||||||
|
if (!panelGroupElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!panelHandleRef?.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newMinSizeRef.current = (arg.minSize * 100) / entry.contentRect.width;
|
||||||
|
currentSizeRef.current =
|
||||||
|
panelHandleRef.current.getSize() ?? arg.fallbackMinSizePct;
|
||||||
|
if (currentSizeRef.current < newMinSizeRef.current) {
|
||||||
|
panelHandleRef.current.resize(newMinSizeRef.current);
|
||||||
|
}
|
||||||
|
_setMinSize(newMinSizeRef.current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(panelGroupElement);
|
||||||
|
// _setMinSize(
|
||||||
|
// (arg.minSize * 100) / panelGroupElement.getBoundingClientRect().width
|
||||||
|
// );
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
}, [arg]);
|
||||||
|
|
||||||
const [isCollapsed, setIsCollapsed] = useState(() =>
|
const [isCollapsed, setIsCollapsed] = useState(() =>
|
||||||
Boolean(ref.current?.isCollapsed())
|
Boolean(panelHandleRef.current?.isCollapsed())
|
||||||
);
|
);
|
||||||
|
|
||||||
const onCollapse = useCallback<PanelOnCollapse>(() => {
|
const onCollapse = useCallback<PanelOnCollapse>(() => {
|
||||||
@ -22,38 +70,35 @@ export const usePanel = (minSize: number) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggle = useCallback(() => {
|
const toggle = useCallback(() => {
|
||||||
if (ref.current?.isCollapsed()) {
|
if (panelHandleRef.current?.isCollapsed()) {
|
||||||
flushSync(() => {
|
panelHandleRef.current?.expand();
|
||||||
ref.current?.expand();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
flushSync(() => {
|
panelHandleRef.current?.collapse();
|
||||||
ref.current?.collapse();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const expand = useCallback(() => {
|
const expand = useCallback(() => {
|
||||||
flushSync(() => {
|
panelHandleRef.current?.expand();
|
||||||
ref.current?.expand();
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const collapse = useCallback(() => {
|
const collapse = useCallback(() => {
|
||||||
flushSync(() => {
|
panelHandleRef.current?.collapse();
|
||||||
ref.current?.collapse();
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
flushSync(() => {
|
// If the panel is really super close to the min size, collapse it
|
||||||
ref.current?.resize(minSize);
|
const shouldCollapse =
|
||||||
});
|
Math.abs((panelHandleRef.current?.getSize() ?? 0) - _minSize) < 0.01;
|
||||||
}, [minSize]);
|
if (shouldCollapse) {
|
||||||
|
collapse();
|
||||||
|
} else {
|
||||||
|
panelHandleRef.current?.resize(_minSize);
|
||||||
|
}
|
||||||
|
}, [_minSize, collapse]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
ref: panelHandleRef,
|
||||||
minSize,
|
minSize: _minSize,
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
onCollapse,
|
onCollapse,
|
||||||
onExpand,
|
onExpand,
|
||||||
|
@ -6,5 +6,4 @@ import type { UIState } from './uiTypes';
|
|||||||
export const uiPersistDenylist: (keyof UIState)[] = [
|
export const uiPersistDenylist: (keyof UIState)[] = [
|
||||||
'shouldShowImageDetails',
|
'shouldShowImageDetails',
|
||||||
'globalMenuCloseTrigger',
|
'globalMenuCloseTrigger',
|
||||||
'panels',
|
|
||||||
];
|
];
|
||||||
|
@ -24,6 +24,7 @@ import { tabsTheme } from 'common/components/InvTabs/theme';
|
|||||||
import { textTheme } from 'common/components/InvText/theme';
|
import { textTheme } from 'common/components/InvText/theme';
|
||||||
import { textareaTheme } from 'common/components/InvTextarea/theme';
|
import { textareaTheme } from 'common/components/InvTextarea/theme';
|
||||||
import { tooltipTheme } from 'common/components/InvTooltip/theme';
|
import { tooltipTheme } from 'common/components/InvTooltip/theme';
|
||||||
|
import { resizeHandleTheme } from 'features/ui/components/tabs/ResizeHandle';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InvokeAIColors,
|
InvokeAIColors,
|
||||||
@ -117,6 +118,7 @@ export const theme: ThemeOverride = {
|
|||||||
Textarea: textareaTheme,
|
Textarea: textareaTheme,
|
||||||
Tooltip: tooltipTheme,
|
Tooltip: tooltipTheme,
|
||||||
FormError: formErrorTheme,
|
FormError: formErrorTheme,
|
||||||
|
ResizeHandle: resizeHandleTheme,
|
||||||
},
|
},
|
||||||
space: space,
|
space: space,
|
||||||
sizes: space,
|
sizes: space,
|
||||||
|
Loading…
Reference in New Issue
Block a user