From 9e2e7400331ae4d70e1970c24856f0a3d551693c Mon Sep 17 00:00:00 2001 From: Mary Hipp Rogers Date: Thu, 4 Jan 2024 10:30:27 -0500 Subject: [PATCH] custom components for nav, gallery header, and app info (#5400) * replace custom header with custom nav component to go below settings * add option for custom gallery header * add option for custom app info text on logo hover * add data-testid for tabs * remove descriptions * lint * lint --------- Co-authored-by: Mary Hipp --- .../web/src/app/components/InvokeAIUI.tsx | 40 +++++++++++++++---- .../src/app/store/nanostores/customAppInfo.ts | 3 ++ .../store/nanostores/customNavComponent.ts | 4 ++ .../{headerComponent.ts => galleryHeader.ts} | 2 +- .../components/ImageGalleryContent.tsx | 4 ++ .../components/InvokeAILogoComponent.tsx | 9 ++++- .../src/features/ui/components/InvokeTabs.tsx | 5 +++ 7 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/nanostores/customAppInfo.ts create mode 100644 invokeai/frontend/web/src/app/store/nanostores/customNavComponent.ts rename invokeai/frontend/web/src/app/store/nanostores/{headerComponent.ts => galleryHeader.ts} (51%) diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx index e1fa294654..16164ee0a2 100644 --- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx +++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx @@ -4,9 +4,11 @@ import type { Middleware } from '@reduxjs/toolkit'; import { $socketOptions } from 'app/hooks/useSocketIO'; import { $authToken } from 'app/store/nanostores/authToken'; import { $baseUrl } from 'app/store/nanostores/baseUrl'; +import { $customAppInfo } from 'app/store/nanostores/customAppInfo'; +import { $customNavComponent } from 'app/store/nanostores/customNavComponent'; import type { CustomStarUi } from 'app/store/nanostores/customStarUI'; import { $customStarUI } from 'app/store/nanostores/customStarUI'; -import { $headerComponent } from 'app/store/nanostores/headerComponent'; +import { $galleryHeader } from 'app/store/nanostores/galleryHeader'; import { $isDebugging } from 'app/store/nanostores/isDebugging'; import { $projectId } from 'app/store/nanostores/projectId'; import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId'; @@ -28,9 +30,10 @@ interface Props extends PropsWithChildren { apiUrl?: string; token?: string; config?: PartialAppConfig; - headerComponent?: ReactNode; + customNavComponent?: ReactNode; middleware?: Middleware[]; projectId?: string; + galleryHeader?: ReactNode; queueId?: string; selectedImage?: { imageName: string; @@ -39,20 +42,23 @@ interface Props extends PropsWithChildren { customStarUi?: CustomStarUi; socketOptions?: Partial; isDebugging?: boolean; + customAppInfo?: string; } const InvokeAIUI = ({ apiUrl, token, config, - headerComponent, + customNavComponent, middleware, projectId, + galleryHeader, queueId, selectedImage, customStarUi, socketOptions, isDebugging = false, + customAppInfo, }: Props) => { useEffect(() => { // configure API client token @@ -108,14 +114,34 @@ const InvokeAIUI = ({ }, [customStarUi]); useEffect(() => { - if (headerComponent) { - $headerComponent.set(headerComponent); + if (customNavComponent) { + $customNavComponent.set(customNavComponent); } return () => { - $headerComponent.set(undefined); + $customNavComponent.set(undefined); }; - }, [headerComponent]); + }, [customNavComponent]); + + useEffect(() => { + if (galleryHeader) { + $galleryHeader.set(galleryHeader); + } + + return () => { + $galleryHeader.set(undefined); + }; + }, [galleryHeader]); + + useEffect(() => { + if (customAppInfo) { + $customAppInfo.set(customAppInfo); + } + + return () => { + $customAppInfo.set(undefined); + }; + }, [customAppInfo]); useEffect(() => { if (socketOptions) { diff --git a/invokeai/frontend/web/src/app/store/nanostores/customAppInfo.ts b/invokeai/frontend/web/src/app/store/nanostores/customAppInfo.ts new file mode 100644 index 0000000000..0f7b73e38a --- /dev/null +++ b/invokeai/frontend/web/src/app/store/nanostores/customAppInfo.ts @@ -0,0 +1,3 @@ +import { atom } from 'nanostores'; + +export const $customAppInfo = atom(); diff --git a/invokeai/frontend/web/src/app/store/nanostores/customNavComponent.ts b/invokeai/frontend/web/src/app/store/nanostores/customNavComponent.ts new file mode 100644 index 0000000000..1a6a5571a0 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/nanostores/customNavComponent.ts @@ -0,0 +1,4 @@ +import { atom } from 'nanostores'; +import type { ReactNode } from 'react'; + +export const $customNavComponent = atom(undefined); diff --git a/invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts b/invokeai/frontend/web/src/app/store/nanostores/galleryHeader.ts similarity index 51% rename from invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts rename to invokeai/frontend/web/src/app/store/nanostores/galleryHeader.ts index 0b8a1398ec..5de7b1dd40 100644 --- a/invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts +++ b/invokeai/frontend/web/src/app/store/nanostores/galleryHeader.ts @@ -1,4 +1,4 @@ import { atom } from 'nanostores'; import type { ReactNode } from 'react'; -export const $headerComponent = atom(undefined); +export const $galleryHeader = atom(undefined); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 0b8b8b1055..b121a73b3e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -7,7 +7,9 @@ import { useDisclosure, VStack, } from '@chakra-ui/react'; +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; +import { $galleryHeader } from 'app/store/nanostores/galleryHeader'; import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; @@ -36,6 +38,7 @@ const ImageGalleryContent = () => { const galleryGridRef = useRef(null); const { galleryView } = useAppSelector(selector); const dispatch = useAppDispatch(); + const galleryHeader = useStore($galleryHeader); const { isOpen: isBoardListOpen, onToggle: onToggleBoardList } = useDisclosure({ defaultIsOpen: true }); @@ -56,6 +59,7 @@ const ImageGalleryContent = () => { borderRadius="base" p={2} > + {galleryHeader} { const { data: appVersion } = useGetAppVersionQuery(); const ref = useRef(null); + const customAppInfo = useStore($customAppInfo); const tooltip = useMemo(() => { + if (customAppInfo) { + return {customAppInfo}; + } + if (appVersion) { return v{appVersion.version}; } return null; - }, [appVersion]); + }, [appVersion, customAppInfo]); return ( diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index f94ef631c0..1f02294a38 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -1,5 +1,7 @@ import { Flex, Spacer } from '@chakra-ui/react'; +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; +import { $customNavComponent } from 'app/store/nanostores/customNavComponent'; import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; @@ -117,6 +119,7 @@ const InvokeTabs = () => { const enabledTabs = useAppSelector(enabledTabsSelector); const { t } = useTranslation(); const dispatch = useAppDispatch(); + const customNavComponent = useStore($customNavComponent); const panelGroupRef = useRef(null); const handleClickTab = useCallback((e: MouseEvent) => { if (e.target instanceof HTMLElement) { @@ -146,6 +149,7 @@ const InvokeTabs = () => { variant="appTab" data-selected={activeTabName === tab.id} aria-label={t(tab.translationKey)} + data-testid={t(tab.translationKey)} /> )), @@ -251,6 +255,7 @@ const InvokeTabs = () => { + {customNavComponent}