@jupyterlab/filebrowser 4.3.0-alpha.2 → 4.3.0-beta.0
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/browser.d.ts +12 -0
- package/lib/browser.js +20 -1
- package/lib/browser.js.map +1 -1
- package/lib/listing.d.ts +129 -15
- package/lib/listing.js +574 -208
- package/lib/listing.js.map +1 -1
- package/lib/model.d.ts +2 -1
- package/lib/model.js +5 -4
- package/lib/model.js.map +1 -1
- package/package.json +11 -11
- package/src/browser.ts +28 -1
- package/src/listing.ts +791 -220
- package/src/model.ts +7 -4
- package/style/base.css +68 -39
package/lib/listing.js
CHANGED
|
@@ -9,6 +9,7 @@ import { caretDownIcon, caretUpIcon, classes, LabIcon } from '@jupyterlab/ui-com
|
|
|
9
9
|
import { ArrayExt, filter, StringExt } from '@lumino/algorithm';
|
|
10
10
|
import { MimeData, PromiseDelegate } from '@lumino/coreutils';
|
|
11
11
|
import { ElementExt } from '@lumino/domutils';
|
|
12
|
+
import { DisposableDelegate } from '@lumino/disposable';
|
|
12
13
|
import { Drag } from '@lumino/dragdrop';
|
|
13
14
|
import { MessageLoop } from '@lumino/messaging';
|
|
14
15
|
import { Signal } from '@lumino/signaling';
|
|
@@ -46,6 +47,10 @@ const ITEM_CLASS = 'jp-DirListing-item';
|
|
|
46
47
|
* The class name added to the listing item text cell.
|
|
47
48
|
*/
|
|
48
49
|
const ITEM_TEXT_CLASS = 'jp-DirListing-itemText';
|
|
50
|
+
/**
|
|
51
|
+
* The class name added to the listing item text cell.
|
|
52
|
+
*/
|
|
53
|
+
const ITEM_NAME_COLUMN_CLASS = 'jp-DirListing-itemName';
|
|
49
54
|
/**
|
|
50
55
|
* The class name added to the listing item icon cell.
|
|
51
56
|
*/
|
|
@@ -79,18 +84,6 @@ const MODIFIED_ID_CLASS = 'jp-id-modified';
|
|
|
79
84
|
* The class name added to the file size column header cell.
|
|
80
85
|
*/
|
|
81
86
|
const FILE_SIZE_ID_CLASS = 'jp-id-filesize';
|
|
82
|
-
/**
|
|
83
|
-
* The class name added to the narrow column header cell.
|
|
84
|
-
*/
|
|
85
|
-
const NARROW_ID_CLASS = 'jp-id-narrow';
|
|
86
|
-
/**
|
|
87
|
-
* The class name added to the modified column header cell and modified item cell when hidden.
|
|
88
|
-
*/
|
|
89
|
-
const MODIFIED_COLUMN_HIDDEN = 'jp-LastModified-hidden';
|
|
90
|
-
/**
|
|
91
|
-
* The class name added to the size column header cell and size item cell when hidden.
|
|
92
|
-
*/
|
|
93
|
-
const FILE_SIZE_COLUMN_HIDDEN = 'jp-FileSize-hidden';
|
|
94
87
|
/**
|
|
95
88
|
* The mime type for a contents drag object.
|
|
96
89
|
*/
|
|
@@ -111,6 +104,10 @@ const SELECTED_CLASS = 'jp-mod-selected';
|
|
|
111
104
|
* The class name added to drag state icons to add space between the icon and the file name
|
|
112
105
|
*/
|
|
113
106
|
const DRAG_ICON_CLASS = 'jp-DragIcon';
|
|
107
|
+
/**
|
|
108
|
+
* The class name added to column resize handle.
|
|
109
|
+
*/
|
|
110
|
+
const RESIZE_HANDLE_CLASS = 'jp-DirListing-resizeHandle';
|
|
114
111
|
/**
|
|
115
112
|
* The class name added to the widget when there are items on the clipboard.
|
|
116
113
|
*/
|
|
@@ -127,6 +124,10 @@ const MULTI_SELECTED_CLASS = 'jp-mod-multiSelected';
|
|
|
127
124
|
* The class name added to indicate running notebook.
|
|
128
125
|
*/
|
|
129
126
|
const RUNNING_CLASS = 'jp-mod-running';
|
|
127
|
+
/**
|
|
128
|
+
* The class name added to indicate the active element.
|
|
129
|
+
*/
|
|
130
|
+
const ACTIVE_CLASS = 'jp-mod-active';
|
|
130
131
|
/**
|
|
131
132
|
* The class name added for a descending sort.
|
|
132
133
|
*/
|
|
@@ -169,6 +170,7 @@ export class DirListing extends Widget {
|
|
|
169
170
|
this._onItemOpened = new Signal(this);
|
|
170
171
|
this._drag = null;
|
|
171
172
|
this._dragData = null;
|
|
173
|
+
this._resizeData = null;
|
|
172
174
|
this._selectTimer = -1;
|
|
173
175
|
this._isCut = false;
|
|
174
176
|
this._prevPath = '';
|
|
@@ -180,9 +182,19 @@ export class DirListing extends Widget {
|
|
|
180
182
|
this._inRename = false;
|
|
181
183
|
this._isDirty = false;
|
|
182
184
|
this._hiddenColumns = new Set();
|
|
185
|
+
this._columnSizes = {
|
|
186
|
+
name: null,
|
|
187
|
+
file_size: null,
|
|
188
|
+
is_selected: null,
|
|
189
|
+
last_modified: null
|
|
190
|
+
};
|
|
183
191
|
this._sortNotebooksFirst = false;
|
|
192
|
+
this._allowSingleClick = false;
|
|
184
193
|
// _focusIndex should never be set outside the range [0, this._items.length - 1]
|
|
185
194
|
this._focusIndex = 0;
|
|
195
|
+
this._allUploaded = new Signal(this);
|
|
196
|
+
this._width = null;
|
|
197
|
+
this._state = null;
|
|
186
198
|
this.addClass(DIR_LISTING_CLASS);
|
|
187
199
|
this.translator = options.translator || nullTranslator;
|
|
188
200
|
this._trans = this.translator.load('jupyterlab');
|
|
@@ -194,12 +206,13 @@ export class DirListing extends Widget {
|
|
|
194
206
|
this._editNode.className = EDITOR_CLASS;
|
|
195
207
|
this._manager = this._model.manager;
|
|
196
208
|
this._renderer = options.renderer || DirListing.defaultRenderer;
|
|
209
|
+
this._state = options.state || null;
|
|
197
210
|
// Get the width of the "modified" column
|
|
198
211
|
this._updateModifiedSize(this.node);
|
|
199
212
|
const headerNode = DOMUtils.findElement(this.node, HEADER_CLASS);
|
|
200
213
|
// hide the file size column by default
|
|
201
214
|
this._hiddenColumns.add('file_size');
|
|
202
|
-
this._renderer.populateHeaderNode(headerNode, this.translator, this._hiddenColumns);
|
|
215
|
+
this._renderer.populateHeaderNode(headerNode, this.translator, this._hiddenColumns, this._columnSizes);
|
|
203
216
|
this._manager.activateRequested.connect(this._onActivateRequested, this);
|
|
204
217
|
}
|
|
205
218
|
/**
|
|
@@ -409,6 +422,37 @@ export class DirListing extends Widget {
|
|
|
409
422
|
.filter(item => item.type !== 'directory')
|
|
410
423
|
.map(item => this._model.download(item.path)));
|
|
411
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Restore the state of the file browser listing.
|
|
427
|
+
*
|
|
428
|
+
* @param id - The unique ID that is used to construct a state database key.
|
|
429
|
+
*
|
|
430
|
+
*/
|
|
431
|
+
async restore(id) {
|
|
432
|
+
const key = `file-browser-${id}:columns`;
|
|
433
|
+
const state = this._state;
|
|
434
|
+
this._stateColumnsKey = key;
|
|
435
|
+
if (!state) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const columns = await state.fetch(key);
|
|
440
|
+
if (!columns) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const sizes = columns['sizes'];
|
|
444
|
+
if (!sizes) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
for (const [key, size] of Object.entries(sizes)) {
|
|
448
|
+
this._columnSizes[key] = size;
|
|
449
|
+
}
|
|
450
|
+
this._updateColumnSizes();
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
await state.remove(key);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
412
456
|
/**
|
|
413
457
|
* Shut down kernels on the applicable currently selected items.
|
|
414
458
|
*
|
|
@@ -649,6 +693,7 @@ export class DirListing extends Widget {
|
|
|
649
693
|
onAfterAttach(msg) {
|
|
650
694
|
super.onAfterAttach(msg);
|
|
651
695
|
const node = this.node;
|
|
696
|
+
this._width = this.node.getBoundingClientRect().width;
|
|
652
697
|
const content = DOMUtils.findElement(node, CONTENT_CLASS);
|
|
653
698
|
node.addEventListener('mousedown', this);
|
|
654
699
|
node.addEventListener('keydown', this);
|
|
@@ -699,12 +744,15 @@ export class DirListing extends Widget {
|
|
|
699
744
|
this.update();
|
|
700
745
|
}
|
|
701
746
|
}
|
|
702
|
-
|
|
747
|
+
/**
|
|
748
|
+
* Update the modified column's size
|
|
749
|
+
*/
|
|
703
750
|
_updateModifiedSize(node) {
|
|
704
|
-
var _a;
|
|
751
|
+
var _a, _b;
|
|
705
752
|
// Look for the modified column's header
|
|
706
753
|
const modified = DOMUtils.findElement(node, MODIFIED_ID_CLASS);
|
|
707
|
-
this._modifiedWidth =
|
|
754
|
+
this._modifiedWidth =
|
|
755
|
+
(_b = (_a = this._columnSizes['last_modified']) !== null && _a !== void 0 ? _a : modified === null || modified === void 0 ? void 0 : modified.getBoundingClientRect().width) !== null && _b !== void 0 ? _b : 83;
|
|
708
756
|
this._modifiedStyle =
|
|
709
757
|
this._modifiedWidth < 100
|
|
710
758
|
? 'narrow'
|
|
@@ -712,7 +760,20 @@ export class DirListing extends Widget {
|
|
|
712
760
|
? 'long'
|
|
713
761
|
: 'short';
|
|
714
762
|
}
|
|
715
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Rerender item nodes' modified dates, if the modified style has changed.
|
|
765
|
+
*/
|
|
766
|
+
_updateModifiedStyleAndSize() {
|
|
767
|
+
const oldModifiedStyle = this._modifiedStyle;
|
|
768
|
+
// Update both size and style
|
|
769
|
+
this._updateModifiedSize(this.node);
|
|
770
|
+
if (oldModifiedStyle !== this._modifiedStyle) {
|
|
771
|
+
this.updateModified(this._sortedItems, this._items);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Update only the modified dates.
|
|
776
|
+
*/
|
|
716
777
|
updateModified(items, nodes) {
|
|
717
778
|
items.forEach((item, i) => {
|
|
718
779
|
const node = nodes[i];
|
|
@@ -728,12 +789,15 @@ export class DirListing extends Widget {
|
|
|
728
789
|
});
|
|
729
790
|
}
|
|
730
791
|
// Update item nodes based on widget state.
|
|
731
|
-
updateNodes(items, nodes) {
|
|
792
|
+
updateNodes(items, nodes, sizeOnly = false) {
|
|
732
793
|
var _a;
|
|
733
794
|
items.forEach((item, i) => {
|
|
734
795
|
const node = nodes[i];
|
|
796
|
+
if (sizeOnly && this.renderer.updateItemSize) {
|
|
797
|
+
return this.renderer.updateItemSize(node, item, this._modifiedStyle, this._columnSizes);
|
|
798
|
+
}
|
|
735
799
|
const ft = this._manager.registry.getFileTypeForModel(item);
|
|
736
|
-
this.renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns, this.selection[item.path], this._modifiedStyle);
|
|
800
|
+
this.renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns, this.selection[item.path], this._modifiedStyle, this._columnSizes);
|
|
737
801
|
if (this.selection[item.path] &&
|
|
738
802
|
this._isCut &&
|
|
739
803
|
this._model.path === this._prevPath) {
|
|
@@ -786,7 +850,7 @@ export class DirListing extends Widget {
|
|
|
786
850
|
}
|
|
787
851
|
// Add any missing item nodes.
|
|
788
852
|
while (nodes.length < items.length) {
|
|
789
|
-
const node = renderer.createItemNode(this._hiddenColumns);
|
|
853
|
+
const node = renderer.createItemNode(this._hiddenColumns, this._columnSizes);
|
|
790
854
|
node.classList.add(ITEM_CLASS);
|
|
791
855
|
nodes.push(node);
|
|
792
856
|
content.appendChild(node);
|
|
@@ -833,14 +897,8 @@ export class DirListing extends Widget {
|
|
|
833
897
|
}
|
|
834
898
|
onResize(msg) {
|
|
835
899
|
const { width } = msg.width === -1 ? this.node.getBoundingClientRect() : msg;
|
|
836
|
-
this.
|
|
837
|
-
|
|
838
|
-
const oldModifiedStyle = this._modifiedStyle;
|
|
839
|
-
// Update both size and style
|
|
840
|
-
this._updateModifiedSize(this.node);
|
|
841
|
-
if (oldModifiedStyle !== this._modifiedStyle) {
|
|
842
|
-
this.updateModified(this._sortedItems, this._items);
|
|
843
|
-
}
|
|
900
|
+
this._width = width;
|
|
901
|
+
this._updateColumnSizes();
|
|
844
902
|
}
|
|
845
903
|
setColumnVisibility(name, visible) {
|
|
846
904
|
if (visible) {
|
|
@@ -850,7 +908,99 @@ export class DirListing extends Widget {
|
|
|
850
908
|
this._hiddenColumns.add(name);
|
|
851
909
|
}
|
|
852
910
|
this.headerNode.innerHTML = '';
|
|
853
|
-
this._renderer.populateHeaderNode(this.headerNode, this.translator, this._hiddenColumns);
|
|
911
|
+
this._renderer.populateHeaderNode(this.headerNode, this.translator, this._hiddenColumns, this._columnSizes);
|
|
912
|
+
this._updateColumnSizes();
|
|
913
|
+
}
|
|
914
|
+
_updateColumnSizes() {
|
|
915
|
+
// adjust column sizes so that they add up to the total width available, preserving ratios
|
|
916
|
+
// and removing width from the last column so that it fills the width available;
|
|
917
|
+
const visibleColumns = this._visibleColumns.map(column => ({
|
|
918
|
+
...column,
|
|
919
|
+
element: DOMUtils.findElement(this.node, column.className)
|
|
920
|
+
}));
|
|
921
|
+
// read from DOM
|
|
922
|
+
let total = 0;
|
|
923
|
+
for (const column of visibleColumns) {
|
|
924
|
+
let size = this._columnSizes[column.id];
|
|
925
|
+
if (size === null) {
|
|
926
|
+
size = column.element.getBoundingClientRect().width;
|
|
927
|
+
}
|
|
928
|
+
// restrict the minimum and maximum width
|
|
929
|
+
size = Math.max(size, column.minWidth);
|
|
930
|
+
if (this._width) {
|
|
931
|
+
let reservedForOtherColums = 0;
|
|
932
|
+
for (const other of visibleColumns) {
|
|
933
|
+
if (other.id === column.id) {
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
reservedForOtherColums += other.minWidth;
|
|
937
|
+
}
|
|
938
|
+
size = Math.min(size, this._width - reservedForOtherColums);
|
|
939
|
+
}
|
|
940
|
+
this._columnSizes[column.id] = size;
|
|
941
|
+
total += size;
|
|
942
|
+
}
|
|
943
|
+
// Ensure that total fits
|
|
944
|
+
if (this._width && total > this._width) {
|
|
945
|
+
for (const column of visibleColumns) {
|
|
946
|
+
this._columnSizes[column.id] =
|
|
947
|
+
(this._columnSizes[column.id] / total) * this._width;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
// Write to DOM
|
|
951
|
+
for (const column of visibleColumns) {
|
|
952
|
+
const size = this._columnSizes[column.id];
|
|
953
|
+
column.element.style.width = size === null ? '' : size + 'px';
|
|
954
|
+
}
|
|
955
|
+
this._updateModifiedStyleAndSize();
|
|
956
|
+
// Refresh sizes on the per item widths
|
|
957
|
+
if (this.isVisible) {
|
|
958
|
+
const items = this._items;
|
|
959
|
+
if (items.length !== 0) {
|
|
960
|
+
this.updateNodes(this._sortedItems, this._items, true);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (this._state && this._stateColumnsKey) {
|
|
964
|
+
void this._state.save(this._stateColumnsKey, {
|
|
965
|
+
sizes: this._columnSizes
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
get _visibleColumns() {
|
|
970
|
+
return DirListing.columns.filter(column => { var _a; return column.id === 'name' || !((_a = this._hiddenColumns) === null || _a === void 0 ? void 0 : _a.has(column.id)); });
|
|
971
|
+
}
|
|
972
|
+
_setColumnSize(name, size) {
|
|
973
|
+
var _a;
|
|
974
|
+
const previousSize = this._columnSizes[name];
|
|
975
|
+
if (previousSize && size && size > previousSize) {
|
|
976
|
+
// check if we can resize up
|
|
977
|
+
let total = 0;
|
|
978
|
+
let before = true;
|
|
979
|
+
for (const column of this._visibleColumns) {
|
|
980
|
+
if (column.id === name) {
|
|
981
|
+
// add proposed size for the current columns
|
|
982
|
+
total += size;
|
|
983
|
+
before = false;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (before) {
|
|
987
|
+
// add size as-is for columns before
|
|
988
|
+
const element = DOMUtils.findElement(this.node, column.className);
|
|
989
|
+
total +=
|
|
990
|
+
(_a = this._columnSizes[column.id]) !== null && _a !== void 0 ? _a : element.getBoundingClientRect().width;
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
// add minimum acceptable size for columns after
|
|
994
|
+
total += column.minWidth;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (this._width && total > this._width) {
|
|
998
|
+
// up sizing is no longer possible
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
this._columnSizes[name] = size;
|
|
1003
|
+
this._updateColumnSizes();
|
|
854
1004
|
}
|
|
855
1005
|
/**
|
|
856
1006
|
* Update the setting to sort notebooks above files.
|
|
@@ -863,6 +1013,13 @@ export class DirListing extends Widget {
|
|
|
863
1013
|
this.sort(this._sortState);
|
|
864
1014
|
}
|
|
865
1015
|
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Update the setting to allow single click navigation.
|
|
1018
|
+
* This enables opening files/directories with a single click.
|
|
1019
|
+
*/
|
|
1020
|
+
setAllowSingleClickNavigation(isEnabled) {
|
|
1021
|
+
this._allowSingleClick = isEnabled;
|
|
1022
|
+
}
|
|
866
1023
|
/**
|
|
867
1024
|
* Would this click (or other event type) hit the checkbox by default?
|
|
868
1025
|
*/
|
|
@@ -945,6 +1102,36 @@ export class DirListing extends Widget {
|
|
|
945
1102
|
}
|
|
946
1103
|
let index = Private.hitTestNodes(this._items, event);
|
|
947
1104
|
if (index === -1) {
|
|
1105
|
+
// Left mouse press for drag or resize start.
|
|
1106
|
+
if (event.button === 0) {
|
|
1107
|
+
const resizeHandle = event.target;
|
|
1108
|
+
if (resizeHandle instanceof HTMLElement &&
|
|
1109
|
+
resizeHandle.classList.contains(RESIZE_HANDLE_CLASS)) {
|
|
1110
|
+
const columnId = resizeHandle.dataset.column;
|
|
1111
|
+
if (!columnId) {
|
|
1112
|
+
throw Error('Column resize handle is missing data-column attribute');
|
|
1113
|
+
}
|
|
1114
|
+
const column = DirListing.columns.find(c => c.id === columnId);
|
|
1115
|
+
if (!column) {
|
|
1116
|
+
throw Error(`Column with identifier ${columnId} not found`);
|
|
1117
|
+
}
|
|
1118
|
+
const element = DOMUtils.findElement(this.node, column.className);
|
|
1119
|
+
resizeHandle.classList.add(ACTIVE_CLASS);
|
|
1120
|
+
const cursorOverride = Drag.overrideCursor('col-resize');
|
|
1121
|
+
this._resizeData = {
|
|
1122
|
+
pressX: event.clientX,
|
|
1123
|
+
column: columnId,
|
|
1124
|
+
initialSize: element.getBoundingClientRect().width,
|
|
1125
|
+
overrides: new DisposableDelegate(() => {
|
|
1126
|
+
cursorOverride.dispose();
|
|
1127
|
+
resizeHandle.classList.remove(ACTIVE_CLASS);
|
|
1128
|
+
})
|
|
1129
|
+
};
|
|
1130
|
+
document.addEventListener('mouseup', this, true);
|
|
1131
|
+
document.addEventListener('mousemove', this, true);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
948
1135
|
return;
|
|
949
1136
|
}
|
|
950
1137
|
this.handleFileSelect(event);
|
|
@@ -956,7 +1143,7 @@ export class DirListing extends Widget {
|
|
|
956
1143
|
if (newContext) {
|
|
957
1144
|
return;
|
|
958
1145
|
}
|
|
959
|
-
// Left mouse press for drag start.
|
|
1146
|
+
// Left mouse press for drag or resize start.
|
|
960
1147
|
if (event.button === 0) {
|
|
961
1148
|
this._dragData = {
|
|
962
1149
|
pressX: event.clientX,
|
|
@@ -966,6 +1153,9 @@ export class DirListing extends Widget {
|
|
|
966
1153
|
document.addEventListener('mouseup', this, true);
|
|
967
1154
|
document.addEventListener('mousemove', this, true);
|
|
968
1155
|
}
|
|
1156
|
+
if (this._allowSingleClick) {
|
|
1157
|
+
this.evtDblClick(event);
|
|
1158
|
+
}
|
|
969
1159
|
}
|
|
970
1160
|
/**
|
|
971
1161
|
* Handle the `'mouseup'` event for the widget.
|
|
@@ -988,6 +1178,13 @@ export class DirListing extends Widget {
|
|
|
988
1178
|
if (event.button === 0) {
|
|
989
1179
|
this._focusItem(this._focusIndex);
|
|
990
1180
|
}
|
|
1181
|
+
// Remove the resize listeners if necessary.
|
|
1182
|
+
if (this._resizeData) {
|
|
1183
|
+
this._resizeData.overrides.dispose();
|
|
1184
|
+
document.removeEventListener('mousemove', this, true);
|
|
1185
|
+
document.removeEventListener('mouseup', this, true);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
991
1188
|
// Remove the drag listeners if necessary.
|
|
992
1189
|
if (event.button !== 0 || !this._drag) {
|
|
993
1190
|
document.removeEventListener('mousemove', this, true);
|
|
@@ -1003,6 +1200,11 @@ export class DirListing extends Widget {
|
|
|
1003
1200
|
_evtMousemove(event) {
|
|
1004
1201
|
event.preventDefault();
|
|
1005
1202
|
event.stopPropagation();
|
|
1203
|
+
if (this._resizeData) {
|
|
1204
|
+
const { initialSize, column, pressX } = this._resizeData;
|
|
1205
|
+
this._setColumnSize(column, initialSize + event.clientX - pressX);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1006
1208
|
// Bail if we are the one dragging.
|
|
1007
1209
|
if (this._drag || !this._dragData) {
|
|
1008
1210
|
return;
|
|
@@ -1245,29 +1447,61 @@ export class DirListing extends Widget {
|
|
|
1245
1447
|
*/
|
|
1246
1448
|
evtNativeDrop(event) {
|
|
1247
1449
|
var _a, _b, _c;
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1450
|
+
// Prevent navigation
|
|
1451
|
+
event.preventDefault();
|
|
1452
|
+
const items = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.items;
|
|
1453
|
+
if (!items) {
|
|
1454
|
+
// Fallback to simple upload of files (if any)
|
|
1455
|
+
const files = (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.files;
|
|
1456
|
+
if (!files || files.length === 0) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
const promises = [];
|
|
1460
|
+
for (const file of files) {
|
|
1461
|
+
const promise = this._model.upload(file);
|
|
1462
|
+
promises.push(promise);
|
|
1463
|
+
}
|
|
1464
|
+
Promise.all(promises)
|
|
1465
|
+
.then(() => this._allUploaded.emit())
|
|
1466
|
+
.catch(err => {
|
|
1467
|
+
console.error('Error while uploading files: ', err);
|
|
1468
|
+
});
|
|
1254
1469
|
return;
|
|
1255
1470
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
});
|
|
1471
|
+
const uploadEntry = async (entry, path) => {
|
|
1472
|
+
if (Private.isDirectoryEntry(entry)) {
|
|
1473
|
+
const dirPath = await Private.createDirectory(this._model.manager, path, entry.name);
|
|
1474
|
+
const directoryReader = entry.createReader();
|
|
1475
|
+
const allEntries = await Private.collectEntries(directoryReader);
|
|
1476
|
+
for (const childEntry of allEntries) {
|
|
1477
|
+
await uploadEntry(childEntry, dirPath);
|
|
1478
|
+
}
|
|
1265
1479
|
}
|
|
1480
|
+
else if (Private.isFileEntry(entry)) {
|
|
1481
|
+
const file = await Private.readFile(entry);
|
|
1482
|
+
await this._model.upload(file, path);
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
const promises = [];
|
|
1486
|
+
for (const item of items) {
|
|
1487
|
+
const entry = Private.defensiveGetAsEntry(item);
|
|
1488
|
+
if (!entry) {
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
const promise = uploadEntry(entry, (_c = this._model.path) !== null && _c !== void 0 ? _c : '/');
|
|
1492
|
+
promises.push(promise);
|
|
1266
1493
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1494
|
+
Promise.all(promises)
|
|
1495
|
+
.then(() => this._allUploaded.emit())
|
|
1496
|
+
.catch(err => {
|
|
1497
|
+
console.error('Error while uploading files: ', err);
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Signal emitted on when all files were uploaded after native drag.
|
|
1502
|
+
*/
|
|
1503
|
+
get allUploaded() {
|
|
1504
|
+
return this._allUploaded;
|
|
1271
1505
|
}
|
|
1272
1506
|
/**
|
|
1273
1507
|
* Handle the `'lm-dragenter'` event for the widget.
|
|
@@ -1801,10 +2035,83 @@ export class DirListing extends Widget {
|
|
|
1801
2035
|
* The namespace for the `DirListing` class statics.
|
|
1802
2036
|
*/
|
|
1803
2037
|
(function (DirListing) {
|
|
2038
|
+
/**
|
|
2039
|
+
* Column definitions.
|
|
2040
|
+
*/
|
|
2041
|
+
DirListing.columns = [
|
|
2042
|
+
{
|
|
2043
|
+
id: 'is_selected',
|
|
2044
|
+
className: CHECKBOX_WRAPPER_CLASS,
|
|
2045
|
+
itemClassName: CHECKBOX_WRAPPER_CLASS,
|
|
2046
|
+
minWidth: 18,
|
|
2047
|
+
resizable: false,
|
|
2048
|
+
sortable: false
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
id: 'name',
|
|
2052
|
+
className: NAME_ID_CLASS,
|
|
2053
|
+
itemClassName: ITEM_NAME_COLUMN_CLASS,
|
|
2054
|
+
minWidth: 60,
|
|
2055
|
+
resizable: true,
|
|
2056
|
+
sortable: true,
|
|
2057
|
+
caretSide: 'right'
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
id: 'last_modified',
|
|
2061
|
+
className: MODIFIED_ID_CLASS,
|
|
2062
|
+
itemClassName: ITEM_MODIFIED_CLASS,
|
|
2063
|
+
minWidth: 60,
|
|
2064
|
+
resizable: true,
|
|
2065
|
+
sortable: true,
|
|
2066
|
+
caretSide: 'left'
|
|
2067
|
+
},
|
|
2068
|
+
{
|
|
2069
|
+
id: 'file_size',
|
|
2070
|
+
className: FILE_SIZE_ID_CLASS,
|
|
2071
|
+
itemClassName: ITEM_FILE_SIZE_CLASS,
|
|
2072
|
+
minWidth: 60,
|
|
2073
|
+
resizable: true,
|
|
2074
|
+
sortable: true,
|
|
2075
|
+
caretSide: 'left'
|
|
2076
|
+
}
|
|
2077
|
+
];
|
|
1804
2078
|
/**
|
|
1805
2079
|
* The default implementation of an `IRenderer`.
|
|
1806
2080
|
*/
|
|
1807
2081
|
class Renderer {
|
|
2082
|
+
constructor() {
|
|
2083
|
+
/**
|
|
2084
|
+
* Factories for individual parts of the item.
|
|
2085
|
+
*/
|
|
2086
|
+
this.itemFactories = {
|
|
2087
|
+
name: () => {
|
|
2088
|
+
const name = document.createElement('span');
|
|
2089
|
+
const icon = document.createElement('span');
|
|
2090
|
+
const text = document.createElement('span');
|
|
2091
|
+
icon.className = ITEM_ICON_CLASS;
|
|
2092
|
+
text.className = ITEM_TEXT_CLASS;
|
|
2093
|
+
name.className = ITEM_NAME_COLUMN_CLASS;
|
|
2094
|
+
name.appendChild(icon);
|
|
2095
|
+
name.appendChild(text);
|
|
2096
|
+
return name;
|
|
2097
|
+
},
|
|
2098
|
+
last_modified: () => {
|
|
2099
|
+
const modified = document.createElement('span');
|
|
2100
|
+
modified.className = ITEM_MODIFIED_CLASS;
|
|
2101
|
+
return modified;
|
|
2102
|
+
},
|
|
2103
|
+
file_size: () => {
|
|
2104
|
+
const fileSize = document.createElement('span');
|
|
2105
|
+
fileSize.className = ITEM_FILE_SIZE_CLASS;
|
|
2106
|
+
return fileSize;
|
|
2107
|
+
},
|
|
2108
|
+
is_selected: () => this.createCheckboxWrapperNode()
|
|
2109
|
+
};
|
|
2110
|
+
/**
|
|
2111
|
+
* Register of most recent arguments for last modified column update.
|
|
2112
|
+
*/
|
|
2113
|
+
this._modifiedColumnLastUpdate = new WeakMap();
|
|
2114
|
+
}
|
|
1808
2115
|
/**
|
|
1809
2116
|
* Create the DOM node for a dir listing.
|
|
1810
2117
|
*/
|
|
@@ -1827,45 +2134,43 @@ export class DirListing extends Widget {
|
|
|
1827
2134
|
*
|
|
1828
2135
|
* @param node - The header node to populate.
|
|
1829
2136
|
*/
|
|
1830
|
-
populateHeaderNode(node, translator, hiddenColumns) {
|
|
2137
|
+
populateHeaderNode(node, translator, hiddenColumns, columnsSizes) {
|
|
1831
2138
|
translator = translator || nullTranslator;
|
|
1832
2139
|
const trans = translator.load('jupyterlab');
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
name.classList.add(SELECTED_CLASS);
|
|
1842
|
-
modified.classList.add(MODIFIED_ID_CLASS);
|
|
1843
|
-
fileSize.classList.add(FILE_SIZE_ID_CLASS);
|
|
1844
|
-
narrow.classList.add(NARROW_ID_CLASS);
|
|
1845
|
-
narrow.textContent = '...';
|
|
1846
|
-
if (!(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'))) {
|
|
1847
|
-
const checkboxWrapper = this.createCheckboxWrapperNode({
|
|
2140
|
+
const elementCreators = {
|
|
2141
|
+
name: () => this.createHeaderItemNode(trans.__('Name')),
|
|
2142
|
+
last_modified: () => this._createHeaderItemNodeWithSizes({
|
|
2143
|
+
small: trans.__('Modified'),
|
|
2144
|
+
large: trans.__('Last Modified')
|
|
2145
|
+
}),
|
|
2146
|
+
file_size: () => this.createHeaderItemNode(trans.__('File Size')),
|
|
2147
|
+
is_selected: () => this.createCheckboxWrapperNode({
|
|
1848
2148
|
alwaysVisible: true,
|
|
1849
2149
|
headerNode: true
|
|
1850
|
-
})
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2150
|
+
})
|
|
2151
|
+
};
|
|
2152
|
+
const visibleColumns = DirListing.columns.filter(column => column.id === 'name' || !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has(column.id)));
|
|
2153
|
+
for (const column of visibleColumns) {
|
|
2154
|
+
const createElement = elementCreators[column.id];
|
|
2155
|
+
const element = createElement();
|
|
2156
|
+
element.classList.add(column.className);
|
|
2157
|
+
const isLastVisible = column.id === visibleColumns[visibleColumns.length - 1].id;
|
|
2158
|
+
if (columnsSizes) {
|
|
2159
|
+
const size = columnsSizes[column.id];
|
|
2160
|
+
if (!isLastVisible) {
|
|
2161
|
+
element.style.width = size + 'px';
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
node.appendChild(element);
|
|
2165
|
+
if (Private.isResizable(column) && !isLastVisible) {
|
|
2166
|
+
const resizer = document.createElement('div');
|
|
2167
|
+
resizer.classList.add(RESIZE_HANDLE_CLASS);
|
|
2168
|
+
resizer.dataset.column = column.id;
|
|
2169
|
+
node.appendChild(resizer);
|
|
2170
|
+
}
|
|
1868
2171
|
}
|
|
2172
|
+
const name = DOMUtils.findElement(node, NAME_ID_CLASS);
|
|
2173
|
+
name.classList.add(SELECTED_CLASS);
|
|
1869
2174
|
// set the initial caret icon
|
|
1870
2175
|
Private.updateCaret(DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS), 'right', 'up');
|
|
1871
2176
|
}
|
|
@@ -1879,90 +2184,51 @@ export class DirListing extends Widget {
|
|
|
1879
2184
|
* @returns The sort state of the header after the click event.
|
|
1880
2185
|
*/
|
|
1881
2186
|
handleHeaderClick(node, event) {
|
|
1882
|
-
const name = DOMUtils.findElement(node, NAME_ID_CLASS);
|
|
1883
|
-
const modified = DOMUtils.findElement(node, MODIFIED_ID_CLASS);
|
|
1884
|
-
const fileSize = DOMUtils.findElement(node, FILE_SIZE_ID_CLASS);
|
|
1885
2187
|
const state = { direction: 'ascending', key: 'name' };
|
|
1886
2188
|
const target = event.target;
|
|
1887
|
-
const
|
|
1888
|
-
|
|
1889
|
-
const
|
|
1890
|
-
|
|
1891
|
-
if (
|
|
1892
|
-
if
|
|
1893
|
-
|
|
1894
|
-
name.classList.add(DESCENDING_CLASS);
|
|
1895
|
-
Private.updateCaret(nameIcon, 'right', 'down');
|
|
1896
|
-
}
|
|
1897
|
-
else {
|
|
1898
|
-
name.classList.remove(DESCENDING_CLASS);
|
|
1899
|
-
Private.updateCaret(nameIcon, 'right', 'up');
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
else {
|
|
1903
|
-
name.classList.remove(DESCENDING_CLASS);
|
|
1904
|
-
Private.updateCaret(nameIcon, 'right', 'up');
|
|
2189
|
+
const sortableColumns = DirListing.columns.filter(Private.isSortable);
|
|
2190
|
+
console.log(sortableColumns);
|
|
2191
|
+
for (const column of sortableColumns) {
|
|
2192
|
+
const header = node.querySelector(`.${column.className}`);
|
|
2193
|
+
if (!header) {
|
|
2194
|
+
// skip if the column is hidden
|
|
2195
|
+
continue;
|
|
1905
2196
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
state.direction = 'descending';
|
|
1920
|
-
modified.classList.add(DESCENDING_CLASS);
|
|
1921
|
-
Private.updateCaret(modifiedIcon, 'left', 'down');
|
|
2197
|
+
if (header.contains(target)) {
|
|
2198
|
+
state.key = column.id;
|
|
2199
|
+
const headerIcon = DOMUtils.findElement(header, HEADER_ITEM_ICON_CLASS);
|
|
2200
|
+
if (header.classList.contains(SELECTED_CLASS)) {
|
|
2201
|
+
if (!header.classList.contains(DESCENDING_CLASS)) {
|
|
2202
|
+
state.direction = 'descending';
|
|
2203
|
+
header.classList.add(DESCENDING_CLASS);
|
|
2204
|
+
Private.updateCaret(headerIcon, column.caretSide, 'down');
|
|
2205
|
+
}
|
|
2206
|
+
else {
|
|
2207
|
+
header.classList.remove(DESCENDING_CLASS);
|
|
2208
|
+
Private.updateCaret(headerIcon, column.caretSide, 'up');
|
|
2209
|
+
}
|
|
1922
2210
|
}
|
|
1923
2211
|
else {
|
|
1924
|
-
|
|
1925
|
-
Private.updateCaret(
|
|
2212
|
+
header.classList.remove(DESCENDING_CLASS);
|
|
2213
|
+
Private.updateCaret(headerIcon, column.caretSide, 'up');
|
|
1926
2214
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
if (fileSize.contains(target)) {
|
|
1942
|
-
state.key = 'file_size';
|
|
1943
|
-
if (fileSize.classList.contains(SELECTED_CLASS)) {
|
|
1944
|
-
if (!fileSize.classList.contains(DESCENDING_CLASS)) {
|
|
1945
|
-
state.direction = 'descending';
|
|
1946
|
-
fileSize.classList.add(DESCENDING_CLASS);
|
|
1947
|
-
Private.updateCaret(fileSizeIcon, 'left', 'down');
|
|
2215
|
+
header.classList.add(SELECTED_CLASS);
|
|
2216
|
+
for (const otherColumn of sortableColumns) {
|
|
2217
|
+
if (otherColumn.id === column.id) {
|
|
2218
|
+
continue;
|
|
2219
|
+
}
|
|
2220
|
+
const otherHeader = node.querySelector(`.${otherColumn.className}`);
|
|
2221
|
+
if (!otherHeader) {
|
|
2222
|
+
// skip if hidden
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2225
|
+
otherHeader.classList.remove(SELECTED_CLASS);
|
|
2226
|
+
otherHeader.classList.remove(DESCENDING_CLASS);
|
|
2227
|
+
const otherHeaderIcon = DOMUtils.findElement(otherHeader, HEADER_ITEM_ICON_CLASS);
|
|
2228
|
+
Private.updateCaret(otherHeaderIcon, otherColumn.caretSide);
|
|
1948
2229
|
}
|
|
1949
|
-
|
|
1950
|
-
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1951
|
-
Private.updateCaret(fileSizeIcon, 'left', 'up');
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
else {
|
|
1955
|
-
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1956
|
-
Private.updateCaret(fileSizeIcon, 'left', 'up');
|
|
2230
|
+
return state;
|
|
1957
2231
|
}
|
|
1958
|
-
fileSize.classList.add(SELECTED_CLASS);
|
|
1959
|
-
name.classList.remove(SELECTED_CLASS);
|
|
1960
|
-
name.classList.remove(DESCENDING_CLASS);
|
|
1961
|
-
modified.classList.remove(SELECTED_CLASS);
|
|
1962
|
-
modified.classList.remove(DESCENDING_CLASS);
|
|
1963
|
-
Private.updateCaret(nameIcon, 'right');
|
|
1964
|
-
Private.updateCaret(modifiedIcon, 'left');
|
|
1965
|
-
return state;
|
|
1966
2232
|
}
|
|
1967
2233
|
return state;
|
|
1968
2234
|
}
|
|
@@ -1971,35 +2237,19 @@ export class DirListing extends Widget {
|
|
|
1971
2237
|
*
|
|
1972
2238
|
* @returns A new DOM node to use as a content item.
|
|
1973
2239
|
*/
|
|
1974
|
-
createItemNode(hiddenColumns) {
|
|
2240
|
+
createItemNode(hiddenColumns, columnsSizes) {
|
|
1975
2241
|
const node = document.createElement('li');
|
|
1976
|
-
const
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
}
|
|
1988
|
-
node.appendChild(icon);
|
|
1989
|
-
node.appendChild(text);
|
|
1990
|
-
node.appendChild(modified);
|
|
1991
|
-
node.appendChild(fileSize);
|
|
1992
|
-
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified')) {
|
|
1993
|
-
modified.classList.add(MODIFIED_COLUMN_HIDDEN);
|
|
1994
|
-
}
|
|
1995
|
-
else {
|
|
1996
|
-
modified.classList.remove(MODIFIED_COLUMN_HIDDEN);
|
|
1997
|
-
}
|
|
1998
|
-
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size')) {
|
|
1999
|
-
fileSize.classList.add(FILE_SIZE_COLUMN_HIDDEN);
|
|
2000
|
-
}
|
|
2001
|
-
else {
|
|
2002
|
-
fileSize.classList.remove(FILE_SIZE_COLUMN_HIDDEN);
|
|
2242
|
+
for (const column of DirListing.columns) {
|
|
2243
|
+
if (column.id != 'name' && (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has(column.id))) {
|
|
2244
|
+
continue;
|
|
2245
|
+
}
|
|
2246
|
+
const createElement = this.itemFactories[column.id];
|
|
2247
|
+
const element = createElement();
|
|
2248
|
+
node.appendChild(element);
|
|
2249
|
+
if (columnsSizes) {
|
|
2250
|
+
const size = columnsSizes[column.id];
|
|
2251
|
+
element.style.width = size + 'px';
|
|
2252
|
+
}
|
|
2003
2253
|
}
|
|
2004
2254
|
return node;
|
|
2005
2255
|
}
|
|
@@ -2053,14 +2303,25 @@ export class DirListing extends Widget {
|
|
|
2053
2303
|
* @param modifiedStyle - The date style for the modified column: narrow, short, or long
|
|
2054
2304
|
*/
|
|
2055
2305
|
updateItemModified(modified, modifiedDate, modifiedStyle) {
|
|
2056
|
-
|
|
2057
|
-
|
|
2306
|
+
// Formatting dates is expensive (0.1-0.2ms per call,
|
|
2307
|
+
// so over 150 files can easily already choke the renderer),
|
|
2308
|
+
// let's do the bare minimum check of comparing if an update
|
|
2309
|
+
// is needed using a last update cache:
|
|
2310
|
+
const previousUpdate = this._modifiedColumnLastUpdate.get(modified);
|
|
2311
|
+
if ((previousUpdate === null || previousUpdate === void 0 ? void 0 : previousUpdate.date) === modifiedDate &&
|
|
2312
|
+
(previousUpdate === null || previousUpdate === void 0 ? void 0 : previousUpdate.style) === modifiedStyle) {
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2058
2315
|
const parsedDate = new Date(modifiedDate);
|
|
2059
2316
|
// Render the date in one of multiple formats, depending on the container's size
|
|
2060
|
-
modText = Time.formatHuman(parsedDate, modifiedStyle);
|
|
2061
|
-
modTitle = Time.format(parsedDate);
|
|
2317
|
+
const modText = Time.formatHuman(parsedDate, modifiedStyle);
|
|
2318
|
+
const modTitle = Time.format(parsedDate);
|
|
2062
2319
|
modified.textContent = modText;
|
|
2063
2320
|
modified.title = modTitle;
|
|
2321
|
+
this._modifiedColumnLastUpdate.set(modified, {
|
|
2322
|
+
date: modifiedDate,
|
|
2323
|
+
style: modifiedStyle
|
|
2324
|
+
});
|
|
2064
2325
|
}
|
|
2065
2326
|
/**
|
|
2066
2327
|
* Update an item node to reflect the current state of a model.
|
|
@@ -2072,7 +2333,7 @@ export class DirListing extends Widget {
|
|
|
2072
2333
|
* @param fileType - The file type of the item, if applicable.
|
|
2073
2334
|
*
|
|
2074
2335
|
*/
|
|
2075
|
-
updateItemNode(node, model, fileType, translator, hiddenColumns, selected, modifiedStyle) {
|
|
2336
|
+
updateItemNode(node, model, fileType, translator, hiddenColumns, selected, modifiedStyle, columnsSizes) {
|
|
2076
2337
|
if (selected) {
|
|
2077
2338
|
node.classList.add(SELECTED_CLASS);
|
|
2078
2339
|
}
|
|
@@ -2083,8 +2344,9 @@ export class DirListing extends Widget {
|
|
|
2083
2344
|
const trans = translator.load('jupyterlab');
|
|
2084
2345
|
const iconContainer = DOMUtils.findElement(node, ITEM_ICON_CLASS);
|
|
2085
2346
|
const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS);
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2347
|
+
const nameColumn = DOMUtils.findElement(node, ITEM_NAME_COLUMN_CLASS);
|
|
2348
|
+
let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS);
|
|
2349
|
+
let fileSize = DOMUtils.findElement(node, ITEM_FILE_SIZE_CLASS);
|
|
2088
2350
|
const checkboxWrapper = DOMUtils.findElement(node, CHECKBOX_WRAPPER_CLASS);
|
|
2089
2351
|
const showFileCheckboxes = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'));
|
|
2090
2352
|
if (checkboxWrapper && !showFileCheckboxes) {
|
|
@@ -2092,19 +2354,23 @@ export class DirListing extends Widget {
|
|
|
2092
2354
|
}
|
|
2093
2355
|
else if (showFileCheckboxes && !checkboxWrapper) {
|
|
2094
2356
|
const checkboxWrapper = this.createCheckboxWrapperNode();
|
|
2095
|
-
|
|
2357
|
+
nameColumn.insertAdjacentElement('beforebegin', checkboxWrapper);
|
|
2096
2358
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2359
|
+
const showModified = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified'));
|
|
2360
|
+
if (modified && !showModified) {
|
|
2361
|
+
node.removeChild(modified);
|
|
2099
2362
|
}
|
|
2100
|
-
else {
|
|
2101
|
-
modified.
|
|
2363
|
+
else if (showModified && !modified) {
|
|
2364
|
+
modified = this.itemFactories.last_modified();
|
|
2365
|
+
nameColumn.insertAdjacentElement('afterend', modified);
|
|
2102
2366
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2367
|
+
const showFileSize = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size'));
|
|
2368
|
+
if (fileSize && !showFileSize) {
|
|
2369
|
+
node.removeChild(fileSize);
|
|
2105
2370
|
}
|
|
2106
|
-
else {
|
|
2107
|
-
fileSize.
|
|
2371
|
+
else if (showFileSize && !fileSize) {
|
|
2372
|
+
fileSize = this.itemFactories.file_size();
|
|
2373
|
+
(modified !== null && modified !== void 0 ? modified : nameColumn).insertAdjacentElement('afterend', fileSize);
|
|
2108
2374
|
}
|
|
2109
2375
|
// render the file item's icon
|
|
2110
2376
|
LabIcon.resolveElement({
|
|
@@ -2118,10 +2384,12 @@ export class DirListing extends Widget {
|
|
|
2118
2384
|
// add file size to pop up if its available
|
|
2119
2385
|
if (model.size !== null && model.size !== undefined) {
|
|
2120
2386
|
const fileSizeText = Private.formatFileSize(model.size, 1, 1024);
|
|
2121
|
-
fileSize
|
|
2387
|
+
if (fileSize) {
|
|
2388
|
+
fileSize.textContent = fileSizeText;
|
|
2389
|
+
}
|
|
2122
2390
|
hoverText += trans.__('\nSize: %1', Private.formatFileSize(model.size, 1, 1024));
|
|
2123
2391
|
}
|
|
2124
|
-
else {
|
|
2392
|
+
else if (fileSize) {
|
|
2125
2393
|
fileSize.textContent = '';
|
|
2126
2394
|
}
|
|
2127
2395
|
if (model.path) {
|
|
@@ -2171,7 +2439,27 @@ export class DirListing extends Widget {
|
|
|
2171
2439
|
checkbox.setAttribute('aria-label', ariaLabel);
|
|
2172
2440
|
checkbox.checked = selected !== null && selected !== void 0 ? selected : false;
|
|
2173
2441
|
}
|
|
2174
|
-
|
|
2442
|
+
this.updateItemSize(node, model, modifiedStyle, columnsSizes);
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Update size of item nodes, assuming that model has not changed.
|
|
2446
|
+
*/
|
|
2447
|
+
updateItemSize(node, model, modifiedStyle, columnsSizes) {
|
|
2448
|
+
if (columnsSizes) {
|
|
2449
|
+
for (const column of DirListing.columns) {
|
|
2450
|
+
const element = DOMUtils.findElement(node, column.itemClassName);
|
|
2451
|
+
if (!element) {
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
const sizeSpec = columnsSizes[column.id];
|
|
2455
|
+
const newWidth = sizeSpec === null ? '' : sizeSpec + 'px';
|
|
2456
|
+
if (newWidth !== element.style.width) {
|
|
2457
|
+
element.style.width = newWidth;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS);
|
|
2462
|
+
if (model.last_modified && modified) {
|
|
2175
2463
|
this.updateItemModified(modified, model.last_modified, modifiedStyle !== null && modifiedStyle !== void 0 ? modifiedStyle : 'short');
|
|
2176
2464
|
}
|
|
2177
2465
|
}
|
|
@@ -2381,6 +2669,18 @@ var Private;
|
|
|
2381
2669
|
return copy;
|
|
2382
2670
|
}
|
|
2383
2671
|
Private.sort = sort;
|
|
2672
|
+
/**
|
|
2673
|
+
* Check if the column is resizable.
|
|
2674
|
+
*/
|
|
2675
|
+
Private.isResizable = (column) => {
|
|
2676
|
+
return 'resizable' in column && column.resizable;
|
|
2677
|
+
};
|
|
2678
|
+
/**
|
|
2679
|
+
* Check if the column is sortable.
|
|
2680
|
+
*/
|
|
2681
|
+
Private.isSortable = (column) => {
|
|
2682
|
+
return 'sortable' in column && column.sortable;
|
|
2683
|
+
};
|
|
2384
2684
|
/**
|
|
2385
2685
|
* Get the index of the node at a client position, or `-1`.
|
|
2386
2686
|
*/
|
|
@@ -2416,9 +2716,14 @@ var Private;
|
|
|
2416
2716
|
(state === 'down' ? caretDownIcon : caretUpIcon).element({
|
|
2417
2717
|
container,
|
|
2418
2718
|
tag: 'span',
|
|
2419
|
-
stylesheet: 'listingHeaderItem'
|
|
2420
|
-
float
|
|
2719
|
+
stylesheet: 'listingHeaderItem'
|
|
2421
2720
|
});
|
|
2721
|
+
if (float === 'left') {
|
|
2722
|
+
container.style.order = '-1';
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
container.style.order = '';
|
|
2726
|
+
}
|
|
2422
2727
|
}
|
|
2423
2728
|
else {
|
|
2424
2729
|
LabIcon.remove(container);
|
|
@@ -2426,5 +2731,66 @@ var Private;
|
|
|
2426
2731
|
}
|
|
2427
2732
|
}
|
|
2428
2733
|
Private.updateCaret = updateCaret;
|
|
2734
|
+
async function createDirectory(manager, path, name) {
|
|
2735
|
+
const model = await manager.newUntitled({
|
|
2736
|
+
path: path,
|
|
2737
|
+
type: 'directory'
|
|
2738
|
+
});
|
|
2739
|
+
const tmpDirPath = PathExt.join(path, model.name);
|
|
2740
|
+
const dirPath = PathExt.join(path, name);
|
|
2741
|
+
try {
|
|
2742
|
+
await manager.rename(tmpDirPath, dirPath);
|
|
2743
|
+
}
|
|
2744
|
+
catch (e) {
|
|
2745
|
+
// The `dirPath` already exists, remove the temporary new directory
|
|
2746
|
+
await manager.deleteFile(tmpDirPath);
|
|
2747
|
+
}
|
|
2748
|
+
return dirPath;
|
|
2749
|
+
}
|
|
2750
|
+
Private.createDirectory = createDirectory;
|
|
2751
|
+
function isDirectoryEntry(entry) {
|
|
2752
|
+
return entry.isDirectory;
|
|
2753
|
+
}
|
|
2754
|
+
Private.isDirectoryEntry = isDirectoryEntry;
|
|
2755
|
+
function isFileEntry(entry) {
|
|
2756
|
+
return entry.isFile;
|
|
2757
|
+
}
|
|
2758
|
+
Private.isFileEntry = isFileEntry;
|
|
2759
|
+
function defensiveGetAsEntry(item) {
|
|
2760
|
+
if (item.webkitGetAsEntry) {
|
|
2761
|
+
return item.webkitGetAsEntry();
|
|
2762
|
+
}
|
|
2763
|
+
if ('getAsEntry' in item) {
|
|
2764
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
|
|
2765
|
+
return item['getAsEntry']();
|
|
2766
|
+
}
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
Private.defensiveGetAsEntry = defensiveGetAsEntry;
|
|
2770
|
+
function readEntries(reader) {
|
|
2771
|
+
return new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
|
2772
|
+
}
|
|
2773
|
+
function readFile(entry) {
|
|
2774
|
+
return new Promise((resolve, reject) => entry.file(resolve, reject));
|
|
2775
|
+
}
|
|
2776
|
+
Private.readFile = readFile;
|
|
2777
|
+
async function collectEntries(reader) {
|
|
2778
|
+
// Spec requires calling `readEntries` until these are exhausted;
|
|
2779
|
+
// in practice this is only required in Chromium-based browsers for >100 files.
|
|
2780
|
+
// https://issues.chromium.org/issues/41110876
|
|
2781
|
+
const allEntries = [];
|
|
2782
|
+
let done = false;
|
|
2783
|
+
while (!done) {
|
|
2784
|
+
const entries = await readEntries(reader);
|
|
2785
|
+
if (entries.length === 0) {
|
|
2786
|
+
done = true;
|
|
2787
|
+
}
|
|
2788
|
+
else {
|
|
2789
|
+
allEntries.push(...entries);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
return allEntries;
|
|
2793
|
+
}
|
|
2794
|
+
Private.collectEntries = collectEntries;
|
|
2429
2795
|
})(Private || (Private = {}));
|
|
2430
2796
|
//# sourceMappingURL=listing.js.map
|