mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): canvas v2 (wip)
Fix a few more components
This commit is contained in:
parent
2cc60f253a
commit
8a6690a57c
@ -5,70 +5,73 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton';
|
||||
import { CALayer } from 'features/controlLayers/components/CALayer/CALayer';
|
||||
import { CA } from 'features/controlLayers/components/ControlAdapter/CA';
|
||||
import { DeleteAllLayersButton } from 'features/controlLayers/components/DeleteAllLayersButton';
|
||||
import { IILayer } from 'features/controlLayers/components/IILayer/IILayer';
|
||||
import { IPAEntity } from 'features/controlLayers/components/IPALayer/IPALayer';
|
||||
import { Layer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
||||
import { RGLayer } from 'features/controlLayers/components/RGLayer/RGLayer';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import type { LayerData } from 'features/controlLayers/store/types';
|
||||
import { isRenderableLayer } from 'features/controlLayers/store/types';
|
||||
import { partition } from 'lodash-es';
|
||||
import { memo } from 'react';
|
||||
import { IPA } from 'features/controlLayers/components/IPAdapter/IPA';
|
||||
import { Layer } from 'features/controlLayers/components/Layer/Layer';
|
||||
import { RG } from 'features/controlLayers/components/RegionalGuidance/RG';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectLayerIdTypePairs = createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||
const [renderableLayers, ipAdapterLayers] = partition(canvasV2.layers, isRenderableLayer);
|
||||
return [...ipAdapterLayers, ...renderableLayers].map((l) => ({ id: l.id, type: l.type })).reverse();
|
||||
const selectRGIds = createMemoizedSelector(selectRegionalGuidanceSlice, (rgState) => {
|
||||
return rgState.regions.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectCAIds = createMemoizedSelector(selectControlAdaptersV2Slice, (caState) => {
|
||||
return caState.controlAdapters.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectIPAIds = createMemoizedSelector(selectIPAdaptersSlice, (ipaState) => {
|
||||
return ipaState.ipAdapters.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectLayerIds = createMemoizedSelector(selectLayersSlice, (layersState) => {
|
||||
return layersState.layers.map(mapId).reverse();
|
||||
});
|
||||
|
||||
export const ControlLayersPanelContent = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const layerIdTypePairs = useAppSelector(selectLayerIdTypePairs);
|
||||
const rgIds = useAppSelector(selectRGIds);
|
||||
const caIds = useAppSelector(selectCAIds);
|
||||
const ipaIds = useAppSelector(selectIPAIds);
|
||||
const layerIds = useAppSelector(selectLayerIds);
|
||||
const entityCount = useMemo(
|
||||
() => rgIds.length + caIds.length + ipaIds.length + layerIds.length,
|
||||
[rgIds.length, caIds.length, ipaIds.length, layerIds.length]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||
<Flex justifyContent="space-around">
|
||||
<AddLayerButton />
|
||||
<DeleteAllLayersButton />
|
||||
</Flex>
|
||||
{layerIdTypePairs.length > 0 && (
|
||||
{entityCount > 0 && (
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column" gap={2} data-testid="control-layers-layer-list">
|
||||
{layerIdTypePairs.map(({ id, type }) => (
|
||||
<LayerWrapper key={id} id={id} type={type} />
|
||||
{rgIds.map((id) => (
|
||||
<RG key={id} id={id} />
|
||||
))}
|
||||
{caIds.map((id) => (
|
||||
<CA key={id} id={id} />
|
||||
))}
|
||||
{ipaIds.map((id) => (
|
||||
<IPA key={id} id={id} />
|
||||
))}
|
||||
{layerIds.map((id) => (
|
||||
<Layer key={id} id={id} />
|
||||
))}
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
)}
|
||||
{layerIdTypePairs.length === 0 && <IAINoContentFallback icon={null} label={t('controlLayers.noLayersAdded')} />}
|
||||
{entityCount === 0 && <IAINoContentFallback icon={null} label={t('controlLayers.noLayersAdded')} />}
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
ControlLayersPanelContent.displayName = 'ControlLayersPanelContent';
|
||||
|
||||
type LayerWrapperProps = {
|
||||
id: string;
|
||||
type: LayerData['type'];
|
||||
};
|
||||
|
||||
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
||||
if (type === 'regional_guidance_layer') {
|
||||
return <RGLayer key={id} layerId={id} />;
|
||||
}
|
||||
if (type === 'control_adapter_layer') {
|
||||
return <CALayer key={id} id={id} />;
|
||||
}
|
||||
if (type === 'ip_adapter_layer') {
|
||||
return <IPAEntity key={id} layerId={id} />;
|
||||
}
|
||||
if (type === 'initial_image_layer') {
|
||||
return <IILayer key={id} layerId={id} />;
|
||||
}
|
||||
if (type === 'raster_layer') {
|
||||
return <Layer key={id} layerId={id} />;
|
||||
}
|
||||
});
|
||||
|
||||
LayerWrapper.displayName = 'LayerWrapper';
|
||||
|
@ -1,24 +1,18 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { BrushColorPicker } from 'features/controlLayers/components/BrushColorPicker';
|
||||
import { BrushWidth } from 'features/controlLayers/components/BrushSize';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { BrushWidth } from 'features/controlLayers/components/BrushWidth';
|
||||
import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover';
|
||||
import { EraserWidth } from 'features/controlLayers/components/EraserWidth';
|
||||
import { FillColorPicker } from 'features/controlLayers/components/FillColorPicker';
|
||||
import { ToolChooser } from 'features/controlLayers/components/ToolChooser';
|
||||
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
||||
import { $tool } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||
import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ControlLayersToolbar = memo(() => {
|
||||
const tool = useStore($tool);
|
||||
const withBrushSize = useMemo(() => {
|
||||
return tool === 'brush' || tool === 'eraser';
|
||||
}, [tool]);
|
||||
const withBrushColor = useMemo(() => {
|
||||
return tool === 'brush' || tool === 'rect';
|
||||
}, [tool]);
|
||||
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
||||
return (
|
||||
<Flex w="full" gap={2}>
|
||||
<Flex flex={1} justifyContent="center">
|
||||
@ -28,8 +22,9 @@ export const ControlLayersToolbar = memo(() => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex flex={1} gap={2} justifyContent="center" alignItems="center">
|
||||
{withBrushSize && <BrushWidth />}
|
||||
{withBrushColor && <BrushColorPicker />}
|
||||
{tool === 'brush' && <BrushWidth />}
|
||||
{tool === 'eraser' && <EraserWidth />}
|
||||
<FillColorPicker />
|
||||
</Flex>
|
||||
<Flex flex={1} justifyContent="center">
|
||||
<Flex gap={2} marginInlineStart="auto">
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { Button } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { allLayersDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { caAllDeleted } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { ipaAllDeleted } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { layerAllDeleted } from 'features/controlLayers/store/layersSlice';
|
||||
import { rgAllDeleted } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
@ -8,9 +12,12 @@ import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
export const DeleteAllLayersButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isDisabled = useAppSelector((s) => s.canvasV2.layers.length === 0);
|
||||
const entityCount = useAppSelector(selectEntityCount);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(allLayersDeleted());
|
||||
dispatch(caAllDeleted());
|
||||
dispatch(rgAllDeleted());
|
||||
dispatch(ipaAllDeleted());
|
||||
dispatch(layerAllDeleted());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
@ -19,7 +26,7 @@ export const DeleteAllLayersButton = memo(() => {
|
||||
leftIcon={<PiTrashSimpleBold />}
|
||||
variant="ghost"
|
||||
colorScheme="error"
|
||||
isDisabled={isDisabled}
|
||||
isDisabled={entityCount > 0}
|
||||
data-testid="control-layers-delete-all-layers-button"
|
||||
>
|
||||
{t('controlLayers.deleteAll')}
|
||||
|
@ -246,6 +246,9 @@ export const controlAdaptersV2Slice = createSlice({
|
||||
}
|
||||
ca.beginEndStepPct = beginEndStepPct;
|
||||
},
|
||||
caAllDeleted: (state) => {
|
||||
state.controlAdapters = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -270,6 +273,7 @@ export const {
|
||||
caProcessorPendingBatchIdChanged,
|
||||
caWeightChanged,
|
||||
caBeginEndStepPctChanged,
|
||||
caAllDeleted,
|
||||
} = controlAdaptersV2Slice.actions;
|
||||
|
||||
export const selectControlAdaptersV2Slice = (state: RootState) => state.controlAdaptersV2;
|
||||
|
@ -114,6 +114,9 @@ export const ipAdaptersSlice = createSlice({
|
||||
}
|
||||
ipa.beginEndStepPct = beginEndStepPct;
|
||||
},
|
||||
ipaAllDeleted: (state) => {
|
||||
state.ipAdapters = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -128,6 +131,7 @@ export const {
|
||||
ipaCLIPVisionModelChanged,
|
||||
ipaWeightChanged,
|
||||
ipaBeginEndStepPctChanged,
|
||||
ipaAllDeleted,
|
||||
} = ipAdaptersSlice.actions;
|
||||
|
||||
export const selectIPAdaptersSlice = (state: RootState) => state.ipAdapters;
|
||||
|
@ -234,6 +234,9 @@ export const layersSlice = createSlice({
|
||||
},
|
||||
prepare: (payload: ImageObjectAddedArg) => ({ payload: { ...payload, imageId: uuidv4() } }),
|
||||
},
|
||||
layerAllDeleted: (state) => {
|
||||
state.layers = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -254,6 +257,7 @@ export const {
|
||||
layerLinePointAdded,
|
||||
layerRectAdded,
|
||||
layerImageAdded,
|
||||
layerAllDeleted,
|
||||
} = layersSlice.actions;
|
||||
|
||||
export const selectLayersSlice = (state: RootState) => state.layers;
|
||||
|
@ -398,6 +398,9 @@ export const regionalGuidanceSlice = createSlice({
|
||||
},
|
||||
prepare: (payload: RectShapeAddedArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
||||
},
|
||||
rgAllDeleted: (state) => {
|
||||
state.regions = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -431,6 +434,7 @@ export const {
|
||||
rgEraserLineAdded,
|
||||
rgLinePointAdded,
|
||||
rgRectAdded,
|
||||
rgAllDeleted,
|
||||
} = regionalGuidanceSlice.actions;
|
||||
|
||||
export const selectRegionalGuidanceSlice = (state: RootState) => state.regionalGuidance;
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
|
||||
export const selectEntityCount = createSelector(
|
||||
selectRegionalGuidanceSlice,
|
||||
selectControlAdaptersV2Slice,
|
||||
selectIPAdaptersSlice,
|
||||
selectLayersSlice,
|
||||
(rgState, caState, ipaState, layersState) => {
|
||||
return (
|
||||
rgState.regions.length + caState.controlAdapters.length + ipaState.ipAdapters.length + layersState.layers.length
|
||||
);
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue
Block a user