// ***************************************************************************** // Copyright (C) 2023 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import * as React from '@theia/core/shared/react'; import { CommandMenu, CommandRegistry, CompoundMenuNode, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath, RenderedMenuNode } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookCellSidebar, NotebookCellToolbar } from './notebook-cell-toolbar'; import { ContextMenuRenderer } from '@theia/core/lib/browser'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookContextManager } from '../service/notebook-context-manager'; export interface NotebookCellToolbarItem { id: string; icon?: string; label?: string; onClick: (e: React.MouseEvent) => void; isVisible: () => boolean; } export interface toolbarItemOptions { contextMenuArgs?: () => unknown[]; commandArgs?: () => unknown[]; } @injectable() export class NotebookCellToolbarFactory { @inject(MenuModelRegistry) protected menuRegistry: MenuModelRegistry; @inject(ContextKeyService) protected contextKeyService: ContextKeyService; @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager; protected readonly onDidChangeContextEmitter = new Emitter; readonly onDidChangeContext: Event = this.onDidChangeContextEmitter.event; protected toDisposeOnRender = new DisposableCollection(); renderCellToolbar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode { return this.getMenuItems(menuPath, cell, itemOptions)} onContextChanged={this.onDidChangeContext} />; } renderSidebar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode { return this.getMenuItems(menuPath, cell, itemOptions)} onContextChanged={this.onDidChangeContext} />; } private getMenuItems(menuItemPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): NotebookCellToolbarItem[] { this.toDisposeOnRender.dispose(); this.toDisposeOnRender = new DisposableCollection(); const inlineItems: NotebookCellToolbarItem[] = []; const menu = this.menuRegistry.getMenu(menuItemPath); this.toDisposeOnRender.push(this.notebookContextManager.scopedStore?.onDidChangeContext(() => { this.onDidChangeContextEmitter.fire(); })); if (menu) { for (const menuNode of menu.children) { const itemPath = [...menuItemPath, menuNode.id]; if (menuNode.isVisible(itemPath, this.notebookContextManager.getCellContext(cell.handle), this.notebookContextManager.context, itemOptions.commandArgs?.() ?? [])) { if (RenderedMenuNode.is(menuNode)) { inlineItems.push(this.createToolbarItem(itemPath, menuNode, itemOptions)); } } } } return inlineItems; } private createToolbarItem(menuPath: MenuPath, menuNode: RenderedMenuNode, itemOptions: toolbarItemOptions): NotebookCellToolbarItem { return { id: menuNode.id, icon: menuNode.icon, label: menuNode.label, onClick: e => { if (CompoundMenuNode.is(menuNode)) { this.contextMenuRenderer.render( { anchor: e.nativeEvent, menuPath: menuPath, menu: menuNode, includeAnchorArg: false, args: itemOptions.contextMenuArgs?.(), context: this.notebookContextManager.context || (e.currentTarget as HTMLElement) }); } else if (CommandMenu.is(menuNode)) { menuNode.run(menuPath, ...(itemOptions.commandArgs?.() ?? [])); }; }, isVisible: () => true }; } }