mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): boards and gallery panel collapse
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
Reference in New Issue
Block a user