mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
tidy(ui): organize rp layer components
This commit is contained in:
committed by
Kent Keirsey
parent
642a0de3dd
commit
eb781272f7
@@ -1,47 +0,0 @@
|
|||||||
import { Flex, Spacer } from '@invoke-ai/ui-library';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
|
||||||
import LayerAutoNegativeCombobox from 'features/regionalPrompts/components/LayerAutoNegativeCombobox';
|
|
||||||
import { LayerColorPicker } from 'features/regionalPrompts/components/LayerColorPicker';
|
|
||||||
import { LayerMenu } from 'features/regionalPrompts/components/LayerMenu';
|
|
||||||
import { LayerVisibilityToggle } from 'features/regionalPrompts/components/LayerVisibilityToggle';
|
|
||||||
import { RegionalPromptsNegativePrompt } from 'features/regionalPrompts/components/RegionalPromptsNegativePrompt';
|
|
||||||
import { RegionalPromptsPositivePrompt } from 'features/regionalPrompts/components/RegionalPromptsPositivePrompt';
|
|
||||||
import { isRegionalPromptLayer, rpLayerSelected } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LayerListItem = memo(({ id }: Props) => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const selectedLayer = useAppSelector((s) => s.regionalPrompts.present.selectedLayer);
|
|
||||||
const color = useAppSelector((s) => {
|
|
||||||
const layer = s.regionalPrompts.present.layers.find((l) => l.id === id);
|
|
||||||
assert(isRegionalPromptLayer(layer), `Layer ${id} not found or not an RP layer`);
|
|
||||||
return rgbaColorToString({ ...layer.color, a: selectedLayer === id ? 1 : 0.35 });
|
|
||||||
});
|
|
||||||
const onClickCapture = useCallback(() => {
|
|
||||||
// Must be capture so that the layer is selected before deleting/resetting/etc
|
|
||||||
dispatch(rpLayerSelected(id));
|
|
||||||
}, [dispatch, id]);
|
|
||||||
return (
|
|
||||||
<Flex gap={2} onClickCapture={onClickCapture} bg={color} borderRadius="base" p="1px" ps={3}>
|
|
||||||
<Flex flexDir="column" gap={2} w="full" bg="base.850" borderRadius="base" p={2}>
|
|
||||||
<Flex gap={2} alignItems="center">
|
|
||||||
<LayerColorPicker id={id} />
|
|
||||||
<LayerVisibilityToggle id={id} />
|
|
||||||
<Spacer />
|
|
||||||
<LayerAutoNegativeCombobox layerId={id} />
|
|
||||||
<LayerMenu id={id} />
|
|
||||||
</Flex>
|
|
||||||
<RegionalPromptsPositivePrompt layerId={id} />
|
|
||||||
<RegionalPromptsNegativePrompt layerId={id} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
LayerListItem.displayName = 'LayerListItem';
|
|
||||||
@@ -35,7 +35,7 @@ const useAutoNegative = (layerId: string) => {
|
|||||||
return autoNegative;
|
return autoNegative;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AutoNegativeCombobox = ({ layerId }: Props) => {
|
export const RPLayerAutoNegativeCombobox = memo(({ layerId }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const autoNegative = useAutoNegative(layerId);
|
const autoNegative = useAutoNegative(layerId);
|
||||||
@@ -58,6 +58,6 @@ const AutoNegativeCombobox = ({ layerId }: Props) => {
|
|||||||
<Combobox value={value} options={options} onChange={onChange} isSearchable={false} sx={{ w: '5.2rem' }} />
|
<Combobox value={value} options={options} onChange={onChange} isSearchable={false} sx={{ w: '5.2rem' }} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default memo(AutoNegativeCombobox);
|
RPLayerAutoNegativeCombobox.displayName = 'RPLayerAutoNegativeCombobox';
|
||||||
@@ -13,26 +13,26 @@ import { PiEyedropperBold } from 'react-icons/pi';
|
|||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
layerId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayerColorPicker = memo(({ id }: Props) => {
|
export const RPLayerColorPicker = memo(({ layerId }: Props) => {
|
||||||
const selectColor = useMemo(
|
const selectColor = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
|
createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
|
||||||
const layer = regionalPrompts.present.layers.find((l) => l.id === id);
|
const layer = regionalPrompts.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalPromptLayer(layer), `Layer ${id} not found or not an RP layer`);
|
assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
return layer.color;
|
return layer.color;
|
||||||
}),
|
}),
|
||||||
[id]
|
[layerId]
|
||||||
);
|
);
|
||||||
const color = useAppSelector(selectColor);
|
const color = useAppSelector(selectColor);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onColorChange = useCallback(
|
const onColorChange = useCallback(
|
||||||
(color: RgbColor) => {
|
(color: RgbColor) => {
|
||||||
dispatch(rpLayerColorChanged({ layerId: id, color }));
|
dispatch(rpLayerColorChanged({ layerId, color }));
|
||||||
},
|
},
|
||||||
[dispatch, id]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Popover isLazy>
|
<Popover isLazy>
|
||||||
@@ -48,4 +48,4 @@ export const LayerColorPicker = memo(({ id }: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
LayerColorPicker.displayName = 'LayerColorPicker';
|
RPLayerColorPicker.displayName = 'RPLayerColorPicker';
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Flex, Spacer } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
|
import { RPLayerAutoNegativeCombobox } from 'features/regionalPrompts/components/RPLayerAutoNegativeCombobox';
|
||||||
|
import { RPLayerColorPicker } from 'features/regionalPrompts/components/RPLayerColorPicker';
|
||||||
|
import { RPLayerMenu } from 'features/regionalPrompts/components/RPLayerMenu';
|
||||||
|
import { RPLayerNegativePrompt } from 'features/regionalPrompts/components/RPLayerNegativePrompt';
|
||||||
|
import { RPLayerPositivePrompt } from 'features/regionalPrompts/components/RPLayerPositivePrompt';
|
||||||
|
import { RPLayerVisibilityToggle } from 'features/regionalPrompts/components/RPLayerVisibilityToggle';
|
||||||
|
import { isRegionalPromptLayer, rpLayerSelected } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RPLayerListItem = memo(({ layerId }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const selectedLayer = useAppSelector((s) => s.regionalPrompts.present.selectedLayer);
|
||||||
|
const color = useAppSelector((s) => {
|
||||||
|
const layer = s.regionalPrompts.present.layers.find((l) => l.id === layerId);
|
||||||
|
assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
|
return rgbaColorToString({ ...layer.color, a: selectedLayer === layerId ? 1 : 0.35 });
|
||||||
|
});
|
||||||
|
const onClickCapture = useCallback(() => {
|
||||||
|
// Must be capture so that the layer is selected before deleting/resetting/etc
|
||||||
|
dispatch(rpLayerSelected(layerId));
|
||||||
|
}, [dispatch, layerId]);
|
||||||
|
return (
|
||||||
|
<Flex gap={2} onClickCapture={onClickCapture} bg={color} borderRadius="base" p="1px" ps={3}>
|
||||||
|
<Flex flexDir="column" gap={2} w="full" bg="base.850" borderRadius="base" p={2}>
|
||||||
|
<Flex gap={2} alignItems="center">
|
||||||
|
<RPLayerColorPicker layerId={layerId} />
|
||||||
|
<RPLayerVisibilityToggle layerId={layerId} />
|
||||||
|
<Spacer />
|
||||||
|
<RPLayerAutoNegativeCombobox layerId={layerId} />
|
||||||
|
<RPLayerMenu layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
<RPLayerPositivePrompt layerId={layerId} />
|
||||||
|
<RPLayerNegativePrompt layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RPLayerListItem.displayName = 'RPLayerListItem';
|
||||||
@@ -2,6 +2,7 @@ import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
|
isRegionalPromptLayer,
|
||||||
layerDeleted,
|
layerDeleted,
|
||||||
layerMovedBackward,
|
layerMovedBackward,
|
||||||
layerMovedForward,
|
layerMovedForward,
|
||||||
@@ -21,16 +22,19 @@ import {
|
|||||||
PiDotsThreeVerticalBold,
|
PiDotsThreeVerticalBold,
|
||||||
PiTrashSimpleBold,
|
PiTrashSimpleBold,
|
||||||
} from 'react-icons/pi';
|
} from 'react-icons/pi';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
type Props = { id: string };
|
type Props = { layerId: string };
|
||||||
|
|
||||||
export const LayerMenu = memo(({ id }: Props) => {
|
export const RPLayerMenu = memo(({ layerId }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
|
createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
|
||||||
const layerIndex = regionalPrompts.present.layers.findIndex((l) => l.id === id);
|
const layer = regionalPrompts.present.layers.find((l) => l.id === layerId);
|
||||||
|
assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
|
const layerIndex = regionalPrompts.present.layers.findIndex((l) => l.id === layerId);
|
||||||
const layerCount = regionalPrompts.present.layers.length;
|
const layerCount = regionalPrompts.present.layers.length;
|
||||||
return {
|
return {
|
||||||
canMoveForward: layerIndex < layerCount - 1,
|
canMoveForward: layerIndex < layerCount - 1,
|
||||||
@@ -39,27 +43,27 @@ export const LayerMenu = memo(({ id }: Props) => {
|
|||||||
canMoveToBack: layerIndex > 0,
|
canMoveToBack: layerIndex > 0,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
[id]
|
[layerId]
|
||||||
);
|
);
|
||||||
const validActions = useAppSelector(selectValidActions);
|
const validActions = useAppSelector(selectValidActions);
|
||||||
const moveForward = useCallback(() => {
|
const moveForward = useCallback(() => {
|
||||||
dispatch(layerMovedForward(id));
|
dispatch(layerMovedForward(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
const moveToFront = useCallback(() => {
|
const moveToFront = useCallback(() => {
|
||||||
dispatch(layerMovedToFront(id));
|
dispatch(layerMovedToFront(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
const moveBackward = useCallback(() => {
|
const moveBackward = useCallback(() => {
|
||||||
dispatch(layerMovedBackward(id));
|
dispatch(layerMovedBackward(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
const moveToBack = useCallback(() => {
|
const moveToBack = useCallback(() => {
|
||||||
dispatch(layerMovedToBack(id));
|
dispatch(layerMovedToBack(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
const resetLayer = useCallback(() => {
|
const resetLayer = useCallback(() => {
|
||||||
dispatch(rpLayerReset(id));
|
dispatch(rpLayerReset(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
const deleteLayer = useCallback(() => {
|
const deleteLayer = useCallback(() => {
|
||||||
dispatch(layerDeleted(id));
|
dispatch(layerDeleted(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton as={IconButton} aria-label="Layer menu" size="sm" icon={<PiDotsThreeVerticalBold />} />
|
<MenuButton as={IconButton} aria-label="Layer menu" size="sm" icon={<PiDotsThreeVerticalBold />} />
|
||||||
@@ -88,4 +92,4 @@ export const LayerMenu = memo(({ id }: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
LayerMenu.displayName = 'LayerMenu';
|
RPLayerMenu.displayName = 'RPLayerMenu';
|
||||||
@@ -15,7 +15,7 @@ type Props = {
|
|||||||
layerId: string;
|
layerId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RegionalPromptsNegativePrompt = memo((props: Props) => {
|
export const RPLayerNegativePrompt = memo((props: Props) => {
|
||||||
const prompt = useLayerNegativePrompt(props.layerId);
|
const prompt = useLayerNegativePrompt(props.layerId);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@@ -64,4 +64,4 @@ export const RegionalPromptsNegativePrompt = memo((props: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
RegionalPromptsNegativePrompt.displayName = 'RegionalPromptsPrompt';
|
RPLayerNegativePrompt.displayName = 'RPLayerNegativePrompt';
|
||||||
@@ -15,7 +15,7 @@ type Props = {
|
|||||||
layerId: string;
|
layerId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RegionalPromptsPositivePrompt = memo((props: Props) => {
|
export const RPLayerPositivePrompt = memo((props: Props) => {
|
||||||
const prompt = useLayerPositivePrompt(props.layerId);
|
const prompt = useLayerPositivePrompt(props.layerId);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@@ -64,4 +64,4 @@ export const RegionalPromptsPositivePrompt = memo((props: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
RegionalPromptsPositivePrompt.displayName = 'RegionalPromptsPrompt';
|
RPLayerPositivePrompt.displayName = 'RPLayerPositivePrompt';
|
||||||
@@ -6,15 +6,15 @@ import { memo, useCallback } from 'react';
|
|||||||
import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi';
|
import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
layerId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayerVisibilityToggle = memo(({ id }: Props) => {
|
export const RPLayerVisibilityToggle = memo(({ layerId }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isVisible = useLayerIsVisible(id);
|
const isVisible = useLayerIsVisible(layerId);
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
dispatch(rpLayerIsVisibleToggled(id));
|
dispatch(rpLayerIsVisibleToggled(layerId));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, layerId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -27,4 +27,4 @@ export const LayerVisibilityToggle = memo(({ id }: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
LayerVisibilityToggle.displayName = 'LayerVisibilityToggle';
|
RPLayerVisibilityToggle.displayName = 'RPLayerVisibilityToggle';
|
||||||
@@ -5,17 +5,20 @@ import { useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
|
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
|
||||||
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
|
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
|
||||||
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
|
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
|
||||||
import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem';
|
|
||||||
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
|
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
|
||||||
|
import { RPLayerListItem } from 'features/regionalPrompts/components/RPLayerListItem';
|
||||||
import { StageComponent } from 'features/regionalPrompts/components/StageComponent';
|
import { StageComponent } from 'features/regionalPrompts/components/StageComponent';
|
||||||
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
|
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
|
||||||
import { UndoRedoButtonGroup } from 'features/regionalPrompts/components/UndoRedoButtonGroup';
|
import { UndoRedoButtonGroup } from 'features/regionalPrompts/components/UndoRedoButtonGroup';
|
||||||
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
import { isRegionalPromptLayer, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
|
import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
const selectLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
const selectRPLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
||||||
regionalPrompts.present.layers.map((l) => l.id).reverse()
|
regionalPrompts.present.layers
|
||||||
|
.filter(isRegionalPromptLayer)
|
||||||
|
.map((l) => l.id)
|
||||||
|
.reverse()
|
||||||
);
|
);
|
||||||
|
|
||||||
const debugBlobs = () => {
|
const debugBlobs = () => {
|
||||||
@@ -23,7 +26,7 @@ const debugBlobs = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RegionalPromptsEditor = memo(() => {
|
export const RegionalPromptsEditor = memo(() => {
|
||||||
const layerIdsReversed = useAppSelector(selectLayerIdsReversed);
|
const rpLayerIdsReversed = useAppSelector(selectRPLayerIdsReversed);
|
||||||
return (
|
return (
|
||||||
<Flex gap={4} w="full" h="full">
|
<Flex gap={4} w="full" h="full">
|
||||||
<Flex flexDir="column" gap={4} flexShrink={0}>
|
<Flex flexDir="column" gap={4} flexShrink={0}>
|
||||||
@@ -38,8 +41,8 @@ export const RegionalPromptsEditor = memo(() => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<BrushSize />
|
<BrushSize />
|
||||||
<PromptLayerOpacity />
|
<PromptLayerOpacity />
|
||||||
{layerIdsReversed.map((id) => (
|
{rpLayerIdsReversed.map((id) => (
|
||||||
<LayerListItem key={id} id={id} />
|
<RPLayerListItem key={id} layerId={id} />
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
<StageComponent />
|
<StageComponent />
|
||||||
|
|||||||
Reference in New Issue
Block a user