feat(ui): tweaked entity & group selection styles

This commit is contained in:
psychedelicious 2024-07-17 16:22:03 +10:00
parent 88c57a9750
commit e70339ff3e
17 changed files with 69 additions and 70 deletions

View File

@ -20,7 +20,7 @@ export const CA = memo(({ id }: Props) => {
return ( return (
<CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}> <CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}>
<CAHeader id={id} onToggleVisibility={onToggle} /> <CAHeader id={id} isSelected={isSelected} onToggleVisibility={onToggle} />
{isOpen && <CASettings id={id} />} {isOpen && <CASettings id={id} />}
</CanvasEntityContainer> </CanvasEntityContainer>
); );

View File

@ -13,10 +13,11 @@ import { useTranslation } from 'react-i18next';
type Props = { type Props = {
id: string; id: string;
isSelected: boolean;
onToggleVisibility: () => void; onToggleVisibility: () => void;
}; };
export const CAHeader = memo(({ id, onToggleVisibility }: Props) => { export const CAHeader = memo(({ id, isSelected, onToggleVisibility }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isEnabled = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id).isEnabled); const isEnabled = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id).isEnabled);
@ -30,7 +31,7 @@ export const CAHeader = memo(({ id, onToggleVisibility }: Props) => {
return ( return (
<CanvasEntityHeader onToggle={onToggleVisibility}> <CanvasEntityHeader onToggle={onToggleVisibility}>
<CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} /> <CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} />
<CanvasEntityTitle title={t('controlLayers.globalControlAdapter')} /> <CanvasEntityTitle title={t('controlLayers.globalControlAdapter')} isSelected={isSelected} />
<Spacer /> <Spacer />
<CAOpacityAndFilter id={id} /> <CAOpacityAndFilter id={id} />
<CAActionsMenu id={id} /> <CAActionsMenu id={id} />

View File

@ -1,6 +1,6 @@
import { Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
import { CA } from 'features/controlLayers/components/ControlAdapter/CA'; import { CA } from 'features/controlLayers/components/ControlAdapter/CA';
import { mapId } from 'features/controlLayers/konva/util'; import { mapId } from 'features/controlLayers/konva/util';
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice'; import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
@ -13,10 +13,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
export const CAEntityList = memo(() => { export const CAEntityList = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const isTypeSelected = useAppSelector((s) => const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'control_adapter'));
Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'control_adapter')
);
const caIds = useAppSelector(selectEntityIds); const caIds = useAppSelector(selectEntityIds);
if (caIds.length === 0) { if (caIds.length === 0) {
@ -26,13 +23,10 @@ export const CAEntityList = memo(() => {
if (caIds.length > 0) { if (caIds.length > 0) {
return ( return (
<> <>
<Text <CanvasEntityGroupTitle
color={isTypeSelected ? 'base.100' : 'base.300'} title={t('controlLayers.controlAdapters_withCount', { count: caIds.length })}
fontWeight={isTypeSelected ? 'semibold' : 'normal'} isSelected={isSelected}
userSelect="none" />
>
{t('controlLayers.controlAdapters_withCount', { count: caIds.length })}
</Text>
{caIds.map((id) => ( {caIds.map((id) => (
<CA key={id} id={id} /> <CA key={id} id={id} />
))} ))}

View File

@ -20,7 +20,7 @@ export const IPA = memo(({ id }: Props) => {
return ( return (
<CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}> <CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}>
<IPAHeader id={id} onToggleVisibility={onToggle} /> <IPAHeader id={id} isSelected={isSelected} onToggleVisibility={onToggle} />
{isOpen && <IPASettings id={id} />} {isOpen && <IPASettings id={id} />}
</CanvasEntityContainer> </CanvasEntityContainer>
); );

View File

@ -1,7 +1,7 @@
/* eslint-disable i18next/no-literal-string */ /* eslint-disable i18next/no-literal-string */
import { Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
import { IPA } from 'features/controlLayers/components/IPAdapter/IPA'; import { IPA } from 'features/controlLayers/components/IPAdapter/IPA';
import { mapId } from 'features/controlLayers/konva/util'; import { mapId } from 'features/controlLayers/konva/util';
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice'; import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
@ -14,7 +14,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
export const IPAEntityList = memo(() => { export const IPAEntityList = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const isTypeSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'ip_adapter')); const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'ip_adapter'));
const ipaIds = useAppSelector(selectEntityIds); const ipaIds = useAppSelector(selectEntityIds);
if (ipaIds.length === 0) { if (ipaIds.length === 0) {
@ -24,13 +24,10 @@ export const IPAEntityList = memo(() => {
if (ipaIds.length > 0) { if (ipaIds.length > 0) {
return ( return (
<> <>
<Text <CanvasEntityGroupTitle
userSelect="none" title={t('controlLayers.ipAdapters_withCount', { count: ipaIds.length })}
color={isTypeSelected ? 'base.100' : 'base.300'} isSelected={isSelected}
fontWeight={isTypeSelected ? 'semibold' : 'normal'} />
>
{t('controlLayers.ipAdapters_withCount', { count: ipaIds.length })}
</Text>
{ipaIds.map((id) => ( {ipaIds.map((id) => (
<IPA key={id} id={id} /> <IPA key={id} id={id} />
))} ))}

View File

@ -11,10 +11,11 @@ import { useTranslation } from 'react-i18next';
type Props = { type Props = {
id: string; id: string;
isSelected: boolean;
onToggleVisibility: () => void; onToggleVisibility: () => void;
}; };
export const IPAHeader = memo(({ id, onToggleVisibility }: Props) => { export const IPAHeader = memo(({ id, isSelected, onToggleVisibility }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id).isEnabled); const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id).isEnabled);
@ -28,7 +29,7 @@ export const IPAHeader = memo(({ id, onToggleVisibility }: Props) => {
return ( return (
<CanvasEntityHeader onToggle={onToggleVisibility}> <CanvasEntityHeader onToggle={onToggleVisibility}>
<CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} /> <CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} />
<CanvasEntityTitle title={t('controlLayers.ipAdapter')} /> <CanvasEntityTitle title={t('controlLayers.ipAdapter')} isSelected={isSelected} />
<Spacer /> <Spacer />
<CanvasEntityDeleteButton onDelete={onDelete} /> <CanvasEntityDeleteButton onDelete={onDelete} />
</CanvasEntityHeader> </CanvasEntityHeader>

View File

@ -17,7 +17,7 @@ export const IM = memo(() => {
}, [dispatch]); }, [dispatch]);
return ( return (
<CanvasEntityContainer isSelected={isSelected} onSelect={onSelect} selectedBorderColor={selectedBorderColor}> <CanvasEntityContainer isSelected={isSelected} onSelect={onSelect} selectedBorderColor={selectedBorderColor}>
<IMHeader onToggleVisibility={onToggle} /> <IMHeader isSelected={isSelected} onToggleVisibility={onToggle} />
{isOpen && <IMSettings />} {isOpen && <IMSettings />}
</CanvasEntityContainer> </CanvasEntityContainer>
); );

View File

@ -11,10 +11,11 @@ import { useTranslation } from 'react-i18next';
import { IMMaskFillColorPicker } from './IMMaskFillColorPicker'; import { IMMaskFillColorPicker } from './IMMaskFillColorPicker';
type Props = { type Props = {
isSelected: boolean;
onToggleVisibility: () => void; onToggleVisibility: () => void;
}; };
export const IMHeader = memo(({ onToggleVisibility }: Props) => { export const IMHeader = memo(({ isSelected, onToggleVisibility }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isEnabled = useAppSelector((s) => s.canvasV2.inpaintMask.isEnabled); const isEnabled = useAppSelector((s) => s.canvasV2.inpaintMask.isEnabled);
@ -25,7 +26,7 @@ export const IMHeader = memo(({ onToggleVisibility }: Props) => {
return ( return (
<CanvasEntityHeader onToggle={onToggleVisibility}> <CanvasEntityHeader onToggle={onToggleVisibility}>
<CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} /> <CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} />
<CanvasEntityTitle title={t('controlLayers.inpaintMask')} /> <CanvasEntityTitle title={t('controlLayers.inpaintMask')} isSelected={isSelected} />
<Spacer /> <Spacer />
<IMMaskFillColorPicker /> <IMMaskFillColorPicker />
<IMActionsMenu /> <IMActionsMenu />

View File

@ -26,7 +26,7 @@ export const Layer = memo(({ id }: Props) => {
return ( return (
<CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}> <CanvasEntityContainer isSelected={isSelected} onSelect={onSelect}>
<LayerHeader id={id} onToggleVisibility={onToggle} /> <LayerHeader id={id} onToggleVisibility={onToggle} isSelected={isSelected} />
{isOpen && <LayerSettings id={id} />} {isOpen && <LayerSettings id={id} />}
<IAIDroppable data={droppableData} /> <IAIDroppable data={droppableData} />
</CanvasEntityContainer> </CanvasEntityContainer>

View File

@ -1,6 +1,6 @@
import { Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
import { Layer } from 'features/controlLayers/components/Layer/Layer'; import { Layer } from 'features/controlLayers/components/Layer/Layer';
import { mapId } from 'features/controlLayers/konva/util'; import { mapId } from 'features/controlLayers/konva/util';
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice'; import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
@ -13,7 +13,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
export const LayerEntityList = memo(() => { export const LayerEntityList = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const isTypeSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'layer')); const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'layer'));
const layerIds = useAppSelector(selectEntityIds); const layerIds = useAppSelector(selectEntityIds);
if (layerIds.length === 0) { if (layerIds.length === 0) {
@ -23,13 +23,10 @@ export const LayerEntityList = memo(() => {
if (layerIds.length > 0) { if (layerIds.length > 0) {
return ( return (
<> <>
<Text <CanvasEntityGroupTitle
color={isTypeSelected ? 'base.100' : 'base.300'} title={t('controlLayers.layers_withCount', { count: layerIds.length })}
fontWeight={isTypeSelected ? 'semibold' : 'normal'} isSelected={isSelected}
userSelect="none" />
>
{t('controlLayers.layers_withCount', { count: layerIds.length })}
</Text>
{layerIds.map((id) => ( {layerIds.map((id) => (
<Layer key={id} id={id} /> <Layer key={id} id={id} />
))} ))}

View File

@ -14,10 +14,11 @@ import { LayerOpacity } from './LayerOpacity';
type Props = { type Props = {
id: string; id: string;
isSelected: boolean;
onToggleVisibility: () => void; onToggleVisibility: () => void;
}; };
export const LayerHeader = memo(({ id, onToggleVisibility }: Props) => { export const LayerHeader = memo(({ id, isSelected, onToggleVisibility }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.canvasV2, id).isEnabled); const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.canvasV2, id).isEnabled);
@ -35,7 +36,7 @@ export const LayerHeader = memo(({ id, onToggleVisibility }: Props) => {
return ( return (
<CanvasEntityHeader onToggle={onToggleVisibility}> <CanvasEntityHeader onToggle={onToggleVisibility}>
<CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} /> <CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} />
<CanvasEntityTitle title={title} /> <CanvasEntityTitle title={title} isSelected={isSelected} />
<Spacer /> <Spacer />
<LayerOpacity id={id} /> <LayerOpacity id={id} />
<LayerActionsMenu id={id} /> <LayerActionsMenu id={id} />

View File

@ -22,7 +22,7 @@ export const RG = memo(({ id }: Props) => {
}, [dispatch, id]); }, [dispatch, id]);
return ( return (
<CanvasEntityContainer isSelected={isSelected} onSelect={onSelect} selectedBorderColor={selectedBorderColor}> <CanvasEntityContainer isSelected={isSelected} onSelect={onSelect} selectedBorderColor={selectedBorderColor}>
<RGHeader id={id} onToggleVisibility={onToggle} /> <RGHeader id={id} isSelected={isSelected} onToggleVisibility={onToggle} />
{isOpen && <RGSettings id={id} />} {isOpen && <RGSettings id={id} />}
</CanvasEntityContainer> </CanvasEntityContainer>
); );

View File

@ -1,6 +1,6 @@
import { Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
import { RG } from 'features/controlLayers/components/RegionalGuidance/RG'; import { RG } from 'features/controlLayers/components/RegionalGuidance/RG';
import { mapId } from 'features/controlLayers/konva/util'; import { mapId } from 'features/controlLayers/konva/util';
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice'; import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
@ -13,10 +13,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
export const RGEntityList = memo(() => { export const RGEntityList = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const isTypeSelected = useAppSelector((s) => const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'regional_guidance'));
Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'regional_guidance')
);
const rgIds = useAppSelector(selectEntityIds); const rgIds = useAppSelector(selectEntityIds);
if (rgIds.length === 0) { if (rgIds.length === 0) {
@ -26,13 +23,10 @@ export const RGEntityList = memo(() => {
if (rgIds.length > 0) { if (rgIds.length > 0) {
return ( return (
<> <>
<Text <CanvasEntityGroupTitle
color={isTypeSelected ? 'base.100' : 'base.300'} title={t('controlLayers.regionalGuidance_withCount', { count: rgIds.length })}
fontWeight={isTypeSelected ? 'semibold' : 'normal'} isSelected={isSelected}
userSelect="none" />
>
{t('controlLayers.regionalGuidance_withCount', { count: rgIds.length })}
</Text>
{rgIds.map((id) => ( {rgIds.map((id) => (
<RG key={id} id={id} /> <RG key={id} id={id} />
))} ))}

View File

@ -15,10 +15,11 @@ import { RGSettingsPopover } from './RGSettingsPopover';
type Props = { type Props = {
id: string; id: string;
isSelected: boolean;
onToggleVisibility: () => void; onToggleVisibility: () => void;
}; };
export const RGHeader = memo(({ id, onToggleVisibility }: Props) => { export const RGHeader = memo(({ id, isSelected, onToggleVisibility }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isEnabled = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).isEnabled); const isEnabled = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).isEnabled);
@ -33,7 +34,7 @@ export const RGHeader = memo(({ id, onToggleVisibility }: Props) => {
return ( return (
<CanvasEntityHeader onToggle={onToggleVisibility}> <CanvasEntityHeader onToggle={onToggleVisibility}>
<CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} /> <CanvasEntityEnabledToggle isEnabled={isEnabled} onToggle={onToggleIsEnabled} />
<CanvasEntityTitle title={t('controlLayers.regionalGuidance')} /> <CanvasEntityTitle title={t('controlLayers.regionalGuidance')} isSelected={isSelected} />
<Spacer /> <Spacer />
{autoNegative === 'invert' && ( {autoNegative === 'invert' && (
<Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none"> <Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none">

View File

@ -1,7 +1,7 @@
import type { ChakraProps } from '@invoke-ai/ui-library'; import type { ChakraProps } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library'; import { Flex } from '@invoke-ai/ui-library';
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback } from 'react';
type Props = PropsWithChildren<{ type Props = PropsWithChildren<{
isSelected: boolean; isSelected: boolean;
@ -9,13 +9,8 @@ type Props = PropsWithChildren<{
selectedBorderColor?: ChakraProps['bg']; selectedBorderColor?: ChakraProps['bg'];
}>; }>;
export const CanvasEntityContainer = memo(({ isSelected, onSelect, selectedBorderColor, children }: Props) => { export const CanvasEntityContainer = memo((props: Props) => {
const borderColor = useMemo(() => { const { isSelected, onSelect, selectedBorderColor = 'base.400', children } = props;
if (isSelected) {
return selectedBorderColor ?? 'base.400';
}
return 'base.800';
}, [isSelected, selectedBorderColor]);
const _onSelect = useCallback(() => { const _onSelect = useCallback(() => {
if (isSelected) { if (isSelected) {
return; return;
@ -28,11 +23,10 @@ export const CanvasEntityContainer = memo(({ isSelected, onSelect, selectedBorde
position="relative" // necessary for drop overlay position="relative" // necessary for drop overlay
flexDir="column" flexDir="column"
w="full" w="full"
bg="base.850" bg={isSelected ? 'base.800' : 'base.850'}
onClick={_onSelect} onClick={_onSelect}
borderInlineStartWidth={5} borderInlineStartWidth={5}
borderColor={borderColor} borderColor={isSelected ? selectedBorderColor : 'base.800'}
opacity={isSelected ? 1 : 0.6}
borderRadius="base" borderRadius="base"
> >
{children} {children}

View File

@ -0,0 +1,17 @@
import { Text } from '@invoke-ai/ui-library';
import { memo } from 'react';
type Props = {
title: string;
isSelected: boolean;
};
export const CanvasEntityGroupTitle = memo(({ title, isSelected }: Props) => {
return (
<Text color={isSelected ? 'base.100' : 'base.300'} userSelect="none">
{title}
</Text>
);
});
CanvasEntityGroupTitle.displayName = 'CanvasEntityGroupTitle';

View File

@ -3,11 +3,12 @@ import { memo } from 'react';
type Props = { type Props = {
title: string; title: string;
isSelected: boolean;
}; };
export const CanvasEntityTitle = memo(({ title }: Props) => { export const CanvasEntityTitle = memo(({ title, isSelected }: Props) => {
return ( return (
<Text size="sm" fontWeight="semibold" userSelect="none" color="base.300"> <Text size="sm" fontWeight="semibold" userSelect="none" color={isSelected ? 'base.100' : 'base.300'}>
{title} {title}
</Text> </Text>
); );