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
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip} {...tooltipProps}>
|
<FormControl
|
||||||
<FormControl
|
isDisabled={isDisabled}
|
||||||
isDisabled={isDisabled}
|
className={`invokeai__select ${styleClass}`}
|
||||||
className={`invokeai__select ${styleClass}`}
|
onClick={(e: MouseEvent<HTMLDivElement>) => {
|
||||||
onClick={(e: MouseEvent<HTMLDivElement>) => {
|
e.stopPropagation();
|
||||||
e.stopPropagation();
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
e.nativeEvent.stopPropagation();
|
||||||
e.nativeEvent.stopPropagation();
|
e.nativeEvent.cancelBubble = true;
|
||||||
e.nativeEvent.cancelBubble = true;
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{label && (
|
||||||
{label && (
|
<FormLabel
|
||||||
<FormLabel
|
className="invokeai__select-label"
|
||||||
className="invokeai__select-label"
|
fontSize={fontSize}
|
||||||
fontSize={fontSize}
|
marginBottom={1}
|
||||||
marginBottom={1}
|
flexGrow={2}
|
||||||
flexGrow={2}
|
whiteSpace="nowrap"
|
||||||
whiteSpace="nowrap"
|
>
|
||||||
>
|
{label}
|
||||||
{label}
|
</FormLabel>
|
||||||
</FormLabel>
|
)}
|
||||||
)}
|
<Tooltip label={tooltip} {...tooltipProps}>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
className="invokeai__select-picker"
|
className="invokeai__select-picker"
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
@ -78,8 +77,8 @@ const IAISelect = (props: IAISelectProps) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Select>
|
</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));
|
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<IAIPopover
|
||||||
<IAISelect
|
trigger="hover"
|
||||||
label={'Layer (Q)'}
|
triggerComponent={
|
||||||
tooltipProps={{ hasArrow: true, placement: 'top' }}
|
<ButtonGroup>
|
||||||
value={layer}
|
<IAIIconButton
|
||||||
validValues={LAYER_NAMES_DICT}
|
aria-label="Masking Options"
|
||||||
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
|
tooltip="Masking Options"
|
||||||
dispatch(setLayer(e.target.value as CanvasLayer))
|
icon={<FaMask />}
|
||||||
}
|
|
||||||
/>
|
|
||||||
<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}
|
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
</ButtonGroup>
|
||||||
label="Preserve Masked Area"
|
}
|
||||||
isChecked={shouldPreserveMaskedArea}
|
>
|
||||||
onChange={(e) =>
|
<Flex direction={'column'} gap={'0.5rem'}>
|
||||||
dispatch(setShouldPreserveMaskedArea(e.target.checked))
|
<IAICheckbox
|
||||||
}
|
label="Enable Mask (H)"
|
||||||
/>
|
isChecked={isMaskEnabled}
|
||||||
<IAIColorPicker
|
onChange={handleToggleEnableMask}
|
||||||
style={{ paddingTop: '0.5rem', paddingBottom: '0.5rem' }}
|
/>
|
||||||
color={maskColor}
|
<IAICheckbox
|
||||||
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
label="Preserve Masked Area"
|
||||||
/>
|
isChecked={shouldPreserveMaskedArea}
|
||||||
<IAIButton
|
onChange={(e) =>
|
||||||
size={'sm'}
|
dispatch(setShouldPreserveMaskedArea(e.target.checked))
|
||||||
leftIcon={<FaTrash />}
|
}
|
||||||
onClick={handleClearMask}
|
/>
|
||||||
>
|
<IAIColorPicker
|
||||||
Clear Mask (Shift+C)
|
style={{ paddingTop: '0.5rem', paddingBottom: '0.5rem' }}
|
||||||
</IAIButton>
|
color={maskColor}
|
||||||
</Flex>
|
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
||||||
</IAIPopover>
|
/>
|
||||||
</>
|
<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 IAICheckbox from 'common/components/IAICheckbox';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
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(
|
export const canvasControlsSelector = createSelector(
|
||||||
[canvasSelector],
|
[canvasSelector],
|
||||||
@ -117,14 +118,8 @@ const IAICanvasSettingsButtonPopover = () => {
|
|||||||
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAIButton
|
<ClearCanvasHistoryButtonModal />
|
||||||
size={'sm'}
|
<EmptyTempFolderButtonModal />
|
||||||
leftIcon={<FaTrash />}
|
|
||||||
onClick={() => dispatch(clearCanvasHistory())}
|
|
||||||
>
|
|
||||||
Clear Canvas History
|
|
||||||
</IAIButton>
|
|
||||||
<ClearTempFolderButtonModal />
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</IAIPopover>
|
</IAIPopover>
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,8 @@ import {
|
|||||||
resetCanvas,
|
resetCanvas,
|
||||||
resetCanvasView,
|
resetCanvasView,
|
||||||
resizeAndScaleCanvas,
|
resizeAndScaleCanvas,
|
||||||
|
setIsMaskEnabled,
|
||||||
|
setLayer,
|
||||||
setTool,
|
setTool,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||||
@ -33,17 +35,26 @@ import {
|
|||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
} from 'features/canvas/store/canvasSelectors';
|
} 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(
|
export const selector = createSelector(
|
||||||
[systemSelector, canvasSelector, isStagingSelector],
|
[systemSelector, canvasSelector, isStagingSelector],
|
||||||
(system, canvas, isStaging) => {
|
(system, canvas, isStaging) => {
|
||||||
const { isProcessing } = system;
|
const { isProcessing } = system;
|
||||||
const { tool, shouldCropToBoundingBoxOnSave } = canvas;
|
const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } =
|
||||||
|
canvas;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isProcessing,
|
isProcessing,
|
||||||
isStaging,
|
isStaging,
|
||||||
|
isMaskEnabled,
|
||||||
tool,
|
tool,
|
||||||
|
layer,
|
||||||
shouldCropToBoundingBoxOnSave,
|
shouldCropToBoundingBoxOnSave,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -56,8 +67,14 @@ export const selector = createSelector(
|
|||||||
|
|
||||||
const IAICanvasOutpaintingControls = () => {
|
const IAICanvasOutpaintingControls = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { isProcessing, isStaging, tool, shouldCropToBoundingBoxOnSave } =
|
const {
|
||||||
useAppSelector(selector);
|
isProcessing,
|
||||||
|
isStaging,
|
||||||
|
isMaskEnabled,
|
||||||
|
layer,
|
||||||
|
tool,
|
||||||
|
shouldCropToBoundingBoxOnSave,
|
||||||
|
} = useAppSelector(selector);
|
||||||
const canvasBaseLayer = getCanvasBaseLayer();
|
const canvasBaseLayer = getCanvasBaseLayer();
|
||||||
|
|
||||||
const { openUploader } = useImageUploader();
|
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 (
|
return (
|
||||||
<div className="inpainting-settings">
|
<div className="inpainting-settings">
|
||||||
|
<IAISelect
|
||||||
|
tooltip={'Layer (Q)'}
|
||||||
|
tooltipProps={{ hasArrow: true, placement: 'top' }}
|
||||||
|
value={layer}
|
||||||
|
validValues={LAYER_NAMES_DICT}
|
||||||
|
onChange={handleChangeLayer}
|
||||||
|
/>
|
||||||
|
|
||||||
<IAICanvasMaskOptions />
|
<IAICanvasMaskOptions />
|
||||||
<IAICanvasToolChooserOptions />
|
<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 { emptyTempFolder } from 'app/socketio/actions';
|
||||||
import { useAppDispatch } from 'app/store';
|
import { useAppDispatch } from 'app/store';
|
||||||
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import {
|
import {
|
||||||
clearCanvasHistory,
|
clearCanvasHistory,
|
||||||
resetCanvas,
|
resetCanvas,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { useRef } from 'react';
|
|
||||||
import { FaTrash } from 'react-icons/fa';
|
import { FaTrash } from 'react-icons/fa';
|
||||||
|
|
||||||
const ClearTempFolderButtonModal = () => {
|
const EmptyTempFolderButtonModal = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const cancelRef = useRef<HTMLButtonElement | null>(null);
|
|
||||||
|
|
||||||
const handleClear = () => {
|
const acceptCallback = () => {
|
||||||
dispatch(emptyTempFolder());
|
dispatch(emptyTempFolder());
|
||||||
dispatch(clearCanvasHistory());
|
|
||||||
dispatch(resetCanvas());
|
dispatch(resetCanvas());
|
||||||
onClose();
|
dispatch(clearCanvasHistory());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<IAIAlertDialog
|
||||||
<IAIButton leftIcon={<FaTrash />} size={'sm'} onClick={onOpen}>
|
title={'Empty Temp Image Folder'}
|
||||||
Clear Temp Image Folder
|
acceptCallback={acceptCallback}
|
||||||
</IAIButton>
|
acceptButtonText={'Empty Folder'}
|
||||||
|
triggerComponent={
|
||||||
<AlertDialog
|
<IAIButton leftIcon={<FaTrash />} size={'sm'}>
|
||||||
isOpen={isOpen}
|
Empty Temp Image Folder
|
||||||
leastDestructiveRef={cancelRef}
|
</IAIButton>
|
||||||
onClose={onClose}
|
}
|
||||||
>
|
>
|
||||||
<AlertDialogOverlay>
|
<p>
|
||||||
<AlertDialogContent className="modal">
|
Emptying the temp image folder also fully resets the Unified Canvas.
|
||||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
This includes all undo/redo history, images in the staging area, and the
|
||||||
Clear Temp Image Folder
|
canvas base layer.
|
||||||
</AlertDialogHeader>
|
</p>
|
||||||
|
<br />
|
||||||
<AlertDialogBody>
|
<p>Are you sure you want to empty the temp folder?</p>
|
||||||
<p>
|
</IAIAlertDialog>
|
||||||
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>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
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 IAISwitch from 'common/components/IAISwitch';
|
||||||
import IAISelect from 'common/components/IAISelect';
|
import IAISelect from 'common/components/IAISelect';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
import ClearTempFolderButtonModal from '../ClearTempFolderButtonModal';
|
import EmptyTempFolderButtonModal from '../ClearTempFolderButtonModal';
|
||||||
|
|
||||||
const systemSelector = createSelector(
|
const systemSelector = createSelector(
|
||||||
(state: RootState) => state.system,
|
(state: RootState) => state.system,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user