UNPKG

10.3 kBTypeScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3
4import {
5 Toolbar as AppToolbar,
6 Dialog,
7 ISessionContext,
8 ISessionContextDialogs,
9 SessionContextDialogs,
10 showDialog
11} from '@jupyterlab/apputils';
12import { DocumentRegistry } from '@jupyterlab/docregistry';
13import * as nbformat from '@jupyterlab/nbformat';
14import {
15 ITranslator,
16 nullTranslator,
17 TranslationBundle
18} from '@jupyterlab/translation';
19import {
20 addIcon,
21 addToolbarButtonClass,
22 copyIcon,
23 cutIcon,
24 fastForwardIcon,
25 HTMLSelect,
26 pasteIcon,
27 ReactWidget,
28 runIcon,
29 saveIcon,
30 Toolbar,
31 ToolbarButton,
32 ToolbarButtonComponent,
33 UseSignal
34} from '@jupyterlab/ui-components';
35import * as React from 'react';
36import { NotebookActions } from './actions';
37import { NotebookPanel } from './panel';
38import { Notebook } from './widget';
39
40/**
41 * The class name added to toolbar cell type dropdown wrapper.
42 */
43const TOOLBAR_CELLTYPE_CLASS = 'jp-Notebook-toolbarCellType';
44
45/**
46 * The class name added to toolbar cell type dropdown.
47 */
48const TOOLBAR_CELLTYPE_DROPDOWN_CLASS = 'jp-Notebook-toolbarCellTypeDropdown';
49
50/**
51 * A namespace for the default toolbar items.
52 */
53export namespace ToolbarItems {
54 /**
55 * Create save button toolbar item.
56 *
57 * @deprecated since v3.2
58 * This is dead code now.
59 */
60 export function createSaveButton(
61 panel: NotebookPanel,
62 translator?: ITranslator
63 ): ReactWidget {
64 const trans = (translator || nullTranslator).load('jupyterlab');
65 function onClick() {
66 if (panel.context.model.readOnly) {
67 return showDialog({
68 title: trans.__('Cannot Save'),
69 body: trans.__('Document is read-only'),
70 buttons: [Dialog.okButton()]
71 });
72 }
73 void panel.context.save().then(() => {
74 if (!panel.isDisposed) {
75 return panel.context.createCheckpoint();
76 }
77 });
78 }
79
80 return addToolbarButtonClass<ReactWidget>(
81 ReactWidget.create(
82 <UseSignal signal={panel.context.fileChanged}>
83 {() => (
84 <ToolbarButtonComponent
85 icon={saveIcon}
86 onClick={onClick}
87 tooltip={trans.__(
88 'Save the notebook contents and create checkpoint'
89 )}
90 enabled={
91 !!(
92 panel &&
93 panel.context &&
94 panel.context.contentsModel &&
95 panel.context.contentsModel.writable
96 )
97 }
98 />
99 )}
100 </UseSignal>
101 )
102 );
103 }
104
105 /**
106 * Create an insert toolbar item.
107 *
108 * @deprecated since v3.2
109 * This is dead code now.
110 */
111 export function createInsertButton(
112 panel: NotebookPanel,
113 translator?: ITranslator
114 ): ReactWidget {
115 const trans = (translator || nullTranslator).load('jupyterlab');
116 return new ToolbarButton({
117 icon: addIcon,
118 onClick: () => {
119 NotebookActions.insertBelow(panel.content);
120 },
121 tooltip: trans.__('Insert a cell below')
122 });
123 }
124
125 /**
126 * Create a cut toolbar item.
127 *
128 * @deprecated since v3.2
129 * This is dead code now.
130 */
131 export function createCutButton(
132 panel: NotebookPanel,
133 translator?: ITranslator
134 ): ReactWidget {
135 const trans = (translator || nullTranslator).load('jupyterlab');
136 return new ToolbarButton({
137 icon: cutIcon,
138 onClick: () => {
139 NotebookActions.cut(panel.content);
140 },
141 tooltip: trans.__('Cut the selected cells')
142 });
143 }
144
145 /**
146 * Create a copy toolbar item.
147 *
148 * @deprecated since v3.2
149 * This is dead code now.
150 */
151 export function createCopyButton(
152 panel: NotebookPanel,
153 translator?: ITranslator
154 ): ReactWidget {
155 const trans = (translator || nullTranslator).load('jupyterlab');
156 return new ToolbarButton({
157 icon: copyIcon,
158 onClick: () => {
159 NotebookActions.copy(panel.content);
160 },
161 tooltip: trans.__('Copy the selected cells')
162 });
163 }
164
165 /**
166 * Create a paste toolbar item.
167 *
168 * @deprecated since v3.2
169 * This is dead code now.
170 */
171 export function createPasteButton(
172 panel: NotebookPanel,
173 translator?: ITranslator
174 ): ReactWidget {
175 const trans = (translator || nullTranslator).load('jupyterlab');
176 return new ToolbarButton({
177 icon: pasteIcon,
178 onClick: () => {
179 NotebookActions.paste(panel.content);
180 },
181 tooltip: trans.__('Paste cells from the clipboard')
182 });
183 }
184
185 /**
186 * Create a run toolbar item.
187 *
188 * @deprecated since v3.2
189 * This is dead code now.
190 */
191 export function createRunButton(
192 panel: NotebookPanel,
193 sessionDialogs?: ISessionContextDialogs,
194 translator?: ITranslator
195 ): ReactWidget {
196 const trans = (translator ?? nullTranslator).load('jupyterlab');
197 return new ToolbarButton({
198 icon: runIcon,
199 onClick: () => {
200 void NotebookActions.runAndAdvance(
201 panel.content,
202 panel.sessionContext,
203 sessionDialogs,
204 translator
205 );
206 },
207 tooltip: trans.__('Run the selected cells and advance')
208 });
209 }
210 /**
211 * Create a restart run all toolbar item
212 *
213 * @deprecated since v3.2
214 * This is dead code now.
215 */
216 export function createRestartRunAllButton(
217 panel: NotebookPanel,
218 dialogs?: ISessionContext.IDialogs,
219 translator?: ITranslator
220 ): ReactWidget {
221 const trans = (translator ?? nullTranslator).load('jupyterlab');
222 return new ToolbarButton({
223 icon: fastForwardIcon,
224 onClick: () => {
225 const dialogs_ = dialogs ?? new SessionContextDialogs({ translator });
226 void dialogs_.restart(panel.sessionContext).then(restarted => {
227 if (restarted) {
228 void NotebookActions.runAll(
229 panel.content,
230 panel.sessionContext,
231 dialogs_,
232 translator
233 );
234 }
235 return restarted;
236 });
237 },
238 tooltip: trans.__('Restart the kernel, then re-run the whole notebook')
239 });
240 }
241
242 /**
243 * Create a cell type switcher item.
244 *
245 * #### Notes
246 * It will display the type of the current active cell.
247 * If more than one cell is selected but are of different types,
248 * it will display `'-'`.
249 * When the user changes the cell type, it will change the
250 * cell types of the selected cells.
251 * It can handle a change to the context.
252 */
253 export function createCellTypeItem(
254 panel: NotebookPanel,
255 translator?: ITranslator
256 ): ReactWidget {
257 return new CellTypeSwitcher(panel.content, translator);
258 }
259
260 /**
261 * Get the default toolbar items for panel
262 *
263 * @deprecated since v4
264 */
265 export function getDefaultItems(
266 panel: NotebookPanel,
267 sessionDialogs?: ISessionContextDialogs,
268 translator?: ITranslator
269 ): DocumentRegistry.IToolbarItem[] {
270 return [
271 { name: 'save', widget: createSaveButton(panel, translator) },
272 { name: 'insert', widget: createInsertButton(panel, translator) },
273 { name: 'cut', widget: createCutButton(panel, translator) },
274 { name: 'copy', widget: createCopyButton(panel, translator) },
275 { name: 'paste', widget: createPasteButton(panel, translator) },
276 {
277 name: 'run',
278 widget: createRunButton(panel, sessionDialogs, translator)
279 },
280 {
281 name: 'interrupt',
282 widget: AppToolbar.createInterruptButton(
283 panel.sessionContext,
284 translator
285 )
286 },
287 {
288 name: 'restart',
289 widget: AppToolbar.createRestartButton(
290 panel.sessionContext,
291 sessionDialogs,
292 translator
293 )
294 },
295 {
296 name: 'restart-and-run',
297 widget: createRestartRunAllButton(panel, sessionDialogs, translator)
298 },
299 { name: 'cellType', widget: createCellTypeItem(panel, translator) },
300 { name: 'spacer', widget: Toolbar.createSpacerItem() },
301 {
302 name: 'kernelName',
303 widget: AppToolbar.createKernelNameItem(
304 panel.sessionContext,
305 sessionDialogs,
306 translator
307 )
308 }
309 ];
310 }
311}
312
313/**
314 * A toolbar widget that switches cell types.
315 */
316export class CellTypeSwitcher extends ReactWidget {
317 /**
318 * Construct a new cell type switcher.
319 */
320 constructor(widget: Notebook, translator?: ITranslator) {
321 super();
322 this._trans = (translator || nullTranslator).load('jupyterlab');
323 this.addClass(TOOLBAR_CELLTYPE_CLASS);
324 this._notebook = widget;
325 if (widget.model) {
326 this.update();
327 }
328 widget.activeCellChanged.connect(this.update, this);
329 // Follow a change in the selection.
330 widget.selectionChanged.connect(this.update, this);
331 }
332
333 /**
334 * Handle `change` events for the HTMLSelect component.
335 */
336 handleChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
337 if (event.target.value !== '-') {
338 NotebookActions.changeCellType(
339 this._notebook,
340 event.target.value as nbformat.CellType
341 );
342 this._notebook.activate();
343 }
344 };
345
346 /**
347 * Handle `keydown` events for the HTMLSelect component.
348 */
349 handleKeyDown = (event: React.KeyboardEvent): void => {
350 if (event.keyCode === 13) {
351 this._notebook.activate();
352 }
353 };
354
355 render(): JSX.Element {
356 let value = '-';
357 if (this._notebook.activeCell) {
358 value = this._notebook.activeCell.model.type;
359 }
360 for (const widget of this._notebook.widgets) {
361 if (this._notebook.isSelectedOrActive(widget)) {
362 if (widget.model.type !== value) {
363 value = '-';
364 break;
365 }
366 }
367 }
368 return (
369 <HTMLSelect
370 className={TOOLBAR_CELLTYPE_DROPDOWN_CLASS}
371 onChange={this.handleChange}
372 onKeyDown={this.handleKeyDown}
373 value={value}
374 aria-label={this._trans.__('Cell type')}
375 title={this._trans.__('Select the cell type')}
376 >
377 <option value="-">-</option>
378 <option value="code">{this._trans.__('Code')}</option>
379 <option value="markdown">{this._trans.__('Markdown')}</option>
380 <option value="raw">{this._trans.__('Raw')}</option>
381 </HTMLSelect>
382 );
383 }
384
385 private _trans: TranslationBundle;
386 private _notebook: Notebook;
387}