@jupyter-notebook/application 7.0.0-alpha.1 → 7.0.0-alpha.10

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/lib/app.d.ts CHANGED
@@ -24,6 +24,10 @@ export declare class NotebookApp extends JupyterFrontEnd<INotebookShell> {
24
24
  * The application busy and dirty status signals and flags.
25
25
  */
26
26
  readonly status: LabStatus;
27
+ /**
28
+ * Promise that resolves when the state is first restored
29
+ */
30
+ readonly restored: Promise<void>;
27
31
  /**
28
32
  * The version of the application.
29
33
  */
package/lib/app.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
  import { JupyterFrontEnd } from '@jupyterlab/application';
4
+ import { Base64ModelFactory } from '@jupyterlab/docregistry';
4
5
  import { createRendermimePlugins } from '@jupyterlab/application/lib/mimerenderers';
5
6
  import { LabStatus } from '@jupyterlab/application/lib/status';
6
7
  import { PageConfig } from '@jupyterlab/coreutils';
@@ -17,7 +18,7 @@ export class NotebookApp extends JupyterFrontEnd {
17
18
  */
18
19
  constructor(options = { shell: new NotebookShell() }) {
19
20
  var _a, _b;
20
- super(Object.assign(Object.assign({}, options), { shell: (_a = options.shell) !== null && _a !== void 0 ? _a : new NotebookShell() }));
21
+ super({ ...options, shell: (_a = options.shell) !== null && _a !== void 0 ? _a : new NotebookShell() });
21
22
  /**
22
23
  * The name of the application.
23
24
  */
@@ -37,11 +38,14 @@ export class NotebookApp extends JupyterFrontEnd {
37
38
  this._formatter = new Throttler(() => {
38
39
  Private.setFormat(this);
39
40
  }, 250);
41
+ // Add initial model factory.
42
+ this.docRegistry.addModelFactory(new Base64ModelFactory());
40
43
  if (options.mimeExtensions) {
41
44
  for (const plugin of createRendermimePlugins(options.mimeExtensions)) {
42
45
  this.registerPlugin(plugin);
43
46
  }
44
47
  }
48
+ this.restored = this.shell.restored;
45
49
  this.restored.then(() => this._formatter.invoke());
46
50
  }
47
51
  /**
package/lib/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './app';
2
2
  export * from './shell';
3
+ export * from './panelhandler';
package/lib/index.js CHANGED
@@ -2,3 +2,4 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
  export * from './app';
4
4
  export * from './shell';
5
+ export * from './panelhandler';
@@ -0,0 +1,212 @@
1
+ import { ICommandPalette } from '@jupyterlab/apputils';
2
+ import { IDisposable } from '@lumino/disposable';
3
+ import { ISignal } from '@lumino/signaling';
4
+ import { Panel, Widget } from '@lumino/widgets';
5
+ /**
6
+ * A class which manages a panel and sorts its widgets by rank.
7
+ */
8
+ export declare class PanelHandler {
9
+ constructor();
10
+ /**
11
+ * Get the panel managed by the handler.
12
+ */
13
+ get panel(): Panel;
14
+ /**
15
+ * Add a widget to the panel.
16
+ *
17
+ * If the widget is already added, it will be moved.
18
+ */
19
+ addWidget(widget: Widget, rank: number): void;
20
+ /**
21
+ * A message hook for child add/remove messages on the main area dock panel.
22
+ */
23
+ private _panelChildHook;
24
+ protected _items: Private.IRankItem[];
25
+ protected _panel: Panel;
26
+ }
27
+ /**
28
+ * A class which manages a side panel that can show at most one widget at a time.
29
+ */
30
+ export declare class SidePanelHandler extends PanelHandler {
31
+ /**
32
+ * Construct a new side panel handler.
33
+ */
34
+ constructor(area: SidePanel.Area);
35
+ /**
36
+ * Get the current widget in the sidebar panel.
37
+ */
38
+ get currentWidget(): Widget | null;
39
+ /**
40
+ * Get the area of the side panel
41
+ */
42
+ get area(): SidePanel.Area;
43
+ /**
44
+ * Whether the panel is visible
45
+ */
46
+ get isVisible(): boolean;
47
+ /**
48
+ * Get the stacked panel managed by the handler
49
+ */
50
+ get panel(): Panel;
51
+ /**
52
+ * Get the widgets list.
53
+ */
54
+ get widgets(): Readonly<Widget[]>;
55
+ /**
56
+ * Signal fired when a widget is added to the panel
57
+ */
58
+ get widgetAdded(): ISignal<SidePanelHandler, Widget>;
59
+ /**
60
+ * Signal fired when a widget is removed from the panel
61
+ */
62
+ get widgetRemoved(): ISignal<SidePanelHandler, Widget>;
63
+ /**
64
+ * Expand the sidebar.
65
+ *
66
+ * #### Notes
67
+ * This will open the most recently used widget, or the first widget
68
+ * if there is no most recently used.
69
+ */
70
+ expand(id?: string): void;
71
+ /**
72
+ * Activate a widget residing in the stacked panel by ID.
73
+ *
74
+ * @param id - The widget's unique ID.
75
+ */
76
+ activate(id: string): void;
77
+ /**
78
+ * Test whether the sidebar has the given widget by id.
79
+ */
80
+ has(id: string): boolean;
81
+ /**
82
+ * Collapse the sidebar so no items are expanded.
83
+ */
84
+ collapse(): void;
85
+ /**
86
+ * Add a widget and its title to the stacked panel.
87
+ *
88
+ * If the widget is already added, it will be moved.
89
+ */
90
+ addWidget(widget: Widget, rank: number): void;
91
+ /**
92
+ * Hide the side panel
93
+ */
94
+ hide(): void;
95
+ /**
96
+ * Show the side panel
97
+ */
98
+ show(): void;
99
+ /**
100
+ * Find the insertion index for a rank item.
101
+ */
102
+ private _findInsertIndex;
103
+ /**
104
+ * Find the index of the item with the given widget, or `-1`.
105
+ */
106
+ private _findWidgetIndex;
107
+ /**
108
+ * Find the widget with the given id, or `null`.
109
+ */
110
+ private _findWidgetByID;
111
+ /**
112
+ * Refresh the visibility of the stacked panel.
113
+ */
114
+ private _refreshVisibility;
115
+ private _onWidgetRemoved;
116
+ private _area;
117
+ private _isHiddenByUser;
118
+ private _widgetPanel;
119
+ private _currentWidget;
120
+ private _lastCurrentWidget;
121
+ private _widgetAdded;
122
+ private _widgetRemoved;
123
+ }
124
+ /**
125
+ * A name space for SideBarPanel functions.
126
+ */
127
+ export declare namespace SidePanel {
128
+ /**
129
+ * The areas of the sidebar panel
130
+ */
131
+ type Area = 'left' | 'right';
132
+ }
133
+ /**
134
+ * A class to manages the palette entries associated to the side panels.
135
+ */
136
+ export declare class SidePanelPalette {
137
+ /**
138
+ * Construct a new side panel palette.
139
+ */
140
+ constructor(options: SidePanelPaletteOption);
141
+ /**
142
+ * Get a command palette item from the widget id and the area.
143
+ */
144
+ getItem(widget: Readonly<Widget>, area: 'left' | 'right'): SidePanelPaletteItem | null;
145
+ /**
146
+ * Add an item to the command palette.
147
+ */
148
+ addItem(widget: Readonly<Widget>, area: 'left' | 'right'): void;
149
+ /**
150
+ * Remove an item from the command palette.
151
+ */
152
+ removeItem(widget: Readonly<Widget>, area: 'left' | 'right'): void;
153
+ _command: string;
154
+ _commandPalette: ICommandPalette;
155
+ _items: SidePanelPaletteItem[];
156
+ }
157
+ type SidePanelPaletteItem = {
158
+ /**
159
+ * The ID of the widget associated to the command palette.
160
+ */
161
+ widgetId: string;
162
+ /**
163
+ * The area of the panel associated to the command palette.
164
+ */
165
+ area: 'left' | 'right';
166
+ /**
167
+ * The disposable object to remove the item from command palette.
168
+ */
169
+ disposable: IDisposable;
170
+ };
171
+ /**
172
+ * An interface for the options to include in SideBarPalette constructor.
173
+ */
174
+ type SidePanelPaletteOption = {
175
+ /**
176
+ * The commands palette.
177
+ */
178
+ commandPalette: ICommandPalette;
179
+ /**
180
+ * The command to call from each side panel menu entry.
181
+ *
182
+ * ### Notes
183
+ * That command required 3 args :
184
+ * side: 'left' | 'right', the area to toggle
185
+ * title: string, label of the command
186
+ * id: string, id of the widget to activate
187
+ */
188
+ command: string;
189
+ };
190
+ /**
191
+ * A namespace for private module data.
192
+ */
193
+ declare namespace Private {
194
+ /**
195
+ * An object which holds a widget and its sort rank.
196
+ */
197
+ interface IRankItem {
198
+ /**
199
+ * The widget for the item.
200
+ */
201
+ widget: Widget;
202
+ /**
203
+ * The sort rank of the widget.
204
+ */
205
+ rank: number;
206
+ }
207
+ /**
208
+ * A less-than comparison function for side bar rank items.
209
+ */
210
+ function itemCmp(first: IRankItem, second: IRankItem): number;
211
+ }
212
+ export {};
@@ -0,0 +1,329 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+ import { closeIcon } from '@jupyterlab/ui-components';
4
+ import { ArrayExt, find } from '@lumino/algorithm';
5
+ import { MessageLoop } from '@lumino/messaging';
6
+ import { Signal } from '@lumino/signaling';
7
+ import { Panel, StackedPanel, Widget } from '@lumino/widgets';
8
+ /**
9
+ * A class which manages a panel and sorts its widgets by rank.
10
+ */
11
+ export class PanelHandler {
12
+ constructor() {
13
+ /**
14
+ * A message hook for child add/remove messages on the main area dock panel.
15
+ */
16
+ this._panelChildHook = (handler, msg) => {
17
+ switch (msg.type) {
18
+ case 'child-added':
19
+ {
20
+ const widget = msg.child;
21
+ // If we already know about this widget, we're done
22
+ if (this._items.find(v => v.widget === widget)) {
23
+ break;
24
+ }
25
+ // Otherwise, add to the end by default
26
+ const rank = this._items[this._items.length - 1].rank;
27
+ this._items.push({ widget, rank });
28
+ }
29
+ break;
30
+ case 'child-removed':
31
+ {
32
+ const widget = msg.child;
33
+ ArrayExt.removeFirstWhere(this._items, v => v.widget === widget);
34
+ }
35
+ break;
36
+ default:
37
+ break;
38
+ }
39
+ return true;
40
+ };
41
+ this._items = new Array();
42
+ this._panel = new Panel();
43
+ MessageLoop.installMessageHook(this._panel, this._panelChildHook);
44
+ }
45
+ /**
46
+ * Get the panel managed by the handler.
47
+ */
48
+ get panel() {
49
+ return this._panel;
50
+ }
51
+ /**
52
+ * Add a widget to the panel.
53
+ *
54
+ * If the widget is already added, it will be moved.
55
+ */
56
+ addWidget(widget, rank) {
57
+ widget.parent = null;
58
+ const item = { widget, rank };
59
+ const index = ArrayExt.upperBound(this._items, item, Private.itemCmp);
60
+ ArrayExt.insert(this._items, index, item);
61
+ this._panel.insertWidget(index, widget);
62
+ }
63
+ }
64
+ /**
65
+ * A class which manages a side panel that can show at most one widget at a time.
66
+ */
67
+ export class SidePanelHandler extends PanelHandler {
68
+ /**
69
+ * Construct a new side panel handler.
70
+ */
71
+ constructor(area) {
72
+ super();
73
+ this._isHiddenByUser = false;
74
+ this._widgetAdded = new Signal(this);
75
+ this._widgetRemoved = new Signal(this);
76
+ this._area = area;
77
+ this._panel.hide();
78
+ this._currentWidget = null;
79
+ this._lastCurrentWidget = null;
80
+ this._widgetPanel = new StackedPanel();
81
+ this._widgetPanel.widgetRemoved.connect(this._onWidgetRemoved, this);
82
+ const closeButton = document.createElement('button');
83
+ closeIcon.element({
84
+ container: closeButton,
85
+ height: '16px',
86
+ width: 'auto'
87
+ });
88
+ closeButton.onclick = () => {
89
+ this.collapse();
90
+ this.hide();
91
+ };
92
+ closeButton.className = 'jp-Button jp-SidePanel-collapse';
93
+ const icon = new Widget({ node: closeButton });
94
+ this._panel.addWidget(icon);
95
+ this._panel.addWidget(this._widgetPanel);
96
+ }
97
+ /**
98
+ * Get the current widget in the sidebar panel.
99
+ */
100
+ get currentWidget() {
101
+ return (this._currentWidget ||
102
+ this._lastCurrentWidget ||
103
+ (this._items.length > 0 ? this._items[0].widget : null));
104
+ }
105
+ /**
106
+ * Get the area of the side panel
107
+ */
108
+ get area() {
109
+ return this._area;
110
+ }
111
+ /**
112
+ * Whether the panel is visible
113
+ */
114
+ get isVisible() {
115
+ return this._panel.isVisible;
116
+ }
117
+ /**
118
+ * Get the stacked panel managed by the handler
119
+ */
120
+ get panel() {
121
+ return this._panel;
122
+ }
123
+ /**
124
+ * Get the widgets list.
125
+ */
126
+ get widgets() {
127
+ return this._items.map(obj => obj.widget);
128
+ }
129
+ /**
130
+ * Signal fired when a widget is added to the panel
131
+ */
132
+ get widgetAdded() {
133
+ return this._widgetAdded;
134
+ }
135
+ /**
136
+ * Signal fired when a widget is removed from the panel
137
+ */
138
+ get widgetRemoved() {
139
+ return this._widgetRemoved;
140
+ }
141
+ /**
142
+ * Expand the sidebar.
143
+ *
144
+ * #### Notes
145
+ * This will open the most recently used widget, or the first widget
146
+ * if there is no most recently used.
147
+ */
148
+ expand(id) {
149
+ if (this._currentWidget) {
150
+ this.collapse();
151
+ }
152
+ if (id) {
153
+ this.activate(id);
154
+ }
155
+ else {
156
+ const visibleWidget = this.currentWidget;
157
+ if (visibleWidget) {
158
+ this._currentWidget = visibleWidget;
159
+ this.activate(visibleWidget.id);
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Activate a widget residing in the stacked panel by ID.
165
+ *
166
+ * @param id - The widget's unique ID.
167
+ */
168
+ activate(id) {
169
+ const widget = this._findWidgetByID(id);
170
+ if (widget) {
171
+ this._currentWidget = widget;
172
+ widget.show();
173
+ widget.activate();
174
+ }
175
+ }
176
+ /**
177
+ * Test whether the sidebar has the given widget by id.
178
+ */
179
+ has(id) {
180
+ return this._findWidgetByID(id) !== null;
181
+ }
182
+ /**
183
+ * Collapse the sidebar so no items are expanded.
184
+ */
185
+ collapse() {
186
+ var _a;
187
+ (_a = this._currentWidget) === null || _a === void 0 ? void 0 : _a.hide();
188
+ this._currentWidget = null;
189
+ }
190
+ /**
191
+ * Add a widget and its title to the stacked panel.
192
+ *
193
+ * If the widget is already added, it will be moved.
194
+ */
195
+ addWidget(widget, rank) {
196
+ widget.parent = null;
197
+ widget.hide();
198
+ const item = { widget, rank };
199
+ const index = this._findInsertIndex(item);
200
+ ArrayExt.insert(this._items, index, item);
201
+ this._widgetPanel.insertWidget(index, widget);
202
+ this._refreshVisibility();
203
+ this._widgetAdded.emit(widget);
204
+ }
205
+ /**
206
+ * Hide the side panel
207
+ */
208
+ hide() {
209
+ this._isHiddenByUser = true;
210
+ this._refreshVisibility();
211
+ }
212
+ /**
213
+ * Show the side panel
214
+ */
215
+ show() {
216
+ this._isHiddenByUser = false;
217
+ this._refreshVisibility();
218
+ }
219
+ /**
220
+ * Find the insertion index for a rank item.
221
+ */
222
+ _findInsertIndex(item) {
223
+ return ArrayExt.upperBound(this._items, item, Private.itemCmp);
224
+ }
225
+ /**
226
+ * Find the index of the item with the given widget, or `-1`.
227
+ */
228
+ _findWidgetIndex(widget) {
229
+ return ArrayExt.findFirstIndex(this._items, i => i.widget === widget);
230
+ }
231
+ /**
232
+ * Find the widget with the given id, or `null`.
233
+ */
234
+ _findWidgetByID(id) {
235
+ const item = find(this._items, value => value.widget.id === id);
236
+ return item ? item.widget : null;
237
+ }
238
+ /**
239
+ * Refresh the visibility of the stacked panel.
240
+ */
241
+ _refreshVisibility() {
242
+ this._panel.setHidden(this._isHiddenByUser);
243
+ }
244
+ /*
245
+ * Handle the `widgetRemoved` signal from the panel.
246
+ */
247
+ _onWidgetRemoved(sender, widget) {
248
+ if (widget === this._lastCurrentWidget) {
249
+ this._lastCurrentWidget = null;
250
+ }
251
+ ArrayExt.removeAt(this._items, this._findWidgetIndex(widget));
252
+ this._refreshVisibility();
253
+ this._widgetRemoved.emit(widget);
254
+ }
255
+ }
256
+ /**
257
+ * A class to manages the palette entries associated to the side panels.
258
+ */
259
+ export class SidePanelPalette {
260
+ /**
261
+ * Construct a new side panel palette.
262
+ */
263
+ constructor(options) {
264
+ this._items = [];
265
+ this._commandPalette = options.commandPalette;
266
+ this._command = options.command;
267
+ }
268
+ /**
269
+ * Get a command palette item from the widget id and the area.
270
+ */
271
+ getItem(widget, area) {
272
+ const itemList = this._items;
273
+ for (let i = 0; i < itemList.length; i++) {
274
+ const item = itemList[i];
275
+ if (item.widgetId === widget.id && item.area === area) {
276
+ return item;
277
+ }
278
+ }
279
+ return null;
280
+ }
281
+ /**
282
+ * Add an item to the command palette.
283
+ */
284
+ addItem(widget, area) {
285
+ // Check if the item does not already exist.
286
+ if (this.getItem(widget, area)) {
287
+ return;
288
+ }
289
+ // Add a new item in command palette.
290
+ const disposableDelegate = this._commandPalette.addItem({
291
+ command: this._command,
292
+ category: 'View',
293
+ args: {
294
+ side: area,
295
+ title: `Show ${widget.title.caption}`,
296
+ id: widget.id
297
+ }
298
+ });
299
+ // Keep the disposableDelegate objet to be able to dispose of the item if the widget
300
+ // is remove from the side panel.
301
+ this._items.push({
302
+ widgetId: widget.id,
303
+ area: area,
304
+ disposable: disposableDelegate
305
+ });
306
+ }
307
+ /**
308
+ * Remove an item from the command palette.
309
+ */
310
+ removeItem(widget, area) {
311
+ const item = this.getItem(widget, area);
312
+ if (item) {
313
+ item.disposable.dispose();
314
+ }
315
+ }
316
+ }
317
+ /**
318
+ * A namespace for private module data.
319
+ */
320
+ var Private;
321
+ (function (Private) {
322
+ /**
323
+ * A less-than comparison function for side bar rank items.
324
+ */
325
+ function itemCmp(first, second) {
326
+ return first.rank - second.rank;
327
+ }
328
+ Private.itemCmp = itemCmp;
329
+ })(Private || (Private = {}));
package/lib/shell.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { JupyterFrontEnd } from '@jupyterlab/application';
2
2
  import { DocumentRegistry } from '@jupyterlab/docregistry';
3
- import { IIterator } from '@lumino/algorithm';
4
3
  import { Token } from '@lumino/coreutils';
5
4
  import { ISignal } from '@lumino/signaling';
6
5
  import { Widget } from '@lumino/widgets';
6
+ import { SidePanelHandler } from './panelhandler';
7
7
  /**
8
8
  * The Jupyter Notebook application shell token.
9
9
  */
@@ -34,6 +34,26 @@ export declare class NotebookShell extends Widget implements JupyterFrontEnd.ISh
34
34
  * Get the menu area wrapper panel
35
35
  */
36
36
  get menu(): Widget;
37
+ /**
38
+ * Get the left area handler
39
+ */
40
+ get leftHandler(): SidePanelHandler;
41
+ /**
42
+ * Get the right area handler
43
+ */
44
+ get rightHandler(): SidePanelHandler;
45
+ /**
46
+ * Is the left sidebar visible?
47
+ */
48
+ get leftCollapsed(): boolean;
49
+ /**
50
+ * Is the right sidebar visible?
51
+ */
52
+ get rightCollapsed(): boolean;
53
+ /**
54
+ * Promise that resolves when the main widget is loaded
55
+ */
56
+ get restored(): Promise<void>;
37
57
  /**
38
58
  * Activate a widget in its area.
39
59
  */
@@ -63,14 +83,33 @@ export declare class NotebookShell extends Widget implements JupyterFrontEnd.ISh
63
83
  *
64
84
  * @param area The area
65
85
  */
66
- widgets(area: Shell.Area): IIterator<Widget>;
86
+ widgets(area: Shell.Area): IterableIterator<Widget>;
87
+ /**
88
+ * Expand the left panel to show the sidebar with its widget.
89
+ */
90
+ expandLeft(id?: string): void;
91
+ /**
92
+ * Collapse the left panel
93
+ */
94
+ collapseLeft(): void;
95
+ /**
96
+ * Expand the right panel to show the sidebar with its widget.
97
+ */
98
+ expandRight(id?: string): void;
99
+ /**
100
+ * Collapse the right panel
101
+ */
102
+ collapseRight(): void;
67
103
  private _topWrapper;
68
104
  private _topHandler;
69
105
  private _menuWrapper;
70
106
  private _menuHandler;
107
+ private _leftHandler;
108
+ private _rightHandler;
71
109
  private _spacer;
72
110
  private _main;
73
111
  private _currentChanged;
112
+ private _mainWidgetLoaded;
74
113
  }
75
114
  /**
76
115
  * A namespace for Shell statics
@@ -79,5 +118,5 @@ export declare namespace Shell {
79
118
  /**
80
119
  * The areas of the application shell where widgets can reside.
81
120
  */
82
- type Area = 'main' | 'top' | 'menu';
121
+ type Area = 'main' | 'top' | 'left' | 'right' | 'menu';
83
122
  }
package/lib/shell.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
- import { ArrayExt, find, iter } from '@lumino/algorithm';
4
- import { Token } from '@lumino/coreutils';
5
- import { MessageLoop } from '@lumino/messaging';
3
+ import { find } from '@lumino/algorithm';
4
+ import { PromiseDelegate, Token } from '@lumino/coreutils';
6
5
  import { Signal } from '@lumino/signaling';
7
- import { Panel, Widget, BoxLayout } from '@lumino/widgets';
6
+ import { BoxLayout, Panel, SplitPanel, Widget } from '@lumino/widgets';
7
+ import { PanelHandler, SidePanelHandler } from './panelhandler';
8
8
  /**
9
9
  * The Jupyter Notebook application shell token.
10
10
  */
@@ -20,31 +20,63 @@ export class NotebookShell extends Widget {
20
20
  constructor() {
21
21
  super();
22
22
  this._currentChanged = new Signal(this);
23
+ this._mainWidgetLoaded = new PromiseDelegate();
23
24
  this.id = 'main';
24
- const rootLayout = new BoxLayout();
25
- this._topHandler = new Private.PanelHandler();
26
- this._menuHandler = new Private.PanelHandler();
25
+ this._topHandler = new PanelHandler();
26
+ this._menuHandler = new PanelHandler();
27
+ this._leftHandler = new SidePanelHandler('left');
28
+ this._rightHandler = new SidePanelHandler('right');
27
29
  this._main = new Panel();
30
+ const topWrapper = (this._topWrapper = new Panel());
31
+ const menuWrapper = (this._menuWrapper = new Panel());
28
32
  this._topHandler.panel.id = 'top-panel';
29
33
  this._menuHandler.panel.id = 'menu-panel';
30
34
  this._main.id = 'main-panel';
35
+ this._spacer = new Widget();
36
+ this._spacer.id = 'spacer-widget';
31
37
  // create wrappers around the top and menu areas
32
- const topWrapper = (this._topWrapper = new Panel());
33
38
  topWrapper.id = 'top-panel-wrapper';
34
39
  topWrapper.addWidget(this._topHandler.panel);
35
- const menuWrapper = (this._menuWrapper = new Panel());
36
40
  menuWrapper.id = 'menu-panel-wrapper';
37
41
  menuWrapper.addWidget(this._menuHandler.panel);
38
- BoxLayout.setStretch(topWrapper, 0);
39
- BoxLayout.setStretch(menuWrapper, 0);
42
+ const rootLayout = new BoxLayout();
43
+ const leftHandler = this._leftHandler;
44
+ const rightHandler = this._rightHandler;
45
+ leftHandler.panel.id = 'jp-left-stack';
46
+ rightHandler.panel.id = 'jp-right-stack';
47
+ // Hide the side panels by default.
48
+ leftHandler.hide();
49
+ rightHandler.hide();
50
+ const middleLayout = new BoxLayout({
51
+ spacing: 0,
52
+ direction: 'top-to-bottom'
53
+ });
54
+ BoxLayout.setStretch(this._topWrapper, 0);
55
+ BoxLayout.setStretch(this._menuWrapper, 0);
40
56
  BoxLayout.setStretch(this._main, 1);
41
- this._spacer = new Widget();
42
- this._spacer.id = 'spacer-widget';
57
+ const middlePanel = new Panel({ layout: middleLayout });
58
+ middlePanel.addWidget(this._topWrapper);
59
+ middlePanel.addWidget(this._menuWrapper);
60
+ middlePanel.addWidget(this._spacer);
61
+ middlePanel.addWidget(this._main);
62
+ middlePanel.layout = middleLayout;
63
+ // TODO: Consider storing this as an attribute this._hsplitPanel if saving/restoring layout needed
64
+ const hsplitPanel = new SplitPanel();
65
+ hsplitPanel.id = 'main-split-panel';
66
+ hsplitPanel.spacing = 1;
67
+ BoxLayout.setStretch(hsplitPanel, 1);
68
+ SplitPanel.setStretch(leftHandler.panel, 0);
69
+ SplitPanel.setStretch(rightHandler.panel, 0);
70
+ SplitPanel.setStretch(middlePanel, 1);
71
+ hsplitPanel.addWidget(leftHandler.panel);
72
+ hsplitPanel.addWidget(middlePanel);
73
+ hsplitPanel.addWidget(rightHandler.panel);
74
+ // Use relative sizing to set the width of the side panels.
75
+ // This will still respect the min-size of children widget in the stacked
76
+ // panel.
77
+ hsplitPanel.setRelativeSizes([1, 2.5, 1]);
43
78
  rootLayout.spacing = 0;
44
- rootLayout.addWidget(topWrapper);
45
- rootLayout.addWidget(menuWrapper);
46
- rootLayout.addWidget(this._spacer);
47
- rootLayout.addWidget(this._main);
79
+ rootLayout.addWidget(hsplitPanel);
48
80
  this.layout = rootLayout;
49
81
  }
50
82
  /**
@@ -72,13 +104,54 @@ export class NotebookShell extends Widget {
72
104
  get menu() {
73
105
  return this._menuWrapper;
74
106
  }
107
+ /**
108
+ * Get the left area handler
109
+ */
110
+ get leftHandler() {
111
+ return this._leftHandler;
112
+ }
113
+ /**
114
+ * Get the right area handler
115
+ */
116
+ get rightHandler() {
117
+ return this._rightHandler;
118
+ }
119
+ /**
120
+ * Is the left sidebar visible?
121
+ */
122
+ get leftCollapsed() {
123
+ return !(this._leftHandler.isVisible && this._leftHandler.panel.isVisible);
124
+ }
125
+ /**
126
+ * Is the right sidebar visible?
127
+ */
128
+ get rightCollapsed() {
129
+ return !(this._rightHandler.isVisible && this._rightHandler.panel.isVisible);
130
+ }
131
+ /**
132
+ * Promise that resolves when the main widget is loaded
133
+ */
134
+ get restored() {
135
+ return this._mainWidgetLoaded.promise;
136
+ }
75
137
  /**
76
138
  * Activate a widget in its area.
77
139
  */
78
140
  activateById(id) {
79
- const widget = find(this.widgets('main'), w => w.id === id);
80
- if (widget) {
81
- widget.activate();
141
+ // Search all areas that can have widgets for this widget, starting with main.
142
+ for (const area of ['main', 'top', 'left', 'right', 'menu']) {
143
+ const widget = find(this.widgets(area), w => w.id === id);
144
+ if (widget) {
145
+ if (area === 'left') {
146
+ this.expandLeft(id);
147
+ }
148
+ else if (area === 'right') {
149
+ this.expandRight(id);
150
+ }
151
+ else {
152
+ widget.activate();
153
+ }
154
+ }
82
155
  }
83
156
  }
84
157
  /**
@@ -95,20 +168,28 @@ export class NotebookShell extends Widget {
95
168
  add(widget, area, options) {
96
169
  var _a;
97
170
  const rank = (_a = options === null || options === void 0 ? void 0 : options.rank) !== null && _a !== void 0 ? _a : DEFAULT_RANK;
98
- if (area === 'top') {
99
- return this._topHandler.addWidget(widget, rank);
100
- }
101
- if (area === 'menu') {
102
- return this._menuHandler.addWidget(widget, rank);
103
- }
104
- if (area === 'main' || area === undefined) {
105
- if (this._main.widgets.length > 0) {
106
- // do not add the widget if there is already one
107
- return;
108
- }
109
- this._main.addWidget(widget);
110
- this._main.update();
111
- this._currentChanged.emit(void 0);
171
+ switch (area) {
172
+ case 'top':
173
+ return this._topHandler.addWidget(widget, rank);
174
+ case 'menu':
175
+ return this._menuHandler.addWidget(widget, rank);
176
+ case 'main':
177
+ case undefined:
178
+ if (this._main.widgets.length > 0) {
179
+ // do not add the widget if there is already one
180
+ return;
181
+ }
182
+ this._main.addWidget(widget);
183
+ this._main.update();
184
+ this._currentChanged.emit(void 0);
185
+ this._mainWidgetLoaded.resolve();
186
+ break;
187
+ case 'left':
188
+ return this._leftHandler.addWidget(widget, rank);
189
+ case 'right':
190
+ return this._rightHandler.addWidget(widget, rank);
191
+ default:
192
+ console.warn(`Cannot add widget to area: ${area}`);
112
193
  }
113
194
  }
114
195
  /**
@@ -130,86 +211,54 @@ export class NotebookShell extends Widget {
130
211
  *
131
212
  * @param area The area
132
213
  */
133
- widgets(area) {
214
+ *widgets(area) {
134
215
  switch (area !== null && area !== void 0 ? area : 'main') {
135
216
  case 'top':
136
- return iter(this._topHandler.panel.widgets);
217
+ yield* this._topHandler.panel.widgets;
218
+ return;
137
219
  case 'menu':
138
- return iter(this._menuHandler.panel.widgets);
220
+ yield* this._menuHandler.panel.widgets;
221
+ return;
139
222
  case 'main':
140
- return iter(this._main.widgets);
223
+ yield* this._main.widgets;
224
+ return;
225
+ case 'left':
226
+ yield* this._leftHandler.widgets;
227
+ return;
228
+ case 'right':
229
+ yield* this._rightHandler.widgets;
230
+ return;
141
231
  default:
142
- throw new Error(`Invalid area: ${area}`);
232
+ console.error(`This shell has no area called "${area}"`);
233
+ return;
143
234
  }
144
235
  }
145
- }
146
- /**
147
- * A namespace for private module data.
148
- */
149
- var Private;
150
- (function (Private) {
151
- /**
152
- * A less-than comparison function for side bar rank items.
153
- */
154
- function itemCmp(first, second) {
155
- return first.rank - second.rank;
156
- }
157
- Private.itemCmp = itemCmp;
158
- /**
159
- * A class which manages a panel and sorts its widgets by rank.
160
- */
161
- class PanelHandler {
162
- constructor() {
163
- /**
164
- * A message hook for child add/remove messages on the main area dock panel.
165
- */
166
- this._panelChildHook = (handler, msg) => {
167
- switch (msg.type) {
168
- case 'child-added':
169
- {
170
- const widget = msg.child;
171
- // If we already know about this widget, we're done
172
- if (this._items.find(v => v.widget === widget)) {
173
- break;
174
- }
175
- // Otherwise, add to the end by default
176
- const rank = this._items[this._items.length - 1].rank;
177
- this._items.push({ widget, rank });
178
- }
179
- break;
180
- case 'child-removed':
181
- {
182
- const widget = msg.child;
183
- ArrayExt.removeFirstWhere(this._items, v => v.widget === widget);
184
- }
185
- break;
186
- default:
187
- break;
188
- }
189
- return true;
190
- };
191
- this._items = new Array();
192
- this._panel = new Panel();
193
- MessageLoop.installMessageHook(this._panel, this._panelChildHook);
194
- }
195
- /**
196
- * Get the panel managed by the handler.
197
- */
198
- get panel() {
199
- return this._panel;
200
- }
201
- /**
202
- * Add a widget to the panel.
203
- *
204
- * If the widget is already added, it will be moved.
205
- */
206
- addWidget(widget, rank) {
207
- widget.parent = null;
208
- const item = { widget, rank };
209
- const index = ArrayExt.upperBound(this._items, item, Private.itemCmp);
210
- ArrayExt.insert(this._items, index, item);
211
- this._panel.insertWidget(index, widget);
212
- }
236
+ /**
237
+ * Expand the left panel to show the sidebar with its widget.
238
+ */
239
+ expandLeft(id) {
240
+ this._leftHandler.panel.show();
241
+ this._leftHandler.expand(id); // Show the current widget, if any
242
+ }
243
+ /**
244
+ * Collapse the left panel
245
+ */
246
+ collapseLeft() {
247
+ this._leftHandler.collapse();
248
+ this._leftHandler.panel.hide();
213
249
  }
214
- Private.PanelHandler = PanelHandler;
215
- })(Private || (Private = {}));
250
+ /**
251
+ * Expand the right panel to show the sidebar with its widget.
252
+ */
253
+ expandRight(id) {
254
+ this._rightHandler.panel.show();
255
+ this._rightHandler.expand(id); // Show the current widget, if any
256
+ }
257
+ /**
258
+ * Collapse the right panel
259
+ */
260
+ collapseRight() {
261
+ this._rightHandler.collapse();
262
+ this._rightHandler.panel.hide();
263
+ }
264
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter-notebook/application",
3
- "version": "7.0.0-alpha.1",
3
+ "version": "7.0.0-alpha.10",
4
4
  "description": "Jupyter Notebook - Application",
5
5
  "homepage": "https://github.com/jupyter/notebook",
6
6
  "bugs": {
@@ -43,27 +43,27 @@
43
43
  "watch": "tsc -b --watch"
44
44
  },
45
45
  "dependencies": {
46
- "@jupyterlab/application": "^4.0.0-alpha.5",
47
- "@jupyterlab/coreutils": "^6.0.0-alpha.5",
48
- "@jupyterlab/docregistry": "^4.0.0-alpha.5",
49
- "@jupyterlab/rendermime-interfaces": "^4.0.0-alpha.5",
50
- "@jupyterlab/ui-components": "^4.0.0-alpha.19",
51
- "@lumino/algorithm": "^1.9.1",
52
- "@lumino/coreutils": "^1.12.0",
53
- "@lumino/messaging": "^1.10.1",
54
- "@lumino/polling": "^1.10.0",
55
- "@lumino/signaling": "^1.10.1",
56
- "@lumino/widgets": "^1.31.1"
46
+ "@jupyterlab/application": "^4.0.0-alpha.17",
47
+ "@jupyterlab/coreutils": "^6.0.0-alpha.17",
48
+ "@jupyterlab/docregistry": "^4.0.0-alpha.17",
49
+ "@jupyterlab/rendermime-interfaces": "^3.8.0-alpha.17",
50
+ "@jupyterlab/ui-components": "^4.0.0-alpha.32",
51
+ "@lumino/algorithm": "^2.0.0-alpha.7",
52
+ "@lumino/coreutils": "^2.0.0-alpha.7",
53
+ "@lumino/messaging": "^2.0.0-alpha.7",
54
+ "@lumino/polling": "^2.0.0-alpha.7",
55
+ "@lumino/signaling": "^2.0.0-alpha.7",
56
+ "@lumino/widgets": "^2.0.0-alpha.7"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@babel/core": "^7.11.6",
60
60
  "@babel/preset-env": "^7.12.1",
61
- "@jupyterlab/testutils": "^4.0.0-alpha.5",
62
- "@types/jest": "^26.0.10",
63
- "jest": "^26.4.2",
64
- "rimraf": "~3.0.0",
65
- "ts-jest": "^26.3.0",
66
- "typescript": "~4.1.3"
61
+ "@jupyterlab/testutils": "^4.0.0-alpha.17",
62
+ "@types/jest": "^29.2.5",
63
+ "jest": "^29.3.1",
64
+ "rimraf": "^3.0.2",
65
+ "ts-jest": "^29.0.3",
66
+ "typescript": "~4.9.3"
67
67
  },
68
68
  "publishConfig": {
69
69
  "access": "public"
package/style/index.css CHANGED
@@ -8,3 +8,4 @@
8
8
  @import url('~@jupyterlab/ui-components/style/index.css');
9
9
 
10
10
  @import url('./base.css');
11
+ @import url('./sidepanel.css');
package/style/index.js CHANGED
@@ -8,3 +8,4 @@ import '@jupyterlab/mainmenu/style/index.js';
8
8
  import '@jupyterlab/ui-components/style/index.js';
9
9
 
10
10
  import './base.css';
11
+ import './sidepanel.css';
@@ -0,0 +1,48 @@
1
+ /*-----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |
5
+ | Adapted from JupyterLab's packages/application/style/sidepanel.css.
6
+ |----------------------------------------------------------------------------*/
7
+
8
+ /*-----------------------------------------------------------------------------
9
+ | Variables
10
+ |----------------------------------------------------------------------------*/
11
+
12
+ :root {
13
+ --jp-private-sidebar-tab-width: 32px;
14
+ }
15
+
16
+ /*-----------------------------------------------------------------------------
17
+ | SideBar
18
+ |----------------------------------------------------------------------------*/
19
+
20
+ /* Stack panels */
21
+
22
+ #jp-right-stack,
23
+ #jp-left-stack {
24
+ display: flex;
25
+ flex-direction: column;
26
+ min-width: var(--jp-sidebar-min-width);
27
+ }
28
+
29
+ #jp-left-stack .jp-SidePanel-collapse,
30
+ #jp-right-stack .jp-SidePanel-collapse {
31
+ display: flex;
32
+ flex: 0 0 auto;
33
+ min-height: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ #jp-left-stack .jp-SidePanel-collapse {
38
+ justify-content: right;
39
+ }
40
+
41
+ #jp-right-stack .jp-SidePanel-collapse {
42
+ justify-content: left;
43
+ }
44
+
45
+ #jp-left-stack .lm-StackedPanel,
46
+ #jp-right-stack .lm-StackedPanel {
47
+ flex: 1 1 auto;
48
+ }