1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Adding context menu to dock tabs

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2020-12-25 10:59:09 +03:00
parent eb5a775a2d
commit 0120073983
5 changed files with 140 additions and 45 deletions

View File

@ -2,11 +2,13 @@ import "./dock-tab.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { t } from "@lingui/macro"; import { t, Trans } from "@lingui/macro";
import { autobind, cssNames, prevDefault } from "../../utils"; import { autobind, cssNames, prevDefault } from "../../utils";
import { dockStore, IDockTab } from "./dock.store"; import { dockStore, IDockTab } from "./dock.store";
import { Tab, TabProps } from "../tabs"; import { Tab, TabProps } from "../tabs";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Menu, MenuItem } from "../menu";
import { observable } from "mobx";
import { _i18n } from "../../i18n"; import { _i18n } from "../../i18n";
export interface DockTabProps extends TabProps<IDockTab> { export interface DockTabProps extends TabProps<IDockTab> {
@ -15,6 +17,8 @@ export interface DockTabProps extends TabProps<IDockTab> {
@observer @observer
export class DockTab extends React.Component<DockTabProps> { export class DockTab extends React.Component<DockTabProps> {
@observable menuVisible = false;
get tabId() { get tabId() {
return this.props.value.id; return this.props.value.id;
} }
@ -24,6 +28,35 @@ export class DockTab extends React.Component<DockTabProps> {
dockStore.closeTab(this.tabId); dockStore.closeTab(this.tabId);
} }
renderMenu() {
const { closeTab, closeAllTabs, closeOtherTabs, closeTabsToTheRight } = dockStore;
return (
<Menu
usePortal
htmlFor={`tab-${this.tabId}`}
className="DockTabMenu"
isOpen={this.menuVisible}
open={() => this.menuVisible = true}
close={() => this.menuVisible = false}
toggleEvent="contextmenu"
>
<MenuItem onClick={() => closeTab(this.tabId)}>
<Trans>Close</Trans>
</MenuItem>
<MenuItem onClick={() => closeAllTabs()}>
<Trans>Close all tabs</Trans>
</MenuItem>
<MenuItem onClick={() => closeOtherTabs(this.tabId)}>
<Trans>Close other tabs</Trans>
</MenuItem>
<MenuItem onClick={() => closeTabsToTheRight(this.tabId)}>
<Trans>Close tabs to the right</Trans>
</MenuItem>
</Menu>
);
}
render() { render() {
const { className, moreActions, ...tabProps } = this.props; const { className, moreActions, ...tabProps } = this.props;
const { title, pinned } = tabProps.value; const { title, pinned } = tabProps.value;
@ -42,11 +75,16 @@ export class DockTab extends React.Component<DockTabProps> {
); );
return ( return (
<Tab <>
{...tabProps} <Tab
className={cssNames("DockTab", className, { pinned })} {...tabProps}
label={label} id={`tab-${this.tabId}`}
/> className={cssNames("DockTab", className, { pinned })}
onContextMenu={() => this.menuVisible = true}
label={label}
/>
{this.renderMenu()}
</>
); );
} }
} }

View File

@ -0,0 +1,51 @@
import React, { Fragment } from "react";
import { Icon } from "../icon";
import { Tabs } from "../tabs/tabs";
import { isCreateResourceTab } from "./create-resource.store";
import { DockTab } from "./dock-tab";
import { IDockTab } from "./dock.store";
import { isEditResourceTab } from "./edit-resource.store";
import { isInstallChartTab } from "./install-chart.store";
import { isPodLogsTab } from "./pod-logs.store";
import { TerminalTab } from "./terminal-tab";
import { isTerminalTab } from "./terminal.store";
import { isUpgradeChartTab } from "./upgrade-chart.store";
interface Props {
tabs: IDockTab[]
autoFocus: boolean
selectedTab: IDockTab
onChangeTab: (tab: IDockTab) => void
}
export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) => {
const renderTab = (tab: IDockTab) => {
if (isTerminalTab(tab)) {
return <TerminalTab value={tab} />;
}
if (isCreateResourceTab(tab) || isEditResourceTab(tab)) {
return <DockTab value={tab} icon="edit" />;
}
if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) {
return <DockTab value={tab} icon={<Icon svg="install" />} />;
}
if (isPodLogsTab(tab)) {
return <DockTab value={tab} icon="subject" />;
}
};
return (
<Tabs
className="DockTabs"
autoFocus={autoFocus}
value={selectedTab}
onChange={onChangeTab}
>
{tabs.map(tab => <Fragment key={tab.id}>{renderTab(tab)}</Fragment>)}
</Tabs>
);
};

View File

@ -123,6 +123,10 @@ export class DockStore {
return this.tabs.find(tab => tab.id === tabId); return this.tabs.find(tab => tab.id === tabId);
} }
getTabIndex(tabId: TabId) {
return this.tabs.findIndex(tab => tab.id === tabId);
}
protected getNewTabNumber(kind: TabKind) { protected getNewTabNumber(kind: TabKind) {
const tabNumbers = this.tabs const tabNumbers = this.tabs
.filter(tab => tab.kind === kind) .filter(tab => tab.kind === kind)
@ -182,6 +186,28 @@ export class DockStore {
} }
} }
closeTabs(tabs: IDockTab[]) {
tabs.forEach(tab => this.closeTab(tab.id));
}
closeAllTabs() {
this.closeTabs([...this.tabs]);
}
closeOtherTabs(tabId: TabId) {
const index = this.getTabIndex(tabId);
const tabs = [...this.tabs.slice(0, index), ...this.tabs.slice(index + 1)];
this.closeTabs(tabs);
}
closeTabsToTheRight(tabId: TabId) {
const index = this.getTabIndex(tabId);
const tabs = this.tabs.slice(index + 1);
this.closeTabs(tabs);
}
@action @action
selectTab(tabId: TabId) { selectTab(tabId: TabId) {
this.selectedTabId = this.getTabById(tabId)?.id ?? null; this.selectedTabId = this.getTabById(tabId)?.id ?? null;

View File

@ -1,29 +1,28 @@
import "./dock.scss"; import "./dock.scss";
import React, { Fragment } from "react"; import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { autobind, cssNames, prevDefault } from "../../utils"; import { observer } from "mobx-react";
import { ResizingAnchor, ResizeDirection } from "../resizing-anchor";
import { cssNames, prevDefault } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Tabs } from "../tabs/tabs";
import { MenuItem } from "../menu"; import { MenuItem } from "../menu";
import { MenuActions } from "../menu/menu-actions"; import { MenuActions } from "../menu/menu-actions";
import { dockStore, IDockTab } from "./dock.store"; import { ResizeDirection, ResizingAnchor } from "../resizing-anchor";
import { DockTab } from "./dock-tab";
import { TerminalTab } from "./terminal-tab";
import { TerminalWindow } from "./terminal-window";
import { CreateResource } from "./create-resource"; import { CreateResource } from "./create-resource";
import { InstallChart } from "./install-chart";
import { EditResource } from "./edit-resource";
import { UpgradeChart } from "./upgrade-chart";
import { createTerminalTab, isTerminalTab } from "./terminal.store";
import { createResourceTab, isCreateResourceTab } from "./create-resource.store"; import { createResourceTab, isCreateResourceTab } from "./create-resource.store";
import { DockTabs } from "./dock-tabs";
import { dockStore, IDockTab } from "./dock.store";
import { EditResource } from "./edit-resource";
import { isEditResourceTab } from "./edit-resource.store"; import { isEditResourceTab } from "./edit-resource.store";
import { InstallChart } from "./install-chart";
import { isInstallChartTab } from "./install-chart.store"; import { isInstallChartTab } from "./install-chart.store";
import { isUpgradeChartTab } from "./upgrade-chart.store";
import { PodLogs } from "./pod-logs"; import { PodLogs } from "./pod-logs";
import { isPodLogsTab } from "./pod-logs.store"; import { isPodLogsTab } from "./pod-logs.store";
import { TerminalWindow } from "./terminal-window";
import { createTerminalTab, isTerminalTab } from "./terminal.store";
import { UpgradeChart } from "./upgrade-chart";
import { isUpgradeChartTab } from "./upgrade-chart.store";
interface Props { interface Props {
className?: string; className?: string;
@ -54,25 +53,6 @@ export class Dock extends React.Component<Props> {
selectTab(tab.id); selectTab(tab.id);
}; };
@autobind()
renderTab(tab: IDockTab) {
if (isTerminalTab(tab)) {
return <TerminalTab value={tab} />;
}
if (isCreateResourceTab(tab) || isEditResourceTab(tab)) {
return <DockTab value={tab} icon="edit" />;
}
if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) {
return <DockTab value={tab} icon={<Icon svg="install" />} />;
}
if (isPodLogsTab(tab)) {
return <DockTab value={tab} icon="subject" />;
}
}
renderTabContent() { renderTabContent() {
const { isOpen, height, selectedTab: tab } = dockStore; const { isOpen, height, selectedTab: tab } = dockStore;
@ -112,13 +92,12 @@ export class Dock extends React.Component<Props> {
onDrag={dockStore.setHeight} onDrag={dockStore.setHeight}
/> />
<div className="tabs-container flex align-center" onDoubleClick={prevDefault(toggle)}> <div className="tabs-container flex align-center" onDoubleClick={prevDefault(toggle)}>
<Tabs <DockTabs
tabs={tabs}
selectedTab={selectedTab}
autoFocus={isOpen} autoFocus={isOpen}
className="dock-tabs" onChangeTab={this.onChangeTab}
value={selectedTab} onChange={this.onChangeTab} />
>
{tabs.map(tab => <Fragment key={tab.id}>{this.renderTab(tab)}</Fragment>)}
</Tabs>
<div className="toolbar flex gaps align-center box grow"> <div className="toolbar flex gaps align-center box grow">
<div className="dock-menu box grow"> <div className="dock-menu box grow">
<MenuActions usePortal triggerIcon={{ material: "add", className: "new-dock-tab", tooltip: <Trans>New tab</Trans> }} closeOnScroll={false}> <MenuActions usePortal triggerIcon={{ material: "add", className: "new-dock-tab", tooltip: <Trans>New tab</Trans> }} closeOnScroll={false}>

View File

@ -51,6 +51,7 @@ export class Tabs extends React.PureComponent<TabsProps> {
} }
export interface TabProps<D = any> extends DOMAttributes<HTMLElement> { export interface TabProps<D = any> extends DOMAttributes<HTMLElement> {
id?: string;
className?: string; className?: string;
active?: boolean; active?: boolean;
disabled?: boolean; disabled?: boolean;