Fix Slider not being able to take typed input

This commit is contained in:
blessedcoolant 2023-01-24 16:43:29 +13:00
parent 884768c39d
commit 5344481809
11 changed files with 187 additions and 138 deletions

View File

@ -23,7 +23,7 @@ import {
Tooltip,
TooltipProps,
} from '@chakra-ui/react';
import React, { FocusEvent, useEffect, useMemo, useState } from 'react';
import React, { FocusEvent, useMemo, useState, useEffect } from 'react';
import { BiReset } from 'react-icons/bi';
import IAIIconButton, { IAIIconButtonProps } from './IAIIconButton';
import _ from 'lodash';
@ -81,7 +81,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
withInput = false,
isInteger = false,
inputWidth = '5.5rem',
inputReadOnly = true,
inputReadOnly = false,
withReset = false,
hideTooltip = false,
isCompact = false,
@ -103,32 +103,35 @@ export default function IAISlider(props: IAIFullSliderProps) {
...rest
} = props;
const [localInputValue, setLocalInputValue] = useState<string>(String(value));
const [localInputValue, setLocalInputValue] = useState<
string | number | undefined
>(String(value));
useEffect(() => {
setLocalInputValue(value);
}, [value]);
const numberInputMax = useMemo(
() => (sliderNumberInputProps?.max ? sliderNumberInputProps.max : max),
[max, sliderNumberInputProps?.max]
);
useEffect(() => {
if (String(value) !== localInputValue && localInputValue !== '') {
setLocalInputValue(String(value));
}
}, [value, localInputValue, setLocalInputValue]);
const handleSliderChange = (v: number) => {
onChange(v);
};
const handleInputBlur = (e: FocusEvent<HTMLInputElement>) => {
if (e.target.value === '') e.target.value = String(min);
const clamped = _.clamp(
isInteger ? Math.floor(Number(e.target.value)) : Number(e.target.value),
isInteger ? Math.floor(Number(e.target.value)) : Number(localInputValue),
min,
numberInputMax
);
setLocalInputValue(String(clamped));
onChange(clamped);
};
const handleInputChange = (v: number | string) => {
setLocalInputValue(String(v));
onChange(Number(v));
setLocalInputValue(v);
};
const handleResetDisable = () => {
@ -172,7 +175,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
min={min}
max={max}
step={step}
onChange={handleInputChange}
onChange={handleSliderChange}
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
focusThumbOnChange={false}
@ -236,13 +239,19 @@ export default function IAISlider(props: IAIFullSliderProps) {
<NumberInputField
className="invokeai__slider-number-input"
width={inputWidth}
minWidth={inputWidth}
readOnly={inputReadOnly}
minWidth={inputWidth}
{...sliderNumberInputFieldProps}
/>
<NumberInputStepper {...sliderNumberInputStepperProps}>
<NumberIncrementStepper className="invokeai__slider-number-stepper" />
<NumberDecrementStepper className="invokeai__slider-number-stepper" />
<NumberIncrementStepper
onClick={() => onChange(Number(localInputValue))}
className="invokeai__slider-number-stepper"
/>
<NumberDecrementStepper
onClick={() => onChange(Number(localInputValue))}
className="invokeai__slider-number-stepper"
/>
</NumberInputStepper>
</NumberInput>
)}

View File

@ -75,11 +75,12 @@ const BoundingBoxSettings = () => {
step={64}
value={boundingBoxDimensions.width}
onChange={handleChangeWidth}
handleReset={handleResetWidth}
sliderNumberInputProps={{ max: 4096 }}
withSliderMarks
withInput
inputReadOnly
withReset
handleReset={handleResetWidth}
/>
<IAISlider
label={t('options:height')}
@ -88,11 +89,12 @@ const BoundingBoxSettings = () => {
step={64}
value={boundingBoxDimensions.height}
onChange={handleChangeHeight}
handleReset={handleResetHeight}
sliderNumberInputProps={{ max: 4096 }}
withSliderMarks
withInput
inputReadOnly
withReset
handleReset={handleResetHeight}
/>
</Flex>
);

View File

@ -124,11 +124,12 @@ const InfillAndScalingOptions = () => {
step={64}
value={scaledBoundingBoxDimensions.width}
onChange={handleChangeScaledWidth}
handleReset={handleResetScaledWidth}
sliderNumberInputProps={{ max: 4096 }}
withSliderMarks
withInput
inputReadOnly
withReset
handleReset={handleResetScaledWidth}
/>
<IAISlider
isInputDisabled={!isManual}
@ -140,11 +141,12 @@ const InfillAndScalingOptions = () => {
step={64}
value={scaledBoundingBoxDimensions.height}
onChange={handleChangeScaledHeight}
handleReset={handleResetScaledHeight}
sliderNumberInputProps={{ max: 4096 }}
withSliderMarks
withInput
inputReadOnly
withReset
handleReset={handleResetScaledHeight}
/>
<InpaintReplace />
<IAISelect
@ -166,12 +168,12 @@ const InfillAndScalingOptions = () => {
onChange={(v) => {
dispatch(setTileSize(v));
}}
handleReset={() => {
dispatch(setTileSize(32));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setTileSize(32));
}}
/>
</Flex>
);

View File

@ -1,113 +0,0 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { optionsSelector } from 'features/options/store/optionsSelectors';
import {
setSeamBlur,
setSeamSize,
setSeamSteps,
setSeamStrength,
} from 'features/options/store/optionsSlice';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[optionsSelector],
(options) => {
const { seamSize, seamBlur, seamStrength, seamSteps } = options;
return {
seamSize,
seamBlur,
seamStrength,
seamSteps,
};
},
{
memoizeOptions: {
resultEqualityCheck: _.isEqual,
},
}
);
const SeamCorrectionOptions = () => {
const dispatch = useAppDispatch();
const { seamSize, seamBlur, seamStrength, seamSteps } =
useAppSelector(selector);
const { t } = useTranslation();
return (
<Flex direction="column" gap="1rem">
<IAISlider
sliderMarkRightOffset={-6}
label={t('options:seamSize')}
min={1}
max={256}
sliderNumberInputProps={{ max: 512 }}
value={seamSize}
onChange={(v) => {
dispatch(setSeamSize(v));
}}
handleReset={() => dispatch(setSeamSize(96))}
withInput
withSliderMarks
withReset
/>
<IAISlider
sliderMarkRightOffset={-4}
label={t('options:seamBlur')}
min={0}
max={64}
sliderNumberInputProps={{ max: 512 }}
value={seamBlur}
onChange={(v) => {
dispatch(setSeamBlur(v));
}}
handleReset={() => {
dispatch(setSeamBlur(16));
}}
withInput
withSliderMarks
withReset
/>
<IAISlider
sliderMarkRightOffset={-7}
label={t('options:seamStrength')}
min={0.01}
max={0.99}
step={0.01}
value={seamStrength}
onChange={(v) => {
dispatch(setSeamStrength(v));
}}
handleReset={() => {
dispatch(setSeamStrength(0.7));
}}
withInput
withSliderMarks
withReset
/>
<IAISlider
sliderMarkRightOffset={-4}
label={t('options:seamSteps')}
min={1}
max={32}
sliderNumberInputProps={{ max: 100 }}
value={seamSteps}
onChange={(v) => {
dispatch(setSeamSteps(v));
}}
handleReset={() => {
dispatch(setSeamSteps(10));
}}
withInput
withSliderMarks
withReset
/>
</Flex>
);
};
export default SeamCorrectionOptions;

View File

@ -0,0 +1,32 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamBlur } from 'features/options/store/optionsSlice';
import React from 'react';
import { useTranslation } from 'react-i18next';
export default function SeamBlur() {
const dispatch = useAppDispatch();
const seamBlur = useAppSelector((state: RootState) => state.options.seamBlur);
const { t } = useTranslation();
return (
<IAISlider
sliderMarkRightOffset={-4}
label={t('options:seamBlur')}
min={0}
max={64}
sliderNumberInputProps={{ max: 512 }}
value={seamBlur}
onChange={(v) => {
dispatch(setSeamBlur(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamBlur(16));
}}
/>
);
}

View File

@ -0,0 +1,18 @@
import { Flex } from '@chakra-ui/react';
import SeamBlur from './SeamBlur';
import SeamSize from './SeamSize';
import SeamSteps from './SeamSteps';
import SeamStrength from './SeamStrength';
const SeamCorrectionOptions = () => {
return (
<Flex direction="column" gap="1rem">
<SeamSize />
<SeamBlur />
<SeamStrength />
<SeamSteps />
</Flex>
);
};
export default SeamCorrectionOptions;

View File

@ -0,0 +1,31 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamSize } from 'features/options/store/optionsSlice';
import React from 'react';
import { useTranslation } from 'react-i18next';
export default function SeamSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seamSize = useAppSelector((state: RootState) => state.options.seamSize);
return (
<IAISlider
sliderMarkRightOffset={-6}
label={t('options:seamSize')}
min={1}
max={256}
sliderNumberInputProps={{ max: 512 }}
value={seamSize}
onChange={(v) => {
dispatch(setSeamSize(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => dispatch(setSeamSize(96))}
/>
);
}

View File

@ -0,0 +1,34 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamSteps } from 'features/options/store/optionsSlice';
import React from 'react';
import { useTranslation } from 'react-i18next';
export default function SeamSteps() {
const { t } = useTranslation();
const seamSteps = useAppSelector(
(state: RootState) => state.options.seamSteps
);
const dispatch = useAppDispatch();
return (
<IAISlider
sliderMarkRightOffset={-4}
label={t('options:seamSteps')}
min={1}
max={32}
sliderNumberInputProps={{ max: 999 }}
value={seamSteps}
onChange={(v) => {
dispatch(setSeamSteps(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamSteps(10));
}}
/>
);
}

View File

@ -0,0 +1,34 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamStrength } from 'features/options/store/optionsSlice';
import React from 'react';
import { useTranslation } from 'react-i18next';
export default function SeamStrength() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seamStrength = useAppSelector(
(state: RootState) => state.options.seamStrength
);
return (
<IAISlider
sliderMarkRightOffset={-7}
label={t('options:seamStrength')}
min={0.01}
max={0.99}
step={0.01}
value={seamStrength}
onChange={(v) => {
dispatch(setSeamStrength(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamStrength(0.7));
}}
/>
);
}

View File

@ -36,9 +36,9 @@ export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
isInteger={false}
styleClass={styleClass}
withInput
withReset
withSliderMarks
inputWidth={'5.5rem'}
withReset
handleReset={handleImg2ImgStrengthReset}
/>
);

View File

@ -1,7 +1,7 @@
// import { Feature } from 'app/features';
import { Feature } from 'app/features';
import ImageToImageStrength from 'features/options/components/AdvancedOptions/ImageToImage/ImageToImageStrength';
import SeamCorrectionOptions from 'features/options/components/AdvancedOptions/Canvas/SeamCorrectionOptions';
import SeamCorrectionOptions from 'features/options/components/AdvancedOptions/Canvas/SeamCorrectionOptions/SeamCorrectionOptions';
import SeedOptions from 'features/options/components/AdvancedOptions/Seed/SeedOptions';
import GenerateVariationsToggle from 'features/options/components/AdvancedOptions/Variations/GenerateVariations';
import VariationsOptions from 'features/options/components/AdvancedOptions/Variations/VariationsOptions';