Skip to content

Commit 26746c9

Browse files
committed
Customize the file browser toolbar via the settings
Fixes jupyterlab#12196
1 parent 2475a53 commit 26746c9

File tree

8 files changed

+302
-203
lines changed

8 files changed

+302
-203
lines changed

packages/apputils/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ export * from './tokens';
5151
export {
5252
ToolbarWidgetRegistry,
5353
createDefaultFactory,
54-
createToolbarFactory
54+
createToolbarFactory,
55+
setToolbar
5556
} from './toolbar';
5657
export * from './widgettracker';
5758
export * from './windowresolver';

packages/apputils/src/toolbar/factory.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { IObservableList, ObservableList } from '@jupyterlab/observables';
22
import { ISettingRegistry, SettingRegistry } from '@jupyterlab/settingregistry';
33
import { ITranslator, TranslationBundle } from '@jupyterlab/translation';
4-
import { toArray } from '@lumino/algorithm';
4+
import { Toolbar } from '@jupyterlab/ui-components';
5+
import { findIndex, toArray } from '@lumino/algorithm';
56
import { JSONExt, PartialJSONObject } from '@lumino/coreutils';
67
import { Widget } from '@lumino/widgets';
78
import { Dialog, showDialog } from '../dialog';
@@ -227,7 +228,7 @@ async function setToolbarItems(
227228
* @param pluginId Settings plugin id
228229
* @param translator Translator
229230
* @param propertyId Toolbar definition key in the settings plugin
230-
* @returns List of toolbar widgets
231+
* @returns List of toolbar widgets factory
231232
*/
232233
export function createToolbarFactory(
233234
toolbarRegistry: IToolbarWidgetRegistry,
@@ -304,3 +305,98 @@ export function createToolbarFactory(
304305
return toolbar;
305306
};
306307
}
308+
309+
/**
310+
* Set the toolbar items of a widget from a factory
311+
*
312+
* @param widget Widget with the toolbar to set
313+
* @param factory Toolbar items factory
314+
*/
315+
export function setToolbar(
316+
widget: Toolbar.IWidgetToolbar,
317+
factory: (
318+
widget: Widget
319+
) =>
320+
| IObservableList<ToolbarRegistry.IToolbarItem>
321+
| ToolbarRegistry.IToolbarItem[]
322+
): void {
323+
if (!widget.toolbar) {
324+
console.log(`Widget ${widget.id} has no 'toolbar'.`);
325+
return;
326+
}
327+
const items = factory(widget);
328+
329+
if (Array.isArray(items)) {
330+
items.forEach(({ name, widget: item }) => {
331+
widget.toolbar!.addItem(name, item);
332+
});
333+
} else {
334+
const updateToolbar = (
335+
list: IObservableList<ToolbarRegistry.IToolbarItem>,
336+
changes: IObservableList.IChangedArgs<ToolbarRegistry.IToolbarItem>
337+
) => {
338+
switch (changes.type) {
339+
case 'add':
340+
changes.newValues.forEach((item, index) => {
341+
widget.toolbar!.insertItem(
342+
changes.newIndex + index,
343+
item.name,
344+
item.widget
345+
);
346+
});
347+
break;
348+
case 'move':
349+
changes.oldValues.forEach(item => {
350+
item.widget.parent = null;
351+
});
352+
changes.newValues.forEach((item, index) => {
353+
widget.toolbar!.insertItem(
354+
changes.newIndex + index,
355+
item.name,
356+
item.widget
357+
);
358+
});
359+
break;
360+
case 'remove':
361+
changes.oldValues.forEach(item => {
362+
item.widget.parent = null;
363+
});
364+
break;
365+
case 'set':
366+
changes.oldValues.forEach(item => {
367+
item.widget.parent = null;
368+
});
369+
370+
changes.newValues.forEach((item, index) => {
371+
const existingIndex = findIndex(
372+
widget.toolbar!.names(),
373+
name => item.name === name
374+
);
375+
if (existingIndex >= 0) {
376+
toArray(widget.toolbar!.children())[existingIndex].parent = null;
377+
}
378+
379+
widget.toolbar!.insertItem(
380+
changes.newIndex + index,
381+
item.name,
382+
item.widget
383+
);
384+
});
385+
break;
386+
}
387+
};
388+
389+
updateToolbar(items, {
390+
newIndex: 0,
391+
newValues: toArray(items),
392+
oldIndex: 0,
393+
oldValues: [],
394+
type: 'add'
395+
});
396+
397+
items.changed.connect(updateToolbar);
398+
widget.disposed.connect(() => {
399+
items.changed.disconnect(updateToolbar);
400+
});
401+
}
402+
}

packages/docregistry/src/default.ts

Lines changed: 3 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// Copyright (c) Jupyter Development Team.
22
// Distributed under the terms of the Modified BSD License.
33

4-
import { MainAreaWidget } from '@jupyterlab/apputils';
4+
import { MainAreaWidget, setToolbar } from '@jupyterlab/apputils';
55
import { CodeEditor } from '@jupyterlab/codeeditor';
66
import { Mode } from '@jupyterlab/codemirror';
77
import { IChangedArgs, PathExt } from '@jupyterlab/coreutils';
88
import { IModelDB, IObservableList } from '@jupyterlab/observables';
99
import { Contents } from '@jupyterlab/services';
1010
import * as models from '@jupyterlab/shared-models';
1111
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
12-
import { findIndex, toArray } from '@lumino/algorithm';
1312
import { PartialJSONValue } from '@lumino/coreutils';
1413
import { ISignal, Signal } from '@lumino/signaling';
1514
import { Title, Widget } from '@lumino/widgets';
@@ -428,85 +427,8 @@ export abstract class ABCWidgetFactory<
428427
// Create the new widget
429428
const widget = this.createNewWidget(context, source);
430429

431-
// Add toolbar items
432-
const items:
433-
| DocumentRegistry.IToolbarItem[]
434-
| IObservableList<DocumentRegistry.IToolbarItem> = (
435-
this._toolbarFactory?.bind(this) ?? this.defaultToolbarFactory.bind(this)
436-
)(widget);
437-
438-
if (Array.isArray(items)) {
439-
items.forEach(({ name, widget: item }) => {
440-
widget.toolbar.addItem(name, item);
441-
});
442-
} else {
443-
const updateToolbar = (
444-
list: IObservableList<DocumentRegistry.IToolbarItem>,
445-
changes: IObservableList.IChangedArgs<DocumentRegistry.IToolbarItem>
446-
) => {
447-
switch (changes.type) {
448-
case 'add':
449-
changes.newValues.forEach((item, index) => {
450-
widget.toolbar.insertItem(
451-
changes.newIndex + index,
452-
item.name,
453-
item.widget
454-
);
455-
});
456-
break;
457-
case 'move':
458-
changes.oldValues.forEach(item => {
459-
item.widget.parent = null;
460-
});
461-
changes.newValues.forEach((item, index) => {
462-
widget.toolbar.insertItem(
463-
changes.newIndex + index,
464-
item.name,
465-
item.widget
466-
);
467-
});
468-
break;
469-
case 'remove':
470-
changes.oldValues.forEach(item => {
471-
item.widget.parent = null;
472-
});
473-
break;
474-
case 'set':
475-
changes.oldValues.forEach(item => {
476-
item.widget.parent = null;
477-
});
478-
479-
changes.newValues.forEach((item, index) => {
480-
const existingIndex = findIndex(
481-
widget.toolbar.names(),
482-
name => item.name === name
483-
);
484-
if (existingIndex >= 0) {
485-
toArray(widget.toolbar.children())[existingIndex].parent = null;
486-
}
487-
488-
widget.toolbar.insertItem(
489-
changes.newIndex + index,
490-
item.name,
491-
item.widget
492-
);
493-
});
494-
break;
495-
}
496-
};
497-
498-
updateToolbar(items, {
499-
newIndex: 0,
500-
newValues: toArray(items),
501-
oldIndex: 0,
502-
oldValues: [],
503-
type: 'add'
504-
});
505-
items.changed.connect(updateToolbar);
506-
widget.disposed.connect(() => {
507-
items.changed.disconnect(updateToolbar);
508-
});
509-
}
430+
// Add toolbar
431+
setToolbar(widget, this._toolbarFactory ?? this.defaultToolbarFactory);
510432

511433
// Emit widget created signal
512434
this._widgetCreated.emit(widget);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"title": "File Browser Widget",
3+
"description": "File Browser widget settings.",
4+
"jupyter.lab.toolbars": {
5+
"FileBrowser": [
6+
{
7+
"name": "new-launcher",
8+
"command": "filebrowser:create-main-launcher",
9+
"rank": 1
10+
},
11+
{
12+
"name": "new-directory",
13+
"command": "filebrowser:create-new-directory",
14+
"rank": 10
15+
},
16+
{ "name": "uploader", "rank": 20 },
17+
{ "name": "refresh", "command": "filebrowser:refresh", "rank": 30 }
18+
]
19+
},
20+
"jupyter.lab.transform": true,
21+
"properties": {
22+
"toolbar": {
23+
"title": "File browser toolbar items",
24+
"description": "Note: To disable a toolbar item,\ncopy it to User Preferences and add the\n\"disabled\" key. The following example will disable the uploader button:\n{\n \"toolbar\": [\n {\n \"name\": \"uploader\",\n \"disabled\": true\n }\n ]\n}\n\nToolbar description:",
25+
"items": {
26+
"$ref": "#/definitions/toolbarItem"
27+
},
28+
"type": "array",
29+
"default": []
30+
}
31+
},
32+
"additionalProperties": false,
33+
"type": "object",
34+
"definitions": {
35+
"toolbarItem": {
36+
"properties": {
37+
"name": {
38+
"title": "Unique name",
39+
"type": "string"
40+
},
41+
"args": {
42+
"title": "Command arguments",
43+
"type": "object"
44+
},
45+
"command": {
46+
"title": "Command id",
47+
"type": "string",
48+
"default": ""
49+
},
50+
"disabled": {
51+
"title": "Whether the item is ignored or not",
52+
"type": "boolean",
53+
"default": false
54+
},
55+
"icon": {
56+
"title": "Item icon id",
57+
"description": "If defined, it will override the command icon",
58+
"type": "string"
59+
},
60+
"label": {
61+
"title": "Item label",
62+
"description": "If defined, it will override the command label",
63+
"type": "string"
64+
},
65+
"type": {
66+
"title": "Item type",
67+
"type": "string",
68+
"enum": ["command", "spacer"]
69+
},
70+
"rank": {
71+
"title": "Item rank",
72+
"type": "number",
73+
"minimum": 0,
74+
"default": 50
75+
}
76+
},
77+
"required": ["name"],
78+
"additionalProperties": false,
79+
"type": "object"
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)