diff --git a/README.md b/README.md index f30a422..8b5d8ab 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,15 @@ A cell toolbar for JupyterLab. ![Demonstration](https://raw.githubusercontent.com/jupyterlab-contrib/jlab-enhanced-cell-toolbar/main/docs/default_look.png) There are some [Settings](#Settings) to tune the available buttons and the look of this extension. +For example, to show only the tags, you can set the following settings: + +```json +{ + "helperButtons": null, + "leftMenu": null, + "rightMenu": null +} +``` ## Requirements @@ -54,6 +63,17 @@ conda remove -c conda-forge jlab-enhanced-cell-toolbar ![default tags](https://raw.githubusercontent.com/jupyterlab-contrib/jlab-enhanced-cell-toolbar/main/docs/default_tags.png) + +- _helperButtons_: The list of helper buttons to display. For example, using the following settings: + +```json +{ + "helperButtons": [ "insert-cell-below", "run-cell-and-select-next"] +} +``` + +![helper buttons](https://raw.githubusercontent.com/jupyterlab-contrib/jlab-enhanced-cell-toolbar/main/docs/helper_buttons.png) + - _leftMenu_ and _rightMenu_: The action buttons to be displayed on the left and right of the cell toolbar. For example, using the following settings: ```json diff --git a/docs/helper_buttons.png b/docs/helper_buttons.png new file mode 100644 index 0000000..42d009a Binary files /dev/null and b/docs/helper_buttons.png differ diff --git a/package.json b/package.json index 302f215..cf34fe2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jlab-enhanced/cell-toolbar", - "version": "3.1.1", + "version": "3.2.0", "description": "A cell toolbar for JupyterLab.", "keywords": [ "jupyter", diff --git a/schema/plugin.json b/schema/plugin.json index 6677193..cb1e107 100644 --- a/schema/plugin.json +++ b/schema/plugin.json @@ -15,6 +15,34 @@ "pattern": "^[\\w-]+$" } }, + "helperButtons": { + "title": "Helper buttons", + "description": "Set the list of visible helper buttons.", + "default": [ + "insert-cell-below", + "move-cell-down", + "move-cell-up", + "run-cell-and-select-next" + ], + "oneOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "insert-cell-below", + "move-cell-down", + "move-cell-up", + "run-cell-and-select-next" + ] + }, + "uniqueItems": true + } + ] + }, "leftMenu": { "title": "List of left menu items", "description": "An item is defined by a 'command' name and an 'icon' name + optionally a 'tooltip' and the 'cellType' on which it applies", diff --git a/src/cellmenu.ts b/src/cellmenu.ts index ad93631..518c6fc 100644 --- a/src/cellmenu.ts +++ b/src/cellmenu.ts @@ -1,5 +1,4 @@ import { ToolbarButton } from '@jupyterlab/apputils'; -import { IObservableList } from '@jupyterlab/observables'; import { LabIcon } from '@jupyterlab/ui-components'; import { each } from '@lumino/algorithm'; import { CommandRegistry } from '@lumino/commands'; @@ -12,38 +11,21 @@ const CELL_MENU_CLASS = 'jp-enh-cell-menu'; * Toolbar icon menu container */ export class CellMenu extends Widget { - constructor( - commands: CommandRegistry, - items: IObservableList - ) { + constructor(commands: CommandRegistry, items: ICellMenuItem[]) { super(); this._commands = commands; - this._items = items; this.layout = new PanelLayout(); this.addClass(CELL_MENU_CLASS); - this._itemsChanged(items); - this._items.changed.connect(this._itemsChanged, this); + this._addButtons(items); } - dispose(): void { - if (this.isDisposed) { - return; - } - this._items.changed.disconnect(this._itemsChanged, this); - - super.dispose(); - } - - protected _itemsChanged( - items: IObservableList, - changes?: IObservableList.IChangedArgs - ): void { + protected _addButtons(items: ICellMenuItem[]): void { each(this.children(), widget => { widget.dispose(); }); const layout = this.layout as PanelLayout; - each(items.iter(), entry => { + items.forEach(entry => { if (this._commands.hasCommand(entry.command)) { layout.addWidget( new ToolbarButton({ @@ -60,5 +42,4 @@ export class CellMenu extends Widget { } private _commands: CommandRegistry; - private _items: IObservableList; } diff --git a/src/celltoolbartracker.ts b/src/celltoolbartracker.ts index 624084f..aae58ff 100644 --- a/src/celltoolbartracker.ts +++ b/src/celltoolbartracker.ts @@ -14,10 +14,11 @@ import { markdownIcon, runIcon } from '@jupyterlab/ui-components'; +import { each } from '@lumino/algorithm'; import { CommandRegistry } from '@lumino/commands'; import { IDisposable } from '@lumino/disposable'; import { PanelLayout, Widget } from '@lumino/widgets'; -import { CellToolbarWidget, LEFT_SPACER_CLASSNAME } from './celltoolbarwidget'; +import { CellToolbarWidget } from './celltoolbarwidget'; import { codeIcon, deleteIcon, formatIcon } from './icon'; import { PositionedButton } from './positionedbutton'; import { ICellMenuItem } from './tokens'; @@ -48,7 +49,7 @@ const DEFAULT_LEFT_MENU: ICellMenuItem[] = [ } ]; -const POSITIONED_BUTTONS: ICellMenuItem[] = [ +const DEFAULT_HELPER_BUTTONS: ICellMenuItem[] = [ // Originate from @jupyterlab/notebook-extension { command: 'notebook:run-cell-and-select-next', @@ -112,6 +113,7 @@ export class CellToolbarTracker implements IDisposable { const cells = this._panel?.context.model.cells; if (cells) { cells.changed.disconnect(this.updateConnectedCells, this); + each(cells.iter(), model => this._removeToolbar(model)); } this._panel = null; } @@ -132,19 +134,34 @@ export class CellToolbarTracker implements IDisposable { private _addToolbar(model: ICellModel): void { const cell = this._getCell(model); + if (cell) { + let { helperButtons, leftMenu, rightMenu, leftSpace } = this._settings + ?.composite as any; + + helperButtons = + helperButtons === null + ? [] + : helperButtons ?? + DEFAULT_HELPER_BUTTONS.map(entry => entry.command.split(':')[1]); + leftMenu = leftMenu === null ? [] : leftMenu ?? DEFAULT_LEFT_MENU; + rightMenu = rightMenu ?? []; + leftSpace = leftSpace ?? 0; + const toolbar = new CellToolbarWidget( this._commands, model, this._allTags, - this._leftMenuItems, - this._rightMenuItems, - (this._settings?.composite['leftSpace'] as number) || 0 + leftMenu, + rightMenu, + leftSpace ); toolbar.addClass(CELL_BAR_CLASS); (cell.layout as PanelLayout).insertWidget(0, toolbar); - POSITIONED_BUTTONS.forEach(entry => { + DEFAULT_HELPER_BUTTONS.filter(entry => + (helperButtons as string[]).includes(entry.command.split(':')[1]) + ).forEach(entry => { if (this._commands.hasCommand(entry.command)) { const { cellType, command, tooltip, ...others } = entry; const shortName = command.split(':')[1]; @@ -186,29 +203,12 @@ export class CellToolbarTracker implements IDisposable { * Call back on settings changes */ private _onSettingsChanged(): void { - // Update menu items - const leftItems = (this._settings?.composite['leftMenu'] as any) as - | ICellMenuItem[] - | null; - // Test to avoid useless signal emission - if (this._leftMenuItems.length > 0) { - this._leftMenuItems.clear(); - } - if (leftItems) { - if (leftItems.length > 0) { - this._leftMenuItems.pushAll(leftItems); - } - } else { - this._leftMenuItems.pushAll(DEFAULT_LEFT_MENU); - } - const rightItems = ((this._settings?.composite['rightMenu'] as any) || - []) as ICellMenuItem[]; - // Test to avoid useless signal emission - if (this._rightMenuItems.length > 0) { - this._rightMenuItems.clear(); - } - if (rightItems.length > 0) { - this._rightMenuItems.pushAll(rightItems); + // Reset toolbar when settings changes + if (this._panel?.context.model.cells) { + each(this._panel?.context.model.cells.iter(), model => { + this._removeToolbar(model); + this._addToolbar(model); + }); } // Update tags @@ -225,29 +225,13 @@ export class CellToolbarTracker implements IDisposable { .filter(tag => !newDefaultTags.includes(tag)) .forEach(tag => this._allTags.removeValue(tag)); this._previousDefaultTags = newDefaultTags; - - // Update left space - const leftSpace = (this._settings?.composite['leftSpace'] as number) || 0; - if (this._panel) { - this._panel.node - .querySelectorAll(`div.${LEFT_SPACER_CLASSNAME}`) - .forEach(node => { - (node as HTMLElement).style.width = `${leftSpace}px`; - }); - } } private _allTags: ObservableList = new ObservableList(); private _commands: CommandRegistry; private _isDisposed = false; - private _leftMenuItems: ObservableList = new ObservableList< - ICellMenuItem - >(); private _previousDefaultTags = new Array(); private _panel: NotebookPanel | null; - private _rightMenuItems: ObservableList = new ObservableList< - ICellMenuItem - >(); private _settings: ISettingRegistry.ISettings | null; } diff --git a/src/celltoolbarwidget.ts b/src/celltoolbarwidget.ts index 27aa7ab..602a770 100644 --- a/src/celltoolbarwidget.ts +++ b/src/celltoolbarwidget.ts @@ -16,8 +16,8 @@ export class CellToolbarWidget extends Widget { commands: CommandRegistry, model: ICellModel, tagsList: ObservableList, - leftMenuItems: ObservableList, - rightMenuItems: ObservableList, + leftMenuItems: ICellMenuItem[], + rightMenuItems: ICellMenuItem[], leftSpace = 0 ) { super();