mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
reorganize the gallery - move board name to top of image grid, add hide/view boards button for toggle
This commit is contained in:
parent
0d40a7d865
commit
bd73b6b2af
@ -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",
|
||||||
|
@ -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}>
|
||||||
|
@ -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';
|
||||||
|
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user