Fixes bug causing gallery to close on context menu open

This commit is contained in:
psychedelicious 2022-11-20 13:00:03 +11:00 committed by blessedcoolant
parent 76e7e82f5e
commit b6dd5b664c
5 changed files with 76 additions and 39 deletions

View File

@ -1,25 +1,37 @@
import { RefObject, useEffect } from 'react';
import { RefObject, useEffect, useRef } from 'react';
import { Rect } from 'react-konva';
const useClickOutsideWatcher = (
ref: RefObject<HTMLElement>,
callback: () => void,
req = true
) => {
const watchers: {
ref: RefObject<HTMLElement>;
enable: boolean;
callback: () => void;
}[] = [];
const useClickOutsideWatcher = () => {
useEffect(() => {
function handleClickOutside(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) {
callback();
}
}
if (req) {
document.addEventListener('mousedown', handleClickOutside);
watchers.forEach(({ ref, enable, callback }) => {
if (enable && ref.current && !ref.current.contains(e.target as Node)) {
console.log('callback');
callback();
}
});
}
document.addEventListener('mousedown', handleClickOutside);
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;

View File

@ -7,7 +7,10 @@ import {
useToast,
} from '@chakra-ui/react';
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 DeleteImageModal from './DeleteImageModal';
import { memo, useState } from 'react';
@ -153,10 +156,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
return (
<ContextMenu.Root
// onOpenChange={(open: boolean) => {
// dispatch(setShouldHoldGalleryOpen(open));
// dispatch(setShouldShowGallery(true));
// }}
onOpenChange={(open: boolean) => {
dispatch(setShouldHoldGalleryOpen(open));
}}
>
<ContextMenu.Trigger>
<Box
@ -204,6 +206,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
<ContextMenu.Content
className="hoverable-image-context-menu"
sticky={'always'}
onInteractOutside={(e) => {
e.detail.originalEvent.preventDefault();
}}
>
<ContextMenu.Item
onClickCapture={handleUsePrompt}

View File

@ -3,6 +3,7 @@ import { NumberSize, Resizable } from 're-resizable';
import {
ChangeEvent,
useCallback,
useEffect,
useLayoutEffect,
useRef,
@ -39,7 +40,6 @@ import { BiReset } from 'react-icons/bi';
import IAICheckbox from 'common/components/IAICheckbox';
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
import _ from 'lodash';
import useClickOutsideWatcher from 'common/hooks/useClickOutsideWatcher';
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
@ -124,15 +124,15 @@ export default function ImageGallery() {
shouldPinGallery && dispatch(setDoesCanvasNeedScaling(true));
};
const handleCloseGallery = () => {
const handleCloseGallery = useCallback(() => {
dispatch(setShouldShowGallery(false));
dispatch(setShouldHoldGalleryOpen(false));
dispatch(
setGalleryScrollPosition(
galleryContainerRef.current ? galleryContainerRef.current.scrollTop : 0
)
);
dispatch(setShouldHoldGalleryOpen(false));
};
}, [dispatch]);
const handleClickLoadMore = () => {
dispatch(requestImages(currentCategory));
@ -144,6 +144,7 @@ export default function ImageGallery() {
};
const setCloseGalleryTimer = () => {
if (shouldHoldGalleryOpen) return;
timeoutIdRef.current = window.setTimeout(() => handleCloseGallery(), 500);
};
@ -273,12 +274,25 @@ export default function ImageGallery() {
setShouldShowButtons(galleryWidth >= 280);
}, [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 (
<CSSTransition
nodeRef={galleryRef}
in={shouldShowGallery || (shouldHoldGalleryOpen && !shouldPinGallery)}
in={shouldShowGallery || shouldHoldGalleryOpen}
unmountOnExit
timeout={200}
classNames="image-gallery-wrapper"
@ -288,6 +302,7 @@ export default function ImageGallery() {
style={{ zIndex: shouldPinGallery ? 1 : 100 }}
data-pinned={shouldPinGallery}
ref={galleryRef}
// onMouseLeave={setCloseGalleryTimer}
onMouseLeave={!shouldPinGallery ? setCloseGalleryTimer : undefined}
onMouseEnter={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
onMouseOver={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}

View File

@ -1,12 +1,11 @@
import { Tooltip } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
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 { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
import { CSSTransition } from 'react-transition-group';
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
import useClickOutsideWatcher from 'common/hooks/useClickOutsideWatcher';
import {
OptionsState,
setOptionsPanelScrollPosition,
@ -101,15 +100,8 @@ const InvokeOptionsPanel = (props: Props) => {
);
dispatch(setShouldShowOptionsPanel(false));
dispatch(setShouldHoldOptionsPanelOpen(false));
// dispatch(setDoesCanvasNeedScaling(true));
}, [dispatch, shouldPinOptionsPanel]);
useClickOutsideWatcher(
optionsPanelRef,
handleCloseOptionsPanel,
!shouldPinOptionsPanel
);
const setCloseOptionsPanelTimer = () => {
timeoutIdRef.current = window.setTimeout(
() => handleCloseOptionsPanel(),
@ -126,6 +118,21 @@ const InvokeOptionsPanel = (props: Props) => {
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 (
<CSSTransition
nodeRef={optionsPanelRef}
@ -153,7 +160,7 @@ const InvokeOptionsPanel = (props: Props) => {
<div
className="options-panel"
ref={optionsPanelContainerRef}
onMouseLeave={(e: MouseEvent<HTMLDivElement>) => {
onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
if (e.target !== optionsPanelContainerRef.current) {
cancelCloseOptionsPanelTimer();
} else {

View File

@ -6,9 +6,7 @@ import { RootState, useAppDispatch, useAppSelector } from 'app/store';
import NodesWIP from 'common/components/WorkInProgress/NodesWIP';
import { PostProcessingWIP } from 'common/components/WorkInProgress/PostProcessingWIP';
import ImageToImageIcon from 'common/icons/ImageToImageIcon';
import InpaintIcon from 'common/icons/InpaintIcon';
import NodesIcon from 'common/icons/NodesIcon';
import OutpaintIcon from 'common/icons/OutpaintIcon';
import PostprocessingIcon from 'common/icons/PostprocessingIcon';
import TextToImageIcon from 'common/icons/TextToImageIcon';
import {