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 <maryhipp@Marys-MacBook-Air.local>
This commit is contained in:
Mary Hipp Rogers 2024-01-04 10:30:27 -05:00 committed by GitHub
parent d6362ce0bd
commit 9e2e740033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 9 deletions

View File

@ -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<ManagerOptions & SocketOptions>;
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) {

View File

@ -0,0 +1,3 @@
import { atom } from 'nanostores';
export const $customAppInfo = atom<string | undefined>();

View File

@ -0,0 +1,4 @@
import { atom } from 'nanostores';
import type { ReactNode } from 'react';
export const $customNavComponent = atom<ReactNode | undefined>(undefined);

View File

@ -1,4 +1,4 @@
import { atom } from 'nanostores';
import type { ReactNode } from 'react';
export const $headerComponent = atom<ReactNode | undefined>(undefined);
export const $galleryHeader = atom<ReactNode | undefined>(undefined);

View File

@ -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<HTMLDivElement>(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}
<Box w="full">
<Flex
ref={resizeObserverRef}

View File

@ -1,5 +1,7 @@
/* eslint-disable i18next/no-literal-string */
import { Image } from '@chakra-ui/react';
import { useStore } from '@nanostores/react';
import { $customAppInfo } from 'app/store/nanostores/customAppInfo';
import InvokeLogoYellow from 'assets/images/invoke-key-ylw-sm.svg';
import { InvText } from 'common/components/InvText/wrapper';
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
@ -9,12 +11,17 @@ import { useGetAppVersionQuery } from 'services/api/endpoints/appInfo';
const InvokeAILogoComponent = () => {
const { data: appVersion } = useGetAppVersionQuery();
const ref = useRef(null);
const customAppInfo = useStore($customAppInfo);
const tooltip = useMemo(() => {
if (customAppInfo) {
return <InvText fontWeight="semibold">{customAppInfo}</InvText>;
}
if (appVersion) {
return <InvText fontWeight="semibold">v{appVersion.version}</InvText>;
}
return null;
}, [appVersion]);
}, [appVersion, customAppInfo]);
return (
<InvTooltip placement="right" label={tooltip} p={1} px={2} gutter={16}>

View File

@ -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<ImperativePanelGroupHandle>(null);
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
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)}
/>
</InvTooltip>
)),
@ -251,6 +255,7 @@ const InvokeTabs = () => {
<Spacer />
<StatusIndicator />
<SettingsMenu />
{customNavComponent}
</Flex>
<PanelGroup
ref={panelGroupRef}