From 81cf47dd99ac190cbdb1c16cb44db9608fea8169 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 9 Jul 2024 21:58:48 +1000 Subject: [PATCH] feat(ui): boards list layout & style tweaking --- .../src/common/components/IAIDropOverlay.tsx | 4 +- .../Boards/BoardsList/BoardsList.tsx | 127 +++++---- .../Boards/BoardsList/GalleryBoard.tsx | 256 +++++++++--------- .../Boards/BoardsList/NoBoardBoard.tsx | 103 ++++--- .../gallery/components/GalleryBoardName.tsx | 1 - .../components/ImageGalleryContent.tsx | 8 +- 6 files changed, 243 insertions(+), 256 deletions(-) diff --git a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx index cd3e0cbee1..51e5583bc6 100644 --- a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx @@ -52,8 +52,8 @@ const IAIDropOverlay = (props: Props) => { bottom={0.5} opacity={1} borderWidth={2} - borderColor={isOver ? 'base.50' : 'base.300'} - borderRadius="lg" + borderColor={isOver ? 'base.300' : 'base.500'} + borderRadius="base" borderStyle="dashed" transitionProperty="common" transitionDuration="0.1s" diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx index 75f73adb6a..f7fd6150dc 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx @@ -1,4 +1,4 @@ -import { Box, Collapse, Flex, Icon, Text, useDisclosure } from '@invoke-ai/ui-library'; +import { Collapse, Flex, Icon, Text, useDisclosure } from '@invoke-ai/ui-library'; import { EMPTY_ARRAY } from 'app/store/constants'; import { useAppSelector } from 'app/store/storeHooks'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; @@ -45,80 +45,89 @@ const BoardsList = () => { return ( <> - - + + - - - {allowPrivateBoards && ( - <> - - - - - {t('boards.private')} - - - - - - - - {filteredPrivateBoards.map((board) => ( - - ))} - - - - )} - - + {allowPrivateBoards && ( + <> + + - - {allowPrivateBoards ? t('boards.shared') : t('boards.boards')} + + {t('boards.private')} - + - - - {filteredSharedBoards.map((board) => ( - - ))} - + + + + + {filteredPrivateBoards.map((board) => ( + + ))} + + - - + + )} + + + + + {allowPrivateBoards ? t('boards.shared') : t('boards.boards')} + + + + + + + + {filteredSharedBoards.map((board) => ( + + ))} + + + ); }; - export default memo(BoardsList); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx index 246454fa57..fd951a1e89 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx @@ -1,16 +1,25 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Box, Editable, EditableInput, EditablePreview, Flex, Icon, Image, Text, Tooltip } from '@invoke-ai/ui-library'; +import { + Editable, + EditableInput, + EditablePreview, + Flex, + Icon, + Image, + Text, + Tooltip, + useDisclosure, +} from '@invoke-ai/ui-library'; import { skipToken } from '@reduxjs/toolkit/query'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDroppable from 'common/components/IAIDroppable'; -import SelectionOverlay from 'common/components/SelectionOverlay'; import type { AddToBoardDropData } from 'features/dnd/types'; import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu'; import { BoardTotalsTooltip } from 'features/gallery/components/Boards/BoardsList/BoardTotalsTooltip'; import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice'; import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { PiArchiveBold, PiImagesSquare } from 'react-icons/pi'; +import { PiArchiveBold, PiImageSquare } from 'react-icons/pi'; import { useUpdateBoardMutation } from 'services/api/endpoints/boards'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import type { BoardDTO } from 'services/api/types'; @@ -19,14 +28,13 @@ const editableInputStyles: SystemStyleObject = { p: 0, fontSize: 'md', w: '100%', + _focusVisible: { + p: 0, + }, }; -const ArchivedIcon = () => { - return ( - - - - ); +const _hover: SystemStyleObject = { + bg: 'base.800', }; interface GalleryBoardProps { @@ -39,65 +47,51 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps const dispatch = useAppDispatch(); const { t } = useTranslation(); const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick); - - const [isHovered, setIsHovered] = useState(false); - const handleMouseOver = useCallback(() => { - setIsHovered(true); - }, []); - const handleMouseOut = useCallback(() => { - setIsHovered(false); - }, []); - - const { currentData: coverImage } = useGetImageDTOQuery(board.cover_image_name ?? skipToken); - - const { board_name, board_id } = board; - const [localBoardName, setLocalBoardName] = useState(board_name); + const editingDisclosure = useDisclosure(); + const [localBoardName, setLocalBoardName] = useState(board.board_name); const handleSelectBoard = useCallback(() => { - dispatch(boardIdSelected({ boardId: board_id })); + dispatch(boardIdSelected({ boardId: board.board_id })); if (autoAssignBoardOnClick) { - dispatch(autoAddBoardIdChanged(board_id)); + dispatch(autoAddBoardIdChanged(board.board_id)); } - }, [board_id, autoAssignBoardOnClick, dispatch]); + }, [dispatch, board.board_id, autoAssignBoardOnClick]); const [updateBoard, { isLoading: isUpdateBoardLoading }] = useUpdateBoardMutation(); const droppableData: AddToBoardDropData = useMemo( () => ({ - id: board_id, + id: board.board_id, actionType: 'ADD_TO_BOARD', - context: { boardId: board_id }, + context: { boardId: board.board_id }, }), - [board_id] + [board.board_id] ); const handleSubmit = useCallback( async (newBoardName: string) => { - // empty strings are not allowed if (!newBoardName.trim()) { - setLocalBoardName(board_name); - return; - } + // empty strings are not allowed + setLocalBoardName(board.board_name); + } else if (newBoardName === board.board_name) { + // don't updated the board name if it hasn't changed + } else { + try { + const { board_name } = await updateBoard({ + board_id: board.board_id, + changes: { board_name: newBoardName }, + }).unwrap(); - // don't updated the board name if it hasn't changed - if (newBoardName === board_name) { - return; - } - - try { - const { board_name } = await updateBoard({ - board_id, - changes: { board_name: newBoardName }, - }).unwrap(); - - // update local state - setLocalBoardName(board_name); - } catch { - // revert on error - setLocalBoardName(board_name); + // update local state + setLocalBoardName(board_name); + } catch { + // revert on error + setLocalBoardName(board.board_name); + } } + editingDisclosure.onClose(); }, - [board_id, board_name, updateBoard] + [board.board_id, board.board_name, editingDisclosure, updateBoard] ); const handleChange = useCallback((newBoardName: string) => { @@ -105,92 +99,88 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps }, []); return ( - - - - {(ref) => ( - } - openDelay={1000} + + {(ref) => ( + } + openDelay={1000} + > + + + - - - {board.archived && } - {coverImage?.thumbnail_url ? ( - - ) : ( - - - - )} - - - - - - - - - - - {`${t('boards.imagesWithCount', { count: board.image_count })}`} - - - {t('unifiedCanvas.move')}} /> - - - )} - - - + + + + {board.archived && !editingDisclosure.isOpen && ( + + )} + {t('unifiedCanvas.move')}} /> + + + )} + ); }; export default memo(GalleryBoard); + +const CoverImage = ({ board }: { board: BoardDTO }) => { + const { currentData: coverImage } = useGetImageDTOQuery(board.cover_image_name ?? skipToken); + + if (coverImage) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx index 77563859a7..c2ba470067 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx @@ -1,13 +1,12 @@ -import { Box, Flex, Image, Text, Tooltip } from '@invoke-ai/ui-library'; +import type { SystemStyleObject } from '@invoke-ai/ui-library'; +import { Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDroppable from 'common/components/IAIDroppable'; -import SelectionOverlay from 'common/components/SelectionOverlay'; import type { RemoveFromBoardDropData } from 'features/dnd/types'; import { BoardTotalsTooltip } from 'features/gallery/components/Boards/BoardsList/BoardTotalsTooltip'; import NoBoardBoardContextMenu from 'features/gallery/components/Boards/NoBoardBoardContextMenu'; import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice'; -import InvokeLogoSVG from 'public/assets/images/invoke-symbol-wht-lrg.svg'; -import { memo, useCallback, useMemo, useState } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useBoardName } from 'services/api/hooks/useBoardName'; @@ -15,6 +14,10 @@ interface Props { isSelected: boolean; } +const _hover: SystemStyleObject = { + bg: 'base.800', +}; + const NoBoardBoard = memo(({ isSelected }: Props) => { const dispatch = useAppDispatch(); const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick); @@ -25,15 +28,6 @@ const NoBoardBoard = memo(({ isSelected }: Props) => { dispatch(autoAddBoardIdChanged('none')); } }, [dispatch, autoAssignBoardOnClick]); - const [isHovered, setIsHovered] = useState(false); - - const handleMouseOver = useCallback(() => { - setIsHovered(true); - }, []); - - const handleMouseOut = useCallback(() => { - setIsHovered(false); - }, []); const droppableData: RemoveFromBoardDropData = useMemo( () => ({ @@ -44,50 +38,47 @@ const NoBoardBoard = memo(({ isSelected }: Props) => { ); const { t } = useTranslation(); return ( - - - - {(ref) => ( - } openDelay={1000}> - - invoke-ai-logo + {(ref) => ( + } openDelay={1000}> + + + {/* iconified from public/assets/images/invoke-symbol-wht-lrg.svg */} + + - - {boardName} - - - {t('unifiedCanvas.move')}} /> - - - )} - - - + + + + + {boardName} + + {t('unifiedCanvas.move')}} /> + + + )} + ); }); diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx index bc851893c4..d4c22b4fe2 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx @@ -16,7 +16,6 @@ const GalleryBoardName = () => { return ( { gap={2} > {galleryHeader} - - - - + +