1 |
|
2 |
|
3 |
|
4 | import {
|
5 | Toolbar as AppToolbar,
|
6 | Dialog,
|
7 | ISessionContext,
|
8 | ISessionContextDialogs,
|
9 | SessionContextDialogs,
|
10 | showDialog
|
11 | } from '@jupyterlab/apputils';
|
12 | import { DocumentRegistry } from '@jupyterlab/docregistry';
|
13 | import * as nbformat from '@jupyterlab/nbformat';
|
14 | import {
|
15 | ITranslator,
|
16 | nullTranslator,
|
17 | TranslationBundle
|
18 | } from '@jupyterlab/translation';
|
19 | import {
|
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';
|
35 | import * as React from 'react';
|
36 | import { NotebookActions } from './actions';
|
37 | import { NotebookPanel } from './panel';
|
38 | import { Notebook } from './widget';
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | const TOOLBAR_CELLTYPE_CLASS = 'jp-Notebook-toolbarCellType';
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | const TOOLBAR_CELLTYPE_DROPDOWN_CLASS = 'jp-Notebook-toolbarCellTypeDropdown';
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | export namespace ToolbarItems {
|
54 | |
55 |
|
56 |
|
57 |
|
58 |
|
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 |
|
107 |
|
108 |
|
109 |
|
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 |
|
127 |
|
128 |
|
129 |
|
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 |
|
147 |
|
148 |
|
149 |
|
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 |
|
167 |
|
168 |
|
169 |
|
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 |
|
187 |
|
188 |
|
189 |
|
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 |
|
212 |
|
213 |
|
214 |
|
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 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | export function createCellTypeItem(
|
254 | panel: NotebookPanel,
|
255 | translator?: ITranslator
|
256 | ): ReactWidget {
|
257 | return new CellTypeSwitcher(panel.content, translator);
|
258 | }
|
259 |
|
260 | |
261 |
|
262 |
|
263 |
|
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 |
|
315 |
|
316 | export class CellTypeSwitcher extends ReactWidget {
|
317 | |
318 |
|
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 |
|
330 | widget.selectionChanged.connect(this.update, this);
|
331 | }
|
332 |
|
333 | |
334 |
|
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 |
|
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 | }
|