UNPKG

@jupyter-notebook/application

Version:

Jupyter Notebook - Application

329 lines (328 loc) 9.41 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { closeIcon } from '@jupyterlab/ui-components'; import { ArrayExt, find } from '@lumino/algorithm'; import { MessageLoop } from '@lumino/messaging'; import { Signal } from '@lumino/signaling'; import { Panel, StackedPanel, Widget } from '@lumino/widgets'; /** * A class which manages a panel and sorts its widgets by rank. */ export class PanelHandler { constructor() { /** * A message hook for child remove messages on the panel handler. */ this._panelChildHook = (handler, msg) => { switch (msg.type) { case 'child-removed': { const widget = msg.child; ArrayExt.removeFirstWhere(this._items, (v) => v.widget === widget); } break; default: break; } return true; }; this._items = new Array(); this._panel = new Panel(); MessageLoop.installMessageHook(this._panel, this._panelChildHook); } /** * Get the panel managed by the handler. */ get panel() { return this._panel; } /** * Add a widget to the panel. * * If the widget is already added, it will be moved. */ addWidget(widget, rank) { widget.parent = null; const item = { widget, rank }; const index = ArrayExt.upperBound(this._items, item, Private.itemCmp); ArrayExt.insert(this._items, index, item); this._panel.insertWidget(index, widget); } } /** * A class which manages a side panel that can show at most one widget at a time. */ export class SidePanelHandler extends PanelHandler { /** * Construct a new side panel handler. */ constructor(area) { super(); this._isHiddenByUser = false; this._widgetAdded = new Signal(this); this._widgetRemoved = new Signal(this); this._area = area; this._panel.hide(); this._currentWidget = null; this._lastCurrentWidget = null; this._widgetPanel = new StackedPanel(); this._widgetPanel.widgetRemoved.connect(this._onWidgetRemoved, this); this._closeButton = document.createElement('button'); closeIcon.element({ container: this._closeButton, height: '16px', width: 'auto', }); this._closeButton.onclick = () => { this.collapse(); this.hide(); }; this._closeButton.className = 'jp-Button jp-SidePanel-collapse'; this._closeButton.title = 'Collapse side panel'; const icon = new Widget({ node: this._closeButton }); this._panel.addWidget(icon); this._panel.addWidget(this._widgetPanel); } /** * Get the current widget in the sidebar panel. */ get currentWidget() { return (this._currentWidget || this._lastCurrentWidget || (this._items.length > 0 ? this._items[0].widget : null)); } /** * Get the area of the side panel */ get area() { return this._area; } /** * Whether the panel is visible */ get isVisible() { return this._panel.isVisible; } /** * Get the stacked panel managed by the handler */ get panel() { return this._panel; } /** * Get the widgets list. */ get widgets() { return this._items.map((obj) => obj.widget); } /** * Signal fired when a widget is added to the panel */ get widgetAdded() { return this._widgetAdded; } /** * Signal fired when a widget is removed from the panel */ get widgetRemoved() { return this._widgetRemoved; } /** * Get the close button element. */ get closeButton() { return this._closeButton; } /** * Expand the sidebar. * * #### Notes * This will open the most recently used widget, or the first widget * if there is no most recently used. */ expand(id) { if (id) { if (this._currentWidget && this._currentWidget.id === id) { this.collapse(); this.hide(); } else { this.collapse(); this.hide(); this.activate(id); this.show(); } } else if (this.currentWidget) { this._currentWidget = this.currentWidget; this.activate(this._currentWidget.id); this.show(); } } /** * Activate a widget residing in the stacked panel by ID. * * @param id - The widget's unique ID. */ activate(id) { const widget = this._findWidgetByID(id); if (widget) { this._currentWidget = widget; widget.show(); widget.activate(); } } /** * Test whether the sidebar has the given widget by id. */ has(id) { return this._findWidgetByID(id) !== null; } /** * Collapse the sidebar so no items are expanded. */ collapse() { var _a; (_a = this._currentWidget) === null || _a === void 0 ? void 0 : _a.hide(); this._currentWidget = null; } /** * Add a widget and its title to the stacked panel. * * If the widget is already added, it will be moved. */ addWidget(widget, rank) { widget.parent = null; widget.hide(); const item = { widget, rank }; const index = this._findInsertIndex(item); ArrayExt.insert(this._items, index, item); this._widgetPanel.insertWidget(index, widget); this._refreshVisibility(); this._widgetAdded.emit(widget); } /** * Hide the side panel */ hide() { this._isHiddenByUser = true; this._refreshVisibility(); } /** * Show the side panel */ show() { this._isHiddenByUser = false; this._refreshVisibility(); } /** * Find the insertion index for a rank item. */ _findInsertIndex(item) { return ArrayExt.upperBound(this._items, item, Private.itemCmp); } /** * Find the index of the item with the given widget, or `-1`. */ _findWidgetIndex(widget) { return ArrayExt.findFirstIndex(this._items, (i) => i.widget === widget); } /** * Find the widget with the given id, or `null`. */ _findWidgetByID(id) { const item = find(this._items, (value) => value.widget.id === id); return item ? item.widget : null; } /** * Refresh the visibility of the stacked panel. */ _refreshVisibility() { this._panel.setHidden(this._isHiddenByUser); } /* * Handle the `widgetRemoved` signal from the panel. */ _onWidgetRemoved(sender, widget) { if (widget === this._lastCurrentWidget) { this._lastCurrentWidget = null; } ArrayExt.removeAt(this._items, this._findWidgetIndex(widget)); this._refreshVisibility(); this._widgetRemoved.emit(widget); } } /** * A class to manages the palette entries associated to the side panels. */ export class SidePanelPalette { /** * Construct a new side panel palette. */ constructor(options) { this._items = []; this._commandPalette = options.commandPalette; this._command = options.command; } /** * Get a command palette item from the widget id and the area. */ getItem(widget, area) { const itemList = this._items; for (let i = 0; i < itemList.length; i++) { const item = itemList[i]; if (item.widgetId === widget.id && item.area === area) { return item; } } return null; } /** * Add an item to the command palette. */ addItem(widget, area) { // Check if the item does not already exist. if (this.getItem(widget, area)) { return; } // Add a new item in command palette. const disposableDelegate = this._commandPalette.addItem({ command: this._command, category: 'View', args: { side: area, title: `Show ${widget.title.caption}`, id: widget.id, }, }); // Keep the disposableDelegate object to be able to dispose of the item if the widget // is remove from the side panel. this._items.push({ widgetId: widget.id, area: area, disposable: disposableDelegate, }); } /** * Remove an item from the command palette. */ removeItem(widget, area) { const item = this.getItem(widget, area); if (item) { item.disposable.dispose(); } } } /** * A namespace for private module data. */ var Private; (function (Private) { /** * A less-than comparison function for side bar rank items. */ function itemCmp(first, second) { return first.rank - second.rank; } Private.itemCmp = itemCmp; })(Private || (Private = {}));