@jupyterlab/filebrowser 4.3.0-alpha.2 → 4.3.0-beta.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/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 +577 -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 +794 -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,19 @@ 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
|
+
if (!node) {
|
|
798
|
+
// short-circuit in case if node is not yet ready
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
return this.renderer.updateItemSize(node, item, this._modifiedStyle, this._columnSizes);
|
|
802
|
+
}
|
|
735
803
|
const ft = this._manager.registry.getFileTypeForModel(item);
|
|
736
|
-
this.renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns, this.selection[item.path], this._modifiedStyle);
|
|
804
|
+
this.renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns, this.selection[item.path], this._modifiedStyle, this._columnSizes);
|
|
737
805
|
if (this.selection[item.path] &&
|
|
738
806
|
this._isCut &&
|
|
739
807
|
this._model.path === this._prevPath) {
|
|
@@ -786,7 +854,7 @@ export class DirListing extends Widget {
|
|
|
786
854
|
}
|
|
787
855
|
// Add any missing item nodes.
|
|
788
856
|
while (nodes.length < items.length) {
|
|
789
|
-
const node = renderer.createItemNode(this._hiddenColumns);
|
|
857
|
+
const node = renderer.createItemNode(this._hiddenColumns, this._columnSizes);
|
|
790
858
|
node.classList.add(ITEM_CLASS);
|
|
791
859
|
nodes.push(node);
|
|
792
860
|
content.appendChild(node);
|
|
@@ -833,14 +901,8 @@ export class DirListing extends Widget {
|
|
|
833
901
|
}
|
|
834
902
|
onResize(msg) {
|
|
835
903
|
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
|
-
}
|
|
904
|
+
this._width = width;
|
|
905
|
+
this._updateColumnSizes();
|
|
844
906
|
}
|
|
845
907
|
setColumnVisibility(name, visible) {
|
|
846
908
|
if (visible) {
|
|
@@ -850,7 +912,99 @@ export class DirListing extends Widget {
|
|
|
850
912
|
this._hiddenColumns.add(name);
|
|
851
913
|
}
|
|
852
914
|
this.headerNode.innerHTML = '';
|
|
853
|
-
this._renderer.populateHeaderNode(this.headerNode, this.translator, this._hiddenColumns);
|
|
915
|
+
this._renderer.populateHeaderNode(this.headerNode, this.translator, this._hiddenColumns, this._columnSizes);
|
|
916
|
+
this._updateColumnSizes();
|
|
917
|
+
}
|
|
918
|
+
_updateColumnSizes() {
|
|
919
|
+
// adjust column sizes so that they add up to the total width available, preserving ratios
|
|
920
|
+
// and removing width from the last column so that it fills the width available;
|
|
921
|
+
const visibleColumns = this._visibleColumns.map(column => ({
|
|
922
|
+
...column,
|
|
923
|
+
element: DOMUtils.findElement(this.node, column.className)
|
|
924
|
+
}));
|
|
925
|
+
// read from DOM
|
|
926
|
+
let total = 0;
|
|
927
|
+
for (const column of visibleColumns) {
|
|
928
|
+
let size = this._columnSizes[column.id];
|
|
929
|
+
if (size === null) {
|
|
930
|
+
size = column.element.getBoundingClientRect().width;
|
|
931
|
+
}
|
|
932
|
+
// restrict the minimum and maximum width
|
|
933
|
+
size = Math.max(size, column.minWidth);
|
|
934
|
+
if (this._width) {
|
|
935
|
+
let reservedForOtherColums = 0;
|
|
936
|
+
for (const other of visibleColumns) {
|
|
937
|
+
if (other.id === column.id) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
reservedForOtherColums += other.minWidth;
|
|
941
|
+
}
|
|
942
|
+
size = Math.min(size, this._width - reservedForOtherColums);
|
|
943
|
+
}
|
|
944
|
+
this._columnSizes[column.id] = size;
|
|
945
|
+
total += size;
|
|
946
|
+
}
|
|
947
|
+
// Ensure that total fits
|
|
948
|
+
if (this._width && total > this._width) {
|
|
949
|
+
for (const column of visibleColumns) {
|
|
950
|
+
this._columnSizes[column.id] =
|
|
951
|
+
(this._columnSizes[column.id] / total) * this._width;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
// Write to DOM
|
|
955
|
+
for (const column of visibleColumns) {
|
|
956
|
+
const size = this._columnSizes[column.id];
|
|
957
|
+
column.element.style.width = size === null ? '' : size + 'px';
|
|
958
|
+
}
|
|
959
|
+
this._updateModifiedStyleAndSize();
|
|
960
|
+
// Refresh sizes on the per item widths
|
|
961
|
+
if (this.isVisible) {
|
|
962
|
+
const items = this._items;
|
|
963
|
+
if (items.length !== 0) {
|
|
964
|
+
this.updateNodes(this._sortedItems, this._items, true);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if (this._state && this._stateColumnsKey) {
|
|
968
|
+
void this._state.save(this._stateColumnsKey, {
|
|
969
|
+
sizes: this._columnSizes
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
get _visibleColumns() {
|
|
974
|
+
return DirListing.columns.filter(column => { var _a; return column.id === 'name' || !((_a = this._hiddenColumns) === null || _a === void 0 ? void 0 : _a.has(column.id)); });
|
|
975
|
+
}
|
|
976
|
+
_setColumnSize(name, size) {
|
|
977
|
+
var _a;
|
|
978
|
+
const previousSize = this._columnSizes[name];
|
|
979
|
+
if (previousSize && size && size > previousSize) {
|
|
980
|
+
// check if we can resize up
|
|
981
|
+
let total = 0;
|
|
982
|
+
let before = true;
|
|
983
|
+
for (const column of this._visibleColumns) {
|
|
984
|
+
if (column.id === name) {
|
|
985
|
+
// add proposed size for the current columns
|
|
986
|
+
total += size;
|
|
987
|
+
before = false;
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
if (before) {
|
|
991
|
+
// add size as-is for columns before
|
|
992
|
+
const element = DOMUtils.findElement(this.node, column.className);
|
|
993
|
+
total +=
|
|
994
|
+
(_a = this._columnSizes[column.id]) !== null && _a !== void 0 ? _a : element.getBoundingClientRect().width;
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
// add minimum acceptable size for columns after
|
|
998
|
+
total += column.minWidth;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (this._width && total > this._width) {
|
|
1002
|
+
// up sizing is no longer possible
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
this._columnSizes[name] = size;
|
|
1007
|
+
this._updateColumnSizes();
|
|
854
1008
|
}
|
|
855
1009
|
/**
|
|
856
1010
|
* Update the setting to sort notebooks above files.
|
|
@@ -863,6 +1017,13 @@ export class DirListing extends Widget {
|
|
|
863
1017
|
this.sort(this._sortState);
|
|
864
1018
|
}
|
|
865
1019
|
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Update the setting to allow single click navigation.
|
|
1022
|
+
* This enables opening files/directories with a single click.
|
|
1023
|
+
*/
|
|
1024
|
+
setAllowSingleClickNavigation(isEnabled) {
|
|
1025
|
+
this._allowSingleClick = isEnabled;
|
|
1026
|
+
}
|
|
866
1027
|
/**
|
|
867
1028
|
* Would this click (or other event type) hit the checkbox by default?
|
|
868
1029
|
*/
|
|
@@ -945,6 +1106,36 @@ export class DirListing extends Widget {
|
|
|
945
1106
|
}
|
|
946
1107
|
let index = Private.hitTestNodes(this._items, event);
|
|
947
1108
|
if (index === -1) {
|
|
1109
|
+
// Left mouse press for drag or resize start.
|
|
1110
|
+
if (event.button === 0) {
|
|
1111
|
+
const resizeHandle = event.target;
|
|
1112
|
+
if (resizeHandle instanceof HTMLElement &&
|
|
1113
|
+
resizeHandle.classList.contains(RESIZE_HANDLE_CLASS)) {
|
|
1114
|
+
const columnId = resizeHandle.dataset.column;
|
|
1115
|
+
if (!columnId) {
|
|
1116
|
+
throw Error('Column resize handle is missing data-column attribute');
|
|
1117
|
+
}
|
|
1118
|
+
const column = DirListing.columns.find(c => c.id === columnId);
|
|
1119
|
+
if (!column) {
|
|
1120
|
+
throw Error(`Column with identifier ${columnId} not found`);
|
|
1121
|
+
}
|
|
1122
|
+
const element = DOMUtils.findElement(this.node, column.className);
|
|
1123
|
+
resizeHandle.classList.add(ACTIVE_CLASS);
|
|
1124
|
+
const cursorOverride = Drag.overrideCursor('col-resize');
|
|
1125
|
+
this._resizeData = {
|
|
1126
|
+
pressX: event.clientX,
|
|
1127
|
+
column: columnId,
|
|
1128
|
+
initialSize: element.getBoundingClientRect().width,
|
|
1129
|
+
overrides: new DisposableDelegate(() => {
|
|
1130
|
+
cursorOverride.dispose();
|
|
1131
|
+
resizeHandle.classList.remove(ACTIVE_CLASS);
|
|
1132
|
+
})
|
|
1133
|
+
};
|
|
1134
|
+
document.addEventListener('mouseup', this, true);
|
|
1135
|
+
document.addEventListener('mousemove', this, true);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
948
1139
|
return;
|
|
949
1140
|
}
|
|
950
1141
|
this.handleFileSelect(event);
|
|
@@ -956,7 +1147,7 @@ export class DirListing extends Widget {
|
|
|
956
1147
|
if (newContext) {
|
|
957
1148
|
return;
|
|
958
1149
|
}
|
|
959
|
-
// Left mouse press for drag start.
|
|
1150
|
+
// Left mouse press for drag or resize start.
|
|
960
1151
|
if (event.button === 0) {
|
|
961
1152
|
this._dragData = {
|
|
962
1153
|
pressX: event.clientX,
|
|
@@ -966,6 +1157,9 @@ export class DirListing extends Widget {
|
|
|
966
1157
|
document.addEventListener('mouseup', this, true);
|
|
967
1158
|
document.addEventListener('mousemove', this, true);
|
|
968
1159
|
}
|
|
1160
|
+
if (this._allowSingleClick) {
|
|
1161
|
+
this.evtDblClick(event);
|
|
1162
|
+
}
|
|
969
1163
|
}
|
|
970
1164
|
/**
|
|
971
1165
|
* Handle the `'mouseup'` event for the widget.
|
|
@@ -988,6 +1182,13 @@ export class DirListing extends Widget {
|
|
|
988
1182
|
if (event.button === 0) {
|
|
989
1183
|
this._focusItem(this._focusIndex);
|
|
990
1184
|
}
|
|
1185
|
+
// Remove the resize listeners if necessary.
|
|
1186
|
+
if (this._resizeData) {
|
|
1187
|
+
this._resizeData.overrides.dispose();
|
|
1188
|
+
document.removeEventListener('mousemove', this, true);
|
|
1189
|
+
document.removeEventListener('mouseup', this, true);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
991
1192
|
// Remove the drag listeners if necessary.
|
|
992
1193
|
if (event.button !== 0 || !this._drag) {
|
|
993
1194
|
document.removeEventListener('mousemove', this, true);
|
|
@@ -1003,6 +1204,11 @@ export class DirListing extends Widget {
|
|
|
1003
1204
|
_evtMousemove(event) {
|
|
1004
1205
|
event.preventDefault();
|
|
1005
1206
|
event.stopPropagation();
|
|
1207
|
+
if (this._resizeData) {
|
|
1208
|
+
const { initialSize, column, pressX } = this._resizeData;
|
|
1209
|
+
this._setColumnSize(column, initialSize + event.clientX - pressX);
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1006
1212
|
// Bail if we are the one dragging.
|
|
1007
1213
|
if (this._drag || !this._dragData) {
|
|
1008
1214
|
return;
|
|
@@ -1245,29 +1451,61 @@ export class DirListing extends Widget {
|
|
|
1245
1451
|
*/
|
|
1246
1452
|
evtNativeDrop(event) {
|
|
1247
1453
|
var _a, _b, _c;
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1454
|
+
// Prevent navigation
|
|
1455
|
+
event.preventDefault();
|
|
1456
|
+
const items = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.items;
|
|
1457
|
+
if (!items) {
|
|
1458
|
+
// Fallback to simple upload of files (if any)
|
|
1459
|
+
const files = (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.files;
|
|
1460
|
+
if (!files || files.length === 0) {
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const promises = [];
|
|
1464
|
+
for (const file of files) {
|
|
1465
|
+
const promise = this._model.upload(file);
|
|
1466
|
+
promises.push(promise);
|
|
1467
|
+
}
|
|
1468
|
+
Promise.all(promises)
|
|
1469
|
+
.then(() => this._allUploaded.emit())
|
|
1470
|
+
.catch(err => {
|
|
1471
|
+
console.error('Error while uploading files: ', err);
|
|
1472
|
+
});
|
|
1254
1473
|
return;
|
|
1255
1474
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
});
|
|
1475
|
+
const uploadEntry = async (entry, path) => {
|
|
1476
|
+
if (Private.isDirectoryEntry(entry)) {
|
|
1477
|
+
const dirPath = await Private.createDirectory(this._model.manager, path, entry.name);
|
|
1478
|
+
const directoryReader = entry.createReader();
|
|
1479
|
+
const allEntries = await Private.collectEntries(directoryReader);
|
|
1480
|
+
for (const childEntry of allEntries) {
|
|
1481
|
+
await uploadEntry(childEntry, dirPath);
|
|
1482
|
+
}
|
|
1265
1483
|
}
|
|
1484
|
+
else if (Private.isFileEntry(entry)) {
|
|
1485
|
+
const file = await Private.readFile(entry);
|
|
1486
|
+
await this._model.upload(file, path);
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
const promises = [];
|
|
1490
|
+
for (const item of items) {
|
|
1491
|
+
const entry = Private.defensiveGetAsEntry(item);
|
|
1492
|
+
if (!entry) {
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
const promise = uploadEntry(entry, (_c = this._model.path) !== null && _c !== void 0 ? _c : '/');
|
|
1496
|
+
promises.push(promise);
|
|
1266
1497
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1498
|
+
Promise.all(promises)
|
|
1499
|
+
.then(() => this._allUploaded.emit())
|
|
1500
|
+
.catch(err => {
|
|
1501
|
+
console.error('Error while uploading files: ', err);
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Signal emitted on when all files were uploaded after native drag.
|
|
1506
|
+
*/
|
|
1507
|
+
get allUploaded() {
|
|
1508
|
+
return this._allUploaded;
|
|
1271
1509
|
}
|
|
1272
1510
|
/**
|
|
1273
1511
|
* Handle the `'lm-dragenter'` event for the widget.
|
|
@@ -1801,10 +2039,83 @@ export class DirListing extends Widget {
|
|
|
1801
2039
|
* The namespace for the `DirListing` class statics.
|
|
1802
2040
|
*/
|
|
1803
2041
|
(function (DirListing) {
|
|
2042
|
+
/**
|
|
2043
|
+
* Column definitions.
|
|
2044
|
+
*/
|
|
2045
|
+
DirListing.columns = [
|
|
2046
|
+
{
|
|
2047
|
+
id: 'is_selected',
|
|
2048
|
+
className: CHECKBOX_WRAPPER_CLASS,
|
|
2049
|
+
itemClassName: CHECKBOX_WRAPPER_CLASS,
|
|
2050
|
+
minWidth: 18,
|
|
2051
|
+
resizable: false,
|
|
2052
|
+
sortable: false
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
id: 'name',
|
|
2056
|
+
className: NAME_ID_CLASS,
|
|
2057
|
+
itemClassName: ITEM_NAME_COLUMN_CLASS,
|
|
2058
|
+
minWidth: 60,
|
|
2059
|
+
resizable: true,
|
|
2060
|
+
sortable: true,
|
|
2061
|
+
caretSide: 'right'
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
id: 'last_modified',
|
|
2065
|
+
className: MODIFIED_ID_CLASS,
|
|
2066
|
+
itemClassName: ITEM_MODIFIED_CLASS,
|
|
2067
|
+
minWidth: 60,
|
|
2068
|
+
resizable: true,
|
|
2069
|
+
sortable: true,
|
|
2070
|
+
caretSide: 'left'
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
id: 'file_size',
|
|
2074
|
+
className: FILE_SIZE_ID_CLASS,
|
|
2075
|
+
itemClassName: ITEM_FILE_SIZE_CLASS,
|
|
2076
|
+
minWidth: 60,
|
|
2077
|
+
resizable: true,
|
|
2078
|
+
sortable: true,
|
|
2079
|
+
caretSide: 'left'
|
|
2080
|
+
}
|
|
2081
|
+
];
|
|
1804
2082
|
/**
|
|
1805
2083
|
* The default implementation of an `IRenderer`.
|
|
1806
2084
|
*/
|
|
1807
2085
|
class Renderer {
|
|
2086
|
+
constructor() {
|
|
2087
|
+
/**
|
|
2088
|
+
* Factories for individual parts of the item.
|
|
2089
|
+
*/
|
|
2090
|
+
this.itemFactories = {
|
|
2091
|
+
name: () => {
|
|
2092
|
+
const name = document.createElement('span');
|
|
2093
|
+
const icon = document.createElement('span');
|
|
2094
|
+
const text = document.createElement('span');
|
|
2095
|
+
icon.className = ITEM_ICON_CLASS;
|
|
2096
|
+
text.className = ITEM_TEXT_CLASS;
|
|
2097
|
+
name.className = ITEM_NAME_COLUMN_CLASS;
|
|
2098
|
+
name.appendChild(icon);
|
|
2099
|
+
name.appendChild(text);
|
|
2100
|
+
return name;
|
|
2101
|
+
},
|
|
2102
|
+
last_modified: () => {
|
|
2103
|
+
const modified = document.createElement('span');
|
|
2104
|
+
modified.className = ITEM_MODIFIED_CLASS;
|
|
2105
|
+
return modified;
|
|
2106
|
+
},
|
|
2107
|
+
file_size: () => {
|
|
2108
|
+
const fileSize = document.createElement('span');
|
|
2109
|
+
fileSize.className = ITEM_FILE_SIZE_CLASS;
|
|
2110
|
+
return fileSize;
|
|
2111
|
+
},
|
|
2112
|
+
is_selected: () => this.createCheckboxWrapperNode()
|
|
2113
|
+
};
|
|
2114
|
+
/**
|
|
2115
|
+
* Register of most recent arguments for last modified column update.
|
|
2116
|
+
*/
|
|
2117
|
+
this._modifiedColumnLastUpdate = new WeakMap();
|
|
2118
|
+
}
|
|
1808
2119
|
/**
|
|
1809
2120
|
* Create the DOM node for a dir listing.
|
|
1810
2121
|
*/
|
|
@@ -1827,45 +2138,43 @@ export class DirListing extends Widget {
|
|
|
1827
2138
|
*
|
|
1828
2139
|
* @param node - The header node to populate.
|
|
1829
2140
|
*/
|
|
1830
|
-
populateHeaderNode(node, translator, hiddenColumns) {
|
|
2141
|
+
populateHeaderNode(node, translator, hiddenColumns, columnsSizes) {
|
|
1831
2142
|
translator = translator || nullTranslator;
|
|
1832
2143
|
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({
|
|
2144
|
+
const elementCreators = {
|
|
2145
|
+
name: () => this.createHeaderItemNode(trans.__('Name')),
|
|
2146
|
+
last_modified: () => this._createHeaderItemNodeWithSizes({
|
|
2147
|
+
small: trans.__('Modified'),
|
|
2148
|
+
large: trans.__('Last Modified')
|
|
2149
|
+
}),
|
|
2150
|
+
file_size: () => this.createHeaderItemNode(trans.__('File Size')),
|
|
2151
|
+
is_selected: () => this.createCheckboxWrapperNode({
|
|
1848
2152
|
alwaysVisible: true,
|
|
1849
2153
|
headerNode: true
|
|
1850
|
-
})
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2154
|
+
})
|
|
2155
|
+
};
|
|
2156
|
+
const visibleColumns = DirListing.columns.filter(column => column.id === 'name' || !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has(column.id)));
|
|
2157
|
+
for (const column of visibleColumns) {
|
|
2158
|
+
const createElement = elementCreators[column.id];
|
|
2159
|
+
const element = createElement();
|
|
2160
|
+
element.classList.add(column.className);
|
|
2161
|
+
const isLastVisible = column.id === visibleColumns[visibleColumns.length - 1].id;
|
|
2162
|
+
if (columnsSizes) {
|
|
2163
|
+
const size = columnsSizes[column.id];
|
|
2164
|
+
if (!isLastVisible) {
|
|
2165
|
+
element.style.width = size + 'px';
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
node.appendChild(element);
|
|
2169
|
+
if (Private.isResizable(column) && !isLastVisible) {
|
|
2170
|
+
const resizer = document.createElement('div');
|
|
2171
|
+
resizer.classList.add(RESIZE_HANDLE_CLASS);
|
|
2172
|
+
resizer.dataset.column = column.id;
|
|
2173
|
+
node.appendChild(resizer);
|
|
2174
|
+
}
|
|
1868
2175
|
}
|
|
2176
|
+
const name = DOMUtils.findElement(node, NAME_ID_CLASS);
|
|
2177
|
+
name.classList.add(SELECTED_CLASS);
|
|
1869
2178
|
// set the initial caret icon
|
|
1870
2179
|
Private.updateCaret(DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS), 'right', 'up');
|
|
1871
2180
|
}
|
|
@@ -1879,90 +2188,50 @@ export class DirListing extends Widget {
|
|
|
1879
2188
|
* @returns The sort state of the header after the click event.
|
|
1880
2189
|
*/
|
|
1881
2190
|
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
2191
|
const state = { direction: 'ascending', key: 'name' };
|
|
1886
2192
|
const target = event.target;
|
|
1887
|
-
const
|
|
1888
|
-
const
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
state.direction = 'descending';
|
|
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');
|
|
2193
|
+
const sortableColumns = DirListing.columns.filter(Private.isSortable);
|
|
2194
|
+
for (const column of sortableColumns) {
|
|
2195
|
+
const header = node.querySelector(`.${column.className}`);
|
|
2196
|
+
if (!header) {
|
|
2197
|
+
// skip if the column is hidden
|
|
2198
|
+
continue;
|
|
1905
2199
|
}
|
|
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');
|
|
2200
|
+
if (header.contains(target)) {
|
|
2201
|
+
state.key = column.id;
|
|
2202
|
+
const headerIcon = DOMUtils.findElement(header, HEADER_ITEM_ICON_CLASS);
|
|
2203
|
+
if (header.classList.contains(SELECTED_CLASS)) {
|
|
2204
|
+
if (!header.classList.contains(DESCENDING_CLASS)) {
|
|
2205
|
+
state.direction = 'descending';
|
|
2206
|
+
header.classList.add(DESCENDING_CLASS);
|
|
2207
|
+
Private.updateCaret(headerIcon, column.caretSide, 'down');
|
|
2208
|
+
}
|
|
2209
|
+
else {
|
|
2210
|
+
header.classList.remove(DESCENDING_CLASS);
|
|
2211
|
+
Private.updateCaret(headerIcon, column.caretSide, 'up');
|
|
2212
|
+
}
|
|
1922
2213
|
}
|
|
1923
2214
|
else {
|
|
1924
|
-
|
|
1925
|
-
Private.updateCaret(
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
else {
|
|
1929
|
-
modified.classList.remove(DESCENDING_CLASS);
|
|
1930
|
-
Private.updateCaret(modifiedIcon, 'left', 'up');
|
|
1931
|
-
}
|
|
1932
|
-
modified.classList.add(SELECTED_CLASS);
|
|
1933
|
-
name.classList.remove(SELECTED_CLASS);
|
|
1934
|
-
name.classList.remove(DESCENDING_CLASS);
|
|
1935
|
-
fileSize.classList.remove(SELECTED_CLASS);
|
|
1936
|
-
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1937
|
-
Private.updateCaret(nameIcon, 'right');
|
|
1938
|
-
Private.updateCaret(fileSizeIcon, 'left');
|
|
1939
|
-
return state;
|
|
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.remove(DESCENDING_CLASS);
|
|
2216
|
+
Private.updateCaret(headerIcon, column.caretSide, 'up');
|
|
1948
2217
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2218
|
+
header.classList.add(SELECTED_CLASS);
|
|
2219
|
+
for (const otherColumn of sortableColumns) {
|
|
2220
|
+
if (otherColumn.id === column.id) {
|
|
2221
|
+
continue;
|
|
2222
|
+
}
|
|
2223
|
+
const otherHeader = node.querySelector(`.${otherColumn.className}`);
|
|
2224
|
+
if (!otherHeader) {
|
|
2225
|
+
// skip if hidden
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
otherHeader.classList.remove(SELECTED_CLASS);
|
|
2229
|
+
otherHeader.classList.remove(DESCENDING_CLASS);
|
|
2230
|
+
const otherHeaderIcon = DOMUtils.findElement(otherHeader, HEADER_ITEM_ICON_CLASS);
|
|
2231
|
+
Private.updateCaret(otherHeaderIcon, otherColumn.caretSide);
|
|
1952
2232
|
}
|
|
2233
|
+
return state;
|
|
1953
2234
|
}
|
|
1954
|
-
else {
|
|
1955
|
-
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1956
|
-
Private.updateCaret(fileSizeIcon, 'left', 'up');
|
|
1957
|
-
}
|
|
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
2235
|
}
|
|
1967
2236
|
return state;
|
|
1968
2237
|
}
|
|
@@ -1971,35 +2240,19 @@ export class DirListing extends Widget {
|
|
|
1971
2240
|
*
|
|
1972
2241
|
* @returns A new DOM node to use as a content item.
|
|
1973
2242
|
*/
|
|
1974
|
-
createItemNode(hiddenColumns) {
|
|
2243
|
+
createItemNode(hiddenColumns, columnsSizes) {
|
|
1975
2244
|
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);
|
|
2245
|
+
for (const column of DirListing.columns) {
|
|
2246
|
+
if (column.id != 'name' && (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has(column.id))) {
|
|
2247
|
+
continue;
|
|
2248
|
+
}
|
|
2249
|
+
const createElement = this.itemFactories[column.id];
|
|
2250
|
+
const element = createElement();
|
|
2251
|
+
node.appendChild(element);
|
|
2252
|
+
if (columnsSizes) {
|
|
2253
|
+
const size = columnsSizes[column.id];
|
|
2254
|
+
element.style.width = size + 'px';
|
|
2255
|
+
}
|
|
2003
2256
|
}
|
|
2004
2257
|
return node;
|
|
2005
2258
|
}
|
|
@@ -2053,14 +2306,25 @@ export class DirListing extends Widget {
|
|
|
2053
2306
|
* @param modifiedStyle - The date style for the modified column: narrow, short, or long
|
|
2054
2307
|
*/
|
|
2055
2308
|
updateItemModified(modified, modifiedDate, modifiedStyle) {
|
|
2056
|
-
|
|
2057
|
-
|
|
2309
|
+
// Formatting dates is expensive (0.1-0.2ms per call,
|
|
2310
|
+
// so over 150 files can easily already choke the renderer),
|
|
2311
|
+
// let's do the bare minimum check of comparing if an update
|
|
2312
|
+
// is needed using a last update cache:
|
|
2313
|
+
const previousUpdate = this._modifiedColumnLastUpdate.get(modified);
|
|
2314
|
+
if ((previousUpdate === null || previousUpdate === void 0 ? void 0 : previousUpdate.date) === modifiedDate &&
|
|
2315
|
+
(previousUpdate === null || previousUpdate === void 0 ? void 0 : previousUpdate.style) === modifiedStyle) {
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2058
2318
|
const parsedDate = new Date(modifiedDate);
|
|
2059
2319
|
// 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);
|
|
2320
|
+
const modText = Time.formatHuman(parsedDate, modifiedStyle);
|
|
2321
|
+
const modTitle = Time.format(parsedDate);
|
|
2062
2322
|
modified.textContent = modText;
|
|
2063
2323
|
modified.title = modTitle;
|
|
2324
|
+
this._modifiedColumnLastUpdate.set(modified, {
|
|
2325
|
+
date: modifiedDate,
|
|
2326
|
+
style: modifiedStyle
|
|
2327
|
+
});
|
|
2064
2328
|
}
|
|
2065
2329
|
/**
|
|
2066
2330
|
* Update an item node to reflect the current state of a model.
|
|
@@ -2072,7 +2336,7 @@ export class DirListing extends Widget {
|
|
|
2072
2336
|
* @param fileType - The file type of the item, if applicable.
|
|
2073
2337
|
*
|
|
2074
2338
|
*/
|
|
2075
|
-
updateItemNode(node, model, fileType, translator, hiddenColumns, selected, modifiedStyle) {
|
|
2339
|
+
updateItemNode(node, model, fileType, translator, hiddenColumns, selected, modifiedStyle, columnsSizes) {
|
|
2076
2340
|
if (selected) {
|
|
2077
2341
|
node.classList.add(SELECTED_CLASS);
|
|
2078
2342
|
}
|
|
@@ -2083,8 +2347,9 @@ export class DirListing extends Widget {
|
|
|
2083
2347
|
const trans = translator.load('jupyterlab');
|
|
2084
2348
|
const iconContainer = DOMUtils.findElement(node, ITEM_ICON_CLASS);
|
|
2085
2349
|
const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS);
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2350
|
+
const nameColumn = DOMUtils.findElement(node, ITEM_NAME_COLUMN_CLASS);
|
|
2351
|
+
let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS);
|
|
2352
|
+
let fileSize = DOMUtils.findElement(node, ITEM_FILE_SIZE_CLASS);
|
|
2088
2353
|
const checkboxWrapper = DOMUtils.findElement(node, CHECKBOX_WRAPPER_CLASS);
|
|
2089
2354
|
const showFileCheckboxes = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'));
|
|
2090
2355
|
if (checkboxWrapper && !showFileCheckboxes) {
|
|
@@ -2092,19 +2357,23 @@ export class DirListing extends Widget {
|
|
|
2092
2357
|
}
|
|
2093
2358
|
else if (showFileCheckboxes && !checkboxWrapper) {
|
|
2094
2359
|
const checkboxWrapper = this.createCheckboxWrapperNode();
|
|
2095
|
-
|
|
2360
|
+
nameColumn.insertAdjacentElement('beforebegin', checkboxWrapper);
|
|
2096
2361
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2362
|
+
const showModified = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified'));
|
|
2363
|
+
if (modified && !showModified) {
|
|
2364
|
+
node.removeChild(modified);
|
|
2099
2365
|
}
|
|
2100
|
-
else {
|
|
2101
|
-
modified.
|
|
2366
|
+
else if (showModified && !modified) {
|
|
2367
|
+
modified = this.itemFactories.last_modified();
|
|
2368
|
+
nameColumn.insertAdjacentElement('afterend', modified);
|
|
2102
2369
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2370
|
+
const showFileSize = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size'));
|
|
2371
|
+
if (fileSize && !showFileSize) {
|
|
2372
|
+
node.removeChild(fileSize);
|
|
2105
2373
|
}
|
|
2106
|
-
else {
|
|
2107
|
-
fileSize.
|
|
2374
|
+
else if (showFileSize && !fileSize) {
|
|
2375
|
+
fileSize = this.itemFactories.file_size();
|
|
2376
|
+
(modified !== null && modified !== void 0 ? modified : nameColumn).insertAdjacentElement('afterend', fileSize);
|
|
2108
2377
|
}
|
|
2109
2378
|
// render the file item's icon
|
|
2110
2379
|
LabIcon.resolveElement({
|
|
@@ -2118,10 +2387,12 @@ export class DirListing extends Widget {
|
|
|
2118
2387
|
// add file size to pop up if its available
|
|
2119
2388
|
if (model.size !== null && model.size !== undefined) {
|
|
2120
2389
|
const fileSizeText = Private.formatFileSize(model.size, 1, 1024);
|
|
2121
|
-
fileSize
|
|
2390
|
+
if (fileSize) {
|
|
2391
|
+
fileSize.textContent = fileSizeText;
|
|
2392
|
+
}
|
|
2122
2393
|
hoverText += trans.__('\nSize: %1', Private.formatFileSize(model.size, 1, 1024));
|
|
2123
2394
|
}
|
|
2124
|
-
else {
|
|
2395
|
+
else if (fileSize) {
|
|
2125
2396
|
fileSize.textContent = '';
|
|
2126
2397
|
}
|
|
2127
2398
|
if (model.path) {
|
|
@@ -2171,7 +2442,27 @@ export class DirListing extends Widget {
|
|
|
2171
2442
|
checkbox.setAttribute('aria-label', ariaLabel);
|
|
2172
2443
|
checkbox.checked = selected !== null && selected !== void 0 ? selected : false;
|
|
2173
2444
|
}
|
|
2174
|
-
|
|
2445
|
+
this.updateItemSize(node, model, modifiedStyle, columnsSizes);
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Update size of item nodes, assuming that model has not changed.
|
|
2449
|
+
*/
|
|
2450
|
+
updateItemSize(node, model, modifiedStyle, columnsSizes) {
|
|
2451
|
+
if (columnsSizes) {
|
|
2452
|
+
for (const column of DirListing.columns) {
|
|
2453
|
+
const element = DOMUtils.findElement(node, column.itemClassName);
|
|
2454
|
+
if (!element) {
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
2457
|
+
const sizeSpec = columnsSizes[column.id];
|
|
2458
|
+
const newWidth = sizeSpec === null ? '' : sizeSpec + 'px';
|
|
2459
|
+
if (newWidth !== element.style.width) {
|
|
2460
|
+
element.style.width = newWidth;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS);
|
|
2465
|
+
if (model.last_modified && modified) {
|
|
2175
2466
|
this.updateItemModified(modified, model.last_modified, modifiedStyle !== null && modifiedStyle !== void 0 ? modifiedStyle : 'short');
|
|
2176
2467
|
}
|
|
2177
2468
|
}
|
|
@@ -2381,6 +2672,18 @@ var Private;
|
|
|
2381
2672
|
return copy;
|
|
2382
2673
|
}
|
|
2383
2674
|
Private.sort = sort;
|
|
2675
|
+
/**
|
|
2676
|
+
* Check if the column is resizable.
|
|
2677
|
+
*/
|
|
2678
|
+
Private.isResizable = (column) => {
|
|
2679
|
+
return 'resizable' in column && column.resizable;
|
|
2680
|
+
};
|
|
2681
|
+
/**
|
|
2682
|
+
* Check if the column is sortable.
|
|
2683
|
+
*/
|
|
2684
|
+
Private.isSortable = (column) => {
|
|
2685
|
+
return 'sortable' in column && column.sortable;
|
|
2686
|
+
};
|
|
2384
2687
|
/**
|
|
2385
2688
|
* Get the index of the node at a client position, or `-1`.
|
|
2386
2689
|
*/
|
|
@@ -2416,9 +2719,14 @@ var Private;
|
|
|
2416
2719
|
(state === 'down' ? caretDownIcon : caretUpIcon).element({
|
|
2417
2720
|
container,
|
|
2418
2721
|
tag: 'span',
|
|
2419
|
-
stylesheet: 'listingHeaderItem'
|
|
2420
|
-
float
|
|
2722
|
+
stylesheet: 'listingHeaderItem'
|
|
2421
2723
|
});
|
|
2724
|
+
if (float === 'left') {
|
|
2725
|
+
container.style.order = '-1';
|
|
2726
|
+
}
|
|
2727
|
+
else {
|
|
2728
|
+
container.style.order = '';
|
|
2729
|
+
}
|
|
2422
2730
|
}
|
|
2423
2731
|
else {
|
|
2424
2732
|
LabIcon.remove(container);
|
|
@@ -2426,5 +2734,66 @@ var Private;
|
|
|
2426
2734
|
}
|
|
2427
2735
|
}
|
|
2428
2736
|
Private.updateCaret = updateCaret;
|
|
2737
|
+
async function createDirectory(manager, path, name) {
|
|
2738
|
+
const model = await manager.newUntitled({
|
|
2739
|
+
path: path,
|
|
2740
|
+
type: 'directory'
|
|
2741
|
+
});
|
|
2742
|
+
const tmpDirPath = PathExt.join(path, model.name);
|
|
2743
|
+
const dirPath = PathExt.join(path, name);
|
|
2744
|
+
try {
|
|
2745
|
+
await manager.rename(tmpDirPath, dirPath);
|
|
2746
|
+
}
|
|
2747
|
+
catch (e) {
|
|
2748
|
+
// The `dirPath` already exists, remove the temporary new directory
|
|
2749
|
+
await manager.deleteFile(tmpDirPath);
|
|
2750
|
+
}
|
|
2751
|
+
return dirPath;
|
|
2752
|
+
}
|
|
2753
|
+
Private.createDirectory = createDirectory;
|
|
2754
|
+
function isDirectoryEntry(entry) {
|
|
2755
|
+
return entry.isDirectory;
|
|
2756
|
+
}
|
|
2757
|
+
Private.isDirectoryEntry = isDirectoryEntry;
|
|
2758
|
+
function isFileEntry(entry) {
|
|
2759
|
+
return entry.isFile;
|
|
2760
|
+
}
|
|
2761
|
+
Private.isFileEntry = isFileEntry;
|
|
2762
|
+
function defensiveGetAsEntry(item) {
|
|
2763
|
+
if (item.webkitGetAsEntry) {
|
|
2764
|
+
return item.webkitGetAsEntry();
|
|
2765
|
+
}
|
|
2766
|
+
if ('getAsEntry' in item) {
|
|
2767
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
|
|
2768
|
+
return item['getAsEntry']();
|
|
2769
|
+
}
|
|
2770
|
+
return null;
|
|
2771
|
+
}
|
|
2772
|
+
Private.defensiveGetAsEntry = defensiveGetAsEntry;
|
|
2773
|
+
function readEntries(reader) {
|
|
2774
|
+
return new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
|
2775
|
+
}
|
|
2776
|
+
function readFile(entry) {
|
|
2777
|
+
return new Promise((resolve, reject) => entry.file(resolve, reject));
|
|
2778
|
+
}
|
|
2779
|
+
Private.readFile = readFile;
|
|
2780
|
+
async function collectEntries(reader) {
|
|
2781
|
+
// Spec requires calling `readEntries` until these are exhausted;
|
|
2782
|
+
// in practice this is only required in Chromium-based browsers for >100 files.
|
|
2783
|
+
// https://issues.chromium.org/issues/41110876
|
|
2784
|
+
const allEntries = [];
|
|
2785
|
+
let done = false;
|
|
2786
|
+
while (!done) {
|
|
2787
|
+
const entries = await readEntries(reader);
|
|
2788
|
+
if (entries.length === 0) {
|
|
2789
|
+
done = true;
|
|
2790
|
+
}
|
|
2791
|
+
else {
|
|
2792
|
+
allEntries.push(...entries);
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return allEntries;
|
|
2796
|
+
}
|
|
2797
|
+
Private.collectEntries = collectEntries;
|
|
2429
2798
|
})(Private || (Private = {}));
|
|
2430
2799
|
//# sourceMappingURL=listing.js.map
|