mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
193 lines
4.8 KiB
TypeScript
193 lines
4.8 KiB
TypeScript
import MD5 from "crypto-js/md5";
|
|
import { action, computed, IReactionOptions, observable, reaction } from "mobx";
|
|
import { autobind, createStorage } from "../../utils";
|
|
import throttle from "lodash/throttle"
|
|
|
|
export type TabId = string;
|
|
|
|
export enum TabKind {
|
|
TERMINAL = "terminal",
|
|
CREATE_RESOURCE = "create-resource",
|
|
EDIT_RESOURCE = "edit-resource",
|
|
INSTALL_CHART = "install-chart",
|
|
UPGRADE_CHART = "upgrade-chart",
|
|
}
|
|
|
|
export interface IDockTab {
|
|
id?: TabId;
|
|
kind: TabKind;
|
|
title?: string;
|
|
pinned?: boolean; // not closable
|
|
}
|
|
|
|
@autobind()
|
|
export class DockStore {
|
|
protected initialTabs: IDockTab[] = [
|
|
{ id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" },
|
|
];
|
|
|
|
protected storage = createStorage("dock", {}); // keep settings in localStorage
|
|
public defaultTabId = this.initialTabs[0].id;
|
|
public minHeight = 100;
|
|
|
|
@observable isOpen = false;
|
|
@observable fullSize = false;
|
|
@observable height = this.defaultHeight;
|
|
@observable tabs = observable.array<IDockTab>(this.initialTabs);
|
|
@observable selectedTabId = this.defaultTabId;
|
|
|
|
@computed get selectedTab() {
|
|
return this.tabs.find(tab => tab.id === this.selectedTabId);
|
|
}
|
|
|
|
get defaultHeight() {
|
|
return Math.round(window.innerHeight / 2.5);
|
|
}
|
|
|
|
get maxHeight() {
|
|
const mainLayoutHeader = 40;
|
|
const mainLayoutTabs = 33;
|
|
const mainLayoutMargin = 16;
|
|
const dockTabs = 33;
|
|
return window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs;
|
|
}
|
|
|
|
constructor() {
|
|
Object.assign(this, this.storage.get());
|
|
|
|
reaction(() => ({
|
|
isOpen: this.isOpen,
|
|
selectedTabId: this.selectedTabId,
|
|
height: this.height,
|
|
tabs: this.tabs.slice(),
|
|
}), data => {
|
|
this.storage.set(data);
|
|
});
|
|
|
|
// adjust terminal height if window size changes
|
|
this.checkMaxHeight();
|
|
window.addEventListener("resize", throttle(this.checkMaxHeight, 250));
|
|
}
|
|
|
|
protected checkMaxHeight() {
|
|
if (!this.height) {
|
|
this.setHeight(this.defaultHeight || this.minHeight);
|
|
}
|
|
if (this.height > this.maxHeight) {
|
|
this.setHeight(this.maxHeight);
|
|
}
|
|
}
|
|
|
|
onResize(callback: () => void, options?: IReactionOptions) {
|
|
return reaction(() => [this.height, this.fullSize], callback, options);
|
|
}
|
|
|
|
onTabChange(callback: (tabId: TabId) => void, options?: IReactionOptions) {
|
|
return reaction(() => this.selectedTabId, callback, options);
|
|
}
|
|
|
|
hasTabs() {
|
|
return this.tabs.length > 0;
|
|
}
|
|
|
|
@action
|
|
open(fullSize?: boolean) {
|
|
this.isOpen = true;
|
|
if (typeof fullSize === "boolean") {
|
|
this.fullSize = fullSize;
|
|
}
|
|
}
|
|
|
|
@action
|
|
close() {
|
|
this.isOpen = false;
|
|
}
|
|
|
|
@action
|
|
toggle() {
|
|
if (this.isOpen) this.close();
|
|
else this.open();
|
|
}
|
|
|
|
@action
|
|
toggleFillSize() {
|
|
if (!this.isOpen) this.open();
|
|
this.fullSize = !this.fullSize;
|
|
}
|
|
|
|
getTabById(tabId: TabId) {
|
|
return this.tabs.find(tab => tab.id === tabId);
|
|
}
|
|
|
|
protected getNewTabNumber(kind: TabKind) {
|
|
const tabNumbers = this.tabs
|
|
.filter(tab => tab.kind === kind)
|
|
.map(tab => {
|
|
const tabNumber = +tab.title.match(/\d+/);
|
|
return tabNumber === 0 ? 1 : tabNumber; // tab without a number is first
|
|
});
|
|
for (let i = 1; ; i++) {
|
|
if (!tabNumbers.includes(i)) return i;
|
|
}
|
|
}
|
|
|
|
@action
|
|
createTab(anonTab: IDockTab, addNumber = true): IDockTab {
|
|
const tabId = MD5(Math.random().toString() + Date.now()).toString();
|
|
const tab: IDockTab = { id: tabId, ...anonTab };
|
|
if (addNumber) {
|
|
const tabNumber = this.getNewTabNumber(tab.kind);
|
|
if (tabNumber > 1) tab.title += ` (${tabNumber})`
|
|
}
|
|
this.tabs.push(tab);
|
|
this.selectTab(tab.id);
|
|
this.open();
|
|
return tab;
|
|
}
|
|
|
|
@action
|
|
async closeTab(tabId: TabId) {
|
|
const tab = this.getTabById(tabId);
|
|
if (!tab || tab.pinned) {
|
|
return;
|
|
}
|
|
this.tabs.remove(tab);
|
|
if (this.selectedTabId === tab.id) {
|
|
if (this.tabs.length) {
|
|
const newTab = this.tabs.slice(-1)[0]; // last
|
|
if (newTab.kind === TabKind.TERMINAL) {
|
|
// close the dock when selected sibling inactive terminal tab
|
|
const { terminalStore } = await import("./terminal.store");
|
|
if (!terminalStore.isConnected(newTab.id)) this.close();
|
|
}
|
|
this.selectTab(newTab.id);
|
|
}
|
|
else {
|
|
this.selectedTabId = null;
|
|
this.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
@action
|
|
selectTab(tabId: TabId) {
|
|
const tab = this.getTabById(tabId);
|
|
this.selectedTabId = tab ? tab.id : null;
|
|
}
|
|
|
|
@action
|
|
setHeight(height: number) {
|
|
this.height = Math.max(0, Math.min(height, this.maxHeight));
|
|
}
|
|
|
|
@action
|
|
reset() {
|
|
this.selectedTabId = this.defaultTabId;
|
|
this.tabs.replace(this.initialTabs);
|
|
this.height = this.defaultHeight;
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
export const dockStore = new DockStore();
|