mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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:
parent
d6362ce0bd
commit
9e2e740033
@ -4,9 +4,11 @@ import type { Middleware } from '@reduxjs/toolkit';
|
|||||||
import { $socketOptions } from 'app/hooks/useSocketIO';
|
import { $socketOptions } from 'app/hooks/useSocketIO';
|
||||||
import { $authToken } from 'app/store/nanostores/authToken';
|
import { $authToken } from 'app/store/nanostores/authToken';
|
||||||
import { $baseUrl } from 'app/store/nanostores/baseUrl';
|
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 type { CustomStarUi } from 'app/store/nanostores/customStarUI';
|
||||||
import { $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 { $isDebugging } from 'app/store/nanostores/isDebugging';
|
||||||
import { $projectId } from 'app/store/nanostores/projectId';
|
import { $projectId } from 'app/store/nanostores/projectId';
|
||||||
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
|
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
|
||||||
@ -28,9 +30,10 @@ interface Props extends PropsWithChildren {
|
|||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
config?: PartialAppConfig;
|
config?: PartialAppConfig;
|
||||||
headerComponent?: ReactNode;
|
customNavComponent?: ReactNode;
|
||||||
middleware?: Middleware[];
|
middleware?: Middleware[];
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
galleryHeader?: ReactNode;
|
||||||
queueId?: string;
|
queueId?: string;
|
||||||
selectedImage?: {
|
selectedImage?: {
|
||||||
imageName: string;
|
imageName: string;
|
||||||
@ -39,20 +42,23 @@ interface Props extends PropsWithChildren {
|
|||||||
customStarUi?: CustomStarUi;
|
customStarUi?: CustomStarUi;
|
||||||
socketOptions?: Partial<ManagerOptions & SocketOptions>;
|
socketOptions?: Partial<ManagerOptions & SocketOptions>;
|
||||||
isDebugging?: boolean;
|
isDebugging?: boolean;
|
||||||
|
customAppInfo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InvokeAIUI = ({
|
const InvokeAIUI = ({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
config,
|
config,
|
||||||
headerComponent,
|
customNavComponent,
|
||||||
middleware,
|
middleware,
|
||||||
projectId,
|
projectId,
|
||||||
|
galleryHeader,
|
||||||
queueId,
|
queueId,
|
||||||
selectedImage,
|
selectedImage,
|
||||||
customStarUi,
|
customStarUi,
|
||||||
socketOptions,
|
socketOptions,
|
||||||
isDebugging = false,
|
isDebugging = false,
|
||||||
|
customAppInfo,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// configure API client token
|
// configure API client token
|
||||||
@ -108,14 +114,34 @@ const InvokeAIUI = ({
|
|||||||
}, [customStarUi]);
|
}, [customStarUi]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (headerComponent) {
|
if (customNavComponent) {
|
||||||
$headerComponent.set(headerComponent);
|
$customNavComponent.set(customNavComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
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(() => {
|
useEffect(() => {
|
||||||
if (socketOptions) {
|
if (socketOptions) {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
import { atom } from 'nanostores';
|
||||||
|
|
||||||
|
export const $customAppInfo = atom<string | undefined>();
|
@ -0,0 +1,4 @@
|
|||||||
|
import { atom } from 'nanostores';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const $customNavComponent = atom<ReactNode | undefined>(undefined);
|
@ -1,4 +1,4 @@
|
|||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
export const $headerComponent = atom<ReactNode | undefined>(undefined);
|
export const $galleryHeader = atom<ReactNode | undefined>(undefined);
|
@ -7,7 +7,9 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
VStack,
|
VStack,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { useStore } from '@nanostores/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { $galleryHeader } from 'app/store/nanostores/galleryHeader';
|
||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
@ -36,6 +38,7 @@ const ImageGalleryContent = () => {
|
|||||||
const galleryGridRef = useRef<HTMLDivElement>(null);
|
const galleryGridRef = useRef<HTMLDivElement>(null);
|
||||||
const { galleryView } = useAppSelector(selector);
|
const { galleryView } = useAppSelector(selector);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const galleryHeader = useStore($galleryHeader);
|
||||||
const { isOpen: isBoardListOpen, onToggle: onToggleBoardList } =
|
const { isOpen: isBoardListOpen, onToggle: onToggleBoardList } =
|
||||||
useDisclosure({ defaultIsOpen: true });
|
useDisclosure({ defaultIsOpen: true });
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ const ImageGalleryContent = () => {
|
|||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
p={2}
|
p={2}
|
||||||
>
|
>
|
||||||
|
{galleryHeader}
|
||||||
<Box w="full">
|
<Box w="full">
|
||||||
<Flex
|
<Flex
|
||||||
ref={resizeObserverRef}
|
ref={resizeObserverRef}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
/* eslint-disable i18next/no-literal-string */
|
/* eslint-disable i18next/no-literal-string */
|
||||||
import { Image } from '@chakra-ui/react';
|
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 InvokeLogoYellow from 'assets/images/invoke-key-ylw-sm.svg';
|
||||||
import { InvText } from 'common/components/InvText/wrapper';
|
import { InvText } from 'common/components/InvText/wrapper';
|
||||||
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
||||||
@ -9,12 +11,17 @@ import { useGetAppVersionQuery } from 'services/api/endpoints/appInfo';
|
|||||||
const InvokeAILogoComponent = () => {
|
const InvokeAILogoComponent = () => {
|
||||||
const { data: appVersion } = useGetAppVersionQuery();
|
const { data: appVersion } = useGetAppVersionQuery();
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
const customAppInfo = useStore($customAppInfo);
|
||||||
const tooltip = useMemo(() => {
|
const tooltip = useMemo(() => {
|
||||||
|
if (customAppInfo) {
|
||||||
|
return <InvText fontWeight="semibold">{customAppInfo}</InvText>;
|
||||||
|
}
|
||||||
|
|
||||||
if (appVersion) {
|
if (appVersion) {
|
||||||
return <InvText fontWeight="semibold">v{appVersion.version}</InvText>;
|
return <InvText fontWeight="semibold">v{appVersion.version}</InvText>;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [appVersion]);
|
}, [appVersion, customAppInfo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvTooltip placement="right" label={tooltip} p={1} px={2} gutter={16}>
|
<InvTooltip placement="right" label={tooltip} p={1} px={2} gutter={16}>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { Flex, Spacer } from '@chakra-ui/react';
|
import { Flex, Spacer } from '@chakra-ui/react';
|
||||||
|
import { useStore } from '@nanostores/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { $customNavComponent } from 'app/store/nanostores/customNavComponent';
|
||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||||
@ -117,6 +119,7 @@ const InvokeTabs = () => {
|
|||||||
const enabledTabs = useAppSelector(enabledTabsSelector);
|
const enabledTabs = useAppSelector(enabledTabsSelector);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const customNavComponent = useStore($customNavComponent);
|
||||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||||
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
|
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
|
||||||
if (e.target instanceof HTMLElement) {
|
if (e.target instanceof HTMLElement) {
|
||||||
@ -146,6 +149,7 @@ const InvokeTabs = () => {
|
|||||||
variant="appTab"
|
variant="appTab"
|
||||||
data-selected={activeTabName === tab.id}
|
data-selected={activeTabName === tab.id}
|
||||||
aria-label={t(tab.translationKey)}
|
aria-label={t(tab.translationKey)}
|
||||||
|
data-testid={t(tab.translationKey)}
|
||||||
/>
|
/>
|
||||||
</InvTooltip>
|
</InvTooltip>
|
||||||
)),
|
)),
|
||||||
@ -251,6 +255,7 @@ const InvokeTabs = () => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<StatusIndicator />
|
<StatusIndicator />
|
||||||
<SettingsMenu />
|
<SettingsMenu />
|
||||||
|
{customNavComponent}
|
||||||
</Flex>
|
</Flex>
|
||||||
<PanelGroup
|
<PanelGroup
|
||||||
ref={panelGroupRef}
|
ref={panelGroupRef}
|
||||||
|
Loading…
Reference in New Issue
Block a user