fix(ui): popover ref & wrapping of children (wip)

This commit is contained in:
psychedelicious 2023-09-21 20:01:00 +10:00 committed by Kent Keirsey
parent b6e9cd4fe2
commit 5aefa49d7d
13 changed files with 140 additions and 106 deletions

View File

@ -1213,14 +1213,14 @@
},
"dynamicPromptsCombinatorial": {
"heading": "Combinatorial Generation",
"paragraph": "Generate an image for every possible combination of Dynamic Prompt until the Max Prompts is reached."
"paragraph": "Generate an image for every possible combination of Dynamic Prompts until the Max Prompts is reached."
},
"infillMethod": {
"heading": "Infill Method",
"paragraph": "Method to infill the selected area."
},
"lora": {
"heading": "LoRA",
"heading": "LoRA Weight",
"paragraph": "Weight of the LoRA. Higher weight will lead to larger impacts on the final image."
},
"noiseEnable": {
@ -1239,21 +1239,21 @@
"heading": "Denoising Strength",
"paragraph": "How much noise is added to the input image. 0 will result in an identical image, while 1 will result in a completely new image."
},
"paramImages": {
"heading": "Images",
"paragraph": "Number of images that will be generated."
"paramIterations": {
"heading": "Iterations",
"paragraph": "The number of images to generate. If Dynamic Prompts is enabled, each of the prompts will be generated this many times."
},
"paramModel": {
"heading": "Model",
"paragraph": "Model used for the denoising steps. Different models are trained to specialize in producing different aesthetic results and content."
},
"paramNegativeConditioning": {
"heading": "Negative Prompts",
"paragraph": "This is where you enter your negative prompts."
"heading": "Negative Prompt",
"paragraph": "The generation process avoids the concepts in the negative prompt. Use this to exclude qualities or objects from the output. Supports Compel syntax and embeddings."
},
"paramPositiveConditioning": {
"heading": "Positive Prompts",
"paragraph": "This is where you enter your positive prompts."
"heading": "Positive Prompt",
"paragraph": "Guides the generation process. You may use any words or phrases. Supports Compel and Dynamic Prompts syntaxes and embeddings."
},
"paramRatio": {
"heading": "Ratio",

View File

@ -1,37 +1,43 @@
import {
Box,
Button,
Popover,
PopoverTrigger,
PopoverContent,
PopoverArrow,
PopoverCloseButton,
PopoverHeader,
PopoverBody,
PopoverProps,
Divider,
Flex,
Text,
Heading,
Image,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverProps,
PopoverTrigger,
Portal,
Text,
} from '@chakra-ui/react';
import { useAppSelector } from '../../app/store/storeHooks';
import { ReactNode, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppSelector } from '../../app/store/storeHooks';
interface Props extends PopoverProps {
const OPEN_DELAY = 1500;
type Props = Omit<PopoverProps, 'children'> & {
details: string;
children: JSX.Element;
children: ReactNode;
image?: string;
buttonLabel?: string;
buttonHref?: string;
placement?: PopoverProps['placement'];
}
};
function IAIInformationalPopover({
const IAIInformationalPopover = ({
details,
image,
buttonLabel,
buttonHref,
children,
placement,
}: Props): JSX.Element {
}: Props) => {
const shouldEnableInformationalPopovers = useAppSelector(
(state) => state.system.shouldEnableInformationalPopovers
);
@ -41,18 +47,21 @@ function IAIInformationalPopover({
const paragraph = t(`popovers.${details}.paragraph`);
if (!shouldEnableInformationalPopovers) {
return children;
} else {
return (
<Popover
placement={placement || 'top'}
closeOnBlur={false}
trigger="hover"
variant="informational"
>
<PopoverTrigger>
<div>{children}</div>
</PopoverTrigger>
return <>{children}</>;
}
return (
<Popover
placement={placement || 'top'}
closeOnBlur={false}
trigger="hover"
variant="informational"
openDelay={OPEN_DELAY}
>
<PopoverTrigger>
<Box w="full">{children}</Box>
</PopoverTrigger>
<Portal>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
@ -83,14 +92,17 @@ function IAIInformationalPopover({
gap: 3,
flexDirection: 'column',
width: '100%',
p: 3,
pt: heading ? 0 : 3,
}}
>
{heading && <PopoverHeader>{heading}</PopoverHeader>}
<Text sx={{ px: 3 }}>{paragraph}</Text>
{heading && (
<>
<Heading size="sm">{heading}</Heading>
<Divider />
</>
)}
<Text>{paragraph}</Text>
{buttonLabel && (
<Flex sx={{ px: 3 }} justifyContent="flex-end">
<Flex justifyContent="flex-end">
<Button
onClick={() => window.open(buttonHref)}
size="sm"
@ -104,9 +116,9 @@ function IAIInformationalPopover({
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
}
}
</Portal>
</Popover>
);
};
export default IAIInformationalPopover;
export default memo(IAIInformationalPopover);

View File

@ -1,4 +1,4 @@
import { FormControl, FormLabel, Tooltip } from '@chakra-ui/react';
import { FormControl, FormLabel, Tooltip, forwardRef } from '@chakra-ui/react';
import { MultiSelect, MultiSelectProps } from '@mantine/core';
import { useAppDispatch } from 'app/store/storeHooks';
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
@ -11,7 +11,7 @@ type IAIMultiSelectProps = Omit<MultiSelectProps, 'label'> & {
label?: string;
};
const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
const IAIMantineMultiSelect = forwardRef((props: IAIMultiSelectProps, ref) => {
const {
searchable = true,
tooltip,
@ -47,7 +47,7 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
<MultiSelect
label={
label ? (
<FormControl isDisabled={disabled}>
<FormControl ref={ref} isDisabled={disabled}>
<FormLabel>{label}</FormLabel>
</FormControl>
) : undefined
@ -63,6 +63,8 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
/>
</Tooltip>
);
};
});
IAIMantineMultiSelect.displayName = 'IAIMantineMultiSelect';
export default memo(IAIMantineMultiSelect);

View File

@ -1,4 +1,4 @@
import { FormControl, FormLabel, Tooltip } from '@chakra-ui/react';
import { FormControl, FormLabel, Tooltip, forwardRef } from '@chakra-ui/react';
import { Select, SelectProps } from '@mantine/core';
import { useAppDispatch } from 'app/store/storeHooks';
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
@ -17,7 +17,7 @@ type IAISelectProps = Omit<SelectProps, 'label'> & {
inputRef?: RefObject<HTMLInputElement>;
};
const IAIMantineSearchableSelect = (props: IAISelectProps) => {
const IAIMantineSearchableSelect = forwardRef((props: IAISelectProps, ref) => {
const {
searchable = true,
tooltip,
@ -74,7 +74,7 @@ const IAIMantineSearchableSelect = (props: IAISelectProps) => {
ref={inputRef}
label={
label ? (
<FormControl isDisabled={disabled}>
<FormControl ref={ref} isDisabled={disabled}>
<FormLabel>{label}</FormLabel>
</FormControl>
) : undefined
@ -92,6 +92,8 @@ const IAIMantineSearchableSelect = (props: IAISelectProps) => {
/>
</Tooltip>
);
};
});
IAIMantineSearchableSelect.displayName = 'IAIMantineSearchableSelect';
export default memo(IAIMantineSearchableSelect);

View File

@ -1,4 +1,4 @@
import { FormControl, FormLabel, Tooltip } from '@chakra-ui/react';
import { FormControl, FormLabel, Tooltip, forwardRef } from '@chakra-ui/react';
import { Select, SelectProps } from '@mantine/core';
import { useMantineSelectStyles } from 'mantine-theme/hooks/useMantineSelectStyles';
import { RefObject, memo } from 'react';
@ -15,7 +15,7 @@ export type IAISelectProps = Omit<SelectProps, 'label'> & {
label?: string;
};
const IAIMantineSelect = (props: IAISelectProps) => {
const IAIMantineSelect = forwardRef((props: IAISelectProps, ref) => {
const { tooltip, inputRef, label, disabled, required, ...rest } = props;
const styles = useMantineSelectStyles();
@ -25,7 +25,7 @@ const IAIMantineSelect = (props: IAISelectProps) => {
<Select
label={
label ? (
<FormControl isRequired={required} isDisabled={disabled}>
<FormControl ref={ref} isRequired={required} isDisabled={disabled}>
<FormLabel>{label}</FormLabel>
</FormControl>
) : undefined
@ -37,6 +37,8 @@ const IAIMantineSelect = (props: IAISelectProps) => {
/>
</Tooltip>
);
};
});
IAIMantineSelect.displayName = 'IAIMantineSelect';
export default memo(IAIMantineSelect);

View File

@ -13,6 +13,7 @@ import {
NumberInputStepperProps,
Tooltip,
TooltipProps,
forwardRef,
} from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import { stopPastePropagation } from 'common/util/stopPastePropagation';
@ -50,7 +51,7 @@ interface Props extends Omit<NumberInputProps, 'onChange'> {
/**
* Customized Chakra FormControl + NumberInput multi-part component.
*/
const IAINumberInput = (props: Props) => {
const IAINumberInput = forwardRef((props: Props, ref) => {
const {
label,
isDisabled = false,
@ -141,6 +142,7 @@ const IAINumberInput = (props: Props) => {
return (
<Tooltip {...tooltipProps}>
<FormControl
ref={ref}
isDisabled={isDisabled}
isInvalid={isInvalid}
{...formControlProps}
@ -172,6 +174,8 @@ const IAINumberInput = (props: Props) => {
</FormControl>
</Tooltip>
);
};
});
IAINumberInput.displayName = 'IAINumberInput';
export default memo(IAINumberInput);

View File

@ -22,6 +22,7 @@ import {
SliderTrackProps,
Tooltip,
TooltipProps,
forwardRef,
} from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
@ -71,7 +72,7 @@ export type IAIFullSliderProps = {
sliderIAIIconButtonProps?: IAIIconButtonProps;
};
const IAISlider = (props: IAIFullSliderProps) => {
const IAISlider = forwardRef((props: IAIFullSliderProps, ref) => {
const [showTooltip, setShowTooltip] = useState(false);
const {
label,
@ -187,6 +188,7 @@ const IAISlider = (props: IAIFullSliderProps) => {
return (
<FormControl
ref={ref}
onClick={forceInputBlur}
sx={
isCompact
@ -354,6 +356,8 @@ const IAISlider = (props: IAIFullSliderProps) => {
</HStack>
</FormControl>
);
};
});
IAISlider.displayName = 'IAISlider';
export default memo(IAISlider);

View File

@ -72,4 +72,6 @@ const IAISwitch = (props: IAISwitchProps) => {
);
};
IAISwitch.displayName = 'IAISwitch';
export default memo(IAISwitch);

View File

@ -61,7 +61,7 @@ const ParamIterations = ({ asSlider }: Props) => {
}, [dispatch, initial]);
return asSlider || shouldUseSliders ? (
<IAIInformationalPopover details="paramImages">
<IAIInformationalPopover details="paramIterations">
<IAISlider
label={t('parameters.iterations')}
step={step}
@ -77,7 +77,7 @@ const ParamIterations = ({ asSlider }: Props) => {
/>
</IAIInformationalPopover>
) : (
<IAIInformationalPopover details="paramImages">
<IAIInformationalPopover details="paramIterations">
<IAINumberInput
label={t('parameters.iterations')}
step={step}

View File

@ -1,6 +1,7 @@
import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAITextarea from 'common/components/IAITextarea';
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
@ -9,7 +10,6 @@ import { ChangeEvent, KeyboardEvent, memo, useCallback, useRef } from 'react';
import { flushSync } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const ParamNegativeConditioning = () => {
const negativePrompt = useAppSelector(
@ -76,13 +76,16 @@ const ParamNegativeConditioning = () => {
const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled;
return (
<FormControl>
<ParamEmbeddingPopover
isOpen={isOpen}
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAIInformationalPopover details="paramNegativeConditioning">
<IAIInformationalPopover
placement="right"
details="paramNegativeConditioning"
>
<FormControl>
<ParamEmbeddingPopover
isOpen={isOpen}
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAITextarea
id="negativePrompt"
name="negativePrompt"
@ -95,20 +98,20 @@ const ParamNegativeConditioning = () => {
minH={16}
{...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })}
/>
</IAIInformationalPopover>
</ParamEmbeddingPopover>
{!isOpen && isEmbeddingEnabled && (
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
}}
>
<AddEmbeddingButton onClick={onOpen} />
</Box>
)}
</FormControl>
</ParamEmbeddingPopover>
{!isOpen && isEmbeddingEnabled && (
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
}}
>
<AddEmbeddingButton onClick={onOpen} />
</Box>
)}
</FormControl>
</IAIInformationalPopover>
);
};

View File

@ -104,13 +104,16 @@ const ParamPositiveConditioning = () => {
return (
<Box position="relative">
<FormControl>
<ParamEmbeddingPopover
isOpen={isOpen}
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAIInformationalPopover details="paramPositiveConditioning">
<IAIInformationalPopover
placement="right"
details="paramPositiveConditioning"
>
<FormControl>
<ParamEmbeddingPopover
isOpen={isOpen}
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAITextarea
id="prompt"
name="prompt"
@ -122,9 +125,9 @@ const ParamPositiveConditioning = () => {
resize="vertical"
minH={32}
/>
</IAIInformationalPopover>
</ParamEmbeddingPopover>
</FormControl>
</ParamEmbeddingPopover>
</FormControl>
</IAIInformationalPopover>
{!isOpen && isEmbeddingEnabled && (
<Box
sx={{

View File

@ -119,8 +119,8 @@ const ParamMainModelSelect = () => {
data={[]}
/>
) : (
<IAIInformationalPopover details="paramModel" placement="bottom">
<Flex w="100%" alignItems="center" gap={3}>
<Flex w="100%" alignItems="center" gap={3}>
<IAIInformationalPopover details="paramModel" placement="bottom">
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={t('modelManager.model')}
@ -134,13 +134,13 @@ const ParamMainModelSelect = () => {
onChange={handleChangeModel}
w="100%"
/>
{isSyncModelEnabled && (
<Box mt={7}>
<SyncModelsButton iconMode />
</Box>
)}
</Flex>
</IAIInformationalPopover>
</IAIInformationalPopover>
{isSyncModelEnabled && (
<Box mt={7}>
<SyncModelsButton iconMode />
</Box>
)}
</Flex>
);
};

View File

@ -37,7 +37,7 @@ const informationalContent = defineStyle((props) => {
'colors.base.400',
'colors.base.400'
)(props),
p: 0,
p: 4,
bg: mode('base.100', 'base.600')(props),
border: 'none',
shadow: 'dark-lg',