UNPKG

@jupyterlab/apputils

Version:
250 lines 7.99 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { RestorablePool } from '@jupyterlab/statedb'; import { Signal } from '@lumino/signaling'; import { FocusTracker } from '@lumino/widgets'; /** * A class that keeps track of widget instances on an Application shell. * * @typeparam T - The type of widget being tracked. Defaults to `Widget`. * * #### Notes * The API surface area of this concrete implementation is substantially larger * than the widget tracker interface it implements. The interface is intended * for export by JupyterLab plugins that create widgets and have clients who may * wish to keep track of newly created widgets. This class, however, can be used * internally by plugins to restore state as well. */ export class WidgetTracker { /** * Create a new widget tracker. * * @param options - The instantiation options for a widget tracker. */ constructor(options) { this._currentChanged = new Signal(this); this._deferred = null; this._isDisposed = false; this._widgetAdded = new Signal(this); this._widgetUpdated = new Signal(this); const focus = (this._focusTracker = new FocusTracker()); const pool = (this._pool = new RestorablePool(options)); this.namespace = options.namespace; focus.currentChanged.connect((_, current) => { if (current.newValue !== this.currentWidget) { pool.current = current.newValue; } }, this); pool.added.connect((_, widget) => { this._widgetAdded.emit(widget); }, this); pool.currentChanged.connect((_, widget) => { // If the pool's current reference is `null` but the focus tracker has a // current widget, update the pool to match the focus tracker. if (widget === null && focus.currentWidget) { pool.current = focus.currentWidget; return; } this.onCurrentChanged(widget); this._currentChanged.emit(widget); }, this); pool.updated.connect((_, widget) => { this._widgetUpdated.emit(widget); }, this); } /** * A signal emitted when the current widget changes. */ get currentChanged() { return this._currentChanged; } /** * The current widget is the most recently focused or added widget. * * #### Notes * It is the most recently focused widget, or the most recently added * widget if no widget has taken focus. */ get currentWidget() { return this._pool.current || null; } /** * A promise resolved when the tracker has been restored. */ get restored() { if (this._deferred) { return Promise.resolve(); } else { return this._pool.restored; } } /** * The number of widgets held by the tracker. */ get size() { return this._pool.size; } /** * A signal emitted when a widget is added. * * #### Notes * This signal will only fire when a widget is added to the tracker. It will * not fire if a widget is injected into the tracker. */ get widgetAdded() { return this._widgetAdded; } /** * A signal emitted when a widget is updated. */ get widgetUpdated() { return this._widgetUpdated; } /** * Add a new widget to the tracker. * * @param widget - The widget being added. * * #### Notes * The widget passed into the tracker is added synchronously; its existence in * the tracker can be checked with the `has()` method. The promise this method * returns resolves after the widget has been added and saved to an underlying * restoration connector, if one is available. * * The newly added widget becomes the current widget unless the focus tracker * already had a focused widget. */ async add(widget) { this._focusTracker.add(widget); await this._pool.add(widget); if (!this._focusTracker.activeWidget) { this._pool.current = widget; } } /** * Test whether the tracker is disposed. */ get isDisposed() { return this._isDisposed; } /** * Dispose of the resources held by the tracker. */ dispose() { if (this.isDisposed) { return; } this._isDisposed = true; this._pool.dispose(); this._focusTracker.dispose(); Signal.clearData(this); } /** * Find the first widget in the tracker that satisfies a filter function. * * @param fn The filter function to call on each widget. * * #### Notes * If no widget is found, the value returned is `undefined`. */ find(fn) { return this._pool.find(fn); } /** * Iterate through each widget in the tracker. * * @param fn - The function to call on each widget. */ forEach(fn) { return this._pool.forEach(fn); } /** * Filter the widgets in the tracker based on a predicate. * * @param fn - The function by which to filter. */ filter(fn) { return this._pool.filter(fn); } /** * Inject a foreign widget into the widget tracker. * * @param widget - The widget to inject into the tracker. * * #### Notes * Injected widgets will not have their state saved by the tracker. * * The primary use case for widget injection is for a plugin that offers a * sub-class of an extant plugin to have its instances share the same commands * as the parent plugin (since most relevant commands will use the * `currentWidget` of the parent plugin's widget tracker). In this situation, * the sub-class plugin may well have its own widget tracker for layout and * state restoration in addition to injecting its widgets into the parent * plugin's widget tracker. */ inject(widget) { return this._pool.inject(widget); } /** * Check if this tracker has the specified widget. * * @param widget - The widget whose existence is being checked. */ has(widget) { return this._pool.has(widget); } /** * Restore the widgets in this tracker's namespace. * * @param options - The configuration options that describe restoration. * * @returns A promise that resolves when restoration has completed. * * #### Notes * This function should not typically be invoked by client code. * Its primary use case is to be invoked by a restorer. */ async restore(options) { const deferred = this._deferred; if (deferred) { this._deferred = null; return this._pool.restore(deferred); } if (options) { return this._pool.restore(options); } console.warn('No options provided to restore the tracker.'); } /** * Save the restore options for this tracker, but do not restore yet. * * @param options - The configuration options that describe restoration. * * ### Notes * This function is useful when starting the shell in 'single-document' mode, * to avoid restoring all useless widgets. It should not ordinarily be called * by client code. */ defer(options) { this._deferred = options; } /** * Save the restore data for a given widget. * * @param widget - The widget being saved. */ async save(widget) { return this._pool.save(widget); } /** * Handle the current change event. * * #### Notes * The default implementation is a no-op. */ onCurrentChanged(value) { /* no-op */ } } //# sourceMappingURL=widgettracker.js.map