mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
board UI updates: always show search for boards and images if a term is entered, clear search when view is toggled off
This commit is contained in:
parent
2172e4d292
commit
b7383cc0e5
@ -374,6 +374,8 @@
|
||||
"displayBoardSearch": "Display Board Search",
|
||||
"displaySearch": "Display Search",
|
||||
"download": "Download",
|
||||
"exitBoardSearch": "Exit Board Search",
|
||||
"exitSearch": "Exit Search",
|
||||
"featuresWillReset": "If you delete this image, those features will immediately be reset.",
|
||||
"galleryImageSize": "Image Size",
|
||||
"gallerySettings": "Gallery Settings",
|
||||
|
@ -28,8 +28,10 @@ export const BoardsList = ({ isPrivate }: { isPrivate?: boolean }) => {
|
||||
}
|
||||
|
||||
return boards.filter((board) => {
|
||||
if (boardSearchText) {
|
||||
return board.is_private === isPrivate && board.board_name.toLowerCase().includes(boardSearchText.toLowerCase());
|
||||
if (boardSearchText.length) {
|
||||
return (
|
||||
board.is_private === !!isPrivate && board.board_name.toLowerCase().includes(boardSearchText.toLowerCase())
|
||||
);
|
||||
} else {
|
||||
return board.is_private === !!isPrivate;
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||
import { Box, Collapse, Flex, IconButton, Spacer, Tab, TabList, Tabs, useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { galleryViewChanged, searchTermChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdSearch, MdSearchOff } from 'react-icons/md';
|
||||
|
||||
import { COLLAPSE_STYLES } from './ImageGalleryContent';
|
||||
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
|
||||
import { GalleryPagination } from './ImageGrid/GalleryPagination';
|
||||
import { GallerySearch } from './ImageGrid/GallerySearch';
|
||||
|
||||
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',
|
||||
};
|
||||
|
||||
export const Gallery = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const galleryView = useAppSelector((s) => s.gallery.galleryView);
|
||||
const searchTerm = useAppSelector((s) => s.gallery.searchTerm);
|
||||
const searchDisclosure = useDisclosure({ defaultIsOpen: !!searchTerm.length });
|
||||
|
||||
const handleClickImages = useCallback(() => {
|
||||
dispatch(galleryViewChanged('images'));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClickAssets = useCallback(() => {
|
||||
dispatch(galleryViewChanged('assets'));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClickSearch = useCallback(() => {
|
||||
if (searchTerm.length) {
|
||||
dispatch(searchTermChanged(''));
|
||||
searchDisclosure.onToggle();
|
||||
} else {
|
||||
searchDisclosure.onToggle();
|
||||
}
|
||||
}, [searchTerm, dispatch, searchDisclosure]);
|
||||
|
||||
return (
|
||||
<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">
|
||||
<TabList gap={2} fontSize="sm" borderColor="base.800">
|
||||
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickImages} data-testid="images-tab">
|
||||
{t('parameters.images')}
|
||||
</Tab>
|
||||
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab">
|
||||
{t('gallery.assets')}
|
||||
</Tab>
|
||||
<Spacer />
|
||||
<Box position="relative">
|
||||
<IconButton
|
||||
w="full"
|
||||
h="full"
|
||||
onClick={handleClickSearch}
|
||||
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitSearch')}` : `${t('gallery.displaySearch')}`}
|
||||
aria-label={t('gallery.displaySearch')}
|
||||
icon={searchTerm.length ? <MdSearchOff /> : <MdSearch />}
|
||||
variant="link"
|
||||
/>
|
||||
</Box>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<Box w="full">
|
||||
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
|
||||
<Box w="full" pt={2}>
|
||||
<GallerySearch />
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
<GalleryImageGrid />
|
||||
<GalleryPagination />
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -1,57 +1,28 @@
|
||||
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
Divider,
|
||||
Flex,
|
||||
IconButton,
|
||||
Spacer,
|
||||
Tab,
|
||||
TabList,
|
||||
Tabs,
|
||||
useDisclosure,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { Box, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
|
||||
import { galleryViewChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
|
||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||
import { usePanel, type UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiMagnifyingGlassBold } from 'react-icons/pi';
|
||||
import { MdSearch, MdSearchOff } from 'react-icons/md';
|
||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||
|
||||
import BoardsListWrapper from './Boards/BoardsList/BoardsListWrapper';
|
||||
import BoardsSearch from './Boards/BoardsList/BoardsSearch';
|
||||
import { Gallery } from './Gallery';
|
||||
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
|
||||
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
|
||||
import { GalleryPagination } from './ImageGrid/GalleryPagination';
|
||||
import { GallerySearch } from './ImageGrid/GallerySearch';
|
||||
|
||||
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
|
||||
|
||||
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',
|
||||
};
|
||||
export const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
|
||||
|
||||
const ImageGalleryContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const galleryView = useAppSelector((s) => s.gallery.galleryView);
|
||||
const searchTerm = useAppSelector((s) => s.gallery.searchTerm);
|
||||
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
||||
const dispatch = useAppDispatch();
|
||||
const searchDisclosure = useDisclosure({ defaultIsOpen: false });
|
||||
const boardSearchDisclosure = useDisclosure({ defaultIsOpen: false });
|
||||
const boardSearchDisclosure = useDisclosure({ defaultIsOpen: !!boardSearchText.length });
|
||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
|
||||
const boardsListPanelOptions = useMemo<UsePanelOptions>(
|
||||
@ -67,13 +38,20 @@ const ImageGalleryContent = () => {
|
||||
);
|
||||
const boardsListPanel = usePanel(boardsListPanelOptions);
|
||||
|
||||
const handleClickImages = useCallback(() => {
|
||||
dispatch(galleryViewChanged('images'));
|
||||
}, [dispatch]);
|
||||
const handleClickBoardSearch = useCallback(() => {
|
||||
if (boardSearchText.length) {
|
||||
dispatch(boardSearchTextChanged(''));
|
||||
boardSearchDisclosure.onToggle();
|
||||
} else {
|
||||
boardSearchDisclosure.onToggle();
|
||||
}
|
||||
}, [boardSearchText, dispatch, boardSearchDisclosure]);
|
||||
|
||||
const handleClickAssets = useCallback(() => {
|
||||
dispatch(galleryViewChanged('assets'));
|
||||
}, [dispatch]);
|
||||
useEffect(() => {
|
||||
if (boardSearchDisclosure.isOpen) {
|
||||
boardsListPanel.expand();
|
||||
}
|
||||
}, [boardSearchDisclosure, boardsListPanel]);
|
||||
|
||||
return (
|
||||
<Flex position="relative" flexDirection="column" h="full" w="full" pt={2}>
|
||||
@ -84,25 +62,17 @@ const ImageGalleryContent = () => {
|
||||
<IconButton
|
||||
w="full"
|
||||
h="full"
|
||||
onClick={boardSearchDisclosure.onToggle}
|
||||
tooltip={`${t('gallery.displayBoardSearch')}`}
|
||||
onClick={handleClickBoardSearch}
|
||||
tooltip={
|
||||
boardSearchDisclosure.isOpen ? `${t('gallery.exitBoardSearch')}` : `${t('gallery.displayBoardSearch')}`
|
||||
}
|
||||
aria-label={t('gallery.displayBoardSearch')}
|
||||
icon={<PiMagnifyingGlassBold />}
|
||||
icon={boardSearchText.length ? <MdSearchOff /> : <MdSearch />}
|
||||
variant="link"
|
||||
/>
|
||||
{boardSearchText && (
|
||||
<Box
|
||||
position="absolute"
|
||||
w={2}
|
||||
h={2}
|
||||
bg="invokeBlue.300"
|
||||
borderRadius="full"
|
||||
insetBlockStart={2}
|
||||
insetInlineEnd={2}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<PanelGroup ref={panelGroupRef} direction="vertical">
|
||||
<Panel
|
||||
id="boards-list-panel"
|
||||
@ -127,50 +97,7 @@ const ImageGalleryContent = () => {
|
||||
onDoubleClick={boardsListPanel.onDoubleClickHandle}
|
||||
/>
|
||||
<Panel id="gallery-wrapper-panel" minSize={20}>
|
||||
<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">
|
||||
<TabList gap={2} fontSize="sm" borderColor="base.800">
|
||||
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickImages} data-testid="images-tab">
|
||||
{t('parameters.images')}
|
||||
</Tab>
|
||||
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab">
|
||||
{t('gallery.assets')}
|
||||
</Tab>
|
||||
<Spacer />
|
||||
<Box position="relative">
|
||||
<IconButton
|
||||
w="full"
|
||||
h="full"
|
||||
onClick={searchDisclosure.onToggle}
|
||||
tooltip={`${t('gallery.displaySearch')}`}
|
||||
aria-label={t('gallery.displaySearch')}
|
||||
icon={<PiMagnifyingGlassBold />}
|
||||
variant="link"
|
||||
/>
|
||||
{searchTerm && (
|
||||
<Box
|
||||
position="absolute"
|
||||
w={2}
|
||||
h={2}
|
||||
bg="invokeBlue.300"
|
||||
borderRadius="full"
|
||||
insetBlockStart={2}
|
||||
insetInlineEnd={2}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<Box w="full">
|
||||
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
|
||||
<Box w="full" pt={2}>
|
||||
<GallerySearch />
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
<GalleryImageGrid />
|
||||
<GalleryPagination />
|
||||
</Flex>
|
||||
<Gallery />
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
</Flex>
|
||||
|
@ -4,7 +4,7 @@ import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelecto
|
||||
import { searchTermChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { debounce } from 'lodash-es';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import { useListImagesQuery } from 'services/api/endpoints/images';
|
||||
@ -37,6 +37,10 @@ export const GallerySearch = () => {
|
||||
dispatch(searchTermChanged(''));
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchTermInput(searchTerm);
|
||||
}, [searchTerm]);
|
||||
|
||||
return (
|
||||
<InputGroup>
|
||||
<Input
|
||||
|
Loading…
Reference in New Issue
Block a user