mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds IAIAlertDialog component
This commit is contained in:
parent
089c85a017
commit
b0810e1ed7
86
frontend/src/common/components/IAIAlertDialog.tsx
Normal file
86
frontend/src/common/components/IAIAlertDialog.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
forwardRef,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { cloneElement, ReactElement, ReactNode, useRef } from 'react';
|
||||
|
||||
type Props = {
|
||||
acceptButtonText?: string;
|
||||
acceptCallback: () => void;
|
||||
cancelButtonText?: string;
|
||||
cancelCallback?: () => void;
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
triggerComponent: ReactElement;
|
||||
};
|
||||
|
||||
const IAIAlertDialog = forwardRef((props: Props, ref) => {
|
||||
const {
|
||||
acceptButtonText = 'Accept',
|
||||
acceptCallback,
|
||||
cancelButtonText = 'Cancel',
|
||||
cancelCallback,
|
||||
children,
|
||||
title,
|
||||
triggerComponent,
|
||||
} = props;
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const cancelRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleAccept = () => {
|
||||
acceptCallback();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
cancelCallback && cancelCallback();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{cloneElement(triggerComponent, {
|
||||
onClick: onOpen,
|
||||
ref: ref,
|
||||
})}
|
||||
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={onClose}
|
||||
>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent className="modal">
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{title}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>{children}</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
ref={cancelRef}
|
||||
onClick={handleCancel}
|
||||
className="modal-close-btn"
|
||||
>
|
||||
{cancelButtonText}
|
||||
</Button>
|
||||
<Button colorScheme="red" onClick={handleAccept} ml={3}>
|
||||
{acceptButtonText}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
</>
|
||||
);
|
||||
});
|
||||
export default IAIAlertDialog;
|
@ -33,29 +33,28 @@ const IAISelect = (props: IAISelectProps) => {
|
||||
...rest
|
||||
} = props;
|
||||
return (
|
||||
<Tooltip label={tooltip} {...tooltipProps}>
|
||||
<FormControl
|
||||
isDisabled={isDisabled}
|
||||
className={`invokeai__select ${styleClass}`}
|
||||
onClick={(e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
e.nativeEvent.stopPropagation();
|
||||
e.nativeEvent.cancelBubble = true;
|
||||
}}
|
||||
>
|
||||
{label && (
|
||||
<FormLabel
|
||||
className="invokeai__select-label"
|
||||
fontSize={fontSize}
|
||||
marginBottom={1}
|
||||
flexGrow={2}
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{label}
|
||||
</FormLabel>
|
||||
)}
|
||||
|
||||
<FormControl
|
||||
isDisabled={isDisabled}
|
||||
className={`invokeai__select ${styleClass}`}
|
||||
onClick={(e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
e.nativeEvent.stopPropagation();
|
||||
e.nativeEvent.cancelBubble = true;
|
||||
}}
|
||||
>
|
||||
{label && (
|
||||
<FormLabel
|
||||
className="invokeai__select-label"
|
||||
fontSize={fontSize}
|
||||
marginBottom={1}
|
||||
flexGrow={2}
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{label}
|
||||
</FormLabel>
|
||||
)}
|
||||
<Tooltip label={tooltip} {...tooltipProps}>
|
||||
<Select
|
||||
className="invokeai__select-picker"
|
||||
fontSize={fontSize}
|
||||
@ -78,8 +77,8 @@ const IAISelect = (props: IAISelectProps) => {
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { clearCanvasHistory } from 'features/canvas/store/canvasSlice';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
const ClearCanvasHistoryButtonModal = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAIAlertDialog
|
||||
title={'Clear Canvas History'}
|
||||
acceptCallback={() => dispatch(clearCanvasHistory())}
|
||||
acceptButtonText={'Clear History'}
|
||||
triggerComponent={
|
||||
<IAIButton size={'sm'} leftIcon={<FaTrash />}>
|
||||
Clear Canvas History
|
||||
</IAIButton>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Clearing the canvas history leaves your current canvas intact, but
|
||||
irreversibly clears the undo and redo history.
|
||||
</p>
|
||||
<br />
|
||||
<p>Are you sure you want to clear the canvas history?</p>
|
||||
</IAIAlertDialog>
|
||||
);
|
||||
};
|
||||
export default ClearCanvasHistoryButtonModal;
|
@ -99,58 +99,41 @@ const IAICanvasMaskOptions = () => {
|
||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||
|
||||
return (
|
||||
<>
|
||||
<IAISelect
|
||||
label={'Layer (Q)'}
|
||||
tooltipProps={{ hasArrow: true, placement: 'top' }}
|
||||
value={layer}
|
||||
validValues={LAYER_NAMES_DICT}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
|
||||
dispatch(setLayer(e.target.value as CanvasLayer))
|
||||
}
|
||||
/>
|
||||
<IAIPopover
|
||||
isOpen={layer !== 'mask' ? false : undefined}
|
||||
trigger="hover"
|
||||
triggerComponent={
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label="Masking Options"
|
||||
tooltip="Masking Options"
|
||||
icon={<FaMask />}
|
||||
isDisabled={layer !== 'mask'}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
}
|
||||
>
|
||||
<Flex direction={'column'} gap={'0.5rem'}>
|
||||
<IAICheckbox
|
||||
label="Enable Mask (H)"
|
||||
isChecked={isMaskEnabled}
|
||||
onChange={handleToggleEnableMask}
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
triggerComponent={
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label="Masking Options"
|
||||
tooltip="Masking Options"
|
||||
icon={<FaMask />}
|
||||
/>
|
||||
<IAICheckbox
|
||||
label="Preserve Masked Area"
|
||||
isChecked={shouldPreserveMaskedArea}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldPreserveMaskedArea(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAIColorPicker
|
||||
style={{ paddingTop: '0.5rem', paddingBottom: '0.5rem' }}
|
||||
color={maskColor}
|
||||
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
||||
/>
|
||||
<IAIButton
|
||||
size={'sm'}
|
||||
leftIcon={<FaTrash />}
|
||||
onClick={handleClearMask}
|
||||
>
|
||||
Clear Mask (Shift+C)
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
</>
|
||||
</ButtonGroup>
|
||||
}
|
||||
>
|
||||
<Flex direction={'column'} gap={'0.5rem'}>
|
||||
<IAICheckbox
|
||||
label="Enable Mask (H)"
|
||||
isChecked={isMaskEnabled}
|
||||
onChange={handleToggleEnableMask}
|
||||
/>
|
||||
<IAICheckbox
|
||||
label="Preserve Masked Area"
|
||||
isChecked={shouldPreserveMaskedArea}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldPreserveMaskedArea(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAIColorPicker
|
||||
style={{ paddingTop: '0.5rem', paddingBottom: '0.5rem' }}
|
||||
color={maskColor}
|
||||
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
||||
/>
|
||||
<IAIButton size={'sm'} leftIcon={<FaTrash />} onClick={handleClearMask}>
|
||||
Clear Mask (Shift+C)
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,8 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import ClearTempFolderButtonModal from 'features/system/components/ClearTempFolderButtonModal';
|
||||
import EmptyTempFolderButtonModal from 'features/system/components/ClearTempFolderButtonModal';
|
||||
import ClearCanvasHistoryButtonModal from '../ClearCanvasHistoryButtonModal';
|
||||
|
||||
export const canvasControlsSelector = createSelector(
|
||||
[canvasSelector],
|
||||
@ -117,14 +118,8 @@ const IAICanvasSettingsButtonPopover = () => {
|
||||
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAIButton
|
||||
size={'sm'}
|
||||
leftIcon={<FaTrash />}
|
||||
onClick={() => dispatch(clearCanvasHistory())}
|
||||
>
|
||||
Clear Canvas History
|
||||
</IAIButton>
|
||||
<ClearTempFolderButtonModal />
|
||||
<ClearCanvasHistoryButtonModal />
|
||||
<EmptyTempFolderButtonModal />
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
|
@ -4,6 +4,8 @@ import {
|
||||
resetCanvas,
|
||||
resetCanvasView,
|
||||
resizeAndScaleCanvas,
|
||||
setIsMaskEnabled,
|
||||
setLayer,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
@ -33,17 +35,26 @@ import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import {
|
||||
CanvasLayer,
|
||||
LAYER_NAMES_DICT,
|
||||
} from 'features/canvas/store/canvasTypes';
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
export const selector = createSelector(
|
||||
[systemSelector, canvasSelector, isStagingSelector],
|
||||
(system, canvas, isStaging) => {
|
||||
const { isProcessing } = system;
|
||||
const { tool, shouldCropToBoundingBoxOnSave } = canvas;
|
||||
const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } =
|
||||
canvas;
|
||||
|
||||
return {
|
||||
isProcessing,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
tool,
|
||||
layer,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
};
|
||||
},
|
||||
@ -56,8 +67,14 @@ export const selector = createSelector(
|
||||
|
||||
const IAICanvasOutpaintingControls = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isProcessing, isStaging, tool, shouldCropToBoundingBoxOnSave } =
|
||||
useAppSelector(selector);
|
||||
const {
|
||||
isProcessing,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
layer,
|
||||
tool,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
} = useAppSelector(selector);
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const { openUploader } = useImageUploader();
|
||||
@ -193,8 +210,24 @@ const IAICanvasOutpaintingControls = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeLayer = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const newLayer = e.target.value as CanvasLayer;
|
||||
dispatch(setLayer(newLayer));
|
||||
if (newLayer === 'mask' && !isMaskEnabled) {
|
||||
dispatch(setIsMaskEnabled(true));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="inpainting-settings">
|
||||
<IAISelect
|
||||
tooltip={'Layer (Q)'}
|
||||
tooltipProps={{ hasArrow: true, placement: 'top' }}
|
||||
value={layer}
|
||||
validValues={LAYER_NAMES_DICT}
|
||||
onChange={handleChangeLayer}
|
||||
/>
|
||||
|
||||
<IAICanvasMaskOptions />
|
||||
<IAICanvasToolChooserOptions />
|
||||
|
||||
|
@ -1,79 +1,41 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
Flex,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { emptyTempFolder } from 'app/socketio/actions';
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import {
|
||||
clearCanvasHistory,
|
||||
resetCanvas,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useRef } from 'react';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
const ClearTempFolderButtonModal = () => {
|
||||
const EmptyTempFolderButtonModal = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const cancelRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleClear = () => {
|
||||
const acceptCallback = () => {
|
||||
dispatch(emptyTempFolder());
|
||||
dispatch(clearCanvasHistory());
|
||||
dispatch(resetCanvas());
|
||||
onClose();
|
||||
dispatch(clearCanvasHistory());
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IAIButton leftIcon={<FaTrash />} size={'sm'} onClick={onOpen}>
|
||||
Clear Temp Image Folder
|
||||
</IAIButton>
|
||||
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={onClose}
|
||||
>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent className="modal">
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
Clear Temp Image Folder
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>
|
||||
<p>
|
||||
Clearing the temp image folder also fully resets the Unified
|
||||
Canvas. This includes all undo/redo history, images in the
|
||||
staging area, and the canvas base layer.
|
||||
</p>
|
||||
<br />
|
||||
<p>Are you sure you want to clear the temp folder?</p>
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
ref={cancelRef}
|
||||
onClick={onClose}
|
||||
className="modal-close-btn"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button colorScheme="red" onClick={handleClear} ml={3}>
|
||||
Clear
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
</>
|
||||
<IAIAlertDialog
|
||||
title={'Empty Temp Image Folder'}
|
||||
acceptCallback={acceptCallback}
|
||||
acceptButtonText={'Empty Folder'}
|
||||
triggerComponent={
|
||||
<IAIButton leftIcon={<FaTrash />} size={'sm'}>
|
||||
Empty Temp Image Folder
|
||||
</IAIButton>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Emptying the temp image folder also fully resets the Unified Canvas.
|
||||
This includes all undo/redo history, images in the staging area, and the
|
||||
canvas base layer.
|
||||
</p>
|
||||
<br />
|
||||
<p>Are you sure you want to empty the temp folder?</p>
|
||||
</IAIAlertDialog>
|
||||
);
|
||||
};
|
||||
export default ClearTempFolderButtonModal;
|
||||
export default EmptyTempFolderButtonModal;
|
||||
|
@ -31,7 +31,7 @@ import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import ClearTempFolderButtonModal from '../ClearTempFolderButtonModal';
|
||||
import EmptyTempFolderButtonModal from '../ClearTempFolderButtonModal';
|
||||
|
||||
const systemSelector = createSelector(
|
||||
(state: RootState) => state.system,
|
||||
|
Loading…
x
Reference in New Issue
Block a user