Styling changes and settings modal minor refactor

This commit is contained in:
psychedelicious 2022-11-02 15:31:18 +11:00 committed by Lincoln Stein
parent 1e51c39928
commit cefe12f1df
21 changed files with 650 additions and 656 deletions

File diff suppressed because one or more lines are too long

517
frontend/dist/assets/index.552c95d8.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvokeAI - A Stable Diffusion Toolkit</title> <title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" /> <link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
<script type="module" crossorigin src="./assets/index.ae6d2a5e.js"></script> <script type="module" crossorigin src="./assets/index.552c95d8.js"></script>
<link rel="stylesheet" href="./assets/index.14c578ee.css"> <link rel="stylesheet" href="./assets/index.67342d6d.css">
</head> </head>
<body> <body>

View File

@ -3,11 +3,7 @@ import {
IconButton, IconButton,
Tooltip, Tooltip,
TooltipProps, TooltipProps,
ResponsiveValue,
ThemingProps,
isChakraTheme,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { Variant } from 'framer-motion';
interface Props extends IconButtonProps { interface Props extends IconButtonProps {
styleClass?: string; styleClass?: string;

View File

@ -1,8 +1,7 @@
@use '../../styles/Mixins/' as *; @use '../../styles/Mixins/' as *;
.invokeai__select { .invokeai__select {
display: grid; display: flex;
grid-template-columns: repeat(2, max-content);
column-gap: 1rem; column-gap: 1rem;
align-items: center; align-items: center;
width: max-content; width: max-content;

View File

@ -1,17 +1,17 @@
import { FormControl, FormLabel, Select, SelectProps } from '@chakra-ui/react'; import { FormControl, FormLabel, Select, SelectProps } from '@chakra-ui/react';
import { MouseEvent } from 'react'; import { MouseEvent } from 'react';
interface Props extends SelectProps { type IAISelectProps = SelectProps & {
label: string; label: string;
styleClass?: string; styleClass?: string;
validValues: validValues:
| Array<number | string> | Array<number | string>
| Array<{ key: string; value: string | number }>; | Array<{ key: string; value: string | number }>;
} };
/** /**
* Customized Chakra FormControl + Select multi-part component. * Customized Chakra FormControl + Select multi-part component.
*/ */
const IAISelect = (props: Props) => { const IAISelect = (props: IAISelectProps) => {
const { const {
label, label,
isDisabled, isDisabled,
@ -33,19 +33,19 @@ const IAISelect = (props: Props) => {
}} }}
> >
<FormLabel <FormLabel
className="invokeai__select-label"
fontSize={fontSize} fontSize={fontSize}
marginBottom={1} marginBottom={1}
flexGrow={2} flexGrow={2}
whiteSpace="nowrap" whiteSpace="nowrap"
className="invokeai__select-label"
> >
{label} {label}
</FormLabel> </FormLabel>
<Select <Select
className="invokeai__select-picker"
fontSize={fontSize} fontSize={fontSize}
size={size} size={size}
{...rest} {...rest}
className="invokeai__select-picker"
> >
{validValues.map((opt) => { {validValues.map((opt) => {
return typeof opt === 'string' || typeof opt === 'number' ? ( return typeof opt === 'string' || typeof opt === 'number' ? (
@ -53,7 +53,11 @@ const IAISelect = (props: Props) => {
{opt} {opt}
</option> </option>
) : ( ) : (
<option key={opt.value} value={opt.value}> <option
key={opt.value}
value={opt.value}
className="invokeai__select-option"
>
{opt.key} {opt.key}
</option> </option>
); );

View File

@ -22,8 +22,6 @@ const IAISwitch = (props: Props) => {
const { const {
label, label,
isDisabled = false, isDisabled = false,
// fontSize = 'md',
// size = 'md',
width = 'auto', width = 'auto',
formControlProps, formControlProps,
formLabelProps, formLabelProps,
@ -39,17 +37,11 @@ const IAISwitch = (props: Props) => {
> >
<FormLabel <FormLabel
className="invokeai__switch-form-label" className="invokeai__switch-form-label"
// fontSize={fontSize}
whiteSpace="nowrap" whiteSpace="nowrap"
{...formLabelProps} {...formLabelProps}
> >
{label} {label}
<Switch <Switch className="invokeai__switch-root" {...rest} />
className="invokeai__switch-root"
// size={size}
// className="switch-button"
{...rest}
/>
</FormLabel> </FormLabel>
</FormControl> </FormControl>
); );

View File

@ -16,6 +16,7 @@
row-gap: 1rem; row-gap: 1rem;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-color: var(--background-color);
&.is-drag-accept { &.is-drag-accept {
box-shadow: inset 0 0 20rem 1rem var(--accent-color); box-shadow: inset 0 0 20rem 1rem var(--accent-color);

View File

@ -6,6 +6,7 @@ import { uploadImage } from '../../app/socketio/actions';
import { ImageUploadDestination, UploadImagePayload } from '../../app/invokeai'; import { ImageUploadDestination, UploadImagePayload } from '../../app/invokeai';
import { ImageUploaderTriggerContext } from '../../app/contexts/ImageUploaderTriggerContext'; import { ImageUploaderTriggerContext } from '../../app/contexts/ImageUploaderTriggerContext';
import { activeTabNameSelector } from '../../features/options/optionsSelectors'; import { activeTabNameSelector } from '../../features/options/optionsSelectors';
import { tabDict } from '../../features/tabs/InvokeTabs';
type ImageUploaderProps = { type ImageUploaderProps = {
children: ReactNode; children: ReactNode;
@ -128,6 +129,12 @@ const ImageUploader = (props: ImageUploaderProps) => {
}; };
}, [dispatch, toast, activeTabName]); }, [dispatch, toast, activeTabName]);
const overlaySecondaryText = ['img2img', 'inpainting'].includes(
activeTabName
)
? ` to ${tabDict[activeTabName as keyof typeof tabDict].tooltip}`
: ``;
return ( return (
<ImageUploaderTriggerContext.Provider value={open}> <ImageUploaderTriggerContext.Provider value={open}>
<div {...getRootProps({ style: {} })}> <div {...getRootProps({ style: {} })}>
@ -137,7 +144,7 @@ const ImageUploader = (props: ImageUploaderProps) => {
<div className="dropzone-container"> <div className="dropzone-container">
{isDragAccept && ( {isDragAccept && (
<div className="dropzone-overlay is-drag-accept"> <div className="dropzone-overlay is-drag-accept">
<Heading size={'lg'}>Drop Images</Heading> <Heading size={'lg'}>Upload Image{overlaySecondaryText}</Heading>
</div> </div>
)} )}
{isDragReject && ( {isDragReject && (

View File

@ -26,6 +26,9 @@ export const imagesSelector = createSelector(
isOnLastImage: isOnLastImage:
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1, !isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
shouldShowImageDetails, shouldShowImageDetails,
shouldShowPrevImageButton: currentImageIndex === 0,
shouldShowNextImageButton:
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
}; };
}, },
{ {

View File

@ -19,8 +19,6 @@
} }
.image-gallery-wrapper { .image-gallery-wrapper {
z-index: 100;
&[data-pinned='false'] { &[data-pinned='false'] {
position: fixed; position: fixed;
height: 100vh; height: 100vh;

View File

@ -261,6 +261,7 @@ export default function ImageGallery() {
> >
<div <div
className="image-gallery-wrapper" className="image-gallery-wrapper"
style={{ zIndex: shouldPinGallery ? 1 : 100 }}
data-pinned={shouldPinGallery} data-pinned={shouldPinGallery}
ref={galleryRef} ref={galleryRef}
onMouseLeave={!shouldPinGallery ? setCloseGalleryTimer : undefined} onMouseLeave={!shouldPinGallery ? setCloseGalleryTimer : undefined}

View File

@ -19,6 +19,7 @@
.main-option-block { .main-option-block {
border-radius: 0.5rem; border-radius: 0.5rem;
display: grid !important;
grid-template-columns: auto !important; grid-template-columns: auto !important;
row-gap: 0.4rem; row-gap: 0.4rem;

View File

@ -1,24 +1,37 @@
.model-list { // .chakra-accordion {
.chakra-accordion { // display: grid;
display: grid; // row-gap: 0.5rem;
row-gap: 0.5rem; // }
}
.chakra-accordion__item { // .chakra-accordion__item {
border: none; // border: none;
border-radius: 0.3rem; // }
background-color: var(--tab-hover-color);
} // button {
// border-radius: 0.3rem !important;
// &[aria-expanded='true'] {
// // background-color: var(--tab-hover-color);
// border-radius: 0.3rem;
// }
// }
.model-list-accordion {
outline: none;
padding: 0.25rem;
button { button {
border-radius: 0.3rem !important; padding: 0;
margin: 0;
&[aria-expanded='true'] { &:hover {
background-color: var(--tab-hover-color); background-color: unset;
border-radius: 0.3rem;
} }
} }
div {
border: none !important;
}
.model-list-button { .model-list-button {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -64,6 +77,9 @@
} }
} }
.model-list-item-load-btn { .model-list-item-load-btn {
button {
padding: 0.5rem;
}
} }
} }
} }

View File

@ -73,31 +73,33 @@ const ModelList = () => {
const { models } = useAppSelector(modelListSelector); const { models } = useAppSelector(modelListSelector);
return ( return (
<div className="model-list"> <Accordion
<Accordion allowToggle> allowToggle
<AccordionItem> className="model-list-accordion"
<AccordionButton> variant={'unstyled'}
<div className="model-list-button"> >
<h2>Models</h2> <AccordionItem>
<AccordionIcon /> <AccordionButton>
</div> <div className="model-list-button">
</AccordionButton> <h2>Models</h2>
<AccordionIcon />
</div>
</AccordionButton>
<AccordionPanel> <AccordionPanel>
<div className="model-list-list"> <div className="model-list-list">
{models.map((model, i) => ( {models.map((model, i) => (
<ModelListItem <ModelListItem
key={i} key={i}
name={model.name} name={model.name}
status={model.status} status={model.status}
description={model.description} description={model.description}
/> />
))} ))}
</div> </div>
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
</div>
); );
}; };

View File

@ -14,18 +14,18 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import _, { isEqual } from 'lodash'; import _, { isEqual } from 'lodash';
import { cloneElement, ReactElement } from 'react'; import { ChangeEvent, cloneElement, ReactElement } from 'react';
import { RootState, useAppSelector } from '../../../app/store'; import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
import { persistor } from '../../../main'; import { persistor } from '../../../main';
import { import {
setShouldConfirmOnDelete, setShouldConfirmOnDelete,
setShouldDisplayGuides, setShouldDisplayGuides,
setShouldDisplayInProgressType,
SystemState, SystemState,
} from '../systemSlice'; } from '../systemSlice';
import ModelList from './ModelList'; import ModelList from './ModelList';
import { SettingsModalItem, SettingsModalSelectItem } from './SettingsModalItem';
import { IN_PROGRESS_IMAGE_TYPES } from '../../../app/constants'; import { IN_PROGRESS_IMAGE_TYPES } from '../../../app/constants';
import IAISwitch from '../../../common/components/IAISwitch';
import IAISelect from '../../../common/components/IAISelect';
const systemSelector = createSelector( const systemSelector = createSelector(
(state: RootState) => state.system, (state: RootState) => state.system,
@ -60,6 +60,8 @@ type SettingsModalProps = {
* Secondary post-reset modal is included here. * Secondary post-reset modal is included here.
*/ */
const SettingsModal = ({ children }: SettingsModalProps) => { const SettingsModal = ({ children }: SettingsModalProps) => {
const dispatch = useAppDispatch();
const { const {
isOpen: isSettingsModalOpen, isOpen: isSettingsModalOpen,
onOpen: onSettingsModalOpen, onOpen: onSettingsModalOpen,
@ -101,26 +103,32 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
<ModalHeader className="settings-modal-header">Settings</ModalHeader> <ModalHeader className="settings-modal-header">Settings</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody className="settings-modal-content"> <ModalBody className="settings-modal-content">
<ModelList />
<div className="settings-modal-items"> <div className="settings-modal-items">
<div className="settings-modal-item">
<SettingsModalSelectItem <ModelList />
settingTitle="Display In-Progress Images" </div>
<IAISelect
styleClass="settings-modal-item"
label={'Display In-Progress Images'}
validValues={IN_PROGRESS_IMAGE_TYPES} validValues={IN_PROGRESS_IMAGE_TYPES}
defaultValue={shouldDisplayInProgressType} value={shouldDisplayInProgressType}
dispatcher={setShouldDisplayInProgressType}
/> />
<SettingsModalItem <IAISwitch
settingTitle="Confirm on Delete" styleClass="settings-modal-item"
label={'Confirm on Delete'}
isChecked={shouldConfirmOnDelete} isChecked={shouldConfirmOnDelete}
dispatcher={setShouldConfirmOnDelete} onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldConfirmOnDelete(e.target.checked))
}
/> />
<IAISwitch
<SettingsModalItem styleClass="settings-modal-item"
settingTitle="Display Help Icons" label={'Display Help Icons'}
isChecked={shouldDisplayGuides} isChecked={shouldDisplayGuides}
dispatcher={setShouldDisplayGuides} onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldDisplayGuides(e.target.checked))
}
/> />
</div> </div>

View File

@ -1,50 +0,0 @@
import { useAppDispatch } from '../../../app/store';
import IAISelect from '../../../common/components/IAISelect';
import IAISwitch from '../../../common/components/IAISwitch';
export function SettingsModalItem({
settingTitle,
isChecked,
dispatcher,
}: {
settingTitle: string;
isChecked: boolean;
dispatcher: any;
}) {
const dispatch = useAppDispatch();
return (
<IAISwitch
styleClass="settings-modal-item"
label={settingTitle}
isChecked={isChecked}
onChange={(e) => dispatch(dispatcher(e.target.checked))}
/>
);
}
export function SettingsModalSelectItem({
settingTitle,
validValues,
defaultValue,
dispatcher,
}: {
settingTitle: string;
validValues:
Array<number | string>
| Array<{ key: string; value: string | number }>;
defaultValue: string;
dispatcher: any;
}) {
const dispatch = useAppDispatch();
return (
<IAISelect
styleClass="settings-modal-item"
label={settingTitle}
validValues={validValues}
defaultValue={defaultValue}
onChange={(e) => dispatch(dispatcher(e.target.value))}
/>
);
}

View File

@ -1,9 +1,9 @@
import { IconButton, Link, Tooltip, useColorMode } from '@chakra-ui/react'; import { Link, useColorMode } from '@chakra-ui/react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { FaSun, FaMoon, FaGithub, FaDiscord, FaBug } from 'react-icons/fa'; import { FaSun, FaMoon, FaGithub, FaDiscord, FaBug } from 'react-icons/fa';
import { MdHelp, MdKeyboard, MdSettings } from 'react-icons/md'; import { MdKeyboard, MdSettings } from 'react-icons/md';
import InvokeAILogo from '../../assets/images/logo.png'; import InvokeAILogo from '../../assets/images/logo.png';
import IAIIconButton from '../../common/components/IAIIconButton'; import IAIIconButton from '../../common/components/IAIIconButton';
@ -91,18 +91,18 @@ const SiteHeader = () => {
} }
/> />
<IAIIconButton <IAIIconButton
aria-label="Link to Discord Server" aria-label="Link to Discord Server"
tooltip="Discord" tooltip="Discord"
variant="link" variant="link"
fontSize={20} fontSize={20}
size={'sm'} size={'sm'}
icon={ icon={
<Link isExternal href="https://discord.gg/ZmtBAhwWhy"> <Link isExternal href="https://discord.gg/ZmtBAhwWhy">
<FaDiscord /> <FaDiscord />
</Link> </Link>
} }
/> />
<SettingsModal> <SettingsModal>
<IAIIconButton <IAIIconButton

View File

@ -17,7 +17,7 @@ import ImageToImageWorkarea from './ImageToImage';
import InpaintingWorkarea from './Inpainting'; import InpaintingWorkarea from './Inpainting';
import TextToImageWorkarea from './TextToImage'; import TextToImageWorkarea from './TextToImage';
export const tab_dict = { export const tabDict = {
txt2img: { txt2img: {
title: <TextToImageIcon fill={'black'} boxSize={'2.5rem'} />, title: <TextToImageIcon fill={'black'} boxSize={'2.5rem'} />,
workarea: <TextToImageWorkarea />, workarea: <TextToImageWorkarea />,
@ -50,8 +50,8 @@ export const tab_dict = {
}, },
}; };
// Array where index maps to the key of tab_dict // Array where index maps to the key of tabDict
export const tabMap = _.map(tab_dict, (tab, key) => key); export const tabMap = _.map(tabDict, (tab, key) => key);
// Use tabMap to generate a union type of tab names // Use tabMap to generate a union type of tab names
const tabMapTypes = [...tabMap] as const; const tabMapTypes = [...tabMap] as const;
@ -89,15 +89,15 @@ export default function InvokeTabs() {
const renderTabs = () => { const renderTabs = () => {
const tabsToRender: ReactElement[] = []; const tabsToRender: ReactElement[] = [];
Object.keys(tab_dict).forEach((key) => { Object.keys(tabDict).forEach((key) => {
tabsToRender.push( tabsToRender.push(
<Tooltip <Tooltip
key={key} key={key}
hasArrow hasArrow
label={tab_dict[key as keyof typeof tab_dict].tooltip} label={tabDict[key as keyof typeof tabDict].tooltip}
placement={'right'} placement={'right'}
> >
<Tab>{tab_dict[key as keyof typeof tab_dict].title}</Tab> <Tab>{tabDict[key as keyof typeof tabDict].title}</Tab>
</Tooltip> </Tooltip>
); );
}); });
@ -106,10 +106,10 @@ export default function InvokeTabs() {
const renderTabPanels = () => { const renderTabPanels = () => {
const tabPanelsToRender: ReactElement[] = []; const tabPanelsToRender: ReactElement[] = [];
Object.keys(tab_dict).forEach((key) => { Object.keys(tabDict).forEach((key) => {
tabPanelsToRender.push( tabPanelsToRender.push(
<TabPanel className="app-tabs-panel" key={key}> <TabPanel className="app-tabs-panel" key={key}>
{tab_dict[key as keyof typeof tab_dict].workarea} {tabDict[key as keyof typeof tabDict].workarea}
</TabPanel> </TabPanel>
); );
}); });