tidy(ui): organize rp layer components

This commit is contained in:
psychedelicious 2024-04-19 15:42:46 +10:00 committed by Kent Keirsey
parent 642a0de3dd
commit eb781272f7
9 changed files with 99 additions and 92 deletions

View File

@ -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';

View File

@ -35,7 +35,7 @@ const useAutoNegative = (layerId: string) => {
return autoNegative;
};
const AutoNegativeCombobox = ({ layerId }: Props) => {
export const RPLayerAutoNegativeCombobox = memo(({ layerId }: Props) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
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' }} />
</FormControl>
);
};
});
export default memo(AutoNegativeCombobox);
RPLayerAutoNegativeCombobox.displayName = 'RPLayerAutoNegativeCombobox';

View File

@ -13,26 +13,26 @@ import { PiEyedropperBold } from 'react-icons/pi';
import { assert } from 'tsafe';
type Props = {
id: string;
layerId: string;
};
export const LayerColorPicker = memo(({ id }: Props) => {
export const RPLayerColorPicker = memo(({ layerId }: Props) => {
const selectColor = useMemo(
() =>
createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
const layer = regionalPrompts.present.layers.find((l) => l.id === id);
assert(isRegionalPromptLayer(layer), `Layer ${id} not found or not an RP layer`);
const layer = regionalPrompts.present.layers.find((l) => l.id === layerId);
assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`);
return layer.color;
}),
[id]
[layerId]
);
const color = useAppSelector(selectColor);
const dispatch = useAppDispatch();
const onColorChange = useCallback(
(color: RgbColor) => {
dispatch(rpLayerColorChanged({ layerId: id, color }));
dispatch(rpLayerColorChanged({ layerId, color }));
},
[dispatch, id]
[dispatch, layerId]
);
return (
<Popover isLazy>
@ -48,4 +48,4 @@ export const LayerColorPicker = memo(({ id }: Props) => {
);
});
LayerColorPicker.displayName = 'LayerColorPicker';
RPLayerColorPicker.displayName = 'RPLayerColorPicker';

View File

@ -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';

View File

@ -2,6 +2,7 @@ import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
isRegionalPromptLayer,
layerDeleted,
layerMovedBackward,
layerMovedForward,
@ -21,16 +22,19 @@ import {
PiDotsThreeVerticalBold,
PiTrashSimpleBold,
} 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 { t } = useTranslation();
const selectValidActions = useMemo(
() =>
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;
return {
canMoveForward: layerIndex < layerCount - 1,
@ -39,27 +43,27 @@ export const LayerMenu = memo(({ id }: Props) => {
canMoveToBack: layerIndex > 0,
};
}),
[id]
[layerId]
);
const validActions = useAppSelector(selectValidActions);
const moveForward = useCallback(() => {
dispatch(layerMovedForward(id));
}, [dispatch, id]);
dispatch(layerMovedForward(layerId));
}, [dispatch, layerId]);
const moveToFront = useCallback(() => {
dispatch(layerMovedToFront(id));
}, [dispatch, id]);
dispatch(layerMovedToFront(layerId));
}, [dispatch, layerId]);
const moveBackward = useCallback(() => {
dispatch(layerMovedBackward(id));
}, [dispatch, id]);
dispatch(layerMovedBackward(layerId));
}, [dispatch, layerId]);
const moveToBack = useCallback(() => {
dispatch(layerMovedToBack(id));
}, [dispatch, id]);
dispatch(layerMovedToBack(layerId));
}, [dispatch, layerId]);
const resetLayer = useCallback(() => {
dispatch(rpLayerReset(id));
}, [dispatch, id]);
dispatch(rpLayerReset(layerId));
}, [dispatch, layerId]);
const deleteLayer = useCallback(() => {
dispatch(layerDeleted(id));
}, [dispatch, id]);
dispatch(layerDeleted(layerId));
}, [dispatch, layerId]);
return (
<Menu>
<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';

View File

@ -15,7 +15,7 @@ type Props = {
layerId: string;
};
export const RegionalPromptsNegativePrompt = memo((props: Props) => {
export const RPLayerNegativePrompt = memo((props: Props) => {
const prompt = useLayerNegativePrompt(props.layerId);
const dispatch = useAppDispatch();
const textareaRef = useRef<HTMLTextAreaElement>(null);
@ -64,4 +64,4 @@ export const RegionalPromptsNegativePrompt = memo((props: Props) => {
);
});
RegionalPromptsNegativePrompt.displayName = 'RegionalPromptsPrompt';
RPLayerNegativePrompt.displayName = 'RPLayerNegativePrompt';

View File

@ -15,7 +15,7 @@ type Props = {
layerId: string;
};
export const RegionalPromptsPositivePrompt = memo((props: Props) => {
export const RPLayerPositivePrompt = memo((props: Props) => {
const prompt = useLayerPositivePrompt(props.layerId);
const dispatch = useAppDispatch();
const textareaRef = useRef<HTMLTextAreaElement>(null);
@ -64,4 +64,4 @@ export const RegionalPromptsPositivePrompt = memo((props: Props) => {
);
});
RegionalPromptsPositivePrompt.displayName = 'RegionalPromptsPrompt';
RPLayerPositivePrompt.displayName = 'RPLayerPositivePrompt';

View File

@ -6,15 +6,15 @@ import { memo, useCallback } from 'react';
import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi';
type Props = {
id: string;
layerId: string;
};
export const LayerVisibilityToggle = memo(({ id }: Props) => {
export const RPLayerVisibilityToggle = memo(({ layerId }: Props) => {
const dispatch = useAppDispatch();
const isVisible = useLayerIsVisible(id);
const isVisible = useLayerIsVisible(layerId);
const onClick = useCallback(() => {
dispatch(rpLayerIsVisibleToggled(id));
}, [dispatch, id]);
dispatch(rpLayerIsVisibleToggled(layerId));
}, [dispatch, layerId]);
return (
<IconButton
@ -27,4 +27,4 @@ export const LayerVisibilityToggle = memo(({ id }: Props) => {
);
});
LayerVisibilityToggle.displayName = 'LayerVisibilityToggle';
RPLayerVisibilityToggle.displayName = 'RPLayerVisibilityToggle';

View File

@ -5,17 +5,20 @@ import { useAppSelector } from 'app/store/storeHooks';
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem';
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
import { RPLayerListItem } from 'features/regionalPrompts/components/RPLayerListItem';
import { StageComponent } from 'features/regionalPrompts/components/StageComponent';
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
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 { memo } from 'react';
const selectLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
regionalPrompts.present.layers.map((l) => l.id).reverse()
const selectRPLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
regionalPrompts.present.layers
.filter(isRegionalPromptLayer)
.map((l) => l.id)
.reverse()
);
const debugBlobs = () => {
@ -23,7 +26,7 @@ const debugBlobs = () => {
};
export const RegionalPromptsEditor = memo(() => {
const layerIdsReversed = useAppSelector(selectLayerIdsReversed);
const rpLayerIdsReversed = useAppSelector(selectRPLayerIdsReversed);
return (
<Flex gap={4} w="full" h="full">
<Flex flexDir="column" gap={4} flexShrink={0}>
@ -38,8 +41,8 @@ export const RegionalPromptsEditor = memo(() => {
</Flex>
<BrushSize />
<PromptLayerOpacity />
{layerIdsReversed.map((id) => (
<LayerListItem key={id} id={id} />
{rpLayerIdsReversed.map((id) => (
<RPLayerListItem key={id} layerId={id} />
))}
</Flex>
<StageComponent />