mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Fixes bug causing gallery to close on context menu open
This commit is contained in:
parent
76e7e82f5e
commit
b6dd5b664c
@ -1,25 +1,37 @@
|
|||||||
import { RefObject, useEffect } from 'react';
|
import { RefObject, useEffect, useRef } from 'react';
|
||||||
|
import { Rect } from 'react-konva';
|
||||||
|
|
||||||
const useClickOutsideWatcher = (
|
const watchers: {
|
||||||
ref: RefObject<HTMLElement>,
|
ref: RefObject<HTMLElement>;
|
||||||
callback: () => void,
|
enable: boolean;
|
||||||
req = true
|
callback: () => void;
|
||||||
) => {
|
}[] = [];
|
||||||
|
|
||||||
|
const useClickOutsideWatcher = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(e: MouseEvent) {
|
function handleClickOutside(e: MouseEvent) {
|
||||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
watchers.forEach(({ ref, enable, callback }) => {
|
||||||
|
if (enable && ref.current && !ref.current.contains(e.target as Node)) {
|
||||||
|
console.log('callback');
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (req) {
|
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
}
|
|
||||||
return () => {
|
return () => {
|
||||||
if (req) {
|
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, [ref, req, callback]);
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
addWatcher: (watcher: {
|
||||||
|
ref: RefObject<HTMLElement>;
|
||||||
|
callback: () => void;
|
||||||
|
enable: boolean;
|
||||||
|
}) => {
|
||||||
|
watchers.push(watcher);
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useClickOutsideWatcher;
|
export default useClickOutsideWatcher;
|
||||||
|
@ -7,7 +7,10 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||||
import { setCurrentImage } from 'features/gallery/store/gallerySlice';
|
import {
|
||||||
|
setCurrentImage,
|
||||||
|
setShouldHoldGalleryOpen,
|
||||||
|
} from 'features/gallery/store/gallerySlice';
|
||||||
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
|
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
|
||||||
import DeleteImageModal from './DeleteImageModal';
|
import DeleteImageModal from './DeleteImageModal';
|
||||||
import { memo, useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
@ -153,10 +156,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu.Root
|
<ContextMenu.Root
|
||||||
// onOpenChange={(open: boolean) => {
|
onOpenChange={(open: boolean) => {
|
||||||
// dispatch(setShouldHoldGalleryOpen(open));
|
dispatch(setShouldHoldGalleryOpen(open));
|
||||||
// dispatch(setShouldShowGallery(true));
|
}}
|
||||||
// }}
|
|
||||||
>
|
>
|
||||||
<ContextMenu.Trigger>
|
<ContextMenu.Trigger>
|
||||||
<Box
|
<Box
|
||||||
@ -204,6 +206,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
<ContextMenu.Content
|
<ContextMenu.Content
|
||||||
className="hoverable-image-context-menu"
|
className="hoverable-image-context-menu"
|
||||||
sticky={'always'}
|
sticky={'always'}
|
||||||
|
onInteractOutside={(e) => {
|
||||||
|
e.detail.originalEvent.preventDefault();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ContextMenu.Item
|
<ContextMenu.Item
|
||||||
onClickCapture={handleUsePrompt}
|
onClickCapture={handleUsePrompt}
|
||||||
|
@ -3,6 +3,7 @@ import { NumberSize, Resizable } from 're-resizable';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeEvent,
|
ChangeEvent,
|
||||||
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useRef,
|
useRef,
|
||||||
@ -39,7 +40,6 @@ import { BiReset } from 'react-icons/bi';
|
|||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAICheckbox from 'common/components/IAICheckbox';
|
||||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import useClickOutsideWatcher from 'common/hooks/useClickOutsideWatcher';
|
|
||||||
|
|
||||||
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
|
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
|
||||||
|
|
||||||
@ -124,15 +124,15 @@ export default function ImageGallery() {
|
|||||||
shouldPinGallery && dispatch(setDoesCanvasNeedScaling(true));
|
shouldPinGallery && dispatch(setDoesCanvasNeedScaling(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseGallery = () => {
|
const handleCloseGallery = useCallback(() => {
|
||||||
dispatch(setShouldShowGallery(false));
|
dispatch(setShouldShowGallery(false));
|
||||||
|
dispatch(setShouldHoldGalleryOpen(false));
|
||||||
dispatch(
|
dispatch(
|
||||||
setGalleryScrollPosition(
|
setGalleryScrollPosition(
|
||||||
galleryContainerRef.current ? galleryContainerRef.current.scrollTop : 0
|
galleryContainerRef.current ? galleryContainerRef.current.scrollTop : 0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
dispatch(setShouldHoldGalleryOpen(false));
|
}, [dispatch]);
|
||||||
};
|
|
||||||
|
|
||||||
const handleClickLoadMore = () => {
|
const handleClickLoadMore = () => {
|
||||||
dispatch(requestImages(currentCategory));
|
dispatch(requestImages(currentCategory));
|
||||||
@ -144,6 +144,7 @@ export default function ImageGallery() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setCloseGalleryTimer = () => {
|
const setCloseGalleryTimer = () => {
|
||||||
|
if (shouldHoldGalleryOpen) return;
|
||||||
timeoutIdRef.current = window.setTimeout(() => handleCloseGallery(), 500);
|
timeoutIdRef.current = window.setTimeout(() => handleCloseGallery(), 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -273,12 +274,25 @@ export default function ImageGallery() {
|
|||||||
setShouldShowButtons(galleryWidth >= 280);
|
setShouldShowButtons(galleryWidth >= 280);
|
||||||
}, [galleryWidth]);
|
}, [galleryWidth]);
|
||||||
|
|
||||||
useClickOutsideWatcher(galleryRef, handleCloseGallery, !shouldPinGallery);
|
useEffect(() => {
|
||||||
|
function handleClickOutside(e: MouseEvent) {
|
||||||
|
if (
|
||||||
|
galleryRef.current &&
|
||||||
|
!galleryRef.current.contains(e.target as Node)
|
||||||
|
) {
|
||||||
|
handleCloseGallery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [handleCloseGallery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
nodeRef={galleryRef}
|
nodeRef={galleryRef}
|
||||||
in={shouldShowGallery || (shouldHoldGalleryOpen && !shouldPinGallery)}
|
in={shouldShowGallery || shouldHoldGalleryOpen}
|
||||||
unmountOnExit
|
unmountOnExit
|
||||||
timeout={200}
|
timeout={200}
|
||||||
classNames="image-gallery-wrapper"
|
classNames="image-gallery-wrapper"
|
||||||
@ -288,6 +302,7 @@ export default function ImageGallery() {
|
|||||||
style={{ zIndex: shouldPinGallery ? 1 : 100 }}
|
style={{ zIndex: shouldPinGallery ? 1 : 100 }}
|
||||||
data-pinned={shouldPinGallery}
|
data-pinned={shouldPinGallery}
|
||||||
ref={galleryRef}
|
ref={galleryRef}
|
||||||
|
// onMouseLeave={setCloseGalleryTimer}
|
||||||
onMouseLeave={!shouldPinGallery ? setCloseGalleryTimer : undefined}
|
onMouseLeave={!shouldPinGallery ? setCloseGalleryTimer : undefined}
|
||||||
onMouseEnter={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
onMouseEnter={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
||||||
onMouseOver={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
onMouseOver={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Tooltip } from '@chakra-ui/react';
|
import { Tooltip } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { MouseEvent, ReactNode, useCallback, useRef } from 'react';
|
import React, { ReactNode, useCallback, useEffect, useRef } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
||||||
import { CSSTransition } from 'react-transition-group';
|
import { CSSTransition } from 'react-transition-group';
|
||||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||||
import useClickOutsideWatcher from 'common/hooks/useClickOutsideWatcher';
|
|
||||||
import {
|
import {
|
||||||
OptionsState,
|
OptionsState,
|
||||||
setOptionsPanelScrollPosition,
|
setOptionsPanelScrollPosition,
|
||||||
@ -101,15 +100,8 @@ const InvokeOptionsPanel = (props: Props) => {
|
|||||||
);
|
);
|
||||||
dispatch(setShouldShowOptionsPanel(false));
|
dispatch(setShouldShowOptionsPanel(false));
|
||||||
dispatch(setShouldHoldOptionsPanelOpen(false));
|
dispatch(setShouldHoldOptionsPanelOpen(false));
|
||||||
// dispatch(setDoesCanvasNeedScaling(true));
|
|
||||||
}, [dispatch, shouldPinOptionsPanel]);
|
}, [dispatch, shouldPinOptionsPanel]);
|
||||||
|
|
||||||
useClickOutsideWatcher(
|
|
||||||
optionsPanelRef,
|
|
||||||
handleCloseOptionsPanel,
|
|
||||||
!shouldPinOptionsPanel
|
|
||||||
);
|
|
||||||
|
|
||||||
const setCloseOptionsPanelTimer = () => {
|
const setCloseOptionsPanelTimer = () => {
|
||||||
timeoutIdRef.current = window.setTimeout(
|
timeoutIdRef.current = window.setTimeout(
|
||||||
() => handleCloseOptionsPanel(),
|
() => handleCloseOptionsPanel(),
|
||||||
@ -126,6 +118,21 @@ const InvokeOptionsPanel = (props: Props) => {
|
|||||||
dispatch(setDoesCanvasNeedScaling(true));
|
dispatch(setDoesCanvasNeedScaling(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleClickOutside(e: MouseEvent) {
|
||||||
|
if (
|
||||||
|
optionsPanelRef.current &&
|
||||||
|
!optionsPanelRef.current.contains(e.target as Node)
|
||||||
|
) {
|
||||||
|
handleCloseOptionsPanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [handleCloseOptionsPanel]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
nodeRef={optionsPanelRef}
|
nodeRef={optionsPanelRef}
|
||||||
@ -153,7 +160,7 @@ const InvokeOptionsPanel = (props: Props) => {
|
|||||||
<div
|
<div
|
||||||
className="options-panel"
|
className="options-panel"
|
||||||
ref={optionsPanelContainerRef}
|
ref={optionsPanelContainerRef}
|
||||||
onMouseLeave={(e: MouseEvent<HTMLDivElement>) => {
|
onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (e.target !== optionsPanelContainerRef.current) {
|
if (e.target !== optionsPanelContainerRef.current) {
|
||||||
cancelCloseOptionsPanelTimer();
|
cancelCloseOptionsPanelTimer();
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,9 +6,7 @@ import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
|||||||
import NodesWIP from 'common/components/WorkInProgress/NodesWIP';
|
import NodesWIP from 'common/components/WorkInProgress/NodesWIP';
|
||||||
import { PostProcessingWIP } from 'common/components/WorkInProgress/PostProcessingWIP';
|
import { PostProcessingWIP } from 'common/components/WorkInProgress/PostProcessingWIP';
|
||||||
import ImageToImageIcon from 'common/icons/ImageToImageIcon';
|
import ImageToImageIcon from 'common/icons/ImageToImageIcon';
|
||||||
import InpaintIcon from 'common/icons/InpaintIcon';
|
|
||||||
import NodesIcon from 'common/icons/NodesIcon';
|
import NodesIcon from 'common/icons/NodesIcon';
|
||||||
import OutpaintIcon from 'common/icons/OutpaintIcon';
|
|
||||||
import PostprocessingIcon from 'common/icons/PostprocessingIcon';
|
import PostprocessingIcon from 'common/icons/PostprocessingIcon';
|
||||||
import TextToImageIcon from 'common/icons/TextToImageIcon';
|
import TextToImageIcon from 'common/icons/TextToImageIcon';
|
||||||
import {
|
import {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user