mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): boards styling
- Refine layout - Update colors - more minimal, fewer shaded boxes - Add indicator for search icons showing a search term is entered - Handle new `projectName` and `projectUrl` ui props
This commit is contained in:
parent
dfd94bbd0b
commit
48a57f0da8
@ -40,11 +40,11 @@ const BoardsList = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex layerStyle="first" flexDir="column" gap={2} p={2} my={2} borderRadius="base" maxHeight="100%">
|
||||
<Flex flexDir="column" gap={2} borderRadius="base" maxHeight="100%">
|
||||
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
|
||||
{allowPrivateBoards && (
|
||||
<>
|
||||
<Flex w="full" justifyContent="space-between" alignItems="center">
|
||||
<Flex w="full" justifyContent="space-between" alignItems="center" ps={2}>
|
||||
<Text fontSize="md" fontWeight="medium" userSelect="none">
|
||||
{t('boards.private')}
|
||||
</Text>
|
||||
@ -63,7 +63,7 @@ const BoardsList = () => {
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
<Flex w="full" justifyContent="space-between" alignItems="center">
|
||||
<Flex w="full" justifyContent="space-between" alignItems="center" ps={2}>
|
||||
<Text fontSize="md" fontWeight="medium" userSelect="none">
|
||||
{allowPrivateBoards ? t('boards.shared') : t('boards.boards')}
|
||||
</Text>
|
||||
|
@ -40,7 +40,7 @@ const BoardsSearch = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<InputGroup>
|
||||
<InputGroup pt={2}>
|
||||
<Input
|
||||
placeholder={t('boards.searchBoard')}
|
||||
value={boardSearchText}
|
||||
|
@ -35,7 +35,7 @@ const editableInputStyles: SystemStyleObject = {
|
||||
};
|
||||
|
||||
const _hover: SystemStyleObject = {
|
||||
bg: 'base.800',
|
||||
bg: 'base.850',
|
||||
};
|
||||
|
||||
interface GalleryBoardProps {
|
||||
@ -118,7 +118,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
||||
py={1}
|
||||
px={2}
|
||||
gap={2}
|
||||
bg={isSelected ? 'base.800' : undefined}
|
||||
bg={isSelected ? 'base.850' : undefined}
|
||||
_hover={_hover}
|
||||
>
|
||||
<CoverImage board={board} />
|
||||
@ -147,13 +147,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
||||
<EditableInput sx={editableInputStyles} />
|
||||
</Editable>
|
||||
{autoAddBoardId === board.board_id && !editingDisclosure.isOpen && <AutoAddBadge />}
|
||||
{board.archived && !editingDisclosure.isOpen && (
|
||||
<Icon
|
||||
as={PiArchiveBold}
|
||||
fill="base.300"
|
||||
filter="drop-shadow(0px 0px 0.1rem var(--invoke-colors-base-800))"
|
||||
/>
|
||||
)}
|
||||
{board.archived && !editingDisclosure.isOpen && <Icon as={PiArchiveBold} fill="base.300" />}
|
||||
{!editingDisclosure.isOpen && <Text variant="subtext">{board.image_count}</Text>}
|
||||
|
||||
<IAIDroppable data={droppableData} dropLabel={<Text fontSize="md">{t('unifiedCanvas.move')}</Text>} />
|
||||
|
@ -17,7 +17,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const _hover: SystemStyleObject = {
|
||||
bg: 'base.800',
|
||||
bg: 'base.850',
|
||||
};
|
||||
|
||||
const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
@ -71,7 +71,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
px={2}
|
||||
py={1}
|
||||
gap={2}
|
||||
bg={isSelected ? 'base.800' : undefined}
|
||||
bg={isSelected ? 'base.850' : undefined}
|
||||
_hover={_hover}
|
||||
>
|
||||
<Flex w={8} h={8} justifyContent="center" alignItems="center">
|
||||
|
@ -8,7 +8,16 @@ const GalleryBoardName = () => {
|
||||
const boardName = useBoardName(selectedBoardId);
|
||||
|
||||
return (
|
||||
<Flex as="button" w="full" borderWidth={1} borderRadius="base" alignItems="center" justifyContent="center" px={2}>
|
||||
<Flex
|
||||
as="button"
|
||||
h="full"
|
||||
w="full"
|
||||
borderWidth={1}
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
px={2}
|
||||
>
|
||||
<Text fontWeight="semibold" fontSize="md" noOfLines={1} wordBreak="break-all" color="base.200">
|
||||
{boardName}
|
||||
</Text>
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { Flex, Link, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $projectName, $projectUrl } from 'app/store/nanostores/projectId';
|
||||
import { memo } from 'react';
|
||||
|
||||
import GalleryBoardName from './GalleryBoardName';
|
||||
|
||||
const GalleryHeader = () => {
|
||||
const projectName = useStore($projectName);
|
||||
const projectUrl = useStore($projectUrl);
|
||||
|
||||
if (projectName && projectUrl) {
|
||||
return (
|
||||
<Flex gap={2} w="full" alignItems="center" justifyContent="space-evenly" pe={2}>
|
||||
<Text fontSize="md" fontWeight="semibold" noOfLines={1} w="full" textAlign="center">
|
||||
<Link href={projectUrl}>{projectName}</Link>
|
||||
</Text>
|
||||
<GalleryBoardName />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex w="full" pe={2}>
|
||||
<GalleryBoardName />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GalleryHeader);
|
@ -17,7 +17,7 @@ const GallerySettingsPopover = () => {
|
||||
return (
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
<IconButton aria-label={t('gallery.gallerySettings')} size="sm" icon={<RiSettings4Fill />} />
|
||||
<IconButton aria-label={t('gallery.gallerySettings')} icon={<RiSettings4Fill />} variant="link" h="full" />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverBody>
|
||||
|
@ -1,18 +1,16 @@
|
||||
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||
import { Box, Collapse, Flex, IconButton, Tab, TabList, Tabs, useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $galleryHeader } from 'app/store/nanostores/galleryHeader';
|
||||
import { Box, Collapse, Flex, IconButton, Spacer, Tab, TabList, Tabs, 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 ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiImagesBold, PiMagnifyingGlassBold } from 'react-icons/pi';
|
||||
import { PiMagnifyingGlassBold } from 'react-icons/pi';
|
||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||
|
||||
import BoardsList from './Boards/BoardsList/BoardsList';
|
||||
import BoardsSearch from './Boards/BoardsList/BoardsSearch';
|
||||
import GalleryBoardName from './GalleryBoardName';
|
||||
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
|
||||
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
|
||||
import { GalleryPagination } from './ImageGrid/GalleryPagination';
|
||||
@ -20,29 +18,22 @@ import { GallerySearch } from './ImageGrid/GallerySearch';
|
||||
|
||||
const baseStyles: ChakraProps['sx'] = {
|
||||
fontWeight: 'semibold',
|
||||
fontSize: 'md',
|
||||
fontSize: 'sm',
|
||||
color: 'base.300',
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: 'invokeBlue.800',
|
||||
};
|
||||
|
||||
const selectedStyles: ChakraProps['sx'] = {
|
||||
borderColor: 'invokeBlue.800',
|
||||
borderBottomColor: 'base.850',
|
||||
borderColor: 'base.800',
|
||||
borderBottomColor: 'base.900',
|
||||
color: 'invokeBlue.300',
|
||||
};
|
||||
|
||||
const searchIconStyles: ChakraProps['sx'] = {
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: 'invokeBlue.800',
|
||||
maxW: '16',
|
||||
};
|
||||
|
||||
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 galleryHeader = useStore($galleryHeader);
|
||||
const searchDisclosure = useDisclosure({ defaultIsOpen: false });
|
||||
const boardSearchDisclosure = useDisclosure({ defaultIsOpen: false });
|
||||
|
||||
@ -57,62 +48,81 @@ const ImageGalleryContent = () => {
|
||||
const panelGroupRef = useRef(null);
|
||||
|
||||
return (
|
||||
<Flex layerStyle="first" position="relative" flexDirection="column" h="full" w="full" p={2} gap={2}>
|
||||
<Flex alignItems="center" justifyContent="space-between" gap={2}>
|
||||
{galleryHeader}
|
||||
<GalleryBoardName />
|
||||
<Flex position="relative" flexDirection="column" h="full" w="full" pt={2}>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<GalleryHeader />
|
||||
<GallerySettingsPopover />
|
||||
<IconButton
|
||||
onClick={boardSearchDisclosure.onToggle}
|
||||
tooltip={`${t('gallery.displayBoardSearch')}`}
|
||||
aria-label={t('gallery.displayBoardSearch')}
|
||||
icon={<PiMagnifyingGlassBold />}
|
||||
size="sm"
|
||||
/>
|
||||
<Box position="relative" h="full">
|
||||
<IconButton
|
||||
w="full"
|
||||
h="full"
|
||||
onClick={boardSearchDisclosure.onToggle}
|
||||
tooltip={`${t('gallery.displayBoardSearch')}`}
|
||||
aria-label={t('gallery.displayBoardSearch')}
|
||||
icon={<PiMagnifyingGlassBold />}
|
||||
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>
|
||||
<Collapse in={boardSearchDisclosure.isOpen}>
|
||||
<Box mt="2">
|
||||
<BoardsSearch />
|
||||
</Box>
|
||||
<BoardsSearch />
|
||||
</Collapse>
|
||||
<BoardsList />
|
||||
</Panel>
|
||||
<ResizeHandle orientation="horizontal" />
|
||||
<Panel>
|
||||
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" gap={2} h="full">
|
||||
<Tabs isFitted index={galleryView === 'images' ? 0 : 1} variant="enclosed" size="sm" w="full" my="2">
|
||||
<TabList fontSize="sm" borderBottom="none">
|
||||
<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={baseStyles} _selected={selectedStyles} onClick={handleClickImages} data-testid="images-tab">
|
||||
<Flex alignItems="center" justifyContent="center" gap="2" w="full">
|
||||
<PiImagesBold size="16px" />
|
||||
{t('parameters.images')}
|
||||
</Flex>
|
||||
{t('parameters.images')}
|
||||
</Tab>
|
||||
<Tab sx={baseStyles} _selected={selectedStyles} onClick={handleClickAssets} data-testid="assets-tab">
|
||||
<Flex alignItems="center" justifyContent="center" gap="2" w="full">
|
||||
<PiImagesBold size="16px" />
|
||||
{t('gallery.assets')}
|
||||
</Flex>
|
||||
{t('gallery.assets')}
|
||||
</Tab>
|
||||
<Tab sx={searchIconStyles} onClick={searchDisclosure.onToggle}>
|
||||
<Spacer />
|
||||
<Box position="relative">
|
||||
<IconButton
|
||||
w="full"
|
||||
h="full"
|
||||
onClick={searchDisclosure.onToggle}
|
||||
tooltip={`${t('gallery.displaySearch')}`}
|
||||
aria-label={t('gallery.displaySearch')}
|
||||
icon={<PiMagnifyingGlassBold />}
|
||||
fontSize={16}
|
||||
textAlign="center"
|
||||
color="base.400"
|
||||
variant="unstyled"
|
||||
minW="unset"
|
||||
variant="link"
|
||||
/>
|
||||
</Tab>
|
||||
{searchTerm && (
|
||||
<Box
|
||||
position="absolute"
|
||||
w={2}
|
||||
h={2}
|
||||
bg="invokeBlue.300"
|
||||
borderRadius="full"
|
||||
insetBlockStart={2}
|
||||
insetInlineEnd={2}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<Box mb="2" w="full">
|
||||
<Box w="full">
|
||||
<Collapse in={searchDisclosure.isOpen}>
|
||||
<GallerySearch />
|
||||
<Box w="full" pt={2}>
|
||||
<GallerySearch />
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
<GalleryImageGrid />
|
||||
|
@ -124,7 +124,7 @@ const Content = () => {
|
||||
}, [calculateNewLimit, container, dispatch]);
|
||||
|
||||
return (
|
||||
<Box position="relative" w="full" h="full">
|
||||
<Box position="relative" w="full" h="full" mt={2}>
|
||||
<Box
|
||||
ref={containerRef}
|
||||
position="absolute"
|
||||
|
Loading…
Reference in New Issue
Block a user