reorganize the gallery - move board name to top of image grid, add hide/view boards button for toggle

This commit is contained in:
Mary Hipp 2024-07-23 18:51:48 -04:00 committed by psychedelicious
parent 0d40a7d865
commit bd73b6b2af
4 changed files with 67 additions and 50 deletions

View File

@ -32,6 +32,7 @@
"deleteBoardAndImages": "Delete Board and Images", "deleteBoardAndImages": "Delete Board and Images",
"deleteBoardOnly": "Delete Board Only", "deleteBoardOnly": "Delete Board Only",
"deletedBoardsCannotbeRestored": "Deleted boards cannot be restored", "deletedBoardsCannotbeRestored": "Deleted boards cannot be restored",
"hideBoards": "Hide Boards",
"loading": "Loading...", "loading": "Loading...",
"menuItemAutoAdd": "Auto-add to this Board", "menuItemAutoAdd": "Auto-add to this Board",
"move": "Move", "move": "Move",
@ -47,6 +48,7 @@
"topMessage": "This board contains images used in the following features:", "topMessage": "This board contains images used in the following features:",
"unarchiveBoard": "Unarchive Board", "unarchiveBoard": "Unarchive Board",
"uncategorized": "Uncategorized", "uncategorized": "Uncategorized",
"viewBoards": "View Boards",
"downloadBoard": "Download Board", "downloadBoard": "Download Board",
"imagesWithCount_one": "{{count}} image", "imagesWithCount_one": "{{count}} image",
"imagesWithCount_other": "{{count}} images", "imagesWithCount_other": "{{count}} images",

View File

@ -1,10 +1,11 @@
import type { ChakraProps } from '@invoke-ai/ui-library'; import type { ChakraProps } from '@invoke-ai/ui-library';
import { Box, Collapse, Flex, IconButton, Spacer, Tab, TabList, Tabs, useDisclosure } from '@invoke-ai/ui-library'; import { Box, Collapse, Flex, IconButton, Tab, TabList, Tabs, Text, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { galleryViewChanged, searchTermChanged } from 'features/gallery/store/gallerySlice'; import { galleryViewChanged, searchTermChanged } from 'features/gallery/store/gallerySlice';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MdSearch, MdSearchOff } from 'react-icons/md'; import { MdSearch, MdSearchOff } from 'react-icons/md';
import { useBoardName } from 'services/api/hooks/useBoardName';
import { COLLAPSE_STYLES } from './ImageGalleryContent'; import { COLLAPSE_STYLES } from './ImageGalleryContent';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid'; import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
@ -47,17 +48,26 @@ export const Gallery = () => {
} }
}, [searchTerm, dispatch, searchDisclosure]); }, [searchTerm, dispatch, searchDisclosure]);
const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
const boardName = useBoardName(selectedBoardId);
return ( return (
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full"> <Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full">
<Tabs index={galleryView === 'images' ? 0 : 1} variant="enclosed" display="flex" flexDir="column" w="full"> <Flex alignItems="center" justifyContent="space-between" w="full">
<TabList gap={2} fontSize="sm" borderColor="base.800"> <Text fontSize="sm" fontWeight="semibold" noOfLines={1} px="2">
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickImages} data-testid="images-tab"> {boardName}
{t('parameters.images')} </Text>
</Tab> <Flex alignItems="center" justifyContent="space-between">
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab"> <Tabs index={galleryView === 'images' ? 0 : 1} variant="enclosed" display="flex" flexDir="column">
{t('gallery.assets')} <TabList gap={2} fontSize="sm" borderColor="base.800">
</Tab> <Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickImages} data-testid="images-tab">
<Spacer /> {t('parameters.images')}
</Tab>
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab">
{t('gallery.assets')}
</Tab>
</TabList>
</Tabs>
<Box position="relative"> <Box position="relative">
<IconButton <IconButton
w="full" w="full"
@ -69,8 +79,9 @@ export const Gallery = () => {
variant="link" variant="link"
/> />
</Box> </Box>
</TabList> </Flex>
</Tabs> </Flex>
<Box w="full"> <Box w="full">
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}> <Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}> <Box w="full" pt={2}>

View File

@ -3,13 +3,7 @@ import { useStore } from '@nanostores/react';
import { $projectName, $projectUrl } from 'app/store/nanostores/projectId'; import { $projectName, $projectUrl } from 'app/store/nanostores/projectId';
import { memo } from 'react'; import { memo } from 'react';
import GalleryBoardName from './GalleryBoardName'; export const GalleryHeader = memo(() => {
type Props = {
onClickBoardName: () => void;
};
export const GalleryHeader = memo((props: Props) => {
const projectName = useStore($projectName); const projectName = useStore($projectName);
const projectUrl = useStore($projectUrl); const projectUrl = useStore($projectUrl);
@ -19,16 +13,11 @@ export const GalleryHeader = memo((props: Props) => {
<Text fontSize="md" fontWeight="semibold" noOfLines={1} w="full" textAlign="center"> <Text fontSize="md" fontWeight="semibold" noOfLines={1} w="full" textAlign="center">
<Link href={projectUrl}>{projectName}</Link> <Link href={projectUrl}>{projectName}</Link>
</Text> </Text>
<GalleryBoardName onClick={props.onClickBoardName} />
</Flex> </Flex>
); );
} }
return ( return <></>;
<Flex w="full" pe={2}>
<GalleryBoardName onClick={props.onClickBoardName} />
</Flex>
);
}); });
GalleryHeader.displayName = 'GalleryHeader'; GalleryHeader.displayName = 'GalleryHeader';

View File

@ -1,13 +1,14 @@
import { Box, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library'; import { Button, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader'; import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice'; import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle'; import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
import { usePanel, type UsePanelOptions } from 'features/ui/hooks/usePanel'; import { usePanel, type UsePanelOptions } from 'features/ui/hooks/usePanel';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MdSearch, MdSearchOff } from 'react-icons/md'; import { MdSearch, MdSearchOff } from 'react-icons/md';
import { PiCaretDownBold, PiCaretUpBold } from 'react-icons/pi';
import type { ImperativePanelGroupHandle } from 'react-resizable-panels'; import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
import { Panel, PanelGroup } from 'react-resizable-panels'; import { Panel, PanelGroup } from 'react-resizable-panels';
@ -41,36 +42,50 @@ const ImageGalleryContent = () => {
const handleClickBoardSearch = useCallback(() => { const handleClickBoardSearch = useCallback(() => {
if (boardSearchText.length) { if (boardSearchText.length) {
dispatch(boardSearchTextChanged('')); dispatch(boardSearchTextChanged(''));
boardSearchDisclosure.onToggle();
} else {
boardSearchDisclosure.onToggle();
} }
boardSearchDisclosure.onToggle();
}, [boardSearchText, dispatch, boardSearchDisclosure]); }, [boardSearchText, dispatch, boardSearchDisclosure]);
useEffect(() => { const handleToggleBoardPanel = useCallback(() => {
if (boardSearchDisclosure.isOpen) { if (boardSearchText.length) {
boardsListPanel.expand(); dispatch(boardSearchTextChanged(''));
} }
}, [boardSearchDisclosure, boardsListPanel]);
boardsListPanel.toggle();
}, [boardSearchText, dispatch, boardsListPanel]);
return ( return (
<Flex position="relative" flexDirection="column" h="full" w="full" pt={2}> <Flex position="relative" flexDirection="column" h="full" w="full" pt={2}>
<Flex alignItems="center" gap={2}> <Flex alignItems="center" gap={0}>
<GalleryHeader onClickBoardName={boardsListPanel.toggle} /> <GalleryHeader />
<GallerySettingsPopover /> <Flex alignItems="center" justifyContent="space-between" w="full">
<Box position="relative" h="full"> <Button
<IconButton size="sm"
w="full" variant="ghost"
h="full" onClick={handleToggleBoardPanel}
onClick={handleClickBoardSearch} rightIcon={boardsListPanel.isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
tooltip={ >
boardSearchDisclosure.isOpen ? `${t('gallery.exitBoardSearch')}` : `${t('gallery.displayBoardSearch')}` {boardsListPanel.isCollapsed ? t('boards.viewBoards') : t('boards.hideBoards')}
} </Button>
aria-label={t('gallery.displayBoardSearch')} <Flex alignItems="center" justifyContent="space-between">
icon={boardSearchText.length ? <MdSearchOff /> : <MdSearch />} <GallerySettingsPopover />
variant="link" <Flex>
/> <IconButton
</Box> w="full"
h="full"
onClick={handleClickBoardSearch}
tooltip={
boardSearchDisclosure.isOpen
? `${t('gallery.exitBoardSearch')}`
: `${t('gallery.displayBoardSearch')}`
}
aria-label={t('gallery.displayBoardSearch')}
icon={boardSearchText.length ? <MdSearchOff /> : <MdSearch />}
variant="link"
/>
</Flex>
</Flex>
</Flex>
</Flex> </Flex>
<PanelGroup ref={panelGroupRef} direction="vertical"> <PanelGroup ref={panelGroupRef} direction="vertical">