mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): de-jank context menu
There was a lot of convoluted, janky logic related to trying to not mount the context menu's portal until its needed. This was in the library where the component was originally copied from. I've removed that and resolved the jank, at the cost of there being an extra portal for each instance of the context menu. Don't think this is going to be an issue. If it is, the whole context menu could be refactored to be a singleton.
This commit is contained in:
parent
968fb655a4
commit
7d93329401
@ -1,16 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* This is a copy-paste of https://github.com/lukasbach/chakra-ui-contextmenu with a small change.
|
* Adapted from https://github.com/lukasbach/chakra-ui-contextmenu
|
||||||
*
|
|
||||||
* 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 `globalContextMenuCloseTrigger: number` in `ui` slice
|
|
||||||
* - increment it in `onPaneClick` (and wherever else we want to close the menu)
|
|
||||||
* - `useEffect()` to close the menu when `globalContextMenuCloseTrigger` changes
|
|
||||||
*/
|
*/
|
||||||
import type {
|
import type {
|
||||||
ChakraProps,
|
ChakraProps,
|
||||||
@ -18,9 +7,9 @@ import type {
|
|||||||
MenuProps,
|
MenuProps,
|
||||||
PortalProps,
|
PortalProps,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { Portal, useEventListener } from '@chakra-ui/react';
|
import { Portal, useDisclosure, useEventListener } from '@chakra-ui/react';
|
||||||
import { InvMenu, InvMenuButton } from 'common/components/InvMenu/wrapper';
|
import { InvMenu, InvMenuButton } from 'common/components/InvMenu/wrapper';
|
||||||
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
|
||||||
import { typedMemo } from 'common/util/typedMemo';
|
import { typedMemo } from 'common/util/typedMemo';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
@ -34,94 +23,89 @@ export interface InvContextMenuProps<T extends HTMLElement = HTMLDivElement> {
|
|||||||
|
|
||||||
export const InvContextMenu = typedMemo(
|
export const InvContextMenu = typedMemo(
|
||||||
<T extends HTMLElement = HTMLElement>(props: InvContextMenuProps<T>) => {
|
<T extends HTMLElement = HTMLElement>(props: InvContextMenuProps<T>) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [isRendered, setIsRendered] = useState(false);
|
const [position, setPosition] = useState([-1, -1]);
|
||||||
const [isDeferredOpen, setIsDeferredOpen] = useState(false);
|
|
||||||
const [position, setPosition] = useState<[number, number]>([0, 0]);
|
|
||||||
const targetRef = useRef<T>(null);
|
const targetRef = useRef<T>(null);
|
||||||
|
const lastPositionRef = useRef([-1, -1]);
|
||||||
|
const timeoutRef = useRef(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useGlobalMenuClose(onClose);
|
||||||
if (isOpen) {
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsRendered(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsDeferredOpen(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setIsDeferredOpen(false);
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
setIsRendered(isOpen);
|
|
||||||
}, 1000);
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
const onClose = useCallback(() => {
|
const onContextMenu = useCallback(
|
||||||
setIsOpen(false);
|
(e: MouseEvent) => {
|
||||||
setIsDeferredOpen(false);
|
if (e.shiftKey) {
|
||||||
setIsRendered(false);
|
onClose();
|
||||||
}, []);
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
targetRef.current?.contains(e.target as HTMLElement) ||
|
||||||
|
e.target === targetRef.current
|
||||||
|
) {
|
||||||
|
// clear pending delayed open
|
||||||
|
window.clearTimeout(timeoutRef.current);
|
||||||
|
e.preventDefault();
|
||||||
|
if (
|
||||||
|
lastPositionRef.current[0] !== e.pageX ||
|
||||||
|
lastPositionRef.current[1] !== e.pageY
|
||||||
|
) {
|
||||||
|
// if the mouse moved, we need to close, wait for animation and reopen the menu at the new position
|
||||||
|
onClose();
|
||||||
|
timeoutRef.current = window.setTimeout(() => {
|
||||||
|
onOpen();
|
||||||
|
setPosition([e.pageX, e.pageY]);
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
// else we can just open the menu at the current position
|
||||||
|
onOpen();
|
||||||
|
setPosition([e.pageX, e.pageY]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastPositionRef.current = [e.pageX, e.pageY];
|
||||||
|
},
|
||||||
|
[onClose, onOpen]
|
||||||
|
);
|
||||||
|
|
||||||
// This is the change from the original chakra-ui-contextmenu
|
useEffect(
|
||||||
// Close all menus when the globalContextMenuCloseTrigger changes
|
() => () => {
|
||||||
useGlobalMenuCloseTrigger(onClose);
|
window.clearTimeout(timeoutRef.current);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEventListener('contextmenu', (e) => {
|
useEventListener('contextmenu', onContextMenu);
|
||||||
if (
|
|
||||||
targetRef.current?.contains(e.target as HTMLElement) ||
|
|
||||||
e.target === targetRef.current
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsOpen(true);
|
|
||||||
setPosition([e.pageX, e.pageY]);
|
|
||||||
} else {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onCloseHandler = useCallback(() => {
|
|
||||||
props.menuProps?.onClose?.();
|
|
||||||
setIsOpen(false);
|
|
||||||
}, [props.menuProps]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.children(targetRef)}
|
{props.children(targetRef)}
|
||||||
{isRendered && (
|
<Portal {...props.portalProps}>
|
||||||
<Portal {...props.portalProps}>
|
<InvMenu
|
||||||
<InvMenu
|
isLazy
|
||||||
isLazy
|
isOpen={isOpen}
|
||||||
isOpen={isDeferredOpen}
|
gutter={0}
|
||||||
gutter={0}
|
placement="auto-end"
|
||||||
onClose={onCloseHandler}
|
onClose={onClose}
|
||||||
placement="auto-end"
|
{...props.menuProps}
|
||||||
{...props.menuProps}
|
>
|
||||||
>
|
<InvMenuButton
|
||||||
<InvMenuButton
|
aria-hidden={true}
|
||||||
aria-hidden={true}
|
w={1}
|
||||||
w={1}
|
h={1}
|
||||||
h={1}
|
position="absolute"
|
||||||
position="absolute"
|
left={position[0]}
|
||||||
left={position[0]}
|
top={position[1]}
|
||||||
top={position[1]}
|
cursor="default"
|
||||||
cursor="default"
|
bg="transparent"
|
||||||
bg="transparent"
|
size="sm"
|
||||||
size="sm"
|
_hover={_hover}
|
||||||
_hover={_hover}
|
pointerEvents="none"
|
||||||
{...props.menuButtonProps}
|
{...props.menuButtonProps}
|
||||||
/>
|
/>
|
||||||
{props.renderMenu()}
|
{props.renderMenu()}
|
||||||
</InvMenu>
|
</InvMenu>
|
||||||
</Portal>
|
</Portal>
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const _hover: ChakraProps['_hover'] = { bg: 'transparent' };
|
const _hover: ChakraProps['_hover'] = { bg: 'transparent' };
|
||||||
|
|
||||||
Object.assign(InvContextMenu, {
|
|
||||||
displayName: 'InvContextMenu',
|
|
||||||
});
|
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
MenuList as ChakraMenuList,
|
MenuList as ChakraMenuList,
|
||||||
Portal,
|
Portal,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { skipMouseEvent } from 'common/util/skipMouseEvent';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { menuListMotionProps } from './constants';
|
import { menuListMotionProps } from './constants';
|
||||||
@ -16,6 +17,7 @@ export const InvMenuList = memo(
|
|||||||
<ChakraMenuList
|
<ChakraMenuList
|
||||||
ref={ref}
|
ref={ref}
|
||||||
motionProps={menuListMotionProps}
|
motionProps={menuListMotionProps}
|
||||||
|
onContextMenu={skipMouseEvent}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Portal>
|
</Portal>
|
||||||
|
@ -15,7 +15,7 @@ const $onCloseCallbacks = atom<CB[]>([]);
|
|||||||
* This hook provides a way to close all menus by calling `onCloseGlobal()`. Menus that want to be closed
|
* This hook provides a way to close all menus by calling `onCloseGlobal()`. Menus that want to be closed
|
||||||
* in this way should register themselves by passing a callback to `useGlobalMenuCloseTrigger()`.
|
* in this way should register themselves by passing a callback to `useGlobalMenuCloseTrigger()`.
|
||||||
*/
|
*/
|
||||||
export const useGlobalMenuCloseTrigger = (onClose?: CB) => {
|
export const useGlobalMenuClose = (onClose?: CB) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!onClose) {
|
if (!onClose) {
|
||||||
return;
|
return;
|
8
invokeai/frontend/web/src/common/util/skipMouseEvent.ts
Normal file
8
invokeai/frontend/web/src/common/util/skipMouseEvent.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type { MouseEvent } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the default behavior of the event.
|
||||||
|
*/
|
||||||
|
export const skipMouseEvent = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
@ -12,7 +12,6 @@ import {
|
|||||||
import type { BoardId } from 'features/gallery/store/types';
|
import type { BoardId } from 'features/gallery/store/types';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import type { MouseEvent } from 'react';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaDownload, FaPlus } from 'react-icons/fa';
|
import { FaDownload, FaPlus } from 'react-icons/fa';
|
||||||
@ -90,13 +89,9 @@ const BoardContextMenu = ({
|
|||||||
}
|
}
|
||||||
}, [t, board_id, bulkDownload, dispatch]);
|
}, [t, board_id, bulkDownload, dispatch]);
|
||||||
|
|
||||||
const skipEvent = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderMenuFunc = useCallback(
|
const renderMenuFunc = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
<InvMenuList visibility="visible">
|
||||||
<InvMenuGroup title={boardName}>
|
<InvMenuGroup title={boardName}>
|
||||||
<InvMenuItem
|
<InvMenuItem
|
||||||
icon={<FaPlus />}
|
icon={<FaPlus />}
|
||||||
@ -131,7 +126,6 @@ const BoardContextMenu = ({
|
|||||||
isBulkDownloadEnabled,
|
isBulkDownloadEnabled,
|
||||||
isSelectedForAutoAdd,
|
isSelectedForAutoAdd,
|
||||||
setBoardToDelete,
|
setBoardToDelete,
|
||||||
skipEvent,
|
|
||||||
t,
|
t,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,6 @@ import { useAppSelector } from 'app/store/storeHooks';
|
|||||||
import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
|
import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
|
||||||
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
|
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
|
||||||
import { InvMenuList } from 'common/components/InvMenu/InvMenuList';
|
import { InvMenuList } from 'common/components/InvMenu/InvMenuList';
|
||||||
import type { MouseEvent } from 'react';
|
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
@ -17,10 +16,6 @@ type Props = {
|
|||||||
const ImageContextMenu = ({ imageDTO, children }: Props) => {
|
const ImageContextMenu = ({ imageDTO, children }: Props) => {
|
||||||
const selectionCount = useAppSelector((s) => s.gallery.selection.length);
|
const selectionCount = useAppSelector((s) => s.gallery.selection.length);
|
||||||
|
|
||||||
const skipEvent = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderMenuFunc = useCallback(() => {
|
const renderMenuFunc = useCallback(() => {
|
||||||
if (!imageDTO) {
|
if (!imageDTO) {
|
||||||
return null;
|
return null;
|
||||||
@ -28,18 +23,18 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => {
|
|||||||
|
|
||||||
if (selectionCount > 1) {
|
if (selectionCount > 1) {
|
||||||
return (
|
return (
|
||||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
<InvMenuList visibility="visible">
|
||||||
<MultipleSelectionMenuItems />
|
<MultipleSelectionMenuItems />
|
||||||
</InvMenuList>
|
</InvMenuList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
<InvMenuList visibility="visible">
|
||||||
<SingleSelectionMenuItems imageDTO={imageDTO} />
|
<SingleSelectionMenuItems imageDTO={imageDTO} />
|
||||||
</InvMenuList>
|
</InvMenuList>
|
||||||
);
|
);
|
||||||
}, [imageDTO, selectionCount, skipEvent]);
|
}, [imageDTO, selectionCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvContextMenu renderMenu={renderMenuFunc}>{children}</InvContextMenu>
|
<InvContextMenu renderMenu={renderMenuFunc}>{children}</InvContextMenu>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useToken } from '@chakra-ui/react';
|
import { useToken } from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
|
||||||
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
|
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
|
||||||
import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode';
|
import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode';
|
||||||
import { useWorkflowWatcher } from 'features/nodes/hooks/useWorkflowWatcher';
|
import { useWorkflowWatcher } from 'features/nodes/hooks/useWorkflowWatcher';
|
||||||
@ -158,7 +158,7 @@ export const Flow = memo(() => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { onCloseGlobal } = useGlobalMenuCloseTrigger();
|
const { onCloseGlobal } = useGlobalMenuClose();
|
||||||
const handlePaneClick = useCallback(() => {
|
const handlePaneClick = useCallback(() => {
|
||||||
onCloseGlobal();
|
onCloseGlobal();
|
||||||
}, [onCloseGlobal]);
|
}, [onCloseGlobal]);
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
workflowExposedFieldAdded,
|
workflowExposedFieldAdded,
|
||||||
workflowExposedFieldRemoved,
|
workflowExposedFieldRemoved,
|
||||||
} from 'features/nodes/store/workflowSlice';
|
} from 'features/nodes/store/workflowSlice';
|
||||||
import type { MouseEvent, ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaMinus, FaPlus } from 'react-icons/fa';
|
import { FaMinus, FaPlus } from 'react-icons/fa';
|
||||||
@ -32,10 +32,6 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
|
|||||||
const input = useFieldInputKind(nodeId, fieldName);
|
const input = useFieldInputKind(nodeId, fieldName);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const skipEvent = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const selectIsExposed = useMemo(
|
const selectIsExposed = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectWorkflowSlice, (workflow) => {
|
createSelector(selectWorkflowSlice, (workflow) => {
|
||||||
@ -101,7 +97,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
|
|||||||
const renderMenuFunc = useCallback(
|
const renderMenuFunc = useCallback(
|
||||||
() =>
|
() =>
|
||||||
!menuItems.length ? null : (
|
!menuItems.length ? null : (
|
||||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
<InvMenuList visibility="visible">
|
||||||
<InvMenuGroup
|
<InvMenuGroup
|
||||||
title={label || fieldTemplateTitle || t('nodes.unknownField')}
|
title={label || fieldTemplateTitle || t('nodes.unknownField')}
|
||||||
>
|
>
|
||||||
@ -109,7 +105,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
|
|||||||
</InvMenuGroup>
|
</InvMenuGroup>
|
||||||
</InvMenuList>
|
</InvMenuList>
|
||||||
),
|
),
|
||||||
[fieldTemplateTitle, label, menuItems, skipEvent, t]
|
[fieldTemplateTitle, label, menuItems, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -3,7 +3,7 @@ import { Box, useToken } from '@chakra-ui/react';
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay';
|
import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay';
|
||||||
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
|
||||||
import { useMouseOverNode } from 'features/nodes/hooks/useMouseOverNode';
|
import { useMouseOverNode } from 'features/nodes/hooks/useMouseOverNode';
|
||||||
import {
|
import {
|
||||||
nodeExclusivelySelected,
|
nodeExclusivelySelected,
|
||||||
@ -50,7 +50,7 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const opacity = useAppSelector((s) => s.nodes.nodeOpacity);
|
const opacity = useAppSelector((s) => s.nodes.nodeOpacity);
|
||||||
const { onCloseGlobal } = useGlobalMenuCloseTrigger();
|
const { onCloseGlobal } = useGlobalMenuClose();
|
||||||
|
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(e: MouseEvent<HTMLDivElement>) => {
|
(e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
InvMenuButton,
|
InvMenuButton,
|
||||||
InvMenuGroup,
|
InvMenuGroup,
|
||||||
} from 'common/components/InvMenu/wrapper';
|
} from 'common/components/InvMenu/wrapper';
|
||||||
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
|
||||||
import HotkeysModal from 'features/system/components/HotkeysModal/HotkeysModal';
|
import HotkeysModal from 'features/system/components/HotkeysModal/HotkeysModal';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -23,7 +23,7 @@ import SettingsModal from './SettingsModal';
|
|||||||
const SettingsMenu = () => {
|
const SettingsMenu = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
useGlobalMenuCloseTrigger(onClose);
|
useGlobalMenuClose(onClose);
|
||||||
|
|
||||||
const isBugLinkEnabled = useFeatureStatus('bugLink').isFeatureEnabled;
|
const isBugLinkEnabled = useFeatureStatus('bugLink').isFeatureEnabled;
|
||||||
const isDiscordLinkEnabled = useFeatureStatus('discordLink').isFeatureEnabled;
|
const isDiscordLinkEnabled = useFeatureStatus('discordLink').isFeatureEnabled;
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
InvMenuButton,
|
InvMenuButton,
|
||||||
InvMenuDivider,
|
InvMenuDivider,
|
||||||
} from 'common/components/InvMenu/wrapper';
|
} from 'common/components/InvMenu/wrapper';
|
||||||
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
|
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import DownloadWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem';
|
import DownloadWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem';
|
||||||
import NewWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem';
|
import NewWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem';
|
||||||
@ -21,7 +21,7 @@ import { PiDotsThreeOutlineFill } from 'react-icons/pi';
|
|||||||
const WorkflowLibraryMenu = () => {
|
const WorkflowLibraryMenu = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
useGlobalMenuCloseTrigger(onClose);
|
useGlobalMenuClose(onClose);
|
||||||
|
|
||||||
const isWorkflowLibraryEnabled =
|
const isWorkflowLibraryEnabled =
|
||||||
useFeatureStatus('workflowLibrary').isFeatureEnabled;
|
useFeatureStatus('workflowLibrary').isFeatureEnabled;
|
||||||
|
Loading…
Reference in New Issue
Block a user