feat(ui): improve accordion ux

- Accordions now may be opened or closed regardless of whether or not their contents are enabled or active
- Accordions have a short text indicator alerting the user if their contents are enabled, either a simple `Enabled` or, for accordions like LoRA or ControlNet, `X Active` if any are active
This commit is contained in:
psychedelicious 2023-07-05 17:33:03 +10:00
parent 6ce867feb4
commit e41e8606b5
29 changed files with 457 additions and 242 deletions

View File

@ -4,22 +4,25 @@ import {
Collapse, Collapse,
Flex, Flex,
Spacer, Spacer,
Switch, Text,
useColorMode, useColorMode,
useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { AnimatePresence, motion } from 'framer-motion';
import { PropsWithChildren, memo } from 'react'; import { PropsWithChildren, memo } from 'react';
import { mode } from 'theme/util/mode'; import { mode } from 'theme/util/mode';
export type IAIToggleCollapseProps = PropsWithChildren & { export type IAIToggleCollapseProps = PropsWithChildren & {
label: string; label: string;
isOpen: boolean; activeLabel?: string;
onToggle: () => void; defaultIsOpen?: boolean;
withSwitch?: boolean;
}; };
const IAICollapse = (props: IAIToggleCollapseProps) => { const IAICollapse = (props: IAIToggleCollapseProps) => {
const { label, isOpen, onToggle, children, withSwitch = false } = props; const { label, activeLabel, children, defaultIsOpen = false } = props;
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen });
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
return ( return (
<Box> <Box>
<Flex <Flex
@ -28,6 +31,7 @@ const IAICollapse = (props: IAIToggleCollapseProps) => {
alignItems: 'center', alignItems: 'center',
p: 2, p: 2,
px: 4, px: 4,
gap: 2,
borderTopRadius: 'base', borderTopRadius: 'base',
borderBottomRadius: isOpen ? 0 : 'base', borderBottomRadius: isOpen ? 0 : 'base',
bg: isOpen bg: isOpen
@ -48,19 +52,40 @@ const IAICollapse = (props: IAIToggleCollapseProps) => {
}} }}
> >
{label} {label}
<AnimatePresence>
{activeLabel && (
<motion.div
key="statusText"
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
transition: { duration: 0.1 },
}}
exit={{
opacity: 0,
transition: { duration: 0.1 },
}}
>
<Text
sx={{ color: 'accent.500', _dark: { color: 'accent.300' } }}
>
{activeLabel}
</Text>
</motion.div>
)}
</AnimatePresence>
<Spacer /> <Spacer />
{withSwitch && <Switch isChecked={isOpen} pointerEvents="none" />} <ChevronUpIcon
{!withSwitch && ( sx={{
<ChevronUpIcon w: '1rem',
sx={{ h: '1rem',
w: '1rem', transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)',
h: '1rem', transitionProperty: 'common',
transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)', transitionDuration: 'normal',
transitionProperty: 'common', }}
transitionDuration: 'normal', />
}}
/>
)}
</Flex> </Flex>
<Collapse in={isOpen} animateOpacity style={{ overflow: 'unset' }}> <Collapse in={isOpen} animateOpacity style={{ overflow: 'unset' }}>
<Box <Box

View File

@ -36,7 +36,6 @@ const IAISwitch = (props: Props) => {
isDisabled={isDisabled} isDisabled={isDisabled}
width={width} width={width}
display="flex" display="flex"
gap={4}
alignItems="center" alignItems="center"
{...formControlProps} {...formControlProps}
> >
@ -47,6 +46,7 @@ const IAISwitch = (props: Props) => {
sx={{ sx={{
cursor: isDisabled ? 'not-allowed' : 'pointer', cursor: isDisabled ? 'not-allowed' : 'pointer',
...formLabelProps?.sx, ...formLabelProps?.sx,
pe: 4,
}} }}
{...formLabelProps} {...formLabelProps}
> >

View File

@ -0,0 +1,36 @@
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch';
import { isControlNetEnabledToggled } from 'features/controlNet/store/controlNetSlice';
import { useCallback } from 'react';
const selector = createSelector(
stateSelector,
(state) => {
const { isEnabled } = state.controlNet;
return { isEnabled };
},
defaultSelectorOptions
);
const ParamControlNetFeatureToggle = () => {
const { isEnabled } = useAppSelector(selector);
const dispatch = useAppDispatch();
const handleChange = useCallback(() => {
dispatch(isControlNetEnabledToggled());
}, [dispatch]);
return (
<IAISwitch
label="Enable ControlNet"
isChecked={isEnabled}
onChange={handleChange}
/>
);
};
export default ParamControlNetFeatureToggle;

View File

@ -0,0 +1,15 @@
import { filter } from 'lodash-es';
import { ControlNetConfig } from '../store/controlNetSlice';
export const getValidControlNets = (
controlNets: Record<string, ControlNetConfig>
) => {
const validControlNets = filter(
controlNets,
(c) =>
c.isEnabled &&
(Boolean(c.processedControlImage) ||
(c.processorType === 'none' && Boolean(c.controlImage)))
);
return validControlNets;
};

View File

@ -1,40 +1,30 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { useCallback } from 'react';
import { isEnabledToggled } from '../store/slice';
import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts';
import ParamDynamicPromptsCombinatorial from './ParamDynamicPromptsCombinatorial'; import ParamDynamicPromptsCombinatorial from './ParamDynamicPromptsCombinatorial';
import { Flex } from '@chakra-ui/react'; import ParamDynamicPromptsToggle from './ParamDynamicPromptsEnabled';
import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,
(state) => { (state) => {
const { isEnabled } = state.dynamicPrompts; const { isEnabled } = state.dynamicPrompts;
return { isEnabled }; return { activeLabel: isEnabled ? 'Enabled' : undefined };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ParamDynamicPromptsCollapse = () => { const ParamDynamicPromptsCollapse = () => {
const dispatch = useAppDispatch(); const { activeLabel } = useAppSelector(selector);
const { isEnabled } = useAppSelector(selector);
const handleToggleIsEnabled = useCallback(() => {
dispatch(isEnabledToggled());
}, [dispatch]);
return ( return (
<IAICollapse <IAICollapse label="Dynamic Prompts" activeLabel={activeLabel}>
isOpen={isEnabled}
onToggle={handleToggleIsEnabled}
label="Dynamic Prompts"
withSwitch
>
<Flex sx={{ gap: 2, flexDir: 'column' }}> <Flex sx={{ gap: 2, flexDir: 'column' }}>
<ParamDynamicPromptsToggle />
<ParamDynamicPromptsCombinatorial /> <ParamDynamicPromptsCombinatorial />
<ParamDynamicPromptsMaxPrompts /> <ParamDynamicPromptsMaxPrompts />
</Flex> </Flex>

View File

@ -1,23 +1,23 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { combinatorialToggled } from '../store/slice';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useCallback } from 'react';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { useCallback } from 'react';
import { combinatorialToggled } from '../store/slice';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,
(state) => { (state) => {
const { combinatorial } = state.dynamicPrompts; const { combinatorial, isEnabled } = state.dynamicPrompts;
return { combinatorial }; return { combinatorial, isDisabled: !isEnabled };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ParamDynamicPromptsCombinatorial = () => { const ParamDynamicPromptsCombinatorial = () => {
const { combinatorial } = useAppSelector(selector); const { combinatorial, isDisabled } = useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleChange = useCallback(() => { const handleChange = useCallback(() => {
@ -26,6 +26,7 @@ const ParamDynamicPromptsCombinatorial = () => {
return ( return (
<IAISwitch <IAISwitch
isDisabled={isDisabled}
label="Combinatorial Generation" label="Combinatorial Generation"
isChecked={combinatorial} isChecked={combinatorial}
onChange={handleChange} onChange={handleChange}

View File

@ -0,0 +1,36 @@
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch';
import { useCallback } from 'react';
import { isEnabledToggled } from '../store/slice';
const selector = createSelector(
stateSelector,
(state) => {
const { isEnabled } = state.dynamicPrompts;
return { isEnabled };
},
defaultSelectorOptions
);
const ParamDynamicPromptsToggle = () => {
const dispatch = useAppDispatch();
const { isEnabled } = useAppSelector(selector);
const handleToggleIsEnabled = useCallback(() => {
dispatch(isEnabledToggled());
}, [dispatch]);
return (
<IAISwitch
label="Enable Dynamic Prompts"
isChecked={isEnabled}
onChange={handleToggleIsEnabled}
/>
);
};
export default ParamDynamicPromptsToggle;

View File

@ -1,25 +1,31 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { maxPromptsChanged, maxPromptsReset } from '../store/slice';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useCallback } from 'react';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider';
import { useCallback } from 'react';
import { maxPromptsChanged, maxPromptsReset } from '../store/slice';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,
(state) => { (state) => {
const { maxPrompts, combinatorial } = state.dynamicPrompts; const { maxPrompts, combinatorial, isEnabled } = state.dynamicPrompts;
const { min, sliderMax, inputMax } = const { min, sliderMax, inputMax } =
state.config.sd.dynamicPrompts.maxPrompts; state.config.sd.dynamicPrompts.maxPrompts;
return { maxPrompts, min, sliderMax, inputMax, combinatorial }; return {
maxPrompts,
min,
sliderMax,
inputMax,
isDisabled: !isEnabled || !combinatorial,
};
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ParamDynamicPromptsMaxPrompts = () => { const ParamDynamicPromptsMaxPrompts = () => {
const { maxPrompts, min, sliderMax, inputMax, combinatorial } = const { maxPrompts, min, sliderMax, inputMax, isDisabled } =
useAppSelector(selector); useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -37,7 +43,7 @@ const ParamDynamicPromptsMaxPrompts = () => {
return ( return (
<IAISlider <IAISlider
label="Max Prompts" label="Max Prompts"
isDisabled={!combinatorial} isDisabled={isDisabled}
min={min} min={min}
max={sliderMax} max={sliderMax}
value={maxPrompts} value={maxPrompts}

View File

@ -1,14 +1,30 @@
import { Flex, useDisclosure } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { size } from 'lodash-es';
import { memo } from 'react'; import { memo } from 'react';
import ParamLoraList from './ParamLoraList'; import ParamLoraList from './ParamLoraList';
import ParamLoraSelect from './ParamLoraSelect'; import ParamLoraSelect from './ParamLoraSelect';
const selector = createSelector(
stateSelector,
(state) => {
const loraCount = size(state.lora.loras);
return {
activeLabel: loraCount > 0 ? `${loraCount} Active` : undefined,
};
},
defaultSelectorOptions
);
const ParamLoraCollapse = () => { const ParamLoraCollapse = () => {
const { isOpen, onToggle } = useDisclosure(); const { activeLabel } = useAppSelector(selector);
return ( return (
<IAICollapse label="LoRAs" isOpen={isOpen} onToggle={onToggle}> <IAICollapse label={'LoRA'} activeLabel={activeLabel}>
<Flex sx={{ flexDir: 'column', gap: 2 }}> <Flex sx={{ flexDir: 'column', gap: 2 }}>
<ParamLoraSelect /> <ParamLoraSelect />
<ParamLoraList /> <ParamLoraList />

View File

@ -1,5 +1,5 @@
import { RootState } from 'app/store/store'; import { RootState } from 'app/store/store';
import { filter } from 'lodash-es'; import { getValidControlNets } from 'features/controlNet/util/getValidControlNets';
import { CollectInvocation, ControlNetInvocation } from 'services/api/types'; import { CollectInvocation, ControlNetInvocation } from 'services/api/types';
import { NonNullableGraph } from '../types/types'; import { NonNullableGraph } from '../types/types';
import { CONTROL_NET_COLLECT } from './graphBuilders/constants'; import { CONTROL_NET_COLLECT } from './graphBuilders/constants';
@ -11,13 +11,7 @@ export const addControlNetToLinearGraph = (
): void => { ): void => {
const { isEnabled: isControlNetEnabled, controlNets } = state.controlNet; const { isEnabled: isControlNetEnabled, controlNets } = state.controlNet;
const validControlNets = filter( const validControlNets = getValidControlNets(controlNets);
controlNets,
(c) =>
c.isEnabled &&
(Boolean(c.processedControlImage) ||
(c.processorType === 'none' && Boolean(c.controlImage)))
);
if (isControlNetEnabled && Boolean(validControlNets.length)) { if (isControlNetEnabled && Boolean(validControlNets.length)) {
if (validControlNets.length > 1) { if (validControlNets.length > 1) {

View File

@ -1,20 +1,15 @@
import { Flex, useDisclosure } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react'; import { memo } from 'react';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth'; import { useTranslation } from 'react-i18next';
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight'; import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
const ParamBoundingBoxCollapse = () => { const ParamBoundingBoxCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onToggle } = useDisclosure();
return ( return (
<IAICollapse <IAICollapse label={t('parameters.boundingBoxHeader')}>
label={t('parameters.boundingBoxHeader')}
isOpen={isOpen}
onToggle={onToggle}
>
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamBoundingBoxWidth /> <ParamBoundingBoxWidth />
<ParamBoundingBoxHeight /> <ParamBoundingBoxHeight />

View File

@ -1,4 +1,4 @@
import { Flex, useDisclosure } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -6,19 +6,14 @@ import IAICollapse from 'common/components/IAICollapse';
import ParamInfillMethod from './ParamInfillMethod'; import ParamInfillMethod from './ParamInfillMethod';
import ParamInfillTilesize from './ParamInfillTilesize'; import ParamInfillTilesize from './ParamInfillTilesize';
import ParamScaleBeforeProcessing from './ParamScaleBeforeProcessing'; import ParamScaleBeforeProcessing from './ParamScaleBeforeProcessing';
import ParamScaledWidth from './ParamScaledWidth';
import ParamScaledHeight from './ParamScaledHeight'; import ParamScaledHeight from './ParamScaledHeight';
import ParamScaledWidth from './ParamScaledWidth';
const ParamInfillCollapse = () => { const ParamInfillCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onToggle } = useDisclosure();
return ( return (
<IAICollapse <IAICollapse label={t('parameters.infillScalingHeader')}>
label={t('parameters.infillScalingHeader')}
isOpen={isOpen}
onToggle={onToggle}
>
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamInfillMethod /> <ParamInfillMethod />
<ParamInfillTilesize /> <ParamInfillTilesize />

View File

@ -1,22 +1,16 @@
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamSeamBlur from './ParamSeamBlur'; import ParamSeamBlur from './ParamSeamBlur';
import ParamSeamSize from './ParamSeamSize'; import ParamSeamSize from './ParamSeamSize';
import ParamSeamSteps from './ParamSeamSteps'; import ParamSeamSteps from './ParamSeamSteps';
import ParamSeamStrength from './ParamSeamStrength'; import ParamSeamStrength from './ParamSeamStrength';
import { useDisclosure } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
const ParamSeamCorrectionCollapse = () => { const ParamSeamCorrectionCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onToggle } = useDisclosure();
return ( return (
<IAICollapse <IAICollapse label={t('parameters.seamCorrectionHeader')}>
label={t('parameters.seamCorrectionHeader')}
isOpen={isOpen}
onToggle={onToggle}
>
<ParamSeamSize /> <ParamSeamSize />
<ParamSeamBlur /> <ParamSeamBlur />
<ParamSeamStrength /> <ParamSeamStrength />

View File

@ -1,41 +1,45 @@
import { Divider, Flex } from '@chakra-ui/react'; import { Divider, Flex } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import IAICollapse from 'common/components/IAICollapse';
import { Fragment, memo, useCallback } from 'react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import IAICollapse from 'common/components/IAICollapse';
import ControlNet from 'features/controlNet/components/ControlNet';
import ParamControlNetFeatureToggle from 'features/controlNet/components/parameters/ParamControlNetFeatureToggle';
import { import {
controlNetAdded, controlNetAdded,
controlNetSelector, controlNetSelector,
isControlNetEnabledToggled,
} from 'features/controlNet/store/controlNetSlice'; } from 'features/controlNet/store/controlNetSlice';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { getValidControlNets } from 'features/controlNet/util/getValidControlNets';
import { map } from 'lodash-es';
import { v4 as uuidv4 } from 'uuid';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import IAIButton from 'common/components/IAIButton'; import { map } from 'lodash-es';
import ControlNet from 'features/controlNet/components/ControlNet'; import { Fragment, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { v4 as uuidv4 } from 'uuid';
const selector = createSelector( const selector = createSelector(
controlNetSelector, controlNetSelector,
(controlNet) => { (controlNet) => {
const { controlNets, isEnabled } = controlNet; const { controlNets, isEnabled } = controlNet;
return { controlNetsArray: map(controlNets), isEnabled }; const validControlNets = getValidControlNets(controlNets);
const activeLabel =
isEnabled && validControlNets.length > 0
? `${validControlNets.length} Active`
: undefined;
return { controlNetsArray: map(controlNets), activeLabel };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ParamControlNetCollapse = () => { const ParamControlNetCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { controlNetsArray, isEnabled } = useAppSelector(selector); const { controlNetsArray, activeLabel } = useAppSelector(selector);
const isControlNetDisabled = useFeatureStatus('controlNet').isFeatureDisabled; const isControlNetDisabled = useFeatureStatus('controlNet').isFeatureDisabled;
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleClickControlNetToggle = useCallback(() => {
dispatch(isControlNetEnabledToggled());
}, [dispatch]);
const handleClickedAddControlNet = useCallback(() => { const handleClickedAddControlNet = useCallback(() => {
dispatch(controlNetAdded({ controlNetId: uuidv4() })); dispatch(controlNetAdded({ controlNetId: uuidv4() }));
}, [dispatch]); }, [dispatch]);
@ -45,13 +49,9 @@ const ParamControlNetCollapse = () => {
} }
return ( return (
<IAICollapse <IAICollapse label="ControlNet" activeLabel={activeLabel}>
label={'ControlNet'}
isOpen={isEnabled}
onToggle={handleClickControlNetToggle}
withSwitch
>
<Flex sx={{ flexDir: 'column', gap: 3 }}> <Flex sx={{ flexDir: 'column', gap: 3 }}>
<ParamControlNetFeatureToggle />
{controlNetsArray.map((c, i) => ( {controlNetsArray.map((c, i) => (
<Fragment key={c.controlNetId}> <Fragment key={c.controlNetId}>
{i > 0 && <Divider />} {i > 0 && <Divider />}

View File

@ -1,37 +1,39 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { stateSelector } from 'app/store/store';
import { RootState } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { ParamHiresStrength } from './ParamHiresStrength';
import { setHiresFix } from 'features/parameters/store/postprocessingSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ParamHiresStrength } from './ParamHiresStrength';
import { ParamHiresToggle } from './ParamHiresToggle';
const selector = createSelector(
stateSelector,
(state) => {
const activeLabel = state.postprocessing.hiresFix ? 'Enabled' : undefined;
return { activeLabel };
},
defaultSelectorOptions
);
const ParamHiresCollapse = () => { const ParamHiresCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const hiresFix = useAppSelector( const { activeLabel } = useAppSelector(selector);
(state: RootState) => state.postprocessing.hiresFix
);
const isHiresEnabled = useFeatureStatus('hires').isFeatureEnabled; const isHiresEnabled = useFeatureStatus('hires').isFeatureEnabled;
const dispatch = useAppDispatch();
const handleToggle = () => dispatch(setHiresFix(!hiresFix));
if (!isHiresEnabled) { if (!isHiresEnabled) {
return null; return null;
} }
return ( return (
<IAICollapse <IAICollapse label={t('parameters.hiresOptim')} activeLabel={activeLabel}>
label={t('parameters.hiresOptim')}
isOpen={hiresFix}
onToggle={handleToggle}
withSwitch
>
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamHiresToggle />
<ParamHiresStrength /> <ParamHiresStrength />
</Flex> </Flex>
</IAICollapse> </IAICollapse>

View File

@ -23,7 +23,6 @@ export const ParamHiresToggle = () => {
return ( return (
<IAISwitch <IAISwitch
label={t('parameters.hiresOptim')} label={t('parameters.hiresOptim')}
fontSize="md"
isChecked={hiresFix} isChecked={hiresFix}
onChange={handleChangeHiresFix} onChange={handleChangeHiresFix}
/> />

View File

@ -1,27 +1,33 @@
import { useTranslation } from 'react-i18next';
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import ParamPerlinNoise from './ParamPerlinNoise';
import ParamNoiseThreshold from './ParamNoiseThreshold';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setShouldUseNoiseSettings } from 'features/parameters/store/generationSlice';
import { memo } from 'react';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamNoiseThreshold from './ParamNoiseThreshold';
import { ParamNoiseToggle } from './ParamNoiseToggle';
import ParamPerlinNoise from './ParamPerlinNoise';
const selector = createSelector(
stateSelector,
(state) => {
const { shouldUseNoiseSettings } = state.generation;
return {
activeLabel: shouldUseNoiseSettings ? 'Enabled' : undefined,
};
},
defaultSelectorOptions
);
const ParamNoiseCollapse = () => { const ParamNoiseCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const isNoiseEnabled = useFeatureStatus('noise').isFeatureEnabled; const isNoiseEnabled = useFeatureStatus('noise').isFeatureEnabled;
const shouldUseNoiseSettings = useAppSelector( const { activeLabel } = useAppSelector(selector);
(state: RootState) => state.generation.shouldUseNoiseSettings
);
const dispatch = useAppDispatch();
const handleToggle = () =>
dispatch(setShouldUseNoiseSettings(!shouldUseNoiseSettings));
if (!isNoiseEnabled) { if (!isNoiseEnabled) {
return null; return null;
@ -30,11 +36,10 @@ const ParamNoiseCollapse = () => {
return ( return (
<IAICollapse <IAICollapse
label={t('parameters.noiseSettings')} label={t('parameters.noiseSettings')}
isOpen={shouldUseNoiseSettings} activeLabel={activeLabel}
onToggle={handleToggle}
withSwitch
> >
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamNoiseToggle />
<ParamPerlinNoise /> <ParamPerlinNoise />
<ParamNoiseThreshold /> <ParamNoiseThreshold />
</Flex> </Flex>

View File

@ -1,18 +1,31 @@
import { RootState } from 'app/store/store'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setThreshold } from 'features/parameters/store/generationSlice'; import { setThreshold } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createSelector(
stateSelector,
(state) => {
const { shouldUseNoiseSettings, threshold } = state.generation;
return {
isDisabled: !shouldUseNoiseSettings,
threshold,
};
},
defaultSelectorOptions
);
export default function ParamNoiseThreshold() { export default function ParamNoiseThreshold() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const threshold = useAppSelector( const { threshold, isDisabled } = useAppSelector(selector);
(state: RootState) => state.generation.threshold
);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<IAISlider <IAISlider
isDisabled={isDisabled}
label={t('parameters.noiseThreshold')} label={t('parameters.noiseThreshold')}
min={0} min={0}
max={20} max={20}

View File

@ -0,0 +1,27 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISwitch from 'common/components/IAISwitch';
import { setShouldUseNoiseSettings } from 'features/parameters/store/generationSlice';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export const ParamNoiseToggle = () => {
const dispatch = useAppDispatch();
const shouldUseNoiseSettings = useAppSelector(
(state: RootState) => state.generation.shouldUseNoiseSettings
);
const { t } = useTranslation();
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldUseNoiseSettings(e.target.checked));
return (
<IAISwitch
label="Enable Noise Settings"
isChecked={shouldUseNoiseSettings}
onChange={handleChange}
/>
);
};

View File

@ -1,16 +1,31 @@
import { RootState } from 'app/store/store'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setPerlin } from 'features/parameters/store/generationSlice'; import { setPerlin } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createSelector(
stateSelector,
(state) => {
const { shouldUseNoiseSettings, perlin } = state.generation;
return {
isDisabled: !shouldUseNoiseSettings,
perlin,
};
},
defaultSelectorOptions
);
export default function ParamPerlinNoise() { export default function ParamPerlinNoise() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const perlin = useAppSelector((state: RootState) => state.generation.perlin); const { perlin, isDisabled } = useAppSelector(selector);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<IAISlider <IAISlider
isDisabled={isDisabled}
label={t('parameters.perlinNoise')} label={t('parameters.perlinNoise')}
min={0} min={0}
max={1} max={1}

View File

@ -1,36 +1,46 @@
import { useTranslation } from 'react-i18next';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import IAICollapse from 'common/components/IAICollapse';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSeamless } from 'features/parameters/store/generationSlice';
import { memo } from 'react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { generationSelector } from 'features/parameters/store/generationSelectors'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamSeamlessXAxis from './ParamSeamlessXAxis'; import ParamSeamlessXAxis from './ParamSeamlessXAxis';
import ParamSeamlessYAxis from './ParamSeamlessYAxis'; import ParamSeamlessYAxis from './ParamSeamlessYAxis';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
const getActiveLabel = (seamlessXAxis: boolean, seamlessYAxis: boolean) => {
if (seamlessXAxis && seamlessYAxis) {
return 'X & Y';
}
if (seamlessXAxis) {
return 'X';
}
if (seamlessYAxis) {
return 'Y';
}
};
const selector = createSelector( const selector = createSelector(
generationSelector, generationSelector,
(generation) => { (generation) => {
const { shouldUseSeamless, seamlessXAxis, seamlessYAxis } = generation; const { seamlessXAxis, seamlessYAxis } = generation;
return { shouldUseSeamless, seamlessXAxis, seamlessYAxis }; const activeLabel = getActiveLabel(seamlessXAxis, seamlessYAxis);
return { activeLabel };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ParamSeamlessCollapse = () => { const ParamSeamlessCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { shouldUseSeamless } = useAppSelector(selector); const { activeLabel } = useAppSelector(selector);
const isSeamlessEnabled = useFeatureStatus('seamless').isFeatureEnabled; const isSeamlessEnabled = useFeatureStatus('seamless').isFeatureEnabled;
const dispatch = useAppDispatch();
const handleToggle = () => dispatch(setSeamless(!shouldUseSeamless));
if (!isSeamlessEnabled) { if (!isSeamlessEnabled) {
return null; return null;
} }
@ -38,9 +48,7 @@ const ParamSeamlessCollapse = () => {
return ( return (
<IAICollapse <IAICollapse
label={t('parameters.seamlessTiling')} label={t('parameters.seamlessTiling')}
isOpen={shouldUseSeamless} activeLabel={activeLabel}
onToggle={handleToggle}
withSwitch
> >
<Flex sx={{ gap: 5 }}> <Flex sx={{ gap: 5 }}>
<Box flexGrow={1}> <Box flexGrow={1}>

View File

@ -1,39 +1,39 @@
import { memo } from 'react';
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { memo } from 'react';
import ParamSymmetryHorizontal from './ParamSymmetryHorizontal'; import ParamSymmetryHorizontal from './ParamSymmetryHorizontal';
import ParamSymmetryVertical from './ParamSymmetryVertical'; import ParamSymmetryVertical from './ParamSymmetryVertical';
import { useTranslation } from 'react-i18next'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setShouldUseSymmetry } from 'features/parameters/store/generationSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useTranslation } from 'react-i18next';
import ParamSymmetryToggle from './ParamSymmetryToggle';
const selector = createSelector(
stateSelector,
(state) => ({
activeLabel: state.generation.shouldUseSymmetry ? 'Enabled' : undefined,
}),
defaultSelectorOptions
);
const ParamSymmetryCollapse = () => { const ParamSymmetryCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const shouldUseSymmetry = useAppSelector( const { activeLabel } = useAppSelector(selector);
(state: RootState) => state.generation.shouldUseSymmetry
);
const isSymmetryEnabled = useFeatureStatus('symmetry').isFeatureEnabled; const isSymmetryEnabled = useFeatureStatus('symmetry').isFeatureEnabled;
const dispatch = useAppDispatch();
const handleToggle = () => dispatch(setShouldUseSymmetry(!shouldUseSymmetry));
if (!isSymmetryEnabled) { if (!isSymmetryEnabled) {
return null; return null;
} }
return ( return (
<IAICollapse <IAICollapse label={t('parameters.symmetry')} activeLabel={activeLabel}>
label={t('parameters.symmetry')}
isOpen={shouldUseSymmetry}
onToggle={handleToggle}
withSwitch
>
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamSymmetryToggle />
<ParamSymmetryHorizontal /> <ParamSymmetryHorizontal />
<ParamSymmetryVertical /> <ParamSymmetryVertical />
</Flex> </Flex>

View File

@ -12,6 +12,7 @@ export default function ParamSymmetryToggle() {
return ( return (
<IAISwitch <IAISwitch
label="Enable Symmetry"
isChecked={shouldUseSymmetry} isChecked={shouldUseSymmetry}
onChange={(e) => dispatch(setShouldUseSymmetry(e.target.checked))} onChange={(e) => dispatch(setShouldUseSymmetry(e.target.checked))}
/> />

View File

@ -1,39 +1,42 @@
import ParamVariationWeights from './ParamVariationWeights';
import ParamVariationAmount from './ParamVariationAmount';
import { useTranslation } from 'react-i18next';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { RootState } from 'app/store/store';
import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice';
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamVariationAmount from './ParamVariationAmount';
import { ParamVariationToggle } from './ParamVariationToggle';
import ParamVariationWeights from './ParamVariationWeights';
const selector = createSelector(
stateSelector,
(state) => {
const activeLabel = state.generation.shouldGenerateVariations
? 'Enabled'
: undefined;
return { activeLabel };
},
defaultSelectorOptions
);
const ParamVariationCollapse = () => { const ParamVariationCollapse = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const shouldGenerateVariations = useAppSelector( const { activeLabel } = useAppSelector(selector);
(state: RootState) => state.generation.shouldGenerateVariations
);
const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled; const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled;
const dispatch = useAppDispatch();
const handleToggle = () =>
dispatch(setShouldGenerateVariations(!shouldGenerateVariations));
if (!isVariationEnabled) { if (!isVariationEnabled) {
return null; return null;
} }
return ( return (
<IAICollapse <IAICollapse label={t('parameters.variations')} activeLabel={activeLabel}>
label={t('parameters.variations')}
isOpen={shouldGenerateVariations}
onToggle={handleToggle}
withSwitch
>
<Flex sx={{ gap: 2, flexDirection: 'column' }}> <Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamVariationToggle />
<ParamVariationAmount /> <ParamVariationAmount />
<ParamVariationWeights /> <ParamVariationWeights />
</Flex> </Flex>

View File

@ -0,0 +1,27 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISwitch from 'common/components/IAISwitch';
import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export const ParamVariationToggle = () => {
const dispatch = useAppDispatch();
const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.generation.shouldGenerateVariations
);
const { t } = useTranslation();
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldGenerateVariations(e.target.checked));
return (
<IAISwitch
label="Enable Variations"
isChecked={shouldGenerateVariations}
onChange={handleChange}
/>
);
};

View File

@ -49,7 +49,6 @@ export interface GenerationState {
verticalSymmetrySteps: number; verticalSymmetrySteps: number;
model: ModelParam; model: ModelParam;
vae: VAEParam; vae: VAEParam;
shouldUseSeamless: boolean;
seamlessXAxis: boolean; seamlessXAxis: boolean;
seamlessYAxis: boolean; seamlessYAxis: boolean;
} }
@ -84,9 +83,8 @@ export const initialGenerationState: GenerationState = {
verticalSymmetrySteps: 0, verticalSymmetrySteps: 0,
model: '', model: '',
vae: '', vae: '',
shouldUseSeamless: false, seamlessXAxis: false,
seamlessXAxis: true, seamlessYAxis: false,
seamlessYAxis: true,
}; };
const initialState: GenerationState = initialGenerationState; const initialState: GenerationState = initialGenerationState;
@ -144,9 +142,6 @@ export const generationSlice = createSlice({
setImg2imgStrength: (state, action: PayloadAction<number>) => { setImg2imgStrength: (state, action: PayloadAction<number>) => {
state.img2imgStrength = action.payload; state.img2imgStrength = action.payload;
}, },
setSeamless: (state, action: PayloadAction<boolean>) => {
state.shouldUseSeamless = action.payload;
},
setSeamlessXAxis: (state, action: PayloadAction<boolean>) => { setSeamlessXAxis: (state, action: PayloadAction<boolean>) => {
state.seamlessXAxis = action.payload; state.seamlessXAxis = action.payload;
}, },
@ -268,7 +263,6 @@ export const {
modelSelected, modelSelected,
vaeSelected, vaeSelected,
setShouldUseNoiseSettings, setShouldUseNoiseSettings,
setSeamless,
setSeamlessXAxis, setSeamlessXAxis,
setSeamlessYAxis, setSeamlessYAxis,
} = generationSlice.actions; } = generationSlice.actions;

View File

@ -1,4 +1,4 @@
import { Box, Flex, useDisclosure } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
@ -21,19 +21,25 @@ const selector = createSelector(
[uiSelector, generationSelector], [uiSelector, generationSelector],
(ui, generation) => { (ui, generation) => {
const { shouldUseSliders } = ui; const { shouldUseSliders } = ui;
const { shouldFitToWidthHeight } = generation; const { shouldFitToWidthHeight, shouldRandomizeSeed } = generation;
return { shouldUseSliders, shouldFitToWidthHeight }; const activeLabel = !shouldRandomizeSeed ? 'Manual Seed' : undefined;
return { shouldUseSliders, shouldFitToWidthHeight, activeLabel };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const ImageToImageTabCoreParameters = () => { const ImageToImageTabCoreParameters = () => {
const { shouldUseSliders, shouldFitToWidthHeight } = useAppSelector(selector); const { shouldUseSliders, shouldFitToWidthHeight, activeLabel } =
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true }); useAppSelector(selector);
return ( return (
<IAICollapse label={'General'} isOpen={isOpen} onToggle={onToggle}> <IAICollapse
label={'General'}
activeLabel={activeLabel}
defaultIsOpen={true}
>
<Flex <Flex
sx={{ sx={{
flexDirection: 'column', flexDirection: 'column',

View File

@ -1,5 +1,6 @@
import { Box, Flex, useDisclosure } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
@ -11,25 +12,30 @@ import ParamScheduler from 'features/parameters/components/Parameters/Core/Param
import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps'; import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps';
import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth'; import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth';
import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull'; import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react'; import { memo } from 'react';
const selector = createSelector( const selector = createSelector(
uiSelector, stateSelector,
(ui) => { ({ ui, generation }) => {
const { shouldUseSliders } = ui; const { shouldUseSliders } = ui;
const { shouldRandomizeSeed } = generation;
return { shouldUseSliders }; const activeLabel = !shouldRandomizeSeed ? 'Manual Seed' : undefined;
return { shouldUseSliders, activeLabel };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const TextToImageTabCoreParameters = () => { const TextToImageTabCoreParameters = () => {
const { shouldUseSliders } = useAppSelector(selector); const { shouldUseSliders, activeLabel } = useAppSelector(selector);
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
return ( return (
<IAICollapse label={'General'} isOpen={isOpen} onToggle={onToggle}> <IAICollapse
label={'General'}
activeLabel={activeLabel}
defaultIsOpen={true}
>
<Flex <Flex
sx={{ sx={{
flexDirection: 'column', flexDirection: 'column',

View File

@ -1,5 +1,6 @@
import { Box, Flex, useDisclosure } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
@ -12,25 +13,30 @@ import ParamScheduler from 'features/parameters/components/Parameters/Core/Param
import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps'; import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps';
import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength'; import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength';
import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull'; import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react'; import { memo } from 'react';
const selector = createSelector( const selector = createSelector(
uiSelector, stateSelector,
(ui) => { ({ ui, generation }) => {
const { shouldUseSliders } = ui; const { shouldUseSliders } = ui;
const { shouldRandomizeSeed } = generation;
return { shouldUseSliders }; const activeLabel = !shouldRandomizeSeed ? 'Manual Seed' : undefined;
return { shouldUseSliders, activeLabel };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const UnifiedCanvasCoreParameters = () => { const UnifiedCanvasCoreParameters = () => {
const { shouldUseSliders } = useAppSelector(selector); const { shouldUseSliders, activeLabel } = useAppSelector(selector);
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
return ( return (
<IAICollapse label={'General'} isOpen={isOpen} onToggle={onToggle}> <IAICollapse
label={'General'}
activeLabel={activeLabel}
defaultIsOpen={true}
>
<Flex <Flex
sx={{ sx={{
flexDirection: 'column', flexDirection: 'column',