/
githubmirror
/
tabby
Обзор
Документация
Войти
/
githubmirror
/
tabby
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tabby-core/src/components/baseTab.component.ts
264 строки
7 KB
Artur Xavier
feat(core): add pinned tabs (#11146)
11 июл 2026, 12:33
Не верифицирован
11 июл 2026, 12:33
1069978
Код
Авторство
О чём код?
import { Observable, Subject, BehaviorSubject, distinctUntilChanged, filter, debounceTime } from 'rxjs' import { EmbeddedViewRef, Injector, ViewContainerRef, ViewRef } from '@angular/core' import { RecoveryToken } from '../api/tabRecovery' import { BaseComponent } from './base.component' import { ConfigService } from '../services/config.service' /** * Represents an active "process" inside a tab, * for example, a user process running inside a terminal tab */ export interface BaseTabProcess { name: string } export interface GetRecoveryTokenOptions { includeState: boolean } /** * Abstract base class for custom tab components */ // @Component({ template: '' }) 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 */ get color (): string|null { return this._color } set color (value: string|null) { this._color = value } private _color: string|null = null /** * icon override for the tab's header */ get icon (): string|null { return this._icon } set icon (value: string|null) { this._icon = value } private _icon: string|null = null get pinned (): boolean { return this._pinned } set pinned (value: boolean) { this._pinned = value this.recoveryStateChangedHint.next() } private _pinned = false get effectivelyPinned (): boolean { if (this.pinned) { return true } let parent = this.parent while (parent) { if (parent.pinned) { return true } parent = parent.parent ?? null } return false } 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 titleChange = new Subject<string>() private focused = new Subject<void>() private blurred = new Subject<void>() protected visibility = new BehaviorSubject<boolean>(false) protected progress = new BehaviorSubject<number|null>(null) protected activity = new BehaviorSubject<boolean>(false) private destroyed = new Subject<void>() private _destroyCalled = false get focused$ (): Observable<void> { return this.focused } get blurred$ (): Observable<void> { return this.blurred } /* @hidden */ get visibility$ (): Observable<boolean> { return this.visibility } get titleChange$ (): Observable<string> { return this.titleChange.pipe(distinctUntilChanged()) } get progress$ (): Observable<number|null> { return this.progress.pipe(distinctUntilChanged()) } get activity$ (): Observable<boolean> { return this.activity } get destroyed$ (): Observable<void> { return this.destroyed } get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint } protected config: ConfigService protected constructor (injector: Injector) { super() this.config = injector.get(ConfigService) this.focused$.subscribe(() => { this.hasFocus = true }) this.blurred$.subscribe(() => { this.hasFocus = false }) this.subscribeUntilDestroyed(this.progress.pipe( filter(x => x !== null), debounceTime(5000), ), () => { this.setProgress(null) }) } 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) } /** * Shows the activity marker on the tab header */ displayActivity (): void { if (!this.hasActivity) { this.hasActivity = true this.activity.next(true) } } /** * Removes the activity marker from the tab header */ clearActivity (): void { if (this.hasActivity) { 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 (options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> { // eslint-disable-line @typescript-eslint/no-unused-vars 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() } /* @hidden */ emitVisibility (visibility: boolean): void { this.visibility.next(visibility) } 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 } const viewIndex = this.viewContainer.indexOf(this.viewContainerEmbeddedRef) if (viewIndex !== -1) { this.viewContainer.detach(viewIndex) } this.viewContainerEmbeddedRef = undefined this.viewContainer = undefined } get topmostParent (): BaseTabComponent|null { let parent = this.parent while (parent?.parent) { parent = parent.parent } return parent } /** * 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() } /** @hidden */ ngOnDestroy (): void { this.destroy() super.ngOnDestroy() } }