@jupyterlab/application 0.19.1-alpha.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/lib/index.d.ts +241 -241
- package/lib/index.js +283 -283
- package/lib/layoutrestorer.d.ts +205 -205
- package/lib/layoutrestorer.js +423 -423
- package/lib/mimerenderers.d.ts +22 -22
- package/lib/mimerenderers.js +137 -134
- package/lib/router.d.ts +204 -204
- package/lib/router.js +145 -145
- package/lib/shell.d.ts +277 -277
- package/lib/shell.js +857 -854
- package/package.json +13 -10
package/lib/shell.js
CHANGED
|
@@ -1,854 +1,857 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright (c) Jupyter Development Team.
|
|
3
|
-
// Distributed under the terms of the Modified BSD License.
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
const algorithm_1 = require("@phosphor/algorithm");
|
|
6
|
-
const coreutils_1 = require("@phosphor/coreutils");
|
|
7
|
-
const messaging_1 = require("@phosphor/messaging");
|
|
8
|
-
const signaling_1 = require("@phosphor/signaling");
|
|
9
|
-
const widgets_1 = require("@phosphor/widgets");
|
|
10
|
-
/**
|
|
11
|
-
* The class name added to AppShell instances.
|
|
12
|
-
*/
|
|
13
|
-
const APPLICATION_SHELL_CLASS = 'jp-ApplicationShell';
|
|
14
|
-
/**
|
|
15
|
-
* The class name added to side bar instances.
|
|
16
|
-
*/
|
|
17
|
-
const SIDEBAR_CLASS = 'jp-SideBar';
|
|
18
|
-
/**
|
|
19
|
-
* The class name added to the current widget's title.
|
|
20
|
-
*/
|
|
21
|
-
const CURRENT_CLASS = 'jp-mod-current';
|
|
22
|
-
/**
|
|
23
|
-
* The class name added to the active widget's title.
|
|
24
|
-
*/
|
|
25
|
-
const ACTIVE_CLASS = 'jp-mod-active';
|
|
26
|
-
/**
|
|
27
|
-
* The default rank of items added to a sidebar.
|
|
28
|
-
*/
|
|
29
|
-
const DEFAULT_RANK = 500;
|
|
30
|
-
/**
|
|
31
|
-
* The data attribute added to the document body indicating shell's mode.
|
|
32
|
-
*/
|
|
33
|
-
const MODE_ATTRIBUTE = 'data-shell-mode';
|
|
34
|
-
const ACTIVITY_CLASS = 'jp-Activity';
|
|
35
|
-
/**
|
|
36
|
-
* The application shell for JupyterLab.
|
|
37
|
-
*/
|
|
38
|
-
class ApplicationShell extends widgets_1.Widget {
|
|
39
|
-
/**
|
|
40
|
-
* Construct a new application shell.
|
|
41
|
-
*/
|
|
42
|
-
constructor() {
|
|
43
|
-
super();
|
|
44
|
-
/**
|
|
45
|
-
* A message hook for child add/remove messages on the main area dock panel.
|
|
46
|
-
*/
|
|
47
|
-
this._dockChildHook = (handler, msg) => {
|
|
48
|
-
switch (msg.type) {
|
|
49
|
-
case 'child-added':
|
|
50
|
-
msg.child.addClass(ACTIVITY_CLASS);
|
|
51
|
-
this._tracker.add(msg.child);
|
|
52
|
-
break;
|
|
53
|
-
case 'child-removed':
|
|
54
|
-
msg.child.removeClass(ACTIVITY_CLASS);
|
|
55
|
-
this._tracker.remove(msg.child);
|
|
56
|
-
break;
|
|
57
|
-
default:
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
return true;
|
|
61
|
-
};
|
|
62
|
-
this._activeChanged = new signaling_1.Signal(this);
|
|
63
|
-
this._cachedLayout = null;
|
|
64
|
-
this._currentChanged = new signaling_1.Signal(this);
|
|
65
|
-
this._isRestored = false;
|
|
66
|
-
this._layoutModified = new signaling_1.Signal(this);
|
|
67
|
-
this._restored = new coreutils_1.PromiseDelegate();
|
|
68
|
-
this._tracker = new widgets_1.FocusTracker();
|
|
69
|
-
this._debouncer = 0;
|
|
70
|
-
this._addOptionsCache = new Map();
|
|
71
|
-
this._sideOptionsCache = new Map();
|
|
72
|
-
this.addClass(APPLICATION_SHELL_CLASS);
|
|
73
|
-
this.id = 'main';
|
|
74
|
-
let bottomPanel = (this._bottomPanel = new widgets_1.BoxPanel());
|
|
75
|
-
let topPanel = (this._topPanel = new widgets_1.Panel());
|
|
76
|
-
let hboxPanel = new widgets_1.BoxPanel();
|
|
77
|
-
let dockPanel = (this._dockPanel = new widgets_1.DockPanel());
|
|
78
|
-
messaging_1.MessageLoop.installMessageHook(dockPanel, this._dockChildHook);
|
|
79
|
-
let hsplitPanel = new widgets_1.SplitPanel();
|
|
80
|
-
let leftHandler = (this._leftHandler = new Private.SideBarHandler('left'));
|
|
81
|
-
let rightHandler = (this._rightHandler = new Private.SideBarHandler('right'));
|
|
82
|
-
let rootLayout = new widgets_1.BoxLayout();
|
|
83
|
-
bottomPanel.id = 'jp-bottom-panel';
|
|
84
|
-
topPanel.id = 'jp-top-panel';
|
|
85
|
-
hboxPanel.id = 'jp-main-content-panel';
|
|
86
|
-
dockPanel.id = 'jp-main-dock-panel';
|
|
87
|
-
hsplitPanel.id = 'jp-main-split-panel';
|
|
88
|
-
leftHandler.sideBar.addClass(SIDEBAR_CLASS);
|
|
89
|
-
leftHandler.sideBar.addClass('jp-mod-left');
|
|
90
|
-
leftHandler.stackedPanel.id = 'jp-left-stack';
|
|
91
|
-
rightHandler.sideBar.addClass(SIDEBAR_CLASS);
|
|
92
|
-
rightHandler.sideBar.addClass('jp-mod-right');
|
|
93
|
-
rightHandler.stackedPanel.id = 'jp-right-stack';
|
|
94
|
-
bottomPanel.direction = 'bottom-to-top';
|
|
95
|
-
hboxPanel.spacing = 0;
|
|
96
|
-
dockPanel.spacing = 5;
|
|
97
|
-
hsplitPanel.spacing = 1;
|
|
98
|
-
hboxPanel.direction = 'left-to-right';
|
|
99
|
-
hsplitPanel.orientation = 'horizontal';
|
|
100
|
-
widgets_1.SplitPanel.setStretch(leftHandler.stackedPanel, 0);
|
|
101
|
-
widgets_1.SplitPanel.setStretch(dockPanel, 1);
|
|
102
|
-
widgets_1.SplitPanel.setStretch(rightHandler.stackedPanel, 0);
|
|
103
|
-
widgets_1.BoxPanel.setStretch(leftHandler.sideBar, 0);
|
|
104
|
-
widgets_1.BoxPanel.setStretch(hsplitPanel, 1);
|
|
105
|
-
widgets_1.BoxPanel.setStretch(rightHandler.sideBar, 0);
|
|
106
|
-
hsplitPanel.addWidget(leftHandler.stackedPanel);
|
|
107
|
-
hsplitPanel.addWidget(dockPanel);
|
|
108
|
-
hsplitPanel.addWidget(rightHandler.stackedPanel);
|
|
109
|
-
hboxPanel.addWidget(leftHandler.sideBar);
|
|
110
|
-
hboxPanel.addWidget(hsplitPanel);
|
|
111
|
-
hboxPanel.addWidget(rightHandler.sideBar);
|
|
112
|
-
rootLayout.direction = 'top-to-bottom';
|
|
113
|
-
rootLayout.spacing = 0; // TODO make this configurable?
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
126
|
-
// Connect
|
|
127
|
-
this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
dock.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (ci
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (ci
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
*
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.
|
|
339
|
-
this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
*
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
this.
|
|
390
|
-
this.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
*
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
*
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
if (
|
|
501
|
-
this.
|
|
502
|
-
}
|
|
503
|
-
if (
|
|
504
|
-
this.
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
if (args.
|
|
607
|
-
args.
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (args.
|
|
619
|
-
args.
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
this
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (area
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
this.
|
|
685
|
-
this._sideBar.
|
|
686
|
-
this.
|
|
687
|
-
this.
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
title
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
this.
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
? this._findWidgetByTitle(args.
|
|
818
|
-
: null;
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
if (
|
|
823
|
-
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
document.body.
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Jupyter Development Team.
|
|
3
|
+
// Distributed under the terms of the Modified BSD License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
const algorithm_1 = require("@phosphor/algorithm");
|
|
6
|
+
const coreutils_1 = require("@phosphor/coreutils");
|
|
7
|
+
const messaging_1 = require("@phosphor/messaging");
|
|
8
|
+
const signaling_1 = require("@phosphor/signaling");
|
|
9
|
+
const widgets_1 = require("@phosphor/widgets");
|
|
10
|
+
/**
|
|
11
|
+
* The class name added to AppShell instances.
|
|
12
|
+
*/
|
|
13
|
+
const APPLICATION_SHELL_CLASS = 'jp-ApplicationShell';
|
|
14
|
+
/**
|
|
15
|
+
* The class name added to side bar instances.
|
|
16
|
+
*/
|
|
17
|
+
const SIDEBAR_CLASS = 'jp-SideBar';
|
|
18
|
+
/**
|
|
19
|
+
* The class name added to the current widget's title.
|
|
20
|
+
*/
|
|
21
|
+
const CURRENT_CLASS = 'jp-mod-current';
|
|
22
|
+
/**
|
|
23
|
+
* The class name added to the active widget's title.
|
|
24
|
+
*/
|
|
25
|
+
const ACTIVE_CLASS = 'jp-mod-active';
|
|
26
|
+
/**
|
|
27
|
+
* The default rank of items added to a sidebar.
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_RANK = 500;
|
|
30
|
+
/**
|
|
31
|
+
* The data attribute added to the document body indicating shell's mode.
|
|
32
|
+
*/
|
|
33
|
+
const MODE_ATTRIBUTE = 'data-shell-mode';
|
|
34
|
+
const ACTIVITY_CLASS = 'jp-Activity';
|
|
35
|
+
/**
|
|
36
|
+
* The application shell for JupyterLab.
|
|
37
|
+
*/
|
|
38
|
+
class ApplicationShell extends widgets_1.Widget {
|
|
39
|
+
/**
|
|
40
|
+
* Construct a new application shell.
|
|
41
|
+
*/
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
/**
|
|
45
|
+
* A message hook for child add/remove messages on the main area dock panel.
|
|
46
|
+
*/
|
|
47
|
+
this._dockChildHook = (handler, msg) => {
|
|
48
|
+
switch (msg.type) {
|
|
49
|
+
case 'child-added':
|
|
50
|
+
msg.child.addClass(ACTIVITY_CLASS);
|
|
51
|
+
this._tracker.add(msg.child);
|
|
52
|
+
break;
|
|
53
|
+
case 'child-removed':
|
|
54
|
+
msg.child.removeClass(ACTIVITY_CLASS);
|
|
55
|
+
this._tracker.remove(msg.child);
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
this._activeChanged = new signaling_1.Signal(this);
|
|
63
|
+
this._cachedLayout = null;
|
|
64
|
+
this._currentChanged = new signaling_1.Signal(this);
|
|
65
|
+
this._isRestored = false;
|
|
66
|
+
this._layoutModified = new signaling_1.Signal(this);
|
|
67
|
+
this._restored = new coreutils_1.PromiseDelegate();
|
|
68
|
+
this._tracker = new widgets_1.FocusTracker();
|
|
69
|
+
this._debouncer = 0;
|
|
70
|
+
this._addOptionsCache = new Map();
|
|
71
|
+
this._sideOptionsCache = new Map();
|
|
72
|
+
this.addClass(APPLICATION_SHELL_CLASS);
|
|
73
|
+
this.id = 'main';
|
|
74
|
+
let bottomPanel = (this._bottomPanel = new widgets_1.BoxPanel());
|
|
75
|
+
let topPanel = (this._topPanel = new widgets_1.Panel());
|
|
76
|
+
let hboxPanel = new widgets_1.BoxPanel();
|
|
77
|
+
let dockPanel = (this._dockPanel = new widgets_1.DockPanel());
|
|
78
|
+
messaging_1.MessageLoop.installMessageHook(dockPanel, this._dockChildHook);
|
|
79
|
+
let hsplitPanel = new widgets_1.SplitPanel();
|
|
80
|
+
let leftHandler = (this._leftHandler = new Private.SideBarHandler('left'));
|
|
81
|
+
let rightHandler = (this._rightHandler = new Private.SideBarHandler('right'));
|
|
82
|
+
let rootLayout = new widgets_1.BoxLayout();
|
|
83
|
+
bottomPanel.id = 'jp-bottom-panel';
|
|
84
|
+
topPanel.id = 'jp-top-panel';
|
|
85
|
+
hboxPanel.id = 'jp-main-content-panel';
|
|
86
|
+
dockPanel.id = 'jp-main-dock-panel';
|
|
87
|
+
hsplitPanel.id = 'jp-main-split-panel';
|
|
88
|
+
leftHandler.sideBar.addClass(SIDEBAR_CLASS);
|
|
89
|
+
leftHandler.sideBar.addClass('jp-mod-left');
|
|
90
|
+
leftHandler.stackedPanel.id = 'jp-left-stack';
|
|
91
|
+
rightHandler.sideBar.addClass(SIDEBAR_CLASS);
|
|
92
|
+
rightHandler.sideBar.addClass('jp-mod-right');
|
|
93
|
+
rightHandler.stackedPanel.id = 'jp-right-stack';
|
|
94
|
+
bottomPanel.direction = 'bottom-to-top';
|
|
95
|
+
hboxPanel.spacing = 0;
|
|
96
|
+
dockPanel.spacing = 5;
|
|
97
|
+
hsplitPanel.spacing = 1;
|
|
98
|
+
hboxPanel.direction = 'left-to-right';
|
|
99
|
+
hsplitPanel.orientation = 'horizontal';
|
|
100
|
+
widgets_1.SplitPanel.setStretch(leftHandler.stackedPanel, 0);
|
|
101
|
+
widgets_1.SplitPanel.setStretch(dockPanel, 1);
|
|
102
|
+
widgets_1.SplitPanel.setStretch(rightHandler.stackedPanel, 0);
|
|
103
|
+
widgets_1.BoxPanel.setStretch(leftHandler.sideBar, 0);
|
|
104
|
+
widgets_1.BoxPanel.setStretch(hsplitPanel, 1);
|
|
105
|
+
widgets_1.BoxPanel.setStretch(rightHandler.sideBar, 0);
|
|
106
|
+
hsplitPanel.addWidget(leftHandler.stackedPanel);
|
|
107
|
+
hsplitPanel.addWidget(dockPanel);
|
|
108
|
+
hsplitPanel.addWidget(rightHandler.stackedPanel);
|
|
109
|
+
hboxPanel.addWidget(leftHandler.sideBar);
|
|
110
|
+
hboxPanel.addWidget(hsplitPanel);
|
|
111
|
+
hboxPanel.addWidget(rightHandler.sideBar);
|
|
112
|
+
rootLayout.direction = 'top-to-bottom';
|
|
113
|
+
rootLayout.spacing = 0; // TODO make this configurable?
|
|
114
|
+
// Use relative sizing to set the width of the side panels.
|
|
115
|
+
// This will still respect the min-size of children widget in the stacked panel.
|
|
116
|
+
hsplitPanel.setRelativeSizes([1, 2.5, 1]);
|
|
117
|
+
widgets_1.BoxLayout.setStretch(topPanel, 0);
|
|
118
|
+
widgets_1.BoxLayout.setStretch(hboxPanel, 1);
|
|
119
|
+
widgets_1.BoxLayout.setStretch(bottomPanel, 0);
|
|
120
|
+
rootLayout.addWidget(topPanel);
|
|
121
|
+
rootLayout.addWidget(hboxPanel);
|
|
122
|
+
rootLayout.addWidget(bottomPanel);
|
|
123
|
+
// initially hiding bottom panel when no elements inside
|
|
124
|
+
this._bottomPanel.hide();
|
|
125
|
+
this.layout = rootLayout;
|
|
126
|
+
// Connect change listeners.
|
|
127
|
+
this._tracker.currentChanged.connect(this._onCurrentChanged, this);
|
|
128
|
+
this._tracker.activeChanged.connect(this._onActiveChanged, this);
|
|
129
|
+
// Connect main layout change listener.
|
|
130
|
+
this._dockPanel.layoutModified.connect(this._onLayoutModified, this);
|
|
131
|
+
// Catch current changed events on the side handlers.
|
|
132
|
+
this._leftHandler.sideBar.currentChanged.connect(this._onLayoutModified, this);
|
|
133
|
+
this._rightHandler.sideBar.currentChanged.connect(this._onLayoutModified, this);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A signal emitted when main area's active focus changes.
|
|
137
|
+
*/
|
|
138
|
+
get activeChanged() {
|
|
139
|
+
return this._activeChanged;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The active widget in the shell's main area.
|
|
143
|
+
*/
|
|
144
|
+
get activeWidget() {
|
|
145
|
+
return this._tracker.activeWidget;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A signal emitted when main area's current focus changes.
|
|
149
|
+
*/
|
|
150
|
+
get currentChanged() {
|
|
151
|
+
return this._currentChanged;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The current widget in the shell's main area.
|
|
155
|
+
*/
|
|
156
|
+
get currentWidget() {
|
|
157
|
+
return this._tracker.currentWidget;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A signal emitted when the main area's layout is modified.
|
|
161
|
+
*/
|
|
162
|
+
get layoutModified() {
|
|
163
|
+
return this._layoutModified;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Whether the left area is collapsed.
|
|
167
|
+
*/
|
|
168
|
+
get leftCollapsed() {
|
|
169
|
+
return !this._leftHandler.sideBar.currentTitle;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Whether the left area is collapsed.
|
|
173
|
+
*/
|
|
174
|
+
get rightCollapsed() {
|
|
175
|
+
return !this._rightHandler.sideBar.currentTitle;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Whether JupyterLab is in presentation mode with the `jp-mod-presentationMode` CSS class.
|
|
179
|
+
*/
|
|
180
|
+
get presentationMode() {
|
|
181
|
+
return this.hasClass('jp-mod-presentationMode');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Enable/disable presentation mode (`jp-mod-presentationMode` CSS class) with a boolean.
|
|
185
|
+
*/
|
|
186
|
+
set presentationMode(value) {
|
|
187
|
+
this.toggleClass('jp-mod-presentationMode', value);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The main dock area's user interface mode.
|
|
191
|
+
*/
|
|
192
|
+
get mode() {
|
|
193
|
+
return this._dockPanel.mode;
|
|
194
|
+
}
|
|
195
|
+
set mode(mode) {
|
|
196
|
+
const dock = this._dockPanel;
|
|
197
|
+
if (mode === dock.mode) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const applicationCurrentWidget = this.currentWidget;
|
|
201
|
+
if (mode === 'single-document') {
|
|
202
|
+
this._cachedLayout = dock.saveLayout();
|
|
203
|
+
dock.mode = mode;
|
|
204
|
+
// In case the active widget in the dock panel is *not* the active widget
|
|
205
|
+
// of the application, defer to the application.
|
|
206
|
+
if (this.currentWidget) {
|
|
207
|
+
dock.activateWidget(this.currentWidget);
|
|
208
|
+
}
|
|
209
|
+
// Set the mode data attribute on the document body.
|
|
210
|
+
document.body.setAttribute(MODE_ATTRIBUTE, mode);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// Cache a reference to every widget currently in the dock panel.
|
|
214
|
+
const widgets = algorithm_1.toArray(dock.widgets());
|
|
215
|
+
// Toggle back to multiple document mode.
|
|
216
|
+
dock.mode = mode;
|
|
217
|
+
// Restore the original layout.
|
|
218
|
+
if (this._cachedLayout) {
|
|
219
|
+
// Remove any disposed widgets in the cached layout and restore.
|
|
220
|
+
Private.normalizeAreaConfig(dock, this._cachedLayout.main);
|
|
221
|
+
dock.restoreLayout(this._cachedLayout);
|
|
222
|
+
this._cachedLayout = null;
|
|
223
|
+
}
|
|
224
|
+
// Add any widgets created during single document mode, which have
|
|
225
|
+
// subsequently been removed from the dock panel after the multiple document
|
|
226
|
+
// layout has been restored. If the widget has add options cached for
|
|
227
|
+
// it (i.e., if it has been placed with respect to another widget),
|
|
228
|
+
// then take that into account.
|
|
229
|
+
widgets.forEach(widget => {
|
|
230
|
+
if (!widget.parent) {
|
|
231
|
+
this.addToMainArea(widget, Object.assign({}, this._addOptionsCache.get(widget), { activate: false }));
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
this._addOptionsCache.clear();
|
|
235
|
+
// In case the active widget in the dock panel is *not* the active widget
|
|
236
|
+
// of the application, defer to the application.
|
|
237
|
+
if (applicationCurrentWidget) {
|
|
238
|
+
dock.activateWidget(applicationCurrentWidget);
|
|
239
|
+
}
|
|
240
|
+
// Set the mode data attribute on the document body.
|
|
241
|
+
document.body.setAttribute(MODE_ATTRIBUTE, mode);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Promise that resolves when state is first restored, returning layout
|
|
245
|
+
* description.
|
|
246
|
+
*/
|
|
247
|
+
get restored() {
|
|
248
|
+
return this._restored.promise;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Activate a widget in its area.
|
|
252
|
+
*/
|
|
253
|
+
activateById(id) {
|
|
254
|
+
if (this._leftHandler.has(id)) {
|
|
255
|
+
this._leftHandler.activate(id);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (this._rightHandler.has(id)) {
|
|
259
|
+
this._rightHandler.activate(id);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const dock = this._dockPanel;
|
|
263
|
+
const widget = algorithm_1.find(dock.widgets(), value => value.id === id);
|
|
264
|
+
if (widget) {
|
|
265
|
+
dock.activateWidget(widget);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/*
|
|
269
|
+
* Activate the next Tab in the active TabBar.
|
|
270
|
+
*/
|
|
271
|
+
activateNextTab() {
|
|
272
|
+
let current = this._currentTabBar();
|
|
273
|
+
if (!current) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
let ci = current.currentIndex;
|
|
277
|
+
if (ci === -1) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (ci < current.titles.length - 1) {
|
|
281
|
+
current.currentIndex += 1;
|
|
282
|
+
if (current.currentTitle) {
|
|
283
|
+
current.currentTitle.owner.activate();
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (ci === current.titles.length - 1) {
|
|
288
|
+
let nextBar = this._adjacentBar('next');
|
|
289
|
+
if (nextBar) {
|
|
290
|
+
nextBar.currentIndex = 0;
|
|
291
|
+
if (nextBar.currentTitle) {
|
|
292
|
+
nextBar.currentTitle.owner.activate();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/*
|
|
298
|
+
* Activate the previous Tab in the active TabBar.
|
|
299
|
+
*/
|
|
300
|
+
activatePreviousTab() {
|
|
301
|
+
let current = this._currentTabBar();
|
|
302
|
+
if (!current) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
let ci = current.currentIndex;
|
|
306
|
+
if (ci === -1) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (ci > 0) {
|
|
310
|
+
current.currentIndex -= 1;
|
|
311
|
+
if (current.currentTitle) {
|
|
312
|
+
current.currentTitle.owner.activate();
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (ci === 0) {
|
|
317
|
+
let prevBar = this._adjacentBar('previous');
|
|
318
|
+
if (prevBar) {
|
|
319
|
+
let len = prevBar.titles.length;
|
|
320
|
+
prevBar.currentIndex = len - 1;
|
|
321
|
+
if (prevBar.currentTitle) {
|
|
322
|
+
prevBar.currentTitle.owner.activate();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Add a widget to the left content area.
|
|
329
|
+
*
|
|
330
|
+
* #### Notes
|
|
331
|
+
* Widgets must have a unique `id` property, which will be used as the DOM id.
|
|
332
|
+
*/
|
|
333
|
+
addToLeftArea(widget, options) {
|
|
334
|
+
if (!widget.id) {
|
|
335
|
+
console.error('Widgets added to app shell must have unique id property.');
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
options = options || this._sideOptionsCache.get(widget) || {};
|
|
339
|
+
this._sideOptionsCache.set(widget, options);
|
|
340
|
+
let rank = 'rank' in options ? options.rank : DEFAULT_RANK;
|
|
341
|
+
this._leftHandler.addWidget(widget, rank);
|
|
342
|
+
this._onLayoutModified();
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Add a widget to the main content area.
|
|
346
|
+
*
|
|
347
|
+
* #### Notes
|
|
348
|
+
* Widgets must have a unique `id` property, which will be used as the DOM id.
|
|
349
|
+
* All widgets added to the main area should be disposed after removal
|
|
350
|
+
* (disposal before removal will remove the widget automatically).
|
|
351
|
+
*
|
|
352
|
+
* In the options, `ref` defaults to `null`, `mode` defaults to `'tab-after'`,
|
|
353
|
+
* and `activate` defaults to `true`.
|
|
354
|
+
*/
|
|
355
|
+
addToMainArea(widget, options = {}) {
|
|
356
|
+
if (!widget.id) {
|
|
357
|
+
console.error('Widgets added to app shell must have unique id property.');
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
let dock = this._dockPanel;
|
|
361
|
+
let ref = null;
|
|
362
|
+
if (options.ref) {
|
|
363
|
+
ref = algorithm_1.find(dock.widgets(), value => value.id === options.ref) || null;
|
|
364
|
+
}
|
|
365
|
+
let mode = options.mode || 'tab-after';
|
|
366
|
+
dock.addWidget(widget, { mode, ref });
|
|
367
|
+
// The dock panel doesn't account for placement information while
|
|
368
|
+
// in single document mode, so upon rehydrating any widgets that were
|
|
369
|
+
// added will not be in the correct place. Cache the placement information
|
|
370
|
+
// here so that we can later rehydrate correctly.
|
|
371
|
+
if (dock.mode === 'single-document') {
|
|
372
|
+
this._addOptionsCache.set(widget, options);
|
|
373
|
+
}
|
|
374
|
+
if (options.activate !== false) {
|
|
375
|
+
dock.activateWidget(widget);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Add a widget to the right content area.
|
|
380
|
+
*
|
|
381
|
+
* #### Notes
|
|
382
|
+
* Widgets must have a unique `id` property, which will be used as the DOM id.
|
|
383
|
+
*/
|
|
384
|
+
addToRightArea(widget, options) {
|
|
385
|
+
if (!widget.id) {
|
|
386
|
+
console.error('Widgets added to app shell must have unique id property.');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
options = options || this._sideOptionsCache.get(widget) || {};
|
|
390
|
+
this._sideOptionsCache.set(widget, options);
|
|
391
|
+
let rank = 'rank' in options ? options.rank : DEFAULT_RANK;
|
|
392
|
+
this._rightHandler.addWidget(widget, rank);
|
|
393
|
+
this._onLayoutModified();
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Add a widget to the top content area.
|
|
397
|
+
*
|
|
398
|
+
* #### Notes
|
|
399
|
+
* Widgets must have a unique `id` property, which will be used as the DOM id.
|
|
400
|
+
*/
|
|
401
|
+
addToTopArea(widget, options = {}) {
|
|
402
|
+
if (!widget.id) {
|
|
403
|
+
console.error('Widgets added to app shell must have unique id property.');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
// Temporary: widgets are added to the panel in order of insertion.
|
|
407
|
+
this._topPanel.addWidget(widget);
|
|
408
|
+
this._onLayoutModified();
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Add a widget to the bottom content area.
|
|
412
|
+
*
|
|
413
|
+
* #### Notes
|
|
414
|
+
* Widgets must have a unique `id` property, which will be used as the DOM id.
|
|
415
|
+
*/
|
|
416
|
+
addToBottomArea(widget, options = {}) {
|
|
417
|
+
if (!widget.id) {
|
|
418
|
+
console.error('Widgets added to app shell must have unique id property.');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// Temporary: widgets are added to the panel in order of insertion.
|
|
422
|
+
this._bottomPanel.addWidget(widget);
|
|
423
|
+
this._onLayoutModified();
|
|
424
|
+
if (this._bottomPanel.isHidden) {
|
|
425
|
+
this._bottomPanel.show();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Collapse the left area.
|
|
430
|
+
*/
|
|
431
|
+
collapseLeft() {
|
|
432
|
+
this._leftHandler.collapse();
|
|
433
|
+
this._onLayoutModified();
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Collapse the right area.
|
|
437
|
+
*/
|
|
438
|
+
collapseRight() {
|
|
439
|
+
this._rightHandler.collapse();
|
|
440
|
+
this._onLayoutModified();
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Expand the left area.
|
|
444
|
+
*
|
|
445
|
+
* #### Notes
|
|
446
|
+
* This will open the most recently used tab,
|
|
447
|
+
* or the first tab if there is no most recently used.
|
|
448
|
+
*/
|
|
449
|
+
expandLeft() {
|
|
450
|
+
this._leftHandler.expand();
|
|
451
|
+
this._onLayoutModified();
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Expand the right area.
|
|
455
|
+
*
|
|
456
|
+
* #### Notes
|
|
457
|
+
* This will open the most recently used tab,
|
|
458
|
+
* or the first tab if there is no most recently used.
|
|
459
|
+
*/
|
|
460
|
+
expandRight() {
|
|
461
|
+
this._rightHandler.expand();
|
|
462
|
+
this._onLayoutModified();
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Close all widgets in the main area.
|
|
466
|
+
*/
|
|
467
|
+
closeAll() {
|
|
468
|
+
// Make a copy of all the widget in the dock panel (using `toArray()`)
|
|
469
|
+
// before removing them because removing them while iterating through them
|
|
470
|
+
// modifies the underlying data of the iterator.
|
|
471
|
+
algorithm_1.toArray(this._dockPanel.widgets()).forEach(widget => widget.close());
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* True if the given area is empty.
|
|
475
|
+
*/
|
|
476
|
+
isEmpty(area) {
|
|
477
|
+
switch (area) {
|
|
478
|
+
case 'left':
|
|
479
|
+
return this._leftHandler.stackedPanel.widgets.length === 0;
|
|
480
|
+
case 'main':
|
|
481
|
+
return this._dockPanel.isEmpty;
|
|
482
|
+
case 'top':
|
|
483
|
+
return this._topPanel.widgets.length === 0;
|
|
484
|
+
case 'bottom':
|
|
485
|
+
return this._bottomPanel.widgets.length === 0;
|
|
486
|
+
case 'right':
|
|
487
|
+
return this._rightHandler.stackedPanel.widgets.length === 0;
|
|
488
|
+
default:
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Restore the layout state for the application shell.
|
|
494
|
+
*/
|
|
495
|
+
restoreLayout(layout) {
|
|
496
|
+
const { mainArea, leftArea, rightArea } = layout;
|
|
497
|
+
// Rehydrate the main area.
|
|
498
|
+
if (mainArea) {
|
|
499
|
+
const { currentWidget, dock, mode } = mainArea;
|
|
500
|
+
if (dock) {
|
|
501
|
+
this._dockPanel.restoreLayout(dock);
|
|
502
|
+
}
|
|
503
|
+
if (mode) {
|
|
504
|
+
this.mode = mode;
|
|
505
|
+
}
|
|
506
|
+
if (currentWidget) {
|
|
507
|
+
this.activateById(currentWidget.id);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Rehydrate the left area.
|
|
511
|
+
if (leftArea) {
|
|
512
|
+
this._leftHandler.rehydrate(leftArea);
|
|
513
|
+
}
|
|
514
|
+
// Rehydrate the right area.
|
|
515
|
+
if (rightArea) {
|
|
516
|
+
this._rightHandler.rehydrate(rightArea);
|
|
517
|
+
}
|
|
518
|
+
if (!this._isRestored) {
|
|
519
|
+
// Make sure all messages in the queue are finished before notifying
|
|
520
|
+
// any extensions that are waiting for the promise that guarantees the
|
|
521
|
+
// application state has been restored.
|
|
522
|
+
messaging_1.MessageLoop.flush();
|
|
523
|
+
this._restored.resolve(layout);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Save the dehydrated state of the application shell.
|
|
528
|
+
*/
|
|
529
|
+
saveLayout() {
|
|
530
|
+
// If the application is in single document mode, use the cached layout if
|
|
531
|
+
// available. Otherwise, default to querying the dock panel for layout.
|
|
532
|
+
return {
|
|
533
|
+
mainArea: {
|
|
534
|
+
currentWidget: this._tracker.currentWidget,
|
|
535
|
+
dock: this.mode === 'single-document'
|
|
536
|
+
? this._cachedLayout || this._dockPanel.saveLayout()
|
|
537
|
+
: this._dockPanel.saveLayout(),
|
|
538
|
+
mode: this._dockPanel.mode
|
|
539
|
+
},
|
|
540
|
+
leftArea: this._leftHandler.dehydrate(),
|
|
541
|
+
rightArea: this._rightHandler.dehydrate()
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Returns the widgets for an application area.
|
|
546
|
+
*/
|
|
547
|
+
widgets(area) {
|
|
548
|
+
switch (area) {
|
|
549
|
+
case 'main':
|
|
550
|
+
return this._dockPanel.widgets();
|
|
551
|
+
case 'left':
|
|
552
|
+
return algorithm_1.iter(this._leftHandler.sideBar.titles.map(t => t.owner));
|
|
553
|
+
case 'right':
|
|
554
|
+
return algorithm_1.iter(this._rightHandler.sideBar.titles.map(t => t.owner));
|
|
555
|
+
case 'top':
|
|
556
|
+
return this._topPanel.children();
|
|
557
|
+
case 'bottom':
|
|
558
|
+
return this._bottomPanel.children();
|
|
559
|
+
default:
|
|
560
|
+
throw new Error('Invalid area');
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Handle `after-attach` messages for the application shell.
|
|
565
|
+
*/
|
|
566
|
+
onAfterAttach(msg) {
|
|
567
|
+
document.body.setAttribute(MODE_ATTRIBUTE, this.mode);
|
|
568
|
+
}
|
|
569
|
+
/*
|
|
570
|
+
* Return the tab bar adjacent to the current TabBar or `null`.
|
|
571
|
+
*/
|
|
572
|
+
_adjacentBar(direction) {
|
|
573
|
+
const current = this._currentTabBar();
|
|
574
|
+
if (!current) {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
const bars = algorithm_1.toArray(this._dockPanel.tabBars());
|
|
578
|
+
const len = bars.length;
|
|
579
|
+
const index = bars.indexOf(current);
|
|
580
|
+
if (direction === 'previous') {
|
|
581
|
+
return index > 0 ? bars[index - 1] : index === 0 ? bars[len - 1] : null;
|
|
582
|
+
}
|
|
583
|
+
// Otherwise, direction is 'next'.
|
|
584
|
+
return index < len - 1
|
|
585
|
+
? bars[index + 1]
|
|
586
|
+
: index === len - 1
|
|
587
|
+
? bars[0]
|
|
588
|
+
: null;
|
|
589
|
+
}
|
|
590
|
+
/*
|
|
591
|
+
* Return the TabBar that has the currently active Widget or null.
|
|
592
|
+
*/
|
|
593
|
+
_currentTabBar() {
|
|
594
|
+
const current = this._tracker.currentWidget;
|
|
595
|
+
if (!current) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const title = current.title;
|
|
599
|
+
const bars = this._dockPanel.tabBars();
|
|
600
|
+
return algorithm_1.find(bars, bar => bar.titles.indexOf(title) > -1) || null;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Handle a change to the dock area active widget.
|
|
604
|
+
*/
|
|
605
|
+
_onActiveChanged(sender, args) {
|
|
606
|
+
if (args.newValue) {
|
|
607
|
+
args.newValue.title.className += ` ${ACTIVE_CLASS}`;
|
|
608
|
+
}
|
|
609
|
+
if (args.oldValue) {
|
|
610
|
+
args.oldValue.title.className = args.oldValue.title.className.replace(ACTIVE_CLASS, '');
|
|
611
|
+
}
|
|
612
|
+
this._activeChanged.emit(args);
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Handle a change to the dock area current widget.
|
|
616
|
+
*/
|
|
617
|
+
_onCurrentChanged(sender, args) {
|
|
618
|
+
if (args.newValue) {
|
|
619
|
+
args.newValue.title.className += ` ${CURRENT_CLASS}`;
|
|
620
|
+
}
|
|
621
|
+
if (args.oldValue) {
|
|
622
|
+
args.oldValue.title.className = args.oldValue.title.className.replace(CURRENT_CLASS, '');
|
|
623
|
+
}
|
|
624
|
+
this._currentChanged.emit(args);
|
|
625
|
+
this._onLayoutModified();
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Handle a change to the layout.
|
|
629
|
+
*/
|
|
630
|
+
_onLayoutModified() {
|
|
631
|
+
// The dock can emit layout modified signals while in transient
|
|
632
|
+
// states (for instance, when switching from single-document to
|
|
633
|
+
// multiple-document mode). In those states, it can be unreliable
|
|
634
|
+
// for the signal consumers to query layout properties.
|
|
635
|
+
// We fix this by debouncing the layout modified signal so that it
|
|
636
|
+
// is only emitted after rearranging is done.
|
|
637
|
+
window.clearTimeout(this._debouncer);
|
|
638
|
+
this._debouncer = window.setTimeout(() => {
|
|
639
|
+
this._layoutModified.emit(undefined);
|
|
640
|
+
}, 0);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
exports.ApplicationShell = ApplicationShell;
|
|
644
|
+
var Private;
|
|
645
|
+
(function (Private) {
|
|
646
|
+
/**
|
|
647
|
+
* A less-than comparison function for side bar rank items.
|
|
648
|
+
*/
|
|
649
|
+
function itemCmp(first, second) {
|
|
650
|
+
return first.rank - second.rank;
|
|
651
|
+
}
|
|
652
|
+
Private.itemCmp = itemCmp;
|
|
653
|
+
/**
|
|
654
|
+
* Removes widgets that have been disposed from an area config, mutates area.
|
|
655
|
+
*/
|
|
656
|
+
function normalizeAreaConfig(parent, area) {
|
|
657
|
+
if (!area) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (area.type === 'tab-area') {
|
|
661
|
+
area.widgets = area.widgets.filter(widget => !widget.isDisposed && widget.parent === parent);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
area.children.forEach(child => {
|
|
665
|
+
normalizeAreaConfig(parent, child);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
Private.normalizeAreaConfig = normalizeAreaConfig;
|
|
669
|
+
/**
|
|
670
|
+
* A class which manages a side bar and related stacked panel.
|
|
671
|
+
*/
|
|
672
|
+
class SideBarHandler {
|
|
673
|
+
/**
|
|
674
|
+
* Construct a new side bar handler.
|
|
675
|
+
*/
|
|
676
|
+
constructor(side) {
|
|
677
|
+
this._items = new Array();
|
|
678
|
+
this._side = side;
|
|
679
|
+
this._sideBar = new widgets_1.TabBar({
|
|
680
|
+
insertBehavior: 'none',
|
|
681
|
+
removeBehavior: 'none',
|
|
682
|
+
allowDeselect: true
|
|
683
|
+
});
|
|
684
|
+
this._stackedPanel = new widgets_1.StackedPanel();
|
|
685
|
+
this._sideBar.hide();
|
|
686
|
+
this._stackedPanel.hide();
|
|
687
|
+
this._lastCurrent = null;
|
|
688
|
+
this._sideBar.currentChanged.connect(this._onCurrentChanged, this);
|
|
689
|
+
this._sideBar.tabActivateRequested.connect(this._onTabActivateRequested, this);
|
|
690
|
+
this._stackedPanel.widgetRemoved.connect(this._onWidgetRemoved, this);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get the tab bar managed by the handler.
|
|
694
|
+
*/
|
|
695
|
+
get sideBar() {
|
|
696
|
+
return this._sideBar;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Get the stacked panel managed by the handler
|
|
700
|
+
*/
|
|
701
|
+
get stackedPanel() {
|
|
702
|
+
return this._stackedPanel;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Expand the sidebar.
|
|
706
|
+
*
|
|
707
|
+
* #### Notes
|
|
708
|
+
* This will open the most recently used tab, or the first tab
|
|
709
|
+
* if there is no most recently used.
|
|
710
|
+
*/
|
|
711
|
+
expand() {
|
|
712
|
+
const previous = this._lastCurrent || (this._items.length > 0 && this._items[0].widget);
|
|
713
|
+
if (previous) {
|
|
714
|
+
this.activate(previous.id);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Activate a widget residing in the side bar by ID.
|
|
719
|
+
*
|
|
720
|
+
* @param id - The widget's unique ID.
|
|
721
|
+
*/
|
|
722
|
+
activate(id) {
|
|
723
|
+
let widget = this._findWidgetByID(id);
|
|
724
|
+
if (widget) {
|
|
725
|
+
this._sideBar.currentTitle = widget.title;
|
|
726
|
+
widget.activate();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Test whether the sidebar has the given widget by id.
|
|
731
|
+
*/
|
|
732
|
+
has(id) {
|
|
733
|
+
return this._findWidgetByID(id) !== null;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Collapse the sidebar so no items are expanded.
|
|
737
|
+
*/
|
|
738
|
+
collapse() {
|
|
739
|
+
this._sideBar.currentTitle = null;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Add a widget and its title to the stacked panel and side bar.
|
|
743
|
+
*
|
|
744
|
+
* If the widget is already added, it will be moved.
|
|
745
|
+
*/
|
|
746
|
+
addWidget(widget, rank) {
|
|
747
|
+
widget.parent = null;
|
|
748
|
+
widget.hide();
|
|
749
|
+
let item = { widget, rank };
|
|
750
|
+
let index = this._findInsertIndex(item);
|
|
751
|
+
algorithm_1.ArrayExt.insert(this._items, index, item);
|
|
752
|
+
this._stackedPanel.insertWidget(index, widget);
|
|
753
|
+
const title = this._sideBar.insertTab(index, widget.title);
|
|
754
|
+
// Store the parent id in the title dataset
|
|
755
|
+
// in order to dispatch click events to the right widget.
|
|
756
|
+
title.dataset = { id: widget.id };
|
|
757
|
+
this._refreshVisibility();
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Dehydrate the side bar data.
|
|
761
|
+
*/
|
|
762
|
+
dehydrate() {
|
|
763
|
+
let collapsed = this._sideBar.currentTitle === null;
|
|
764
|
+
let widgets = algorithm_1.toArray(this._stackedPanel.widgets);
|
|
765
|
+
let currentWidget = widgets[this._sideBar.currentIndex];
|
|
766
|
+
return { collapsed, currentWidget, widgets };
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Rehydrate the side bar.
|
|
770
|
+
*/
|
|
771
|
+
rehydrate(data) {
|
|
772
|
+
if (data.currentWidget) {
|
|
773
|
+
this.activate(data.currentWidget.id);
|
|
774
|
+
}
|
|
775
|
+
else if (data.collapsed) {
|
|
776
|
+
this.collapse();
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Find the insertion index for a rank item.
|
|
781
|
+
*/
|
|
782
|
+
_findInsertIndex(item) {
|
|
783
|
+
return algorithm_1.ArrayExt.upperBound(this._items, item, Private.itemCmp);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Find the index of the item with the given widget, or `-1`.
|
|
787
|
+
*/
|
|
788
|
+
_findWidgetIndex(widget) {
|
|
789
|
+
return algorithm_1.ArrayExt.findFirstIndex(this._items, i => i.widget === widget);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Find the widget which owns the given title, or `null`.
|
|
793
|
+
*/
|
|
794
|
+
_findWidgetByTitle(title) {
|
|
795
|
+
let item = algorithm_1.find(this._items, value => value.widget.title === title);
|
|
796
|
+
return item ? item.widget : null;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Find the widget with the given id, or `null`.
|
|
800
|
+
*/
|
|
801
|
+
_findWidgetByID(id) {
|
|
802
|
+
let item = algorithm_1.find(this._items, value => value.widget.id === id);
|
|
803
|
+
return item ? item.widget : null;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Refresh the visibility of the side bar and stacked panel.
|
|
807
|
+
*/
|
|
808
|
+
_refreshVisibility() {
|
|
809
|
+
this._sideBar.setHidden(this._sideBar.titles.length === 0);
|
|
810
|
+
this._stackedPanel.setHidden(this._sideBar.currentTitle === null);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Handle the `currentChanged` signal from the sidebar.
|
|
814
|
+
*/
|
|
815
|
+
_onCurrentChanged(sender, args) {
|
|
816
|
+
const oldWidget = args.previousTitle
|
|
817
|
+
? this._findWidgetByTitle(args.previousTitle)
|
|
818
|
+
: null;
|
|
819
|
+
const newWidget = args.currentTitle
|
|
820
|
+
? this._findWidgetByTitle(args.currentTitle)
|
|
821
|
+
: null;
|
|
822
|
+
if (oldWidget) {
|
|
823
|
+
oldWidget.hide();
|
|
824
|
+
}
|
|
825
|
+
if (newWidget) {
|
|
826
|
+
newWidget.show();
|
|
827
|
+
}
|
|
828
|
+
this._lastCurrent = newWidget || oldWidget;
|
|
829
|
+
if (newWidget) {
|
|
830
|
+
const id = newWidget.id;
|
|
831
|
+
document.body.setAttribute(`data-${this._side}-sidebar-widget`, id);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
document.body.removeAttribute(`data-${this._side}-sidebar-widget`);
|
|
835
|
+
}
|
|
836
|
+
this._refreshVisibility();
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Handle a `tabActivateRequest` signal from the sidebar.
|
|
840
|
+
*/
|
|
841
|
+
_onTabActivateRequested(sender, args) {
|
|
842
|
+
args.title.owner.activate();
|
|
843
|
+
}
|
|
844
|
+
/*
|
|
845
|
+
* Handle the `widgetRemoved` signal from the stacked panel.
|
|
846
|
+
*/
|
|
847
|
+
_onWidgetRemoved(sender, widget) {
|
|
848
|
+
if (widget === this._lastCurrent) {
|
|
849
|
+
this._lastCurrent = null;
|
|
850
|
+
}
|
|
851
|
+
algorithm_1.ArrayExt.removeAt(this._items, this._findWidgetIndex(widget));
|
|
852
|
+
this._sideBar.removeTab(widget.title);
|
|
853
|
+
this._refreshVisibility();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
Private.SideBarHandler = SideBarHandler;
|
|
857
|
+
})(Private || (Private = {}));
|