UNPKG

12.7 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { JSONEditor } from '@jupyterlab/codeeditor';
4import { ObservableJSON } from '@jupyterlab/observables';
5import { nullTranslator } from '@jupyterlab/translation';
6import { Collapser } from '@jupyterlab/ui-components';
7import { ArrayExt } from '@lumino/algorithm';
8import { ConflatableMessage, MessageLoop } from '@lumino/messaging';
9import { PanelLayout, Widget } from '@lumino/widgets';
10class RankedPanel extends Widget {
11 constructor() {
12 super();
13 this._items = [];
14 this.layout = new PanelLayout();
15 this.addClass('jp-RankedPanel');
16 }
17 addWidget(widget, rank) {
18 const rankItem = { widget, rank };
19 const index = ArrayExt.upperBound(this._items, rankItem, Private.itemCmp);
20 ArrayExt.insert(this._items, index, rankItem);
21 const layout = this.layout;
22 layout.insertWidget(index, widget);
23 }
24 /**
25 * Handle the removal of a child
26 *
27 */
28 onChildRemoved(msg) {
29 const index = ArrayExt.findFirstIndex(this._items, item => item.widget === msg.child);
30 if (index !== -1) {
31 ArrayExt.removeAt(this._items, index);
32 }
33 }
34}
35/**
36 * A widget that provides metadata tools.
37 */
38export class NotebookTools extends Widget {
39 /**
40 * Construct a new NotebookTools object.
41 */
42 constructor(options) {
43 super();
44 this.addClass('jp-NotebookTools');
45 this.translator = options.translator || nullTranslator;
46 this._tools = [];
47 this.layout = new PanelLayout();
48 this._tracker = options.tracker;
49 this._tracker.currentChanged.connect(this._onActiveNotebookPanelChanged, this);
50 this._tracker.activeCellChanged.connect(this._onActiveCellChanged, this);
51 this._tracker.selectionChanged.connect(this._onSelectionChanged, this);
52 this._onActiveNotebookPanelChanged();
53 this._onActiveCellChanged();
54 this._onSelectionChanged();
55 }
56 /**
57 * The active cell widget.
58 */
59 get activeCell() {
60 return this._tracker.activeCell;
61 }
62 /**
63 * The currently selected cells.
64 */
65 get selectedCells() {
66 const panel = this._tracker.currentWidget;
67 if (!panel) {
68 return [];
69 }
70 const notebook = panel.content;
71 return notebook.widgets.filter(cell => notebook.isSelectedOrActive(cell));
72 }
73 /**
74 * The current notebook.
75 */
76 get activeNotebookPanel() {
77 return this._tracker.currentWidget;
78 }
79 /**
80 * Add a cell tool item.
81 */
82 addItem(options) {
83 var _a;
84 const tool = options.tool;
85 const rank = (_a = options.rank) !== null && _a !== void 0 ? _a : 100;
86 let section;
87 const extendedTool = this._tools.find(extendedTool => extendedTool.section === options.section);
88 if (extendedTool)
89 section = extendedTool.panel;
90 else {
91 throw new Error(`The section ${options.section} does not exist`);
92 }
93 tool.addClass('jp-NotebookTools-tool');
94 section.addWidget(tool, rank);
95 // TODO: perhaps the necessary notebookTools functionality should be
96 // consolidated into a single object, rather than a broad reference to this.
97 tool.notebookTools = this;
98 // Trigger the tool to update its active notebook and cell.
99 MessageLoop.sendMessage(tool, NotebookTools.ActiveNotebookPanelMessage);
100 MessageLoop.sendMessage(tool, NotebookTools.ActiveCellMessage);
101 }
102 /*
103 * Add a section to the notebook tool with its widget
104 */
105 addSection(options) {
106 var _a;
107 const sectionName = options.sectionName;
108 const label = options.label || options.sectionName;
109 const widget = options.tool;
110 let rank = (_a = options.rank) !== null && _a !== void 0 ? _a : null;
111 const newSection = new RankedPanel();
112 newSection.title.label = label;
113 if (widget)
114 newSection.addWidget(widget, 0);
115 this._tools.push({
116 section: sectionName,
117 panel: newSection,
118 rank: rank
119 });
120 if (rank != null)
121 this.layout.insertWidget(rank, new Collapser({ widget: newSection }));
122 else {
123 // If no rank is provided, try to add the new section before the AdvancedTools.
124 let advancedToolsRank = null;
125 const layout = this.layout;
126 for (let i = 0; i < layout.widgets.length; i++) {
127 let w = layout.widgets[i];
128 if (w instanceof Collapser) {
129 if (w.widget.id === 'advancedToolsSection') {
130 advancedToolsRank = i;
131 break;
132 }
133 }
134 }
135 if (advancedToolsRank !== null)
136 this.layout.insertWidget(advancedToolsRank, new Collapser({ widget: newSection }));
137 else
138 this.layout.addWidget(new Collapser({ widget: newSection }));
139 }
140 }
141 /**
142 * Handle a change to the notebook panel.
143 */
144 _onActiveNotebookPanelChanged() {
145 if (this._prevActiveNotebookModel &&
146 !this._prevActiveNotebookModel.isDisposed) {
147 this._prevActiveNotebookModel.metadataChanged.disconnect(this._onActiveNotebookPanelMetadataChanged, this);
148 }
149 const activeNBModel = this.activeNotebookPanel && this.activeNotebookPanel.content
150 ? this.activeNotebookPanel.content.model
151 : null;
152 this._prevActiveNotebookModel = activeNBModel;
153 if (activeNBModel) {
154 activeNBModel.metadataChanged.connect(this._onActiveNotebookPanelMetadataChanged, this);
155 }
156 for (const widget of this._toolChildren()) {
157 MessageLoop.sendMessage(widget, NotebookTools.ActiveNotebookPanelMessage);
158 }
159 }
160 /**
161 * Handle a change to the active cell.
162 */
163 _onActiveCellChanged() {
164 if (this._prevActiveCell && !this._prevActiveCell.isDisposed) {
165 this._prevActiveCell.metadataChanged.disconnect(this._onActiveCellMetadataChanged, this);
166 }
167 const activeCell = this.activeCell ? this.activeCell.model : null;
168 this._prevActiveCell = activeCell;
169 if (activeCell) {
170 activeCell.metadataChanged.connect(this._onActiveCellMetadataChanged, this);
171 }
172 for (const widget of this._toolChildren()) {
173 MessageLoop.sendMessage(widget, NotebookTools.ActiveCellMessage);
174 }
175 }
176 /**
177 * Handle a change in the selection.
178 */
179 _onSelectionChanged() {
180 for (const widget of this._toolChildren()) {
181 MessageLoop.sendMessage(widget, NotebookTools.SelectionMessage);
182 }
183 }
184 /**
185 * Handle a change in the active cell metadata.
186 */
187 _onActiveNotebookPanelMetadataChanged(sender, args) {
188 const message = new ObservableJSON.ChangeMessage('activenotebookpanel-metadata-changed', { oldValue: undefined, newValue: undefined, ...args });
189 for (const widget of this._toolChildren()) {
190 MessageLoop.sendMessage(widget, message);
191 }
192 }
193 /**
194 * Handle a change in the notebook model metadata.
195 */
196 _onActiveCellMetadataChanged(sender, args) {
197 const message = new ObservableJSON.ChangeMessage('activecell-metadata-changed', { newValue: undefined, oldValue: undefined, ...args });
198 for (const widget of this._toolChildren()) {
199 MessageLoop.sendMessage(widget, message);
200 }
201 }
202 *_toolChildren() {
203 for (let tool of this._tools) {
204 yield* tool.panel.children();
205 }
206 }
207}
208/**
209 * The namespace for NotebookTools class statics.
210 */
211(function (NotebookTools) {
212 /**
213 * A singleton conflatable `'activenotebookpanel-changed'` message.
214 */
215 NotebookTools.ActiveNotebookPanelMessage = new ConflatableMessage('activenotebookpanel-changed');
216 /**
217 * A singleton conflatable `'activecell-changed'` message.
218 */
219 NotebookTools.ActiveCellMessage = new ConflatableMessage('activecell-changed');
220 /**
221 * A singleton conflatable `'selection-changed'` message.
222 */
223 NotebookTools.SelectionMessage = new ConflatableMessage('selection-changed');
224 /**
225 * The base notebook tool, meant to be subclassed.
226 */
227 class Tool extends Widget {
228 dispose() {
229 super.dispose();
230 if (this.notebookTools) {
231 this.notebookTools = null;
232 }
233 }
234 /**
235 * Process a message sent to the widget.
236 *
237 * @param msg - The message sent to the widget.
238 */
239 processMessage(msg) {
240 super.processMessage(msg);
241 switch (msg.type) {
242 case 'activenotebookpanel-changed':
243 this.onActiveNotebookPanelChanged(msg);
244 break;
245 case 'activecell-changed':
246 this.onActiveCellChanged(msg);
247 break;
248 case 'selection-changed':
249 this.onSelectionChanged(msg);
250 break;
251 case 'activecell-metadata-changed':
252 this.onActiveCellMetadataChanged(msg);
253 break;
254 case 'activenotebookpanel-metadata-changed':
255 this.onActiveNotebookPanelMetadataChanged(msg);
256 break;
257 default:
258 break;
259 }
260 }
261 /**
262 * Handle a change to the notebook panel.
263 *
264 * #### Notes
265 * The default implementation is a no-op.
266 */
267 onActiveNotebookPanelChanged(msg) {
268 /* no-op */
269 }
270 /**
271 * Handle a change to the active cell.
272 *
273 * #### Notes
274 * The default implementation is a no-op.
275 */
276 onActiveCellChanged(msg) {
277 /* no-op */
278 }
279 /**
280 * Handle a change to the selection.
281 *
282 * #### Notes
283 * The default implementation is a no-op.
284 */
285 onSelectionChanged(msg) {
286 /* no-op */
287 }
288 /**
289 * Handle a change to the metadata of the active cell.
290 *
291 * #### Notes
292 * The default implementation is a no-op.
293 */
294 onActiveCellMetadataChanged(msg) {
295 /* no-op */
296 }
297 /**
298 * Handle a change to the metadata of the active cell.
299 *
300 * #### Notes
301 * The default implementation is a no-op.
302 */
303 onActiveNotebookPanelMetadataChanged(msg) {
304 /* no-op */
305 }
306 }
307 NotebookTools.Tool = Tool;
308 /**
309 * A raw metadata editor.
310 */
311 class MetadataEditorTool extends Tool {
312 /**
313 * Construct a new raw metadata tool.
314 */
315 constructor(options) {
316 super();
317 const { editorFactory } = options;
318 this.addClass('jp-MetadataEditorTool');
319 const layout = (this.layout = new PanelLayout());
320 this._editorFactory = editorFactory;
321 this._editorLabel = options.label || 'Edit Metadata';
322 this.createEditor();
323 const titleNode = new Widget({ node: document.createElement('label') });
324 titleNode.node.textContent = options.label || 'Edit Metadata';
325 layout.addWidget(titleNode);
326 layout.addWidget(this.editor);
327 }
328 /**
329 * The editor used by the tool.
330 */
331 get editor() {
332 return this._editor;
333 }
334 /**
335 * Handle a change to the notebook.
336 */
337 onActiveNotebookPanelChanged(msg) {
338 this.editor.dispose();
339 if (this.notebookTools.activeNotebookPanel) {
340 this.createEditor();
341 }
342 }
343 createEditor() {
344 this._editor = new JSONEditor({
345 editorFactory: this._editorFactory
346 });
347 this.editor.title.label = this._editorLabel;
348 this.layout.addWidget(this.editor);
349 }
350 }
351 NotebookTools.MetadataEditorTool = MetadataEditorTool;
352})(NotebookTools || (NotebookTools = {}));
353/**
354 * A namespace for private data.
355 */
356var Private;
357(function (Private) {
358 /**
359 * A comparator function for widget rank items.
360 */
361 function itemCmp(first, second) {
362 return first.rank - second.rank;
363 }
364 Private.itemCmp = itemCmp;
365})(Private || (Private = {}));
366//# sourceMappingURL=notebooktools.js.map
\No newline at end of file