mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
- fixed animation for open/close <Dock/>'s UI state
- added helper dockStore.onTabClose(): IReactionDisposer for easier clean up Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
dfd1f06977
commit
f86fc022f3
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { comparer, makeObservable, observable, reaction } from "mobx";
|
||||
import { action, makeObservable, observable } from "mobx";
|
||||
import { autoBind, createStorage, disposer, StorageHelper } from "../../utils";
|
||||
import { dockStore, TabId } from "./dock.store";
|
||||
|
||||
@ -52,8 +52,8 @@ export class DockTabStore<T extends {}> {
|
||||
}
|
||||
|
||||
protected constructor(protected options: DockTabStoreOptions = {}) {
|
||||
makeObservable(this); // must be called *before* autoBind() when used with mobx's method decorators
|
||||
autoBind(this);
|
||||
makeObservable(this);
|
||||
|
||||
this.options = {
|
||||
autoInit: true,
|
||||
@ -78,44 +78,34 @@ export class DockTabStore<T extends {}> {
|
||||
}
|
||||
|
||||
protected init() {
|
||||
this.dispose.push(
|
||||
reaction(() => ({
|
||||
dockTabs: dockStore.tabs.map(tab => tab.id) as TabId[],
|
||||
dataTabs: Object.keys(this.data) as TabId[],
|
||||
}), ({ dataTabs, dockTabs }) => {
|
||||
|
||||
// clear data for closed or non-existing dock tabs
|
||||
if (dockTabs.length < dataTabs.length) {
|
||||
const closedDockTabs = dataTabs.filter(id => !dockTabs.includes(id));
|
||||
|
||||
closedDockTabs.forEach(tabId => this.clearData(tabId));
|
||||
}
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
equals: comparer.structural,
|
||||
})
|
||||
);
|
||||
|
||||
this.dataReady = true;
|
||||
|
||||
this.dispose.push(
|
||||
dockStore.onTabClose(({ tabId }) => this.clearData(tabId)),
|
||||
);
|
||||
}
|
||||
|
||||
getData(tabId: TabId): T {
|
||||
return this.data[tabId];
|
||||
}
|
||||
|
||||
@action
|
||||
setData(tabId: TabId, data: T) {
|
||||
this.data[tabId] = data;
|
||||
}
|
||||
|
||||
@action
|
||||
clearData(tabId: TabId) {
|
||||
delete this.data[tabId];
|
||||
}
|
||||
|
||||
@action
|
||||
reset() {
|
||||
this.data = {};
|
||||
this.dataReady = false;
|
||||
}
|
||||
|
||||
@action
|
||||
destroy() {
|
||||
this.reset();
|
||||
this.dispose();
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.isOpen) {
|
||||
&.isClosed {
|
||||
height: auto !important;
|
||||
|
||||
.Tab {
|
||||
@ -71,7 +71,7 @@
|
||||
background: $terminalBackground;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
transition: flex 60ms ease-in-out;
|
||||
transition: flex-basis 25ms ease-in;
|
||||
|
||||
> *:not(.Spinner) {
|
||||
position: absolute;
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
*/
|
||||
|
||||
import * as uuid from "uuid";
|
||||
import { action, comparer, computed, IReactionOptions, makeObservable, observable, reaction } from "mobx";
|
||||
import { autoBind, createStorage, Disposer } from "../../utils";
|
||||
import { action, comparer, computed, IReactionOptions, makeObservable, observable, reaction, runInAction } from "mobx";
|
||||
import { autoBind, createStorage } from "../../utils";
|
||||
import throttle from "lodash/throttle";
|
||||
|
||||
export type TabId = string;
|
||||
@ -89,16 +89,20 @@ export interface DockStorageState {
|
||||
}
|
||||
|
||||
export interface DockTabChangeEvent {
|
||||
selectedTabId?: TabId;
|
||||
tabId?: TabId;
|
||||
tab?: DockTab;
|
||||
prevTab?: DockTab;
|
||||
}
|
||||
|
||||
export interface DockTabChangeEventOptions extends IReactionOptions {
|
||||
kind?: TabKind; // filter: matching by dockStore.selectedTab.kind
|
||||
tabKind?: TabKind; // filter: by dockStore.selectedTab.kind == tabKind
|
||||
dockIsVisible?: boolean; // filter: dock and selected tab should be visible (default: true)
|
||||
}
|
||||
|
||||
export interface DockTabCloseEvent {
|
||||
tabId: TabId; // closed tab id
|
||||
}
|
||||
|
||||
export class DockStore implements DockStorageState {
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
@ -189,37 +193,35 @@ export class DockStore implements DockStorageState {
|
||||
return reaction(() => [this.height, this.fullSize], callback, options);
|
||||
}
|
||||
|
||||
onTabClose(tabId: TabId, callback: () => void, options: IReactionOptions = {}): Disposer {
|
||||
let disposed = false;
|
||||
onTabClose(callback: (evt: DockTabCloseEvent) => void, options: IReactionOptions = {}) {
|
||||
return reaction(() => dockStore.tabs.map(tab => tab.id), (tabs: TabId[], prevTabs?: TabId[]) => {
|
||||
if (!Array.isArray(prevTabs)) {
|
||||
return; // tabs not yet modified
|
||||
}
|
||||
|
||||
const stopWatcher = reaction(() => this.getTabById(tabId), tab => {
|
||||
if (!tab) {
|
||||
disposed = true;
|
||||
stopWatcher();
|
||||
callback();
|
||||
const closedTabs: TabId[] = prevTabs.filter(id => !tabs.includes(id));
|
||||
|
||||
if (closedTabs.length > 0) {
|
||||
runInAction(() => {
|
||||
closedTabs.forEach(tabId => callback({ tabId }));
|
||||
});
|
||||
}
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
equals: comparer.shallow,
|
||||
equals: comparer.structural,
|
||||
...options,
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (disposed) return;
|
||||
stopWatcher();
|
||||
};
|
||||
}
|
||||
|
||||
onTabChange(callback?: (evt: DockTabChangeEvent) => void, options: DockTabChangeEventOptions = {}) {
|
||||
const { kind, dockIsVisible = true, ...reactionOpts } = options;
|
||||
const { tabKind, dockIsVisible = true, ...reactionOpts } = options;
|
||||
|
||||
return reaction(() => this.selectedTab, ((tab, prevTab) => {
|
||||
if (kind && kind !== tab.kind) return;
|
||||
if (dockIsVisible && !dockStore.isOpen) return;
|
||||
if (tabKind && tabKind !== tab.kind) return;
|
||||
if (dockIsVisible && !this.isOpen) return;
|
||||
|
||||
callback({
|
||||
tab, prevTab,
|
||||
selectedTabId: this.selectedTabId,
|
||||
tabId: this.selectedTabId,
|
||||
});
|
||||
}), reactionOpts);
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
import "./dock.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { cssNames, prevDefault } from "../../utils";
|
||||
@ -62,15 +61,15 @@ export class Dock extends React.Component<Props> {
|
||||
open();
|
||||
selectTab(tab.id);
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const { className } = this.props;
|
||||
const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize, height } = dockStore;
|
||||
const TabContent = dockViewsManager.get(selectedTab?.kind)?.tabContent ?? React.Fragment;
|
||||
const DockTabContent = dockViewsManager.get(selectedTab?.kind)?.tabContent;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cssNames("Dock", className, { isOpen, fullSize })}
|
||||
className={cssNames("Dock", className, { isOpen, isClosed: !isOpen, fullSize })}
|
||||
onKeyDown={this.onKeydown}
|
||||
tabIndex={-1}
|
||||
>
|
||||
@ -121,8 +120,8 @@ export class Dock extends React.Component<Props> {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tab-content" style={{ flexBasis: height }}>
|
||||
<TabContent tab={selectedTab}/>
|
||||
<div className="tab-content" style={{ flexBasis: isOpen ? height : 0 }}>
|
||||
{DockTabContent && <DockTabContent tab={selectedTab}/>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autorun, observable, when } from "mobx";
|
||||
import { autoBind, noop, Singleton } from "../../utils";
|
||||
import { action, makeObservable, observable, when } from "mobx";
|
||||
import { autoBind, disposer, noop, Singleton } from "../../utils";
|
||||
import { Terminal } from "./terminal";
|
||||
import { TerminalApi } from "../../api/terminal-api";
|
||||
import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store";
|
||||
@ -40,29 +40,30 @@ export function createTerminalTab(tabParams: DockTabCreateSpecific = {}) {
|
||||
}
|
||||
|
||||
export class TerminalStore extends Singleton {
|
||||
protected terminals = new Map<TabId, Terminal>();
|
||||
protected terminals = observable.map<TabId, Terminal>();
|
||||
protected connections = observable.map<TabId, TerminalApi>();
|
||||
public dispose = disposer();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
this.init();
|
||||
}
|
||||
|
||||
// connect active tab
|
||||
autorun(() => {
|
||||
const { selectedTab, isOpen } = dockStore;
|
||||
protected init() {
|
||||
// connect active dock tab
|
||||
this.dispose.push(
|
||||
dockStore.onTabChange(({ tabId }) => this.connect(tabId), {
|
||||
tabKind: TabKind.TERMINAL,
|
||||
fireImmediately: true,
|
||||
}),
|
||||
);
|
||||
|
||||
if (selectedTab?.kind === TabKind.TERMINAL && isOpen) {
|
||||
this.connect(selectedTab.id);
|
||||
}
|
||||
});
|
||||
// disconnect closed tabs
|
||||
autorun(() => {
|
||||
const currentTabs = dockStore.tabs.map(tab => tab.id);
|
||||
|
||||
for (const [tabId] of this.connections) {
|
||||
if (!currentTabs.includes(tabId)) this.disconnect(tabId);
|
||||
}
|
||||
});
|
||||
// disconnect terminals for closed dock tabs (if any)
|
||||
this.dispose.push(
|
||||
dockStore.onTabClose(({ tabId }) => this.disconnect(tabId)),
|
||||
);
|
||||
}
|
||||
|
||||
connect(tabId: TabId) {
|
||||
@ -80,6 +81,7 @@ export class TerminalStore extends Singleton {
|
||||
this.terminals.set(tabId, terminal);
|
||||
}
|
||||
|
||||
@action
|
||||
disconnect(tabId: TabId) {
|
||||
if (!this.isConnected(tabId)) {
|
||||
return;
|
||||
@ -165,7 +167,7 @@ export const terminalStore = new Proxy({}, {
|
||||
const res = (ts as any)?.[p];
|
||||
|
||||
if (typeof res === "function") {
|
||||
return function(...args: any[]) {
|
||||
return function (...args: any[]) {
|
||||
return res.apply(ts, args);
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user