diff --git a/src/components/menu/__tests__/menu.test.tsx b/src/components/menu/__tests__/menu.test.tsx index f702b31ff..89dea24b9 100644 --- a/src/components/menu/__tests__/menu.test.tsx +++ b/src/components/menu/__tests__/menu.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import renderer from 'react-test-renderer'; @@ -11,6 +11,7 @@ import { horizontalMenuClassName, verticalMenuClassName, } from '../base'; +import { MenuRef } from '../index'; const menuData = [ { @@ -118,6 +119,11 @@ const menuData = [ ]; const TEST_ID = 'test-id'; +function MenuTest(props) { + const ref = useRef(null); + return ; +} + describe('Test the Menu Component', () => { test('Match the List snapshot', () => { const component = renderer.create( @@ -306,4 +312,36 @@ describe('Test the Menu Component', () => { }); }); }); + + test('Dispose the Menu', () => { + const TEST_DATA1 = 'test1'; + const TEST_DATA2 = 'test2'; + const mockData = [ + { + id: TEST_DATA1, + name: TEST_DATA1, + title: TEST_DATA1, + data: [ + { + id: TEST_DATA2, + name: TEST_DATA2, + 'data-testid': TEST_DATA2, + }, + ], + }, + ]; + const menu = renderer.create(); + const menuNode: any = (menu as renderer.ReactTestRenderer).root.findByType( + Menu + ); + expect(menuNode._fiber).not.toBeUndefined(); + + const menuRef = menuNode._fiber.ref; + render(); + expect(menuRef?.current?.dispose).not.toBeUndefined(); + + menuRef.current?.dispose(); + const item = document.body.querySelectorAll('ul')[1]; + expect(item.style.opacity).toEqual('0'); + }); }); diff --git a/src/components/menu/menu.tsx b/src/components/menu/menu.tsx index 89b793c7f..4c123adfb 100644 --- a/src/components/menu/menu.tsx +++ b/src/components/menu/menu.tsx @@ -1,4 +1,10 @@ -import React, { useEffect, useCallback, useRef } from 'react'; +import React, { + useEffect, + useCallback, + useRef, + useImperativeHandle, + forwardRef, +} from 'react'; import { classNames } from 'mo/common/className'; import { debounce } from 'lodash'; import { mergeFunctions } from 'mo/common/utils'; @@ -56,7 +62,7 @@ const setPositionForSubMenu = ( subMenu.style.left = `${pos.x}px`; }; -export function Menu(props: React.PropsWithChildren) { +function MenuComp(props: React.PropsWithChildren, ref) { const { className, mode = MenuMode.Vertical, @@ -82,7 +88,7 @@ export function Menu(props: React.PropsWithChildren) { if (data.length > 0) { const renderMenusByData = (menus: IMenuProps[]) => { return menus.map((item: IMenuProps) => { - if (item.type === 'divider') return ; + if (item.type === 'divider') return ; const handleClick = mergeFunctions(onClick, item.onClick); if (item.data && item.data.length > 0) { @@ -200,6 +206,12 @@ export function Menu(props: React.PropsWithChildren) { }; }, []); + useImperativeHandle(ref, () => ({ + dispose: () => { + initialMenuStyle(); + }, + })); + return (
    ) {
); } + +export type MenuRef = { + dispose: () => void; +}; + +export const Menu = forwardRef>( + MenuComp +); diff --git a/src/controller/__tests__/menuBar.test.ts b/src/controller/__tests__/menuBar.test.ts index 0883e5d20..1ed3a2193 100644 --- a/src/controller/__tests__/menuBar.test.ts +++ b/src/controller/__tests__/menuBar.test.ts @@ -1,25 +1,30 @@ import { ID_SIDE_BAR } from 'mo/common/id'; import { MonacoService } from 'mo/monaco/monacoService'; -import { MenuBarService, BuiltinService } from 'mo/services'; +import { MenuBarService, BuiltinService, LayoutService } from 'mo/services'; import { constants, modules } from 'mo/services/builtinService/const'; import 'reflect-metadata'; import { container } from 'tsyringe'; import { MenuBarController } from '../menuBar'; +import { MenuBarMode } from 'mo/model/workbench/layout'; const menuBarController = container.resolve(MenuBarController); const menuBarService = container.resolve(MenuBarService); const monacoService = container.resolve(MonacoService); const builtinService = container.resolve(BuiltinService); +const layoutService = container.resolve(LayoutService); const mockEle = document.createElement('div'); describe('The menuBar controller', () => { test('Should support to inject the default value', () => { menuBarController.initView(); - - expect(menuBarService.getState().data).toEqual( + const mode = layoutService.getMenuBarMode(); + const menuBarData = menuBarController.getMenuBarDataByMode( + mode, modules.builtInMenuBarData() ); + + expect(menuBarService.getState().data).toEqual(menuBarData); menuBarService.reset(); }); @@ -160,4 +165,41 @@ describe('The menuBar controller', () => { mockExecute.mockClear(); menuBarService.update = originalUpdate; }); + + test('Should support to change the layout mode', () => { + const mockEvent = {} as any; + const mockItem = { id: constants.MENUBAR_MODE_HORIZONTAL }; + const mockExecute = jest.fn(); + const originalSetMenus = menuBarService.setMenus; + const originalUpdateMenuBarMode = menuBarController.updateMenuBarMode; + + // change default mode + const defaultMode = layoutService.getMenuBarMode(); + const anotherMode = + defaultMode === MenuBarMode.horizontal + ? MenuBarMode.vertical + : MenuBarMode.horizontal; + layoutService.setMenuBarMode(anotherMode); + menuBarController.initView(); + expect(layoutService.getMenuBarMode()).toBe(anotherMode); + + // update to horizontal mode + menuBarService.setMenus = mockExecute; + layoutService.setMenuBarMode(MenuBarMode.vertical); + menuBarController.onClick(mockEvent, mockItem); + expect(mockExecute).toBeCalled(); + mockExecute.mockClear(); + + // update to vertical mode + mockItem.id = constants.MENUBAR_MODE_VERTICAL; + layoutService.setMenuBarMode(MenuBarMode.horizontal); + menuBarController.onClick(mockEvent, mockItem); + expect(mockExecute).toBeCalled(); + mockExecute.mockClear(); + + menuBarService.setMenus = originalSetMenus; + menuBarController.updateMenuBarMode = originalUpdateMenuBarMode; + layoutService.reset(); + menuBarService.reset(); + }); }); diff --git a/src/controller/menuBar.ts b/src/controller/menuBar.ts index d288d455a..a879d2a7c 100644 --- a/src/controller/menuBar.ts +++ b/src/controller/menuBar.ts @@ -2,6 +2,7 @@ import 'reflect-metadata'; import { container, singleton } from 'tsyringe'; import { IActivityBarItem, IMenuBarItem } from 'mo/model'; import { MenuBarEvent } from 'mo/model/workbench/menuBar'; +import { MenuBarMode } from 'mo/model/workbench/layout'; import { Controller } from 'mo/react/controller'; import { IMenuBarService, @@ -25,6 +26,11 @@ export interface IMenuBarController extends Partial { updateMenuBar?: () => void; updateActivityBar?: () => void; updateSideBar?: () => void; + updateMenuBarMode?: (mode: keyof typeof MenuBarMode) => void; + getMenuBarDataByMode?: ( + mode: keyof typeof MenuBarMode, + menuData: IMenuBarItem[] + ) => IMenuBarItem[]; } @singleton() @@ -60,9 +66,16 @@ export class MenuBarController MENU_VIEW_STATUSBAR, MENU_QUICK_COMMAND, MENU_VIEW_PANEL, + MENUBAR_MODE_HORIZONTAL, + MENUBAR_MODE_VERTICAL, } = this.builtinService.getConstants(); if (builtInMenuBarData) { - this.menuBarService.setMenus(builtInMenuBarData); + const mode = this.layoutService.getMenuBarMode(); + const menuBarData = this.getMenuBarDataByMode( + mode, + builtInMenuBarData + ); + this.menuBarService.setMenus(menuBarData); } ([ [ACTION_QUICK_CREATE_FILE, () => this.createFile()], @@ -76,6 +89,14 @@ export class MenuBarController [MENU_QUICK_COMMAND, () => this.gotoQuickCommand()], [ID_SIDE_BAR, () => this.updateSideBar()], [MENU_VIEW_PANEL, () => this.updatePanel()], + [ + MENUBAR_MODE_HORIZONTAL, + () => this.updateMenuBarMode(MenuBarMode.horizontal), + ], + [ + MENUBAR_MODE_VERTICAL, + () => this.updateMenuBarMode(MenuBarMode.vertical), + ], ] as [string, () => void][]).forEach(([key, value]) => { if (key) { this.automation[key] = value; @@ -179,6 +200,13 @@ export class MenuBarController } }; + public updateMenuBarMode = (mode: keyof typeof MenuBarMode) => { + this.layoutService.setMenuBarMode(mode); + const { builtInMenuBarData } = this.builtinService.getModules(); + const menuBarData = this.getMenuBarDataByMode(mode, builtInMenuBarData); + this.menuBarService.setMenus(menuBarData); + }; + public updateStatusBar = () => { const hidden = this.layoutService.toggleStatusBarVisibility(); const { MENU_VIEW_STATUSBAR } = this.builtinService.getConstants(); @@ -200,4 +228,47 @@ export class MenuBarController QuickTogglePanelAction.ID ); }; + + /** + * Get the menu bar data after filtering out the menu contained in ids + * @param menuData + * @param ids + * @returns Filtered menu bar data + */ + private getFilteredMenuBarData( + menuData: IMenuBarItem[], + ids: (UniqueId | undefined)[] + ): IMenuBarItem[] { + const newData: IMenuBarItem[] = []; + if (Array.isArray(menuData)) { + menuData.forEach((item: IMenuBarItem) => { + if (ids.includes(item.id)) return; + const newItem = { ...item }; + if (Array.isArray(item.data) && item.data.length > 0) { + newItem.data = this.getFilteredMenuBarData(item.data, ids); + } + newData.push(newItem); + }); + } + return newData; + } + + public getMenuBarDataByMode( + mode: keyof typeof MenuBarMode, + menuData: IMenuBarItem[] + ): IMenuBarItem[] { + const { + MENUBAR_MODE_VERTICAL, + MENUBAR_MODE_HORIZONTAL, + } = this.builtinService.getConstants(); + const ids: (string | undefined)[] = []; + if (mode === MenuBarMode.horizontal) { + ids.push(MENUBAR_MODE_HORIZONTAL); + } else if (mode === MenuBarMode.vertical) { + ids.push(MENUBAR_MODE_VERTICAL); + } + + const menuBarData = this.getFilteredMenuBarData(menuData, ids); + return menuBarData; + } } diff --git a/src/extensions/locales-defaults/locales/en.json b/src/extensions/locales-defaults/locales/en.json index ff7b4b97e..9ea1a92a5 100644 --- a/src/extensions/locales-defaults/locales/en.json +++ b/src/extensions/locales-defaults/locales/en.json @@ -28,6 +28,8 @@ "menu.showPanel.title": "Toggle Panel", "menu.run": "Run", "menu.help": "Help", + "menu.menuBarHorizontal": "Menu Bar Horizontal Mode", + "menu.menuBarVertical": "Menu Bar Vertical Mode", "sidebar.explore.title": "Explorer", "sidebar.explore.folders": "Folders", "sidebar.explore.openEditor": "Open Editors", diff --git a/src/extensions/locales-defaults/locales/zh-CN.json b/src/extensions/locales-defaults/locales/zh-CN.json index b81f46dd8..38e8249f4 100644 --- a/src/extensions/locales-defaults/locales/zh-CN.json +++ b/src/extensions/locales-defaults/locales/zh-CN.json @@ -34,6 +34,8 @@ "menu.runTask": "运行任务", "menu.help": "帮助", "menu.about": "关于", + "menu.menuBarHorizontal": "菜单栏水平模式", + "menu.menuBarVertical": "菜单栏垂直模式", "sidebar.explore.title": "浏览", "sidebar.explore.openEditor": "打开的编辑器", "sidebar.explore.openEditor.group": "第 ${i} 组", diff --git a/src/i18n/localization.ts b/src/i18n/localization.ts index c5280cfc5..ebe4dba39 100644 --- a/src/i18n/localization.ts +++ b/src/i18n/localization.ts @@ -35,6 +35,8 @@ export type LocaleSourceIdType = { 'menu.showPanel.title': string; 'menu.run': string; 'menu.help': string; + 'menu.menuBarHorizontal': string; + 'menu.menuBarVertical': string; 'sidebar.explore.title': string; 'sidebar.explore.folders': string; 'sidebar.explore.openEditor': string; diff --git a/src/model/workbench/layout.ts b/src/model/workbench/layout.ts index c15574e04..57a5760c2 100644 --- a/src/model/workbench/layout.ts +++ b/src/model/workbench/layout.ts @@ -2,6 +2,12 @@ export enum Position { left = 'left', right = 'right', } + +export enum MenuBarMode { + horizontal = 'horizontal', + vertical = 'vertical', +} + export interface ViewVisibility { hidden: boolean; } @@ -12,6 +18,11 @@ export interface IPanelViewState extends ViewVisibility { export interface ISidebarViewState extends ViewVisibility { position: keyof typeof Position; } + +export interface IMenuBarViewState extends ViewVisibility { + mode: keyof typeof MenuBarMode; +} + export interface ILayout { splitPanePos: string[]; horizontalSplitPanePos: string[]; @@ -19,7 +30,7 @@ export interface ILayout { panel: IPanelViewState; statusBar: ViewVisibility; sidebar: ISidebarViewState; - menuBar: ViewVisibility; + menuBar: IMenuBarViewState; } export class LayoutModel implements ILayout { @@ -29,7 +40,7 @@ export class LayoutModel implements ILayout { public panel: IPanelViewState; public statusBar: ViewVisibility; public sidebar: ISidebarViewState; - public menuBar: ViewVisibility; + public menuBar: IMenuBarViewState; constructor( splitPanePos: string[] = ['300px', 'auto'], horizontalSplitPanePos = ['70%', 'auto'], @@ -37,7 +48,7 @@ export class LayoutModel implements ILayout { panel = { hidden: false, panelMaximized: false }, statusBar = { hidden: false }, sidebar = { hidden: false, position: Position.left }, - menuBar = { hidden: false } + menuBar = { hidden: false, mode: MenuBarMode.vertical } ) { this.splitPanePos = splitPanePos; this.horizontalSplitPanePos = horizontalSplitPanePos; diff --git a/src/model/workbench/menuBar.ts b/src/model/workbench/menuBar.ts index 343065b97..5819a5670 100644 --- a/src/model/workbench/menuBar.ts +++ b/src/model/workbench/menuBar.ts @@ -2,6 +2,7 @@ import React from 'react'; import { ISubMenuProps } from 'mo/components/menu/subMenu'; import { IMenuItemProps } from 'mo/components/menu'; import type { UniqueId } from 'mo/common/types'; +import { MenuBarMode } from './layout'; /** * The activity bar event definition */ @@ -22,6 +23,8 @@ export interface IMenuBarItem { export interface IMenuBar { data: IMenuBarItem[]; + mode?: keyof typeof MenuBarMode; + logo?: React.ReactNode; } export class MenuBarModel implements IMenuBar { public data: IMenuBarItem[]; diff --git a/src/services/builtinService/const.ts b/src/services/builtinService/const.ts index f05756543..11452cb67 100644 --- a/src/services/builtinService/const.ts +++ b/src/services/builtinService/const.ts @@ -86,6 +86,9 @@ export const constants = { CONTEXT_MENU_EXPLORER: 'sidebar.explore.title', CONTEXT_MENU_SEARCH: 'sidebar.search.title', CONTEXT_MENU_HIDE: 'menu.hideActivityBar', + MENUBAR_MODE_HORIZONTAL: 'menuBar.mode.horizontal', + MENUBAR_MODE_VERTICAL: 'menuBar.mode.vertical', + MENUBAR_MENU_MODE_DIVIDER: 'menuBar.modeDivider', }; export const modules = { @@ -621,6 +624,24 @@ export const modules = { id: constants.MENU_VIEW_PANEL, name: localize('menu.showPanel', 'Show Panel'), }, + { + id: constants.MENUBAR_MENU_MODE_DIVIDER, + type: 'divider', + }, + { + id: constants.MENUBAR_MODE_HORIZONTAL, + name: localize( + 'menu.menuBarHorizontal', + 'Menu Bar Horizontal Mode' + ), + }, + { + id: constants.MENUBAR_MODE_VERTICAL, + name: localize( + 'menu.menuBarVertical', + 'Menu Bar Vertical Mode' + ), + }, ], }, ], diff --git a/src/services/workbench/layoutService.ts b/src/services/workbench/layoutService.ts index 796382163..013a1c0a4 100644 --- a/src/services/workbench/layoutService.ts +++ b/src/services/workbench/layoutService.ts @@ -1,7 +1,12 @@ import { container, singleton } from 'tsyringe'; import { Component } from 'mo/react'; import { ID_APP } from 'mo/common/id'; -import { ILayout, Position, LayoutModel } from 'mo/model/workbench/layout'; +import { + ILayout, + Position, + LayoutModel, + MenuBarMode, +} from 'mo/model/workbench/layout'; export interface ILayoutService extends Component { /** @@ -48,6 +53,16 @@ export interface ILayoutService extends Component { * @unachieved */ setSideBarPosition(position: keyof typeof Position): void; + /** + * Set the mode of the MenuBar, default is `vertical` + * @param mode + * @unachieved + */ + setMenuBarMode(mode: keyof typeof MenuBarMode): void; + /** + * Get the mode of the MenuBar + */ + getMenuBarMode(): keyof typeof MenuBarMode; /** * Reset all layout data as default value */ @@ -135,6 +150,19 @@ export class LayoutService this.setState({ horizontalSplitPanePos }); } + public setMenuBarMode(mode: keyof typeof MenuBarMode): void { + const { menuBar } = this.state; + const { mode: preMode } = menuBar; + if (preMode !== mode) { + this.setState({ menuBar: { ...menuBar, mode, hidden: false } }); + } + } + + public getMenuBarMode(): keyof typeof MenuBarMode { + const { menuBar } = this.state; + return menuBar.mode; + } + public reset() { this.setState({ splitPanePos: ['300px', 'auto'], @@ -143,7 +171,7 @@ export class LayoutService panel: { hidden: false, panelMaximized: false }, statusBar: { hidden: false }, sidebar: { hidden: false, position: Position.left }, - menuBar: { hidden: false }, + menuBar: { hidden: false, mode: MenuBarMode.vertical }, }); } } diff --git a/src/workbench/__tests__/workbench.test.tsx b/src/workbench/__tests__/workbench.test.tsx index 758cd3f99..4c783496c 100644 --- a/src/workbench/__tests__/workbench.test.tsx +++ b/src/workbench/__tests__/workbench.test.tsx @@ -22,6 +22,8 @@ import { ISidebarViewState, LayoutModel, ViewVisibility, + IMenuBarViewState, + MenuBarMode, } from 'mo/model/workbench/layout'; import { drag } from '@test/utils'; import { select } from 'mo/common/dom'; @@ -43,7 +45,7 @@ describe('Test Workbench Component', () => { activityBar, layout.activityBar ); - const menuBarState = Object.assign( + const menuBarState = Object.assign( menuBar, layout.menuBar ); @@ -184,4 +186,15 @@ describe('Test Workbench Component', () => { rerender(); expect(select('.mo-statusBar')).not.toBeInTheDocument(); }); + + test('Should support to change the layout mode of MenuBar', async () => { + const workbench = workbenchModel(); + workbench.menuBar.mode = MenuBarMode.vertical; + const { rerender } = render(); + expect(select('.mo-menuBar')).toBeInTheDocument(); + + workbench.menuBar.mode = MenuBarMode.horizontal; + rerender(); + expect(select('.mo-menuBar--horizontal')).toBeInTheDocument(); + }); }); diff --git a/src/workbench/menuBar/__tests__/__snapshots__/menubar.test.tsx.snap b/src/workbench/menuBar/__tests__/__snapshots__/menubar.test.tsx.snap index 75f48bf29..d123be761 100644 --- a/src/workbench/menuBar/__tests__/__snapshots__/menubar.test.tsx.snap +++ b/src/workbench/menuBar/__tests__/__snapshots__/menubar.test.tsx.snap @@ -14,3 +14,227 @@ exports[`Test MenuBar Component Match the MenuBar snapshot 1`] = ` `; + +exports[`Test MenuBar Component Match the snapshot of menuBar in horizontal mode 1`] = ` +
+
+ +
+
    +
  • +
    + + test-id + + +
    +
      +
    • +
      + + New File + +
      +
    • +
    • +
      + + Open + +
      +
    • +
    • +
      + + test-data + +
      +
    • +
    +
  • +
+
+`; diff --git a/src/workbench/menuBar/__tests__/menubar.test.tsx b/src/workbench/menuBar/__tests__/menubar.test.tsx index 0363a898c..9c84b0a42 100644 --- a/src/workbench/menuBar/__tests__/menubar.test.tsx +++ b/src/workbench/menuBar/__tests__/menubar.test.tsx @@ -4,8 +4,10 @@ import { cleanup, fireEvent, render } from '@testing-library/react'; import '@testing-library/jest-dom'; import MenuBar, { actionClassName } from '../menuBar'; +import { MenuBarMode } from 'mo/model/workbench/layout'; const TEST_ID = 'test-id'; +const TEST_DATA = 'test-data'; const menuData = [ { id: TEST_ID, @@ -19,6 +21,10 @@ const menuData = [ id: 'OpenFile', name: 'Open', }, + { + id: TEST_DATA, + name: TEST_DATA, + }, ], }, ]; @@ -69,4 +75,52 @@ describe('Test MenuBar Component', () => { expect(mockFn).toBeCalled(); expect(mockFn.mock.calls[0][0]).toEqual(input); }); + + test('Match the snapshot of menuBar in horizontal mode', () => { + const component = renderer.create( + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); + + test('Should support to execute the onClick method in vertical mode', () => { + const mockFn = jest.fn(); + const { container, getByText } = render( + + ); + const component = container.firstElementChild! + .firstElementChild as HTMLDivElement; + + fireEvent.click(component); + const elem = getByText(TEST_DATA); + fireEvent.click(elem); + expect(mockFn).toBeCalled(); + }); + + test('Should support to execute the onClick method in horizontal mode', () => { + const mockFn = jest.fn(); + const { container, getByText } = render( + + ); + const component = container.firstElementChild! + .firstElementChild as HTMLDivElement; + + fireEvent.click(component); + const elem = getByText(TEST_DATA); + fireEvent.click(elem); + expect(mockFn).toBeCalled(); + }); }); diff --git a/src/workbench/menuBar/logo.tsx b/src/workbench/menuBar/logo.tsx new file mode 100644 index 000000000..bbbfef013 --- /dev/null +++ b/src/workbench/menuBar/logo.tsx @@ -0,0 +1,128 @@ +import React from 'react'; + +export default function Logo({ className }) { + return ( + + ); +} diff --git a/src/workbench/menuBar/menuBar.tsx b/src/workbench/menuBar/menuBar.tsx index f44de58f4..3bd3813e3 100644 --- a/src/workbench/menuBar/menuBar.tsx +++ b/src/workbench/menuBar/menuBar.tsx @@ -1,18 +1,37 @@ import React, { useCallback, useEffect, useRef } from 'react'; -import { getBEMElement, prefixClaName } from 'mo/common/className'; +import { + getBEMElement, + prefixClaName, + getBEMModifier, +} from 'mo/common/className'; import { IMenuBar, IMenuBarItem } from 'mo/model/workbench/menuBar'; import { IMenuBarController } from 'mo/controller/menuBar'; import { DropDown, DropDownRef } from 'mo/components/dropdown'; -import { IMenuProps, Menu } from 'mo/components/menu'; +import { IMenuProps, Menu, MenuMode, MenuRef } from 'mo/components/menu'; import { Icon } from 'mo/components/icon'; import { KeybindingHelper } from 'mo/services/keybinding'; +import { MenuBarMode } from 'mo/model/workbench/layout'; +import Logo from './logo'; export const defaultClassName = prefixClaName('menuBar'); export const actionClassName = getBEMElement(defaultClassName, 'action'); +export const horizontalClassName = getBEMModifier( + defaultClassName, + 'horizontal' +); +export const logoClassName = getBEMElement(horizontalClassName, 'logo'); +export const logoContentClassName = getBEMElement(logoClassName, 'content'); export function MenuBar(props: IMenuBar & IMenuBarController) { - const { data, onClick, updateFocusinEle } = props; + const { + data, + mode = MenuBarMode.vertical, + onClick, + updateFocusinEle, + logo, + } = props; const childRef = useRef(null); + const menuRef = useRef(null); const addKeybindingForData = ( rawData: IMenuBarItem[] = [] @@ -42,6 +61,15 @@ export function MenuBar(props: IMenuBar & IMenuBarController) { onClick?.(e, item); childRef.current!.dispose(); }; + + const handleClickHorizontalMenu = ( + e: React.MouseEvent, + item: IMenuBarItem + ) => { + onClick?.(e, item); + menuRef.current!.dispose(); + }; + const overlay = ( +
+ {logo || } +
+ + + ); + } + return (
#{$menu} { + background: var(--activityBar-background); + box-shadow: none; + color: var(--foreground); + + &--horizontal { + #{$menu}__item { + min-width: auto; + } + + > #{$menu}__item > #{$menu}__content { + > #{$menu}__label { + flex: none; + font-size: 12px; + padding: 0 1em; + } + + > #{$menu}__indicator { + display: none; + } + } + } + } + + &__logo { + align-items: center; + display: flex; + height: 100%; + justify-content: center; + overflow: hidden; + width: 48px; + + &__content { + height: 20px; + width: 20px; + } + } + } } diff --git a/src/workbench/style.scss b/src/workbench/style.scss index c6306b860..ff554789c 100644 --- a/src/workbench/style.scss +++ b/src/workbench/style.scss @@ -44,6 +44,12 @@ right: 0; top: 0; z-index: 1; + + &--with-horizontal-menuBar { + #{$mainBench} { + height: calc(100% - 25px); + } + } } #{$compositeBar} { diff --git a/src/workbench/workbench.tsx b/src/workbench/workbench.tsx index bb12b87a4..519b9676e 100644 --- a/src/workbench/workbench.tsx +++ b/src/workbench/workbench.tsx @@ -4,7 +4,12 @@ import SplitPane from 'react-split-pane'; import Pane from 'react-split-pane/lib/Pane'; import { container } from 'tsyringe'; -import { classNames, getFontInMac, prefixClaName } from 'mo/common/className'; +import { + classNames, + getFontInMac, + prefixClaName, + getBEMModifier, +} from 'mo/common/className'; import { EditorView } from 'mo/workbench/editor'; import { SidebarView } from 'mo/workbench/sidebar'; @@ -19,7 +24,7 @@ import { connect } from 'mo/react'; import { ILayoutController, LayoutController } from 'mo/controller/layout'; import { LayoutService } from 'mo/services'; -import { ILayout } from 'mo/model/workbench/layout'; +import { ILayout, MenuBarMode } from 'mo/model/workbench/layout'; import { IWorkbench } from 'mo/model'; @@ -27,6 +32,10 @@ const mainBenchClassName = prefixClaName('mainBench'); const workbenchClassName = prefixClaName('workbench'); const compositeBarClassName = prefixClaName('compositeBar'); const appClassName = classNames(APP_PREFIX, getFontInMac()); +const workbenchWithHorizontalMenuBarClassName = getBEMModifier( + workbenchClassName, + 'with-horizontal-menuBar' +); const layoutController = container.resolve(LayoutController); const layoutService = container.resolve(LayoutService); @@ -71,12 +80,24 @@ export function WorkbenchView(props: IWorkbench & ILayout & ILayoutController) { return [editor, panel]; }; + const isMenuBarHorizontal = + !menuBar.hidden && menuBar.mode === MenuBarMode.horizontal; + const horizontal = isMenuBarHorizontal + ? workbenchWithHorizontalMenuBarClassName + : null; + return (
-
+
+ {isMenuBarHorizontal && ( + + )}
- {!menuBar.hidden && } + {!menuBar.hidden && + menuBar.mode === MenuBarMode.vertical && ( + + )} {!activityBar.hidden && }