diff --git a/.gitignore b/.gitignore index 780356437f..494f328308 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ hs_err_pid* # web -/sources/web/node_modules/ dist/ ## IDEA configs diff --git a/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu.tsx b/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu.tsx index 1392d9a363..e7bd3865c9 100644 --- a/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu.tsx +++ b/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu.tsx @@ -35,7 +35,7 @@ export const TreeNodeMenu = observer(function TreeNodeMenu({ selected, }) { const connectionsInfoResource = useService(ConnectionInfoResource); - const menu = useMenu(MENU_NAV_TREE); + const menu = useMenu({ menu: MENU_NAV_TREE }); menu.context.set(DATA_CONTEXT_NAV_NODE, node); menu.context.set(DATA_CONTEXT_NAV_NODE_ACTIONS, actions); diff --git a/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx b/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx index de867e2098..193dfbe8d8 100644 --- a/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx @@ -67,7 +67,7 @@ const MenuBarElement = observer(function MenuBarElement({ item, menuData, nestedMenuSettings, - style + style, }) { const styles = useStyles(style); @@ -165,11 +165,11 @@ const SubMenuItem = observer(function SubmenuItem({ item, menuData, nestedMenuSettings, - style + style, }) { const styles = useStyles(style); const menuService = useService(MenuService); - const subMenuData = useMenu(item.menu, menuData.context); + const subMenuData = useMenu({ menu: item.menu, context: menuData.context }); const handler = menuService.getHandler(menuData.context); const loading = handler?.isLoading?.(menuData.context); diff --git a/webapp/packages/core-ui/src/ContextMenu/MenuTrigger.tsx b/webapp/packages/core-ui/src/ContextMenu/MenuTrigger.tsx index 0051031428..47c8f650ec 100644 --- a/webapp/packages/core-ui/src/ContextMenu/MenuTrigger.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/MenuTrigger.tsx @@ -348,7 +348,7 @@ export const MenuInnerTrigger = observer(function Tab({ const translate = useTranslate(); const tabContext = useMemo(() => ({ tabId }), [tabId]); const { state, getInfo, handleClose, handleOpen } = useTab(tabId, onOpen, onClose, onClick); - const menu = useMenu(MENU_TAB, menuContext); + const menu = useMenu({ + menu: MENU_TAB, + context: menuContext, + }); const info = getInfo(); const [menuOpened, switchState] = useState(false); diff --git a/webapp/packages/core-view/src/Menu/DATA_CONTEXT_MENU_LOCAL.ts b/webapp/packages/core-view/src/Menu/DATA_CONTEXT_MENU_LOCAL.ts new file mode 100644 index 0000000000..746f32b36e --- /dev/null +++ b/webapp/packages/core-view/src/Menu/DATA_CONTEXT_MENU_LOCAL.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { createDataContext } from '../DataContext/createDataContext'; + +export const DATA_CONTEXT_MENU_LOCAL = createDataContext('menu-local', false); diff --git a/webapp/packages/core-view/src/Menu/IMenuItemsCreator.ts b/webapp/packages/core-view/src/Menu/IMenuItemsCreator.ts index 5655a6a46c..4e7b7c989f 100644 --- a/webapp/packages/core-view/src/Menu/IMenuItemsCreator.ts +++ b/webapp/packages/core-view/src/Menu/IMenuItemsCreator.ts @@ -7,6 +7,7 @@ */ import type { IAction } from '../Action/IAction'; +import type { DataContextGetter } from '../DataContext/DataContextGetter'; import type { IDataContextProvider } from '../DataContext/IDataContextProvider'; import type { IMenu } from './IMenu'; import type { IMenuItem } from './MenuItem/IMenuItem'; @@ -14,6 +15,8 @@ import type { IMenuItem } from './MenuItem/IMenuItem'; export type MenuCreatorItem = IMenuItem | IAction | IMenu; export interface IMenuItemsCreator { + menus?: IMenu[]; + contexts?: DataContextGetter[]; isApplicable?: (context: IDataContextProvider) => boolean; getItems: ( context: IDataContextProvider, diff --git a/webapp/packages/core-view/src/Menu/MenuService.ts b/webapp/packages/core-view/src/Menu/MenuService.ts index bf762824b4..68f25646e5 100644 --- a/webapp/packages/core-view/src/Menu/MenuService.ts +++ b/webapp/packages/core-view/src/Menu/MenuService.ts @@ -13,6 +13,8 @@ import { isAction } from '../Action/createAction'; import type { IAction } from '../Action/IAction'; import type { IDataContextProvider } from '../DataContext/IDataContextProvider'; import { isMenu } from './createMenu'; +import { DATA_CONTEXT_MENU } from './DATA_CONTEXT_MENU'; +import { DATA_CONTEXT_MENU_LOCAL } from './DATA_CONTEXT_MENU_LOCAL'; import type { IMenuHandler } from './IMenuHandler'; import type { IMenuItemsCreator, MenuCreatorItem } from './IMenuItemsCreator'; import type { IMenuActionItem } from './MenuItem/IMenuActionItem'; @@ -22,8 +24,8 @@ import { MenuSubMenuItem } from './MenuItem/MenuSubMenuItem'; @injectable() export class MenuService { - private handlers: Map; - private creators: IMenuItemsCreator[]; + private readonly handlers: Map; + private readonly creators: IMenuItemsCreator[]; constructor( private readonly actionService: ActionService, @@ -102,6 +104,32 @@ export class MenuService { } } -function filterApplicable(context: IDataContextProvider): (creator: IMenuItemsCreator) => boolean { - return (creator: IMenuItemsCreator) => creator.isApplicable?.(context) ?? true; +function filterApplicable(contexts: IDataContextProvider): (creator: IMenuItemsCreator) => boolean { + const local = contexts.get(DATA_CONTEXT_MENU_LOCAL); + + return (creator: IMenuItemsCreator) => { + if (local) { + if (!creator.menus && !creator.contexts) { + return false; + } + + if (creator.menus) { + const applicable = creator.menus.some(menu => contexts.find(DATA_CONTEXT_MENU, menu)); + + if (!applicable) { + return false; + } + } + + if (creator.contexts) { + const applicable = creator.contexts.some(context => contexts.has(context)); + + if (!applicable) { + return false; + } + } + } + + return creator.isApplicable?.(contexts) ?? true; + }; } diff --git a/webapp/packages/core-view/src/Menu/useMenu.ts b/webapp/packages/core-view/src/Menu/useMenu.ts index 21c27373f0..ff8c70cde9 100644 --- a/webapp/packages/core-view/src/Menu/useMenu.ts +++ b/webapp/packages/core-view/src/Menu/useMenu.ts @@ -10,6 +10,7 @@ import { useObjectRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import type { IDataContext } from '../DataContext/IDataContext'; +import { DATA_CONTEXT_MENU_LOCAL } from './DATA_CONTEXT_MENU_LOCAL'; import type { IMenu } from './IMenu'; import type { IMenuItem } from './MenuItem/IMenuItem'; import { MenuService } from './MenuService'; @@ -22,9 +23,17 @@ export interface IMenuData { getItems: () => IMenuItem[]; } -export function useMenu(menu: IMenu, menuContext?: IDataContext): IMenuData { +interface IMenuOptions { + menu: IMenu; + context?: IDataContext; + local?: boolean; +} + +export function useMenu(options: IMenuOptions): IMenuData { const menuService = useService(MenuService); - const context = useMenuContext(menu, menuContext); + const context = useMenuContext(options.menu, options.context); + + context.set(DATA_CONTEXT_MENU_LOCAL, options.local); return useObjectRef(() => ({ isAvailable() { @@ -34,7 +43,7 @@ export function useMenu(menu: IMenu, menuContext?: IDataContext): IMenuData { return menuService.getMenu(this.context); }, }), { - menu, + menu: options.menu, context, }, ['isAvailable', 'getItems']); } diff --git a/webapp/packages/core-view/src/index.ts b/webapp/packages/core-view/src/index.ts index b2c668a152..923b6ac380 100644 --- a/webapp/packages/core-view/src/index.ts +++ b/webapp/packages/core-view/src/index.ts @@ -32,6 +32,7 @@ export * from './Menu/MenuItem/MenuLazyItem'; export * from './Menu/MenuItem/MenuSeparatorItem'; export * from './Menu/MenuItem/MenuSubMenuItem'; export * from './Menu/createMenu'; +export * from './Menu/DATA_CONTEXT_MENU_LOCAL'; export * from './Menu/DATA_CONTEXT_MENU_NESTED'; export * from './Menu/DATA_CONTEXT_MENU'; export * from './Menu/IMenu'; diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableHeaderMenu.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableHeaderMenu.tsx index 1e501838cf..7f4e99dd85 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableHeaderMenu.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableHeaderMenu.tsx @@ -87,7 +87,7 @@ export const TableHeaderMenu: PlaceholderComponent model, resultIndex, }) { - const menu = useMenu(DATA_VIEWER_DATA_MODEL_TOOLS_MENU); + const menu = useMenu({ menu: DATA_VIEWER_DATA_MODEL_TOOLS_MENU }); menu.context.set(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL, model); menu.context.set(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX, resultIndex); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index 3cad4d7c05..4acb78f7c5 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -46,7 +46,7 @@ const styles = css` export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(function DDLViewerTabPanel({ nodeId, folderId }) { const style = useStyles(styles); const controller = useController(DdlViewerController, nodeId); - const menu = useMenu(MENU_DDL_VIEWER_FOOTER); + const menu = useMenu({ menu: MENU_DDL_VIEWER_FOOTER }); useEffect(() => { controller.load(); diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx index a8a404e4d1..b1369e45ae 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx @@ -150,7 +150,7 @@ const ItemName = observer(function ItemName({ property, }) { const connectionsInfoResource = useService(ConnectionInfoResource); - const menu = useMenu(MENU_NAV_TREE); + const menu = useMenu({ menu: MENU_NAV_TREE }); menu.context.set(DATA_CONTEXT_NAV_NODE, node); const connection = connectionsInfoResource.getConnectionForNode(node.id); diff --git a/webapp/packages/plugin-settings-menu/src/SettingsMenu/SettingsMenu.tsx b/webapp/packages/plugin-settings-menu/src/SettingsMenu/SettingsMenu.tsx index 90d3de89a7..487209ca5c 100644 --- a/webapp/packages/plugin-settings-menu/src/SettingsMenu/SettingsMenu.tsx +++ b/webapp/packages/plugin-settings-menu/src/SettingsMenu/SettingsMenu.tsx @@ -9,17 +9,17 @@ import { observer } from 'mobx-react-lite'; import styled from 'reshadow'; +import { topMenuStyles } from '@cloudbeaver/core-app'; +import { Icon } from '@cloudbeaver/core-blocks'; import { useStyles } from '@cloudbeaver/core-theming'; import { ContextMenu } from '@cloudbeaver/core-ui'; import { useMenu } from '@cloudbeaver/core-view'; -import { Icon } from '@cloudbeaver/core-blocks'; -import { topMenuStyles } from '@cloudbeaver/core-app'; import { settingsMenuStyles } from './settingsMenuStyles'; import { TOP_NAV_BAR_SETTINGS_MENU } from './TOP_NAV_BAR_SETTINGS_MENU'; export const SettingsMenu = observer(function SettingsMenu() { - const menu = useMenu(TOP_NAV_BAR_SETTINGS_MENU); + const menu = useMenu({ menu: TOP_NAV_BAR_SETTINGS_MENU }); return styled(useStyles(settingsMenuStyles))(