mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): abstract out the global menu close trigger
This logic is moved into a hook. This is needed for our context menus to close when the user clicks something in reactflow. It needed to be extended to support menus also.
This commit is contained in:
parent
3423b5848f
commit
61060f032a
@ -22,7 +22,7 @@ import {
|
|||||||
PortalProps,
|
PortalProps,
|
||||||
useEventListener,
|
useEventListener,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
MutableRefObject,
|
MutableRefObject,
|
||||||
@ -49,10 +49,6 @@ export function IAIContextMenu<T extends HTMLElement = HTMLElement>(
|
|||||||
const [position, setPosition] = useState<[number, number]>([0, 0]);
|
const [position, setPosition] = useState<[number, number]>([0, 0]);
|
||||||
const targetRef = useRef<T>(null);
|
const targetRef = useRef<T>(null);
|
||||||
|
|
||||||
const globalContextMenuCloseTrigger = useAppSelector(
|
|
||||||
(state) => state.ui.globalContextMenuCloseTrigger
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -70,11 +66,12 @@ export function IAIContextMenu<T extends HTMLElement = HTMLElement>(
|
|||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
useEffect(() => {
|
const onClose = useCallback(() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setIsDeferredOpen(false);
|
setIsDeferredOpen(false);
|
||||||
setIsRendered(false);
|
setIsRendered(false);
|
||||||
}, [globalContextMenuCloseTrigger]);
|
}, []);
|
||||||
|
useGlobalMenuCloseTrigger(onClose);
|
||||||
|
|
||||||
useEventListener('contextmenu', (e) => {
|
useEventListener('contextmenu', (e) => {
|
||||||
if (
|
if (
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reactflow background element somehow prevents the chakra `useOutsideClick()` hook from working.
|
||||||
|
* With a menu open, clicking on the reactflow background element doesn't close the menu.
|
||||||
|
*
|
||||||
|
* Reactflow does provide an `onPaneClick` to handle clicks on the background element, but it is not
|
||||||
|
* straightforward to programatically close the menu.
|
||||||
|
*
|
||||||
|
* As a (hopefully temporary) workaround, we will use a dirty hack:
|
||||||
|
* - create `globalMenuCloseTrigger: number` in `ui` slice
|
||||||
|
* - increment it in `onPaneClick`
|
||||||
|
* - `useEffect()` to close the menu when `globalMenuCloseTrigger` changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const useGlobalMenuCloseTrigger = (onClose: () => void) => {
|
||||||
|
const globalMenuCloseTrigger = useAppSelector(
|
||||||
|
(state) => state.ui.globalMenuCloseTrigger
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onClose();
|
||||||
|
}, [globalMenuCloseTrigger, onClose]);
|
||||||
|
};
|
@ -4,7 +4,7 @@ import { stateSelector } from 'app/store/store';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { $flow } from 'features/nodes/store/reactFlowInstance';
|
import { $flow } from 'features/nodes/store/reactFlowInstance';
|
||||||
import { contextMenusClosed } from 'features/ui/store/uiSlice';
|
import { bumpGlobalMenuCloseTrigger } from 'features/ui/store/uiSlice';
|
||||||
import { MouseEvent, useCallback, useRef } from 'react';
|
import { MouseEvent, useCallback, useRef } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import {
|
import {
|
||||||
@ -153,7 +153,7 @@ export const Flow = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handlePaneClick = useCallback(() => {
|
const handlePaneClick = useCallback(() => {
|
||||||
dispatch(contextMenusClosed());
|
dispatch(bumpGlobalMenuCloseTrigger());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const onInit: OnInit = useCallback((flow) => {
|
const onInit: OnInit = useCallback((flow) => {
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
NODE_WIDTH,
|
NODE_WIDTH,
|
||||||
} from 'features/nodes/types/constants';
|
} from 'features/nodes/types/constants';
|
||||||
import { zNodeStatus } from 'features/nodes/types/invocation';
|
import { zNodeStatus } from 'features/nodes/types/invocation';
|
||||||
import { contextMenusClosed } from 'features/ui/store/uiSlice';
|
import { bumpGlobalMenuCloseTrigger } from 'features/ui/store/uiSlice';
|
||||||
import {
|
import {
|
||||||
MouseEvent,
|
MouseEvent,
|
||||||
PropsWithChildren,
|
PropsWithChildren,
|
||||||
@ -70,7 +70,7 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
|||||||
if (!e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey) {
|
if (!e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey) {
|
||||||
dispatch(nodeExclusivelySelected(nodeId));
|
dispatch(nodeExclusivelySelected(nodeId));
|
||||||
}
|
}
|
||||||
dispatch(contextMenusClosed());
|
dispatch(bumpGlobalMenuCloseTrigger());
|
||||||
},
|
},
|
||||||
[dispatch, nodeId]
|
[dispatch, nodeId]
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
MenuList,
|
MenuList,
|
||||||
Spacer,
|
Spacer,
|
||||||
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -24,9 +25,12 @@ import HotkeysModal from './HotkeysModal/HotkeysModal';
|
|||||||
import InvokeAILogoComponent from './InvokeAILogoComponent';
|
import InvokeAILogoComponent from './InvokeAILogoComponent';
|
||||||
import SettingsModal from './SettingsModal/SettingsModal';
|
import SettingsModal from './SettingsModal/SettingsModal';
|
||||||
import StatusIndicator from './StatusIndicator';
|
import StatusIndicator from './StatusIndicator';
|
||||||
|
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
||||||
|
|
||||||
const SiteHeader = () => {
|
const SiteHeader = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
useGlobalMenuCloseTrigger(onClose);
|
||||||
|
|
||||||
const isBugLinkEnabled = useFeatureStatus('bugLink').isFeatureEnabled;
|
const isBugLinkEnabled = useFeatureStatus('bugLink').isFeatureEnabled;
|
||||||
const isDiscordLinkEnabled = useFeatureStatus('discordLink').isFeatureEnabled;
|
const isDiscordLinkEnabled = useFeatureStatus('discordLink').isFeatureEnabled;
|
||||||
@ -46,7 +50,7 @@ const SiteHeader = () => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<StatusIndicator />
|
<StatusIndicator />
|
||||||
|
|
||||||
<Menu>
|
<Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose}>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IAIIconButton}
|
as={IAIIconButton}
|
||||||
variant="link"
|
variant="link"
|
||||||
|
@ -16,7 +16,7 @@ export const initialUIState: UIState = {
|
|||||||
shouldShowEmbeddingPicker: false,
|
shouldShowEmbeddingPicker: false,
|
||||||
shouldAutoChangeDimensions: false,
|
shouldAutoChangeDimensions: false,
|
||||||
favoriteSchedulers: [],
|
favoriteSchedulers: [],
|
||||||
globalContextMenuCloseTrigger: 0,
|
globalMenuCloseTrigger: 0,
|
||||||
panels: {},
|
panels: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ export const uiSlice = createSlice({
|
|||||||
setShouldAutoChangeDimensions: (state, action: PayloadAction<boolean>) => {
|
setShouldAutoChangeDimensions: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldAutoChangeDimensions = action.payload;
|
state.shouldAutoChangeDimensions = action.payload;
|
||||||
},
|
},
|
||||||
contextMenusClosed: (state) => {
|
bumpGlobalMenuCloseTrigger: (state) => {
|
||||||
state.globalContextMenuCloseTrigger += 1;
|
state.globalMenuCloseTrigger += 1;
|
||||||
},
|
},
|
||||||
panelsChanged: (
|
panelsChanged: (
|
||||||
state,
|
state,
|
||||||
@ -88,7 +88,7 @@ export const {
|
|||||||
favoriteSchedulersChanged,
|
favoriteSchedulersChanged,
|
||||||
toggleEmbeddingPicker,
|
toggleEmbeddingPicker,
|
||||||
setShouldAutoChangeDimensions,
|
setShouldAutoChangeDimensions,
|
||||||
contextMenusClosed,
|
bumpGlobalMenuCloseTrigger,
|
||||||
panelsChanged,
|
panelsChanged,
|
||||||
} = uiSlice.actions;
|
} = uiSlice.actions;
|
||||||
|
|
||||||
|
@ -24,6 +24,6 @@ export interface UIState {
|
|||||||
shouldShowEmbeddingPicker: boolean;
|
shouldShowEmbeddingPicker: boolean;
|
||||||
shouldAutoChangeDimensions: boolean;
|
shouldAutoChangeDimensions: boolean;
|
||||||
favoriteSchedulers: ParameterScheduler[];
|
favoriteSchedulers: ParameterScheduler[];
|
||||||
globalContextMenuCloseTrigger: number;
|
globalMenuCloseTrigger: number;
|
||||||
panels: Record<string, string>;
|
panels: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user