tabby/tabby-core/src/components/baseTab.component.ts

203 lines
5.3 KiB
TypeScript

import { Observable, Subject } from 'rxjs'
import { EmbeddedViewRef, ViewContainerRef, ViewRef } from '@angular/core'
import { RecoveryToken } from '../api/tabRecovery'
import { BaseComponent } from './base.component'
/**
* Represents an active "process" inside a tab,
* for example, a user process running inside a terminal tab
*/
export interface BaseTabProcess {
name: string
}
/**
* Abstract base class for custom tab components
*/
export abstract class BaseTabComponent extends BaseComponent {
/**
* Parent tab (usually a SplitTabComponent)
*/
parent: BaseTabComponent|null = null
/**
* Current tab title
*/
title: string
/**
* User-defined title override
*/
customTitle: string
/**
* Last tab activity state
*/
hasActivity = false
/**
* ViewRef to the tab DOM element
*/
hostView: ViewRef
/**
* CSS color override for the tab's header
*/
color: string|null = null
hasFocus = false
/**
* Ping this if your recovery state has been changed and you want
* your tab state to be saved sooner
*/
protected recoveryStateChangedHint = new Subject<void>()
protected viewContainer?: ViewContainerRef
/* @hidden */
viewContainerEmbeddedRef?: EmbeddedViewRef<any>
private progressClearTimeout: number
private titleChange = new Subject<string>()
private focused = new Subject<void>()
private blurred = new Subject<void>()
private progress = new Subject<number|null>()
private activity = new Subject<boolean>()
private destroyed = new Subject<void>()
private _destroyCalled = false
get focused$ (): Observable<void> { return this.focused }
get blurred$ (): Observable<void> { return this.blurred }
get titleChange$ (): Observable<string> { return this.titleChange }
get progress$ (): Observable<number|null> { return this.progress }
get activity$ (): Observable<boolean> { return this.activity }
get destroyed$ (): Observable<void> { return this.destroyed }
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
protected constructor () {
super()
this.focused$.subscribe(() => {
this.hasFocus = true
})
this.blurred$.subscribe(() => {
this.hasFocus = false
})
}
setTitle (title: string): void {
this.title = title
if (!this.customTitle) {
this.titleChange.next(title)
}
}
/**
* Sets visual progressbar on the tab
*
* @param {type} progress: value between 0 and 1, or `null` to remove
*/
setProgress (progress: number|null): void {
this.progress.next(progress)
if (progress) {
if (this.progressClearTimeout) {
clearTimeout(this.progressClearTimeout)
}
this.progressClearTimeout = setTimeout(() => {
this.setProgress(null)
}, 5000) as any
}
}
/**
* Shows the acticity marker on the tab header
*/
displayActivity (): void {
this.hasActivity = true
this.activity.next(true)
}
/**
* Removes the acticity marker from the tab header
*/
clearActivity (): void {
this.hasActivity = false
this.activity.next(false)
}
/**
* Override this and implement a [[TabRecoveryProvider]] to enable recovery
* for your custom tab
*
* @return JSON serializable tab state representation
* for your [[TabRecoveryProvider]] to parse
*/
async getRecoveryToken (): Promise<RecoveryToken|null> {
return null
}
/**
* Override this to enable task completion notifications for the tab
*/
async getCurrentProcess (): Promise<BaseTabProcess|null> {
return null
}
/**
* Return false to prevent the tab from being closed
*/
async canClose (): Promise<boolean> {
return true
}
emitFocused (): void {
this.focused.next()
}
emitBlurred (): void {
this.blurred.next()
}
insertIntoContainer (container: ViewContainerRef): EmbeddedViewRef<any> {
this.viewContainerEmbeddedRef = container.insert(this.hostView) as EmbeddedViewRef<any>
this.viewContainer = container
return this.viewContainerEmbeddedRef
}
removeFromContainer (): void {
if (!this.viewContainer || !this.viewContainerEmbeddedRef) {
return
}
this.viewContainer.detach(this.viewContainer.indexOf(this.viewContainerEmbeddedRef))
this.viewContainerEmbeddedRef = undefined
this.viewContainer = undefined
}
/**
* Called before the tab is closed
*/
destroy (skipDestroyedEvent = false): void {
if (this._destroyCalled) {
return
}
this._destroyCalled = true
this.focused.complete()
this.blurred.complete()
this.titleChange.complete()
this.progress.complete()
this.activity.complete()
this.recoveryStateChangedHint.complete()
if (!skipDestroyedEvent) {
this.destroyed.next()
}
this.destroyed.complete()
this.hostView.destroy()
}
/** @hidden */
ngOnDestroy (): void {
this.destroy()
super.ngOnDestroy()
}
}