feat(ui): boards and gallery panel collapse

This commit is contained in:
psychedelicious
2025-06-26 19:34:54 +10:00
parent 2c73a5d65e
commit b8e8f05f4c
12 changed files with 283 additions and 130 deletions

View File

@@ -1,6 +1,5 @@
import { IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { buildUseDisclosure } from 'common/hooks/useBoolean';
import { selectBoardSearchText } from 'features/gallery/store/gallerySelectors';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import type { ChangeEvent, KeyboardEvent } from 'react';
@@ -8,8 +7,6 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
export const [useBoardSearchDisclosure, $boardSearchIsOpen] = buildUseDisclosure(false);
export const BoardsSearch = memo(() => {
const dispatch = useAppDispatch();
const boardSearchText = useAppSelector(selectBoardSearchText);

View File

@@ -1,12 +1,14 @@
import {
Box,
Divider,
Flex,
IconButton,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
Portal,
Text,
} from '@invoke-ai/ui-library';
import BoardAutoAddSelect from 'features/gallery/components/Boards/BoardAutoAddSelect';
import AutoAssignBoardCheckbox from 'features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox';
@@ -32,20 +34,28 @@ export const BoardsSettingsPopover = memo(() => {
tooltip={t('gallery.boardsSettings')}
/>
</PopoverTrigger>
<PopoverContent>
<PopoverBody>
<Flex direction="column" gap={2}>
<AutoAssignBoardCheckbox />
<ShowArchivedBoardsCheckbox />
<BoardAutoAddSelect />
<Box py={2}>
<Divider />
</Box>
<Portal>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<Flex direction="column" gap={2}>
<Text fontWeight="semibold" color="base.300">
Boards Settings
</Text>
<BoardsListSortControls />
</Flex>
</PopoverBody>
</PopoverContent>
<Divider />
<AutoAssignBoardCheckbox />
<ShowArchivedBoardsCheckbox />
<BoardAutoAddSelect />
<Divider />
<BoardsListSortControls />
</Flex>
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
);
});

View File

@@ -1,19 +1,80 @@
import { Box, Collapse, Divider, Flex } from '@invoke-ai/ui-library';
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 { useDisclosure } from 'common/hooks/useBoolean';
import { BoardsListWrapper } from 'features/gallery/components/Boards/BoardsList/BoardsListWrapper';
import { $boardSearchIsOpen, BoardsSearch } from 'features/gallery/components/Boards/BoardsList/BoardsSearch';
import { GalleryTopBar } from 'features/gallery/components/GalleryTopBar';
import { BoardsSearch } from 'features/gallery/components/Boards/BoardsList/BoardsSearch';
import { BoardsSettingsPopover } from 'features/gallery/components/Boards/BoardsSettingsPopover';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
import { selectBoardSearchText } from 'features/gallery/store/gallerySelectors';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
import { BOARD_PANEL_DEFAULT_HEIGHT_PX, BOARD_PANEL_MIN_HEIGHT_PX, BOARDS_PANEL_ID } from 'features/ui/layouts/shared';
import { useCollapsibleGridviewPanel } from 'features/ui/layouts/use-collapsible-gridview-panel';
import type { CSSProperties } from 'react';
import { memo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold, PiCaretUpBold, PiMagnifyingGlassBold } from 'react-icons/pi';
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
export const BoardsPanel = memo(() => {
const boardSearchDisclosure = useStore($boardSearchIsOpen);
const boardSearchText = useAppSelector(selectBoardSearchText);
const searchDisclosure = useDisclosure(!!boardSearchText);
const { _$rightPanelApi } = useAutoLayoutContext();
const gridviewPanelApi = useStore(_$rightPanelApi);
const collapsibleApi = useCollapsibleGridviewPanel(
gridviewPanelApi,
BOARDS_PANEL_ID,
'vertical',
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX
);
const isCollapsed = useStore(collapsibleApi.$isCollapsed);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const onClickBoardSearch = useCallback(() => {
if (boardSearchText.length) {
dispatch(boardSearchTextChanged(''));
}
if (!searchDisclosure.isOpen && collapsibleApi.$isCollapsed.get()) {
collapsibleApi.expand();
}
searchDisclosure.toggle();
}, [boardSearchText.length, searchDisclosure, collapsibleApi, dispatch]);
return (
<Flex flexDir="column" w="full" h="full" p={2}>
<GalleryTopBar />
<Collapse in={boardSearchDisclosure} style={COLLAPSE_STYLES}>
<Flex alignItems="center" justifyContent="space-between" w="full">
<Flex flexGrow={1} flexBasis={0}>
<Button
size="sm"
variant="ghost"
onClick={collapsibleApi.toggle}
leftIcon={isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
>
Boards
</Button>
</Flex>
<Flex>
<GalleryHeader />
</Flex>
<Flex flexGrow={1} flexBasis={0} justifyContent="flex-end">
<BoardsSettingsPopover />
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
onClick={onClickBoardSearch}
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitBoardSearch')}` : `${t('gallery.displayBoardSearch')}`}
aria-label={t('gallery.displayBoardSearch')}
icon={<PiMagnifyingGlassBold />}
colorScheme={searchDisclosure.isOpen ? 'invokeBlue' : 'base'}
/>
</Flex>
</Flex>
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}>
<BoardsSearch />
</Box>

View File

@@ -1,26 +1,22 @@
import type { ChakraProps } from '@invoke-ai/ui-library';
import {
Box,
Collapse,
Flex,
IconButton,
Spacer,
Tab,
TabList,
Tabs,
Text,
Tooltip,
useDisclosure,
} from '@invoke-ai/ui-library';
import { Box, Button, ButtonGroup, Collapse, Divider, Flex, IconButton, Spacer } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useDisclosure } from 'common/hooks/useBoolean';
import { useGallerySearchTerm } from 'features/gallery/components/ImageGrid/useGallerySearchTerm';
import { selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { galleryViewChanged, selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
import {
GALLERY_PANEL_DEFAULT_HEIGHT_PX,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
} from 'features/ui/layouts/shared';
import { useCollapsibleGridviewPanel } from 'features/ui/layouts/use-collapsible-gridview-panel';
import type { CSSProperties } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiMagnifyingGlassBold } from 'react-icons/pi';
import { PiCaretDownBold, PiCaretUpBold, PiMagnifyingGlassBold } from 'react-icons/pi';
import { useBoardName } from 'services/api/hooks/useBoardName';
import { GallerySettingsPopover } from './GallerySettingsPopover/GallerySettingsPopover';
@@ -28,18 +24,6 @@ import { GalleryUploadButton } from './GalleryUploadButton';
import { GallerySearch } from './ImageGrid/GallerySearch';
import { NewGallery } from './NewGallery';
const BASE_STYLES: ChakraProps['sx'] = {
fontWeight: 'semibold',
fontSize: 'sm',
color: 'base.300',
};
const SELECTED_STYLES: ChakraProps['sx'] = {
borderColor: 'base.800',
borderBottomColor: 'base.900',
color: 'invokeBlue.300',
};
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0, width: '100%' };
const selectGalleryView = createSelector(selectGallerySlice, (gallery) => gallery.galleryView);
@@ -48,9 +32,20 @@ const selectSearchTerm = createSelector(selectGallerySlice, (gallery) => gallery
export const GalleryPanel = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const { _$rightPanelApi } = useAutoLayoutContext();
const gridviewPanelApi = useStore(_$rightPanelApi);
const collapsibleApi = useCollapsibleGridviewPanel(
gridviewPanelApi,
GALLERY_PANEL_ID,
'vertical',
GALLERY_PANEL_DEFAULT_HEIGHT_PX,
GALLERY_PANEL_MIN_HEIGHT_PX
);
const isCollapsed = useStore(collapsibleApi.$isCollapsed);
const galleryView = useAppSelector(selectGalleryView);
const initialSearchTerm = useAppSelector(selectSearchTerm);
const searchDisclosure = useDisclosure({ defaultIsOpen: initialSearchTerm.length > 0 });
const searchDisclosure = useDisclosure(!!initialSearchTerm);
const [searchTerm, onChangeSearchTerm, onResetSearchTerm] = useGallerySearchTerm();
const handleClickImages = useCallback(() => {
dispatch(galleryViewChanged('images'));
@@ -61,57 +56,73 @@ export const GalleryPanel = memo(() => {
}, [dispatch]);
const handleClickSearch = useCallback(() => {
searchDisclosure.onToggle();
onResetSearchTerm();
}, [onResetSearchTerm, searchDisclosure]);
if (!searchDisclosure.isOpen && collapsibleApi.$isCollapsed.get()) {
collapsibleApi.expand();
}
searchDisclosure.toggle();
}, [collapsibleApi, onResetSearchTerm, searchDisclosure]);
const selectedBoardId = useAppSelector(selectSelectedBoardId);
const boardName = useBoardName(selectedBoardId);
return (
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full" p={2} minH={0}>
<Tabs index={galleryView === 'images' ? 0 : 1} variant="enclosed" display="flex" flexDir="column" w="full" pb={2}>
<TabList gap={2} fontSize="sm" borderColor="base.800" alignItems="center" w="full">
<Text fontSize="sm" fontWeight="semibold" noOfLines={1} px="2" wordBreak="break-all">
{boardName}
</Text>
<Spacer />
<Tooltip label={t('gallery.imagesTab')}>
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickImages} data-testid="images-tab">
{t('parameters.images')}
</Tab>
</Tooltip>
<Tooltip label={t('gallery.assetsTab')}>
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab">
{t('gallery.assets')}
</Tab>
</Tooltip>
<Flex h="full" justifyContent="flex-end">
<GalleryUploadButton />
<GallerySettingsPopover />
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
onClick={handleClickSearch}
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitSearch')}` : `${t('gallery.displaySearch')}`}
aria-label={t('gallery.displaySearch')}
icon={<PiMagnifyingGlassBold />}
/>
</Flex>
</TabList>
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}>
<GallerySearch
searchTerm={searchTerm}
onChangeSearchTerm={onChangeSearchTerm}
onResetSearchTerm={onResetSearchTerm}
/>
</Box>
</Collapse>
</Tabs>
<NewGallery />
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full" minH={0} p={2}>
<Flex gap={2} fontSize="sm" alignItems="center" w="full">
<Button
size="sm"
variant="ghost"
onClick={collapsibleApi.toggle}
leftIcon={isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
>
{boardName}
</Button>
<Spacer />
<ButtonGroup size="sm" variant="outline">
<Button
tooltip={t('gallery.imagesTab')}
onClick={handleClickImages}
data-testid="images-tab"
colorScheme={galleryView === 'images' ? 'invokeBlue' : undefined}
>
{t('parameters.images')}
</Button>
<Button
tooltip={t('gallery.assetsTab')}
onClick={handleClickAssets}
data-testid="assets-tab"
colorScheme={galleryView === 'assets' ? 'invokeBlue' : undefined}
>
{t('gallery.assets')}
</Button>
</ButtonGroup>
<Flex flexGrow={1} flexBasis={0} justifyContent="flex-end">
<GalleryUploadButton />
<GallerySettingsPopover />
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
onClick={handleClickSearch}
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitSearch')}` : `${t('gallery.displaySearch')}`}
aria-label={t('gallery.displaySearch')}
icon={<PiMagnifyingGlassBold />}
/>
</Flex>
</Flex>
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}>
<GallerySearch
searchTerm={searchTerm}
onChangeSearchTerm={onChangeSearchTerm}
onResetSearchTerm={onResetSearchTerm}
/>
</Box>
</Collapse>
<Divider pt={2} />
<Flex w="full" h="full" pt={2}>
<NewGallery />
</Flex>
</Flex>
);
});

View File

@@ -1,4 +1,15 @@
import { Divider, Flex, IconButton, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
import {
Divider,
Flex,
IconButton,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
Portal,
Text,
} from '@invoke-ai/ui-library';
import AlwaysShowImageSizeCheckbox from 'features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox';
import AutoSwitchCheckbox from 'features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox';
import ImageMinimumWidthSlider from 'features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider';
@@ -23,18 +34,29 @@ export const GallerySettingsPopover = memo(() => {
tooltip={t('gallery.imagesSettings')}
/>
</PopoverTrigger>
<PopoverContent>
<PopoverBody>
<Flex direction="column" gap={2}>
<ImageMinimumWidthSlider />
<AutoSwitchCheckbox />
<AlwaysShowImageSizeCheckbox />
<Divider pt={2} />
<ShowStarredFirstCheckbox />
<SortDirectionCombobox />
</Flex>
</PopoverBody>
</PopoverContent>
<Portal>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<Flex direction="column" gap={2}>
<Text fontWeight="semibold" color="base.300">
Gallery Settings
</Text>
<Divider />
<ImageMinimumWidthSlider />
<AutoSwitchCheckbox />
<AlwaysShowImageSizeCheckbox />
<Divider />
<ShowStarredFirstCheckbox />
<SortDirectionCombobox />
</Flex>
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
);
});

View File

@@ -1,15 +1,29 @@
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { Button, Flex, IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useBoardSearchDisclosure } from 'features/gallery/components/Boards/BoardsList/BoardsSearch';
import { BoardsSettingsPopover } from 'features/gallery/components/Boards/BoardsSettingsPopover';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
import { selectBoardSearchText } from 'features/gallery/store/gallerySelectors';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
import { BOARD_PANEL_DEFAULT_HEIGHT_PX, BOARD_PANEL_MIN_HEIGHT_PX, BOARDS_PANEL_ID } from 'features/ui/layouts/shared';
import { useCollapsibleGridviewPanel } from 'features/ui/layouts/use-collapsible-gridview-panel';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiMagnifyingGlassBold } from 'react-icons/pi';
import { PiCaretDownBold, PiCaretUpBold, PiMagnifyingGlassBold } from 'react-icons/pi';
export const GalleryTopBar = memo(() => {
const { _$rightPanelApi } = useAutoLayoutContext();
const gridviewPanelApi = useStore(_$rightPanelApi);
const collapsibleApi = useCollapsibleGridviewPanel(
gridviewPanelApi,
BOARDS_PANEL_ID,
'vertical',
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX
);
const isCollapsed = useStore(collapsibleApi.$isCollapsed);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const boardSearchText = useAppSelector(selectBoardSearchText);
@@ -19,13 +33,23 @@ export const GalleryTopBar = memo(() => {
if (boardSearchText.length) {
dispatch(boardSearchTextChanged(''));
}
if (!boardSearchDisclosure.isOpen && collapsibleApi.$isCollapsed.get()) {
collapsibleApi.expand();
}
boardSearchDisclosure.toggle();
}, [boardSearchText.length, boardSearchDisclosure, dispatch]);
}, [boardSearchText.length, boardSearchDisclosure, collapsibleApi, dispatch]);
return (
<Flex alignItems="center" justifyContent="space-between" w="full">
<Flex flexGrow={1} flexBasis={0}>
<Text>Boards</Text>
<Button
size="sm"
variant="ghost"
onClick={collapsibleApi.toggle}
rightIcon={isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
>
{isCollapsed ? t('boards.viewBoards') : t('boards.hideBoards')}
</Button>
</Flex>
<Flex>
<GalleryHeader />

View File

@@ -17,11 +17,15 @@ import { memo, useCallback, useRef, useState } from 'react';
import { CanvasTabLeftPanel } from './CanvasTabLeftPanel';
import { CanvasWorkspacePanel } from './CanvasWorkspacePanel';
import {
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX,
BOARDS_PANEL_ID,
DEFAULT_TAB_ID,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
LAUNCHPAD_PANEL_ID,
LAYERS_PANEL_ID,
LAYERS_PANEL_MIN_HEIGHT_PX,
LEFT_PANEL_ID,
LEFT_PANEL_MIN_SIZE_PX,
MAIN_PANEL_ID,
@@ -133,12 +137,12 @@ export const initializeRightPanelLayout = (api: GridviewApi) => {
id: GALLERY_PANEL_ID,
component: GALLERY_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
minimumHeight: 232,
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
});
api.addPanel({
id: LAYERS_PANEL_ID,
component: LAYERS_PANEL_ID,
minimumHeight: 256,
minimumHeight: LAYERS_PANEL_MIN_HEIGHT_PX,
position: {
direction: 'below',
referencePanel: GALLERY_PANEL_ID,
@@ -147,13 +151,13 @@ export const initializeRightPanelLayout = (api: GridviewApi) => {
api.addPanel({
id: BOARDS_PANEL_ID,
component: BOARDS_PANEL_ID,
minimumHeight: 36,
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
position: {
direction: 'above',
referencePanel: GALLERY_PANEL_ID,
},
});
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: 256, width: RIGHT_PANEL_MIN_SIZE_PX });
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
};
const RightPanel = memo(() => {

View File

@@ -15,9 +15,12 @@ import { memo, useCallback, useRef, useState } from 'react';
import { GenerateTabLeftPanel } from './GenerateTabLeftPanel';
import {
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX,
BOARDS_PANEL_ID,
DEFAULT_TAB_ID,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
LAUNCHPAD_PANEL_ID,
LEFT_PANEL_ID,
LEFT_PANEL_MIN_SIZE_PX,
@@ -117,18 +120,18 @@ export const initializeRightPanelLayout = (api: GridviewApi) => {
id: GALLERY_PANEL_ID,
component: GALLERY_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
minimumHeight: 232,
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
});
api.addPanel({
id: BOARDS_PANEL_ID,
component: BOARDS_PANEL_ID,
minimumHeight: 36,
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
position: {
direction: 'above',
referencePanel: GALLERY_PANEL_ID,
},
});
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: 256, width: RIGHT_PANEL_MIN_SIZE_PX });
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
};
const RightPanel = memo(() => {

View File

@@ -18,3 +18,12 @@ export const TAB_WITH_PROGRESS_INDICATOR_ID = 'tab-with-progress-indicator';
export const LEFT_PANEL_MIN_SIZE_PX = 420;
export const RIGHT_PANEL_MIN_SIZE_PX = 420;
export const BOARD_PANEL_MIN_HEIGHT_PX = 36;
export const BOARD_PANEL_DEFAULT_HEIGHT_PX = 232;
export const GALLERY_PANEL_MIN_HEIGHT_PX = 36;
export const GALLERY_PANEL_DEFAULT_HEIGHT_PX = 232;
export const LAYERS_PANEL_MIN_HEIGHT_PX = 36;
export const LAYERS_PANEL_DEFAULT_HEIGHT_PX = 232;

View File

@@ -14,9 +14,12 @@ import { atom } from 'nanostores';
import { memo, useCallback, useRef, useState } from 'react';
import {
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX,
BOARDS_PANEL_ID,
DEFAULT_TAB_ID,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
LAUNCHPAD_PANEL_ID,
LEFT_PANEL_ID,
LEFT_PANEL_MIN_SIZE_PX,
@@ -116,18 +119,18 @@ export const initializeRightPanelLayout = (api: GridviewApi) => {
id: GALLERY_PANEL_ID,
component: GALLERY_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
minimumHeight: 232,
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
});
api.addPanel({
id: BOARDS_PANEL_ID,
component: BOARDS_PANEL_ID,
minimumHeight: 36,
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
position: {
direction: 'above',
referencePanel: GALLERY_PANEL_ID,
},
});
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: 256, width: RIGHT_PANEL_MIN_SIZE_PX });
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
};
const onReadyRightPanel: IGridviewReactProps['onReady'] = (event) => {

View File

@@ -1,6 +1,6 @@
import type { GridviewApi, GridviewPanelApi, IGridviewPanel } from 'dockview';
import { atom } from 'nanostores';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
const getIsCollapsed = (
panel: IGridviewPanel<GridviewPanelApi>,
@@ -21,6 +21,7 @@ export const useCollapsibleGridviewPanel = (
collapsedSize?: number
) => {
const $isCollapsed = useState(() => atom(false))[0];
const lastExpandedSizeRef = useRef<number>(0);
const collapse = useCallback(() => {
if (!api) {
return;
@@ -29,6 +30,9 @@ export const useCollapsibleGridviewPanel = (
if (!panel) {
return;
}
lastExpandedSizeRef.current = orientation === 'vertical' ? panel.height : panel.width;
if (orientation === 'vertical') {
panel.api.setSize({ height: collapsedSize ?? panel.minimumHeight });
} else {
@@ -45,9 +49,9 @@ export const useCollapsibleGridviewPanel = (
return;
}
if (orientation === 'vertical') {
panel.api.setSize({ height: defaultSize });
panel.api.setSize({ height: lastExpandedSizeRef.current || defaultSize });
} else {
panel.api.setSize({ width: defaultSize });
panel.api.setSize({ width: lastExpandedSizeRef.current || defaultSize });
}
}, [api, defaultSize, orientation, panelId]);
@@ -76,6 +80,8 @@ export const useCollapsibleGridviewPanel = (
return;
}
lastExpandedSizeRef.current = orientation === 'vertical' ? panel.height : panel.width;
const disposable = panel.api.onDidDimensionsChange(() => {
const isCollapsed = getIsCollapsed(panel, orientation, collapsedSize);
$isCollapsed.set(isCollapsed);

View File

@@ -16,9 +16,12 @@ import { atom } from 'nanostores';
import { memo, useCallback, useRef, useState } from 'react';
import {
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX,
BOARDS_PANEL_ID,
DEFAULT_TAB_ID,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
LAUNCHPAD_PANEL_ID,
LEFT_PANEL_ID,
LEFT_PANEL_MIN_SIZE_PX,
@@ -130,18 +133,18 @@ export const initializeRightPanelLayout = (api: GridviewApi) => {
id: GALLERY_PANEL_ID,
component: GALLERY_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
minimumHeight: 232,
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
});
api.addPanel({
id: BOARDS_PANEL_ID,
component: BOARDS_PANEL_ID,
minimumHeight: 36,
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
position: {
direction: 'above',
referencePanel: GALLERY_PANEL_ID,
},
});
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: 256, width: RIGHT_PANEL_MIN_SIZE_PX });
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX, width: RIGHT_PANEL_MIN_SIZE_PX });
};
const RightPanel = memo(() => {