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}>
-
-
+ {(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}
-
-
-
-
+
+