diff --git a/invokeai/frontend/web/src/common/util/stopPropagation.ts b/invokeai/frontend/web/src/common/util/stopPropagation.ts new file mode 100644 index 0000000000..b3481b7c0e --- /dev/null +++ b/invokeai/frontend/web/src/common/util/stopPropagation.ts @@ -0,0 +1,3 @@ +export const stopPropagation = (e: React.MouseEvent) => { + e.stopPropagation(); +}; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CALayerListItem.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CALayerListItem.tsx index 28ef0866fb..a55c71fad6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CALayerListItem.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CALayerListItem.tsx @@ -1,4 +1,4 @@ -import { Flex, Spacer } from '@invoke-ai/ui-library'; +import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import CALayerOpacity from 'features/controlLayers/components/CALayerOpacity'; @@ -38,6 +38,8 @@ export const CALayerListItem = memo(({ layerId }: Props) => { // Must be capture so that the layer is selected before deleting/resetting/etc dispatch(layerSelected(layerId)); }, [dispatch, layerId]); + const { isOpen, onToggle } = useDisclosure(); + return ( { borderRadius="base" py="1px" > - - + + @@ -56,7 +58,11 @@ export const CALayerListItem = memo(({ layerId }: Props) => { - + {isOpen && ( + + + + )} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CALayerOpacity.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CALayerOpacity.tsx index eb414f5369..a6107da1ec 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CALayerOpacity.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CALayerOpacity.tsx @@ -13,6 +13,7 @@ import { Switch, } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { stopPropagation } from 'common/util/stopPropagation'; import { useLayerOpacity } from 'features/controlLayers/hooks/layerStateHooks'; import { isFilterEnabledChanged, layerOpacityChanged } from 'features/controlLayers/store/controlLayersSlice'; import type { ChangeEvent } from 'react'; @@ -51,6 +52,7 @@ const CALayerOpacity = ({ layerId }: Props) => { size="sm" icon={} variant="ghost" + onDoubleClick={stopPropagation} /> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/IPLayerListItem.tsx b/invokeai/frontend/web/src/features/controlLayers/components/IPLayerListItem.tsx index efb4156137..d9b3121dab 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/IPLayerListItem.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/IPLayerListItem.tsx @@ -1,4 +1,4 @@ -import { Flex, Spacer } from '@invoke-ai/ui-library'; +import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import ControlAdapterLayerConfig from 'features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig'; @@ -24,16 +24,21 @@ export const IPLayerListItem = memo(({ layerId }: Props) => { [layerId] ); const ipAdapterId = useAppSelector(selector); + const { isOpen, onToggle } = useDisclosure(); return ( - - - + + + - + {isOpen && ( + + + + )} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerDeleteButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerDeleteButton.tsx index 5d8937db66..0c74b2a9ea 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerDeleteButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerDeleteButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@invoke-ai/ui-library'; import { guidanceLayerDeleted } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge'; import { useAppDispatch } from 'app/store/storeHooks'; +import { stopPropagation } from 'common/util/stopPropagation'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { PiTrashSimpleBold } from 'react-icons/pi'; @@ -21,6 +22,7 @@ export const LayerDeleteButton = memo(({ layerId }: Props) => { tooltip={t('common.delete')} icon={} onClick={deleteLayer} + onDoubleClick={stopPropagation} // double click expands the layer /> ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerMenu.tsx index cd86fb8cd1..e5c8cc0aac 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerMenu.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerMenu.tsx @@ -1,5 +1,6 @@ import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { stopPropagation } from 'common/util/stopPropagation'; import { LayerMenuArrangeActions } from 'features/controlLayers/components/LayerMenuArrangeActions'; import { LayerMenuRGActions } from 'features/controlLayers/components/LayerMenuRGActions'; import { useLayerType } from 'features/controlLayers/hooks/layerStateHooks'; @@ -22,7 +23,13 @@ export const LayerMenu = memo(({ layerId }: Props) => { }, [dispatch, layerId]); return ( - } /> + } + onDoubleClick={stopPropagation} // double click expands the layer + /> {layerType === 'regional_guidance_layer' && ( <> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerTitle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerTitle.tsx index ba3e01f799..ec13ff7bcc 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerTitle.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerTitle.tsx @@ -20,7 +20,7 @@ export const LayerTitle = memo(({ type }: Props) => { }, [t, type]); return ( - + {title} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerVisibilityToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerVisibilityToggle.tsx index 77fa8cf8e8..d2dab39e36 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerVisibilityToggle.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerVisibilityToggle.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { stopPropagation } from 'common/util/stopPropagation'; import { useLayerIsVisible } from 'features/controlLayers/hooks/layerStateHooks'; import { layerVisibilityToggled } from 'features/controlLayers/store/controlLayersSlice'; import { memo, useCallback } from 'react'; @@ -27,6 +28,7 @@ export const LayerVisibilityToggle = memo(({ layerId }: Props) => { icon={isVisible ? : undefined} onClick={onClick} colorScheme="base" + onDoubleClick={stopPropagation} // double click expands the layer /> ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerColorPicker.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerColorPicker.tsx index 04c6fbf0a2..e76ab57a51 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerColorPicker.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerColorPicker.tsx @@ -2,6 +2,7 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger, Tooltip } f import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import RgbColorPicker from 'common/components/RgbColorPicker'; +import { stopPropagation } from 'common/util/stopPropagation'; import { rgbColorToString } from 'features/canvas/util/colorToString'; import { isRegionalGuidanceLayer, @@ -51,6 +52,7 @@ export const RGLayerColorPicker = memo(({ layerId }: Props) => { h={8} cursor="pointer" tabIndex={-1} + onDoubleClick={stopPropagation} // double click expands the layer /> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerListItem.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerListItem.tsx index 96acfffa50..7c34158edb 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerListItem.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerListItem.tsx @@ -1,4 +1,4 @@ -import { Badge, Flex, Spacer } from '@invoke-ai/ui-library'; +import { Badge, Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { rgbColorToString } from 'features/canvas/util/colorToString'; @@ -47,26 +47,19 @@ export const RGLayerListItem = memo(({ layerId }: Props) => { ); const { autoNegative, color, hasPositivePrompt, hasNegativePrompt, hasIPAdapters, isSelected } = useAppSelector(selector); - const onClickCapture = useCallback(() => { - // Must be capture so that the layer is selected before deleting/resetting/etc + const { isOpen, onToggle } = useDisclosure(); + const onClick = useCallback(() => { dispatch(layerSelected(layerId)); }, [dispatch, layerId]); return ( - - - + + + {autoNegative === 'invert' && ( - + {t('controlLayers.autoNegative')} )} @@ -75,10 +68,14 @@ export const RGLayerListItem = memo(({ layerId }: Props) => { - {!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && } - {hasPositivePrompt && } - {hasNegativePrompt && } - {hasIPAdapters && } + {isOpen && ( + + {!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && } + {hasPositivePrompt && } + {hasNegativePrompt && } + {hasIPAdapters && } + + )} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerSettingsPopover.tsx index acb64e16b8..e270748b9b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RGLayerSettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RGLayerSettingsPopover.tsx @@ -9,6 +9,7 @@ import { PopoverContent, PopoverTrigger, } from '@invoke-ai/ui-library'; +import { stopPropagation } from 'common/util/stopPropagation'; import { RGLayerAutoNegativeCheckbox } from 'features/controlLayers/components/RGLayerAutoNegativeCheckbox'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -34,6 +35,7 @@ const RGLayerSettingsPopover = ({ layerId }: Props) => { aria-label={t('common.settingsLabel')} size="sm" icon={} + onDoubleClick={stopPropagation} // double click expands the layer /> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig.tsx b/invokeai/frontend/web/src/features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig.tsx index 2a8fed7a83..29a3502d37 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig.tsx @@ -22,7 +22,7 @@ const ControlAdapterLayerConfig = (props: { id: string }) => { const [isExpanded, toggleIsExpanded] = useToggle(false); return ( - + {' '}