@jupyterlab/filebrowser 4.0.0-alpha.9 → 4.0.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 +5 -6
- package/lib/browser.js +14 -35
- package/lib/browser.js.map +1 -1
- package/lib/crumbs.js +2 -2
- package/lib/crumbs.js.map +1 -1
- package/lib/listing.d.ts +11 -12
- package/lib/listing.js +157 -56
- package/lib/listing.js.map +1 -1
- package/lib/model.d.ts +9 -9
- package/lib/model.js +18 -12
- package/lib/model.js.map +1 -1
- package/lib/opendialog.d.ts +2 -1
- package/lib/opendialog.js +8 -4
- package/lib/opendialog.js.map +1 -1
- package/lib/tokens.d.ts +12 -8
- package/lib/tokens.js +5 -1
- package/lib/tokens.js.map +1 -1
- package/lib/uploadstatus.js +4 -4
- package/lib/uploadstatus.js.map +1 -1
- package/package.json +28 -28
- package/src/browser.ts +444 -0
- package/src/crumbs.ts +441 -0
- package/src/index.ts +15 -0
- package/src/listing.ts +2609 -0
- package/src/model.ts +832 -0
- package/src/opendialog.ts +257 -0
- package/src/tokens.ts +122 -0
- package/src/upload.ts +119 -0
- package/src/uploadstatus.tsx +259 -0
- package/style/base.css +21 -31
package/lib/listing.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isValidFileName, renameFile } from '@jupyterlab/docmanager';
|
|
|
6
6
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
7
7
|
import { nullTranslator } from '@jupyterlab/translation';
|
|
8
8
|
import { caretDownIcon, caretUpIcon, classes, LabIcon } from '@jupyterlab/ui-components';
|
|
9
|
-
import { ArrayExt,
|
|
9
|
+
import { ArrayExt, filter, StringExt } from '@lumino/algorithm';
|
|
10
10
|
import { MimeData, PromiseDelegate } from '@lumino/coreutils';
|
|
11
11
|
import { ElementExt } from '@lumino/domutils';
|
|
12
12
|
import { Drag } from '@lumino/dragdrop';
|
|
@@ -54,6 +54,10 @@ const ITEM_ICON_CLASS = 'jp-DirListing-itemIcon';
|
|
|
54
54
|
* The class name added to the listing item modified cell.
|
|
55
55
|
*/
|
|
56
56
|
const ITEM_MODIFIED_CLASS = 'jp-DirListing-itemModified';
|
|
57
|
+
/**
|
|
58
|
+
* The class name added to the listing item file size cell.
|
|
59
|
+
*/
|
|
60
|
+
const ITEM_FILE_SIZE_CLASS = 'jp-DirListing-itemFileSize';
|
|
57
61
|
/**
|
|
58
62
|
* The class name added to the label element that wraps each item's checkbox and
|
|
59
63
|
* the header's check-all checkbox.
|
|
@@ -71,6 +75,10 @@ const NAME_ID_CLASS = 'jp-id-name';
|
|
|
71
75
|
* The class name added to the modified column header cell.
|
|
72
76
|
*/
|
|
73
77
|
const MODIFIED_ID_CLASS = 'jp-id-modified';
|
|
78
|
+
/**
|
|
79
|
+
* The class name added to the file size column header cell.
|
|
80
|
+
*/
|
|
81
|
+
const FILE_SIZE_ID_CLASS = 'jp-id-filesize';
|
|
74
82
|
/**
|
|
75
83
|
* The class name added to the narrow column header cell.
|
|
76
84
|
*/
|
|
@@ -79,6 +87,10 @@ const NARROW_ID_CLASS = 'jp-id-narrow';
|
|
|
79
87
|
* The class name added to the modified column header cell and modified item cell when hidden.
|
|
80
88
|
*/
|
|
81
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';
|
|
82
94
|
/**
|
|
83
95
|
* The mime type for a contents drag object.
|
|
84
96
|
*/
|
|
@@ -180,6 +192,8 @@ export class DirListing extends Widget {
|
|
|
180
192
|
this._manager = this._model.manager;
|
|
181
193
|
this._renderer = options.renderer || DirListing.defaultRenderer;
|
|
182
194
|
const headerNode = DOMUtils.findElement(this.node, HEADER_CLASS);
|
|
195
|
+
// hide the file size column by default
|
|
196
|
+
this._hiddenColumns.add('file_size');
|
|
183
197
|
this._renderer.populateHeaderNode(headerNode, this.translator, this._hiddenColumns);
|
|
184
198
|
this._manager.activateRequested.connect(this._onActivateRequested, this);
|
|
185
199
|
}
|
|
@@ -253,7 +267,7 @@ export class DirListing extends Widget {
|
|
|
253
267
|
* @returns A new iterator over the listing's sorted items.
|
|
254
268
|
*/
|
|
255
269
|
sortedItems() {
|
|
256
|
-
return
|
|
270
|
+
return this._sortedItems[Symbol.iterator]();
|
|
257
271
|
}
|
|
258
272
|
/**
|
|
259
273
|
* Sort the items using a sort condition.
|
|
@@ -297,9 +311,10 @@ export class DirListing extends Widget {
|
|
|
297
311
|
}
|
|
298
312
|
const basePath = this._model.path;
|
|
299
313
|
const promises = [];
|
|
300
|
-
|
|
314
|
+
for (const path of this._clipboard) {
|
|
301
315
|
if (this._isCut) {
|
|
302
|
-
const
|
|
316
|
+
const localPath = this._manager.services.contents.localPath(path);
|
|
317
|
+
const parts = localPath.split('/');
|
|
303
318
|
const name = parts[parts.length - 1];
|
|
304
319
|
const newPath = PathExt.join(basePath, name);
|
|
305
320
|
promises.push(this._model.manager.rename(path, newPath));
|
|
@@ -307,11 +322,11 @@ export class DirListing extends Widget {
|
|
|
307
322
|
else {
|
|
308
323
|
promises.push(this._model.manager.copy(path, basePath));
|
|
309
324
|
}
|
|
310
|
-
}
|
|
325
|
+
}
|
|
311
326
|
// Remove any cut modifiers.
|
|
312
|
-
|
|
327
|
+
for (const item of this._items) {
|
|
313
328
|
item.classList.remove(CUT_CLASS);
|
|
314
|
-
}
|
|
329
|
+
}
|
|
315
330
|
this._clipboard.length = 0;
|
|
316
331
|
this._isCut = false;
|
|
317
332
|
this.removeClass(CLIPBOARD_CLASS);
|
|
@@ -359,11 +374,11 @@ export class DirListing extends Widget {
|
|
|
359
374
|
duplicate() {
|
|
360
375
|
const basePath = this._model.path;
|
|
361
376
|
const promises = [];
|
|
362
|
-
|
|
377
|
+
for (const item of this.selectedItems()) {
|
|
363
378
|
if (item.type !== 'directory') {
|
|
364
379
|
promises.push(this._model.manager.copy(item.path, basePath));
|
|
365
380
|
}
|
|
366
|
-
}
|
|
381
|
+
}
|
|
367
382
|
return Promise.all(promises)
|
|
368
383
|
.then(() => {
|
|
369
384
|
return undefined;
|
|
@@ -376,7 +391,7 @@ export class DirListing extends Widget {
|
|
|
376
391
|
* Download the currently selected item(s).
|
|
377
392
|
*/
|
|
378
393
|
async download() {
|
|
379
|
-
await Promise.all(
|
|
394
|
+
await Promise.all(Array.from(this.selectedItems())
|
|
380
395
|
.filter(item => item.type !== 'directory')
|
|
381
396
|
.map(item => this._model.download(item.path)));
|
|
382
397
|
}
|
|
@@ -389,7 +404,7 @@ export class DirListing extends Widget {
|
|
|
389
404
|
const model = this._model;
|
|
390
405
|
const items = this._sortedItems;
|
|
391
406
|
const paths = items.map(item => item.path);
|
|
392
|
-
const promises =
|
|
407
|
+
const promises = Array.from(this._model.sessions())
|
|
393
408
|
.filter(session => {
|
|
394
409
|
const index = ArrayExt.firstIndexOf(paths, session.path);
|
|
395
410
|
return this.selection[items[index].path];
|
|
@@ -490,7 +505,7 @@ export class DirListing extends Widget {
|
|
|
490
505
|
*/
|
|
491
506
|
isSelected(name) {
|
|
492
507
|
const items = this._sortedItems;
|
|
493
|
-
return (
|
|
508
|
+
return (Array.from(filter(items, item => item.name === name && this.selection[item.path])).length !== 0);
|
|
494
509
|
}
|
|
495
510
|
/**
|
|
496
511
|
* Find a model given a click.
|
|
@@ -658,6 +673,7 @@ export class DirListing extends Widget {
|
|
|
658
673
|
* A handler invoked on an `'update-request'` message.
|
|
659
674
|
*/
|
|
660
675
|
onUpdateRequest(msg) {
|
|
676
|
+
var _a;
|
|
661
677
|
this._isDirty = false;
|
|
662
678
|
// Fetch common variables.
|
|
663
679
|
const items = this._sortedItems;
|
|
@@ -692,7 +708,7 @@ export class DirListing extends Widget {
|
|
|
692
708
|
const checkAllCheckbox = renderer.getCheckboxNode(this.headerNode);
|
|
693
709
|
if (checkAllCheckbox) {
|
|
694
710
|
const totalSelected = Object.keys(this.selection).length;
|
|
695
|
-
const allSelected = totalSelected === items.length;
|
|
711
|
+
const allSelected = items.length > 0 && totalSelected === items.length;
|
|
696
712
|
const someSelected = !allSelected && totalSelected > 0;
|
|
697
713
|
checkAllCheckbox.checked = allSelected;
|
|
698
714
|
checkAllCheckbox.indeterminate = someSelected;
|
|
@@ -702,21 +718,20 @@ export class DirListing extends Widget {
|
|
|
702
718
|
// next value).
|
|
703
719
|
checkAllCheckbox.dataset.checked = String(allSelected);
|
|
704
720
|
checkAllCheckbox.dataset.indeterminate = String(someSelected);
|
|
721
|
+
const trans = this.translator.load('jupyterlab');
|
|
722
|
+
checkAllCheckbox === null || checkAllCheckbox === void 0 ? void 0 : checkAllCheckbox.setAttribute('aria-label', allSelected || someSelected
|
|
723
|
+
? trans.__('Deselect all files and directories')
|
|
724
|
+
: trans.__('Select all files and directories'));
|
|
705
725
|
}
|
|
706
726
|
// Add extra classes to item nodes based on widget state.
|
|
707
727
|
items.forEach((item, i) => {
|
|
708
728
|
const node = nodes[i];
|
|
709
729
|
const ft = this._manager.registry.getFileTypeForModel(item);
|
|
710
|
-
renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns);
|
|
711
|
-
if (this.selection[item.path]
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
const checkbox = renderer.getCheckboxNode(node);
|
|
717
|
-
if (checkbox) {
|
|
718
|
-
checkbox.checked = true;
|
|
719
|
-
}
|
|
730
|
+
renderer.updateItemNode(node, item, ft, this.translator, this._hiddenColumns, this.selection[item.path]);
|
|
731
|
+
if (this.selection[item.path] &&
|
|
732
|
+
this._isCut &&
|
|
733
|
+
this._model.path === this._prevPath) {
|
|
734
|
+
node.classList.add(CUT_CLASS);
|
|
720
735
|
}
|
|
721
736
|
// add metadata to the node
|
|
722
737
|
node.setAttribute('data-isdir', item.type === 'directory' ? 'true' : 'false');
|
|
@@ -731,8 +746,7 @@ export class DirListing extends Widget {
|
|
|
731
746
|
}
|
|
732
747
|
// Handle file session statuses.
|
|
733
748
|
const paths = items.map(item => item.path);
|
|
734
|
-
|
|
735
|
-
var _a;
|
|
749
|
+
for (const session of this._model.sessions()) {
|
|
736
750
|
const index = ArrayExt.firstIndexOf(paths, session.path);
|
|
737
751
|
const node = nodes[index];
|
|
738
752
|
// Node may have been filtered out.
|
|
@@ -742,11 +756,11 @@ export class DirListing extends Widget {
|
|
|
742
756
|
node.classList.add(RUNNING_CLASS);
|
|
743
757
|
if (specs && name) {
|
|
744
758
|
const spec = specs.kernelspecs[name];
|
|
745
|
-
name = spec ? spec.display_name : 'unknown';
|
|
759
|
+
name = spec ? spec.display_name : this._trans.__('unknown');
|
|
746
760
|
}
|
|
747
761
|
node.title = this._trans.__('%1\nKernel: %2', node.title, name);
|
|
748
762
|
}
|
|
749
|
-
}
|
|
763
|
+
}
|
|
750
764
|
this._prevPath = this._model.path;
|
|
751
765
|
}
|
|
752
766
|
onResize(msg) {
|
|
@@ -1159,8 +1173,8 @@ export class DirListing extends Widget {
|
|
|
1159
1173
|
}
|
|
1160
1174
|
else {
|
|
1161
1175
|
const path = selectedPaths[0];
|
|
1162
|
-
item = find(
|
|
1163
|
-
selectedItems =
|
|
1176
|
+
item = items.find(value => value.path === path);
|
|
1177
|
+
selectedItems = this.selectedItems();
|
|
1164
1178
|
}
|
|
1165
1179
|
if (!item) {
|
|
1166
1180
|
return;
|
|
@@ -1337,9 +1351,9 @@ export class DirListing extends Widget {
|
|
|
1337
1351
|
*/
|
|
1338
1352
|
_copy() {
|
|
1339
1353
|
this._clipboard.length = 0;
|
|
1340
|
-
|
|
1354
|
+
for (const item of this.selectedItems()) {
|
|
1341
1355
|
this._clipboard.push(item.path);
|
|
1342
|
-
}
|
|
1356
|
+
}
|
|
1343
1357
|
}
|
|
1344
1358
|
/**
|
|
1345
1359
|
* Delete the files with the given paths.
|
|
@@ -1427,12 +1441,12 @@ export class DirListing extends Widget {
|
|
|
1427
1441
|
// Update the selection.
|
|
1428
1442
|
const existing = Object.keys(this.selection);
|
|
1429
1443
|
this.clearSelectedItems();
|
|
1430
|
-
|
|
1444
|
+
for (const item of this._model.items()) {
|
|
1431
1445
|
const path = item.path;
|
|
1432
1446
|
if (existing.indexOf(path) !== -1) {
|
|
1433
1447
|
this.selection[path] = true;
|
|
1434
1448
|
}
|
|
1435
|
-
}
|
|
1449
|
+
}
|
|
1436
1450
|
if (this.isVisible) {
|
|
1437
1451
|
// Update the sorted items.
|
|
1438
1452
|
this.sort(this.sortState);
|
|
@@ -1508,18 +1522,19 @@ export class DirListing extends Widget {
|
|
|
1508
1522
|
* @param node - The header node to populate.
|
|
1509
1523
|
*/
|
|
1510
1524
|
populateHeaderNode(node, translator, hiddenColumns) {
|
|
1511
|
-
var _a, _b;
|
|
1512
1525
|
translator = translator || nullTranslator;
|
|
1513
1526
|
const trans = translator.load('jupyterlab');
|
|
1514
1527
|
const name = this.createHeaderItemNode(trans.__('Name'));
|
|
1515
1528
|
const narrow = document.createElement('div');
|
|
1516
1529
|
const modified = this.createHeaderItemNode(trans.__('Last Modified'));
|
|
1530
|
+
const fileSize = this.createHeaderItemNode(trans.__('File Size'));
|
|
1517
1531
|
name.classList.add(NAME_ID_CLASS);
|
|
1518
1532
|
name.classList.add(SELECTED_CLASS);
|
|
1519
1533
|
modified.classList.add(MODIFIED_ID_CLASS);
|
|
1534
|
+
fileSize.classList.add(FILE_SIZE_ID_CLASS);
|
|
1520
1535
|
narrow.classList.add(NARROW_ID_CLASS);
|
|
1521
1536
|
narrow.textContent = '...';
|
|
1522
|
-
if (!(
|
|
1537
|
+
if (!(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'))) {
|
|
1523
1538
|
const checkboxWrapper = this.createCheckboxWrapperNode({
|
|
1524
1539
|
alwaysVisible: true
|
|
1525
1540
|
});
|
|
@@ -1528,12 +1543,19 @@ export class DirListing extends Widget {
|
|
|
1528
1543
|
node.appendChild(name);
|
|
1529
1544
|
node.appendChild(narrow);
|
|
1530
1545
|
node.appendChild(modified);
|
|
1531
|
-
|
|
1546
|
+
node.appendChild(fileSize);
|
|
1547
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified')) {
|
|
1532
1548
|
modified.classList.add(MODIFIED_COLUMN_HIDDEN);
|
|
1533
1549
|
}
|
|
1534
1550
|
else {
|
|
1535
1551
|
modified.classList.remove(MODIFIED_COLUMN_HIDDEN);
|
|
1536
1552
|
}
|
|
1553
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size')) {
|
|
1554
|
+
fileSize.classList.add(FILE_SIZE_COLUMN_HIDDEN);
|
|
1555
|
+
}
|
|
1556
|
+
else {
|
|
1557
|
+
fileSize.classList.remove(FILE_SIZE_COLUMN_HIDDEN);
|
|
1558
|
+
}
|
|
1537
1559
|
// set the initial caret icon
|
|
1538
1560
|
Private.updateCaret(DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS), 'right', 'up');
|
|
1539
1561
|
}
|
|
@@ -1549,11 +1571,13 @@ export class DirListing extends Widget {
|
|
|
1549
1571
|
handleHeaderClick(node, event) {
|
|
1550
1572
|
const name = DOMUtils.findElement(node, NAME_ID_CLASS);
|
|
1551
1573
|
const modified = DOMUtils.findElement(node, MODIFIED_ID_CLASS);
|
|
1574
|
+
const fileSize = DOMUtils.findElement(node, FILE_SIZE_ID_CLASS);
|
|
1552
1575
|
const state = { direction: 'ascending', key: 'name' };
|
|
1553
1576
|
const target = event.target;
|
|
1577
|
+
const modifiedIcon = DOMUtils.findElement(modified, HEADER_ITEM_ICON_CLASS);
|
|
1578
|
+
const fileSizeIcon = DOMUtils.findElement(fileSize, HEADER_ITEM_ICON_CLASS);
|
|
1579
|
+
const nameIcon = DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS);
|
|
1554
1580
|
if (name.contains(target)) {
|
|
1555
|
-
const modifiedIcon = DOMUtils.findElement(modified, HEADER_ITEM_ICON_CLASS);
|
|
1556
|
-
const nameIcon = DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS);
|
|
1557
1581
|
if (name.classList.contains(SELECTED_CLASS)) {
|
|
1558
1582
|
if (!name.classList.contains(DESCENDING_CLASS)) {
|
|
1559
1583
|
state.direction = 'descending';
|
|
@@ -1572,12 +1596,13 @@ export class DirListing extends Widget {
|
|
|
1572
1596
|
name.classList.add(SELECTED_CLASS);
|
|
1573
1597
|
modified.classList.remove(SELECTED_CLASS);
|
|
1574
1598
|
modified.classList.remove(DESCENDING_CLASS);
|
|
1599
|
+
fileSize.classList.remove(SELECTED_CLASS);
|
|
1600
|
+
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1575
1601
|
Private.updateCaret(modifiedIcon, 'left');
|
|
1602
|
+
Private.updateCaret(fileSizeIcon, 'left');
|
|
1576
1603
|
return state;
|
|
1577
1604
|
}
|
|
1578
1605
|
if (modified.contains(target)) {
|
|
1579
|
-
const modifiedIcon = DOMUtils.findElement(modified, HEADER_ITEM_ICON_CLASS);
|
|
1580
|
-
const nameIcon = DOMUtils.findElement(name, HEADER_ITEM_ICON_CLASS);
|
|
1581
1606
|
state.key = 'last_modified';
|
|
1582
1607
|
if (modified.classList.contains(SELECTED_CLASS)) {
|
|
1583
1608
|
if (!modified.classList.contains(DESCENDING_CLASS)) {
|
|
@@ -1597,7 +1622,36 @@ export class DirListing extends Widget {
|
|
|
1597
1622
|
modified.classList.add(SELECTED_CLASS);
|
|
1598
1623
|
name.classList.remove(SELECTED_CLASS);
|
|
1599
1624
|
name.classList.remove(DESCENDING_CLASS);
|
|
1625
|
+
fileSize.classList.remove(SELECTED_CLASS);
|
|
1626
|
+
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1627
|
+
Private.updateCaret(nameIcon, 'right');
|
|
1628
|
+
Private.updateCaret(fileSizeIcon, 'left');
|
|
1629
|
+
return state;
|
|
1630
|
+
}
|
|
1631
|
+
if (fileSize.contains(target)) {
|
|
1632
|
+
state.key = 'file_size';
|
|
1633
|
+
if (fileSize.classList.contains(SELECTED_CLASS)) {
|
|
1634
|
+
if (!fileSize.classList.contains(DESCENDING_CLASS)) {
|
|
1635
|
+
state.direction = 'descending';
|
|
1636
|
+
fileSize.classList.add(DESCENDING_CLASS);
|
|
1637
|
+
Private.updateCaret(fileSizeIcon, 'left', 'down');
|
|
1638
|
+
}
|
|
1639
|
+
else {
|
|
1640
|
+
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1641
|
+
Private.updateCaret(fileSizeIcon, 'left', 'up');
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
else {
|
|
1645
|
+
fileSize.classList.remove(DESCENDING_CLASS);
|
|
1646
|
+
Private.updateCaret(fileSizeIcon, 'left', 'up');
|
|
1647
|
+
}
|
|
1648
|
+
fileSize.classList.add(SELECTED_CLASS);
|
|
1649
|
+
name.classList.remove(SELECTED_CLASS);
|
|
1650
|
+
name.classList.remove(DESCENDING_CLASS);
|
|
1651
|
+
modified.classList.remove(SELECTED_CLASS);
|
|
1652
|
+
modified.classList.remove(DESCENDING_CLASS);
|
|
1600
1653
|
Private.updateCaret(nameIcon, 'right');
|
|
1654
|
+
Private.updateCaret(modifiedIcon, 'left');
|
|
1601
1655
|
return state;
|
|
1602
1656
|
}
|
|
1603
1657
|
return state;
|
|
@@ -1608,32 +1662,40 @@ export class DirListing extends Widget {
|
|
|
1608
1662
|
* @returns A new DOM node to use as a content item.
|
|
1609
1663
|
*/
|
|
1610
1664
|
createItemNode(hiddenColumns) {
|
|
1611
|
-
var _a, _b;
|
|
1612
1665
|
const node = document.createElement('li');
|
|
1613
1666
|
const icon = document.createElement('span');
|
|
1614
1667
|
const text = document.createElement('span');
|
|
1615
1668
|
const modified = document.createElement('span');
|
|
1669
|
+
const fileSize = document.createElement('span');
|
|
1616
1670
|
icon.className = ITEM_ICON_CLASS;
|
|
1617
1671
|
text.className = ITEM_TEXT_CLASS;
|
|
1618
1672
|
modified.className = ITEM_MODIFIED_CLASS;
|
|
1619
|
-
|
|
1673
|
+
fileSize.className = ITEM_FILE_SIZE_CLASS;
|
|
1674
|
+
if (!(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'))) {
|
|
1620
1675
|
const checkboxWrapper = this.createCheckboxWrapperNode();
|
|
1621
1676
|
node.appendChild(checkboxWrapper);
|
|
1622
1677
|
}
|
|
1623
1678
|
node.appendChild(icon);
|
|
1624
1679
|
node.appendChild(text);
|
|
1625
1680
|
node.appendChild(modified);
|
|
1681
|
+
node.appendChild(fileSize);
|
|
1626
1682
|
// Make the text note focusable so that it receives keyboard events;
|
|
1627
1683
|
// text node was specifically chosen to receive shortcuts because
|
|
1628
1684
|
// text element gets substituted with input area during file name edits
|
|
1629
1685
|
// which conveniently deactivate irrelevant shortcuts.
|
|
1630
1686
|
text.tabIndex = 0;
|
|
1631
|
-
if (
|
|
1687
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified')) {
|
|
1632
1688
|
modified.classList.add(MODIFIED_COLUMN_HIDDEN);
|
|
1633
1689
|
}
|
|
1634
1690
|
else {
|
|
1635
1691
|
modified.classList.remove(MODIFIED_COLUMN_HIDDEN);
|
|
1636
1692
|
}
|
|
1693
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size')) {
|
|
1694
|
+
fileSize.classList.add(FILE_SIZE_COLUMN_HIDDEN);
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
fileSize.classList.remove(FILE_SIZE_COLUMN_HIDDEN);
|
|
1698
|
+
}
|
|
1637
1699
|
return node;
|
|
1638
1700
|
}
|
|
1639
1701
|
/**
|
|
@@ -1680,9 +1742,10 @@ export class DirListing extends Widget {
|
|
|
1680
1742
|
* @param fileType - The file type of the item, if applicable.
|
|
1681
1743
|
*
|
|
1682
1744
|
*/
|
|
1683
|
-
updateItemNode(node, model, fileType, translator, hiddenColumns) {
|
|
1684
|
-
|
|
1685
|
-
|
|
1745
|
+
updateItemNode(node, model, fileType, translator, hiddenColumns, selected) {
|
|
1746
|
+
if (selected) {
|
|
1747
|
+
node.classList.add(SELECTED_CLASS);
|
|
1748
|
+
}
|
|
1686
1749
|
fileType =
|
|
1687
1750
|
fileType || DocumentRegistry.getDefaultTextFileType(translator);
|
|
1688
1751
|
const { icon, iconClass, name } = fileType;
|
|
@@ -1691,8 +1754,9 @@ export class DirListing extends Widget {
|
|
|
1691
1754
|
const iconContainer = DOMUtils.findElement(node, ITEM_ICON_CLASS);
|
|
1692
1755
|
const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS);
|
|
1693
1756
|
const modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS);
|
|
1757
|
+
const fileSize = DOMUtils.findElement(node, ITEM_FILE_SIZE_CLASS);
|
|
1694
1758
|
const checkboxWrapper = DOMUtils.findElement(node, CHECKBOX_WRAPPER_CLASS);
|
|
1695
|
-
const showFileCheckboxes = !(
|
|
1759
|
+
const showFileCheckboxes = !(hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('is_selected'));
|
|
1696
1760
|
if (checkboxWrapper && !showFileCheckboxes) {
|
|
1697
1761
|
node.removeChild(checkboxWrapper);
|
|
1698
1762
|
}
|
|
@@ -1700,12 +1764,18 @@ export class DirListing extends Widget {
|
|
|
1700
1764
|
const checkboxWrapper = this.createCheckboxWrapperNode();
|
|
1701
1765
|
node.insertBefore(checkboxWrapper, iconContainer);
|
|
1702
1766
|
}
|
|
1703
|
-
if (
|
|
1767
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('last_modified')) {
|
|
1704
1768
|
modified.classList.add(MODIFIED_COLUMN_HIDDEN);
|
|
1705
1769
|
}
|
|
1706
1770
|
else {
|
|
1707
1771
|
modified.classList.remove(MODIFIED_COLUMN_HIDDEN);
|
|
1708
1772
|
}
|
|
1773
|
+
if (hiddenColumns === null || hiddenColumns === void 0 ? void 0 : hiddenColumns.has('file_size')) {
|
|
1774
|
+
fileSize.classList.add(FILE_SIZE_COLUMN_HIDDEN);
|
|
1775
|
+
}
|
|
1776
|
+
else {
|
|
1777
|
+
fileSize.classList.remove(FILE_SIZE_COLUMN_HIDDEN);
|
|
1778
|
+
}
|
|
1709
1779
|
// render the file item's icon
|
|
1710
1780
|
LabIcon.resolveElement({
|
|
1711
1781
|
icon,
|
|
@@ -1717,8 +1787,13 @@ export class DirListing extends Widget {
|
|
|
1717
1787
|
let hoverText = trans.__('Name: %1', model.name);
|
|
1718
1788
|
// add file size to pop up if its available
|
|
1719
1789
|
if (model.size !== null && model.size !== undefined) {
|
|
1790
|
+
const fileSizeText = Private.formatFileSize(model.size, 1, 1024);
|
|
1791
|
+
fileSize.textContent = fileSizeText;
|
|
1720
1792
|
hoverText += trans.__('\nSize: %1', Private.formatFileSize(model.size, 1, 1024));
|
|
1721
1793
|
}
|
|
1794
|
+
else {
|
|
1795
|
+
fileSize.textContent = '';
|
|
1796
|
+
}
|
|
1722
1797
|
if (model.path) {
|
|
1723
1798
|
const dirname = PathExt.dirname(model.path);
|
|
1724
1799
|
if (dirname) {
|
|
@@ -1729,10 +1804,10 @@ export class DirListing extends Widget {
|
|
|
1729
1804
|
}
|
|
1730
1805
|
}
|
|
1731
1806
|
if (model.created) {
|
|
1732
|
-
hoverText += trans.__('\nCreated: %1', Time.format(new Date(model.created)
|
|
1807
|
+
hoverText += trans.__('\nCreated: %1', Time.format(new Date(model.created)));
|
|
1733
1808
|
}
|
|
1734
1809
|
if (model.last_modified) {
|
|
1735
|
-
hoverText += trans.__('\nModified: %1', Time.format(new Date(model.last_modified)
|
|
1810
|
+
hoverText += trans.__('\nModified: %1', Time.format(new Date(model.last_modified)));
|
|
1736
1811
|
}
|
|
1737
1812
|
hoverText += trans.__('\nWritable: %1', model.writable);
|
|
1738
1813
|
node.title = hoverText;
|
|
@@ -1744,16 +1819,33 @@ export class DirListing extends Widget {
|
|
|
1744
1819
|
node.removeAttribute('data-is-dot');
|
|
1745
1820
|
}
|
|
1746
1821
|
// If an item is being edited currently, its text node is unavailable.
|
|
1822
|
+
const indices = !model.indices ? [] : model.indices;
|
|
1823
|
+
let highlightedName = StringExt.highlight(model.name, indices, h.mark);
|
|
1747
1824
|
if (text) {
|
|
1748
|
-
const indices = !model.indices ? [] : model.indices;
|
|
1749
|
-
let highlightedName = StringExt.highlight(model.name, indices, h.mark);
|
|
1750
1825
|
VirtualDOM.render(h.span(highlightedName), text);
|
|
1751
1826
|
}
|
|
1827
|
+
// Adds an aria-label to the checkbox element.
|
|
1828
|
+
const checkbox = checkboxWrapper === null || checkboxWrapper === void 0 ? void 0 : checkboxWrapper.querySelector('input[type="checkbox"]');
|
|
1829
|
+
if (checkbox) {
|
|
1830
|
+
let ariaLabel;
|
|
1831
|
+
if (fileType.contentType === 'directory') {
|
|
1832
|
+
ariaLabel = selected
|
|
1833
|
+
? trans.__('Deselect directory "%1"', highlightedName)
|
|
1834
|
+
: trans.__('Select directory "%1"', highlightedName);
|
|
1835
|
+
}
|
|
1836
|
+
else {
|
|
1837
|
+
ariaLabel = selected
|
|
1838
|
+
? trans.__('Deselect file "%1"', highlightedName)
|
|
1839
|
+
: trans.__('Select file "%1"', highlightedName);
|
|
1840
|
+
}
|
|
1841
|
+
checkbox.setAttribute('aria-label', ariaLabel);
|
|
1842
|
+
checkbox.checked = selected !== null && selected !== void 0 ? selected : false;
|
|
1843
|
+
}
|
|
1752
1844
|
let modText = '';
|
|
1753
1845
|
let modTitle = '';
|
|
1754
1846
|
if (model.last_modified) {
|
|
1755
1847
|
modText = Time.formatHuman(new Date(model.last_modified));
|
|
1756
|
-
modTitle = Time.format(new Date(model.last_modified)
|
|
1848
|
+
modTitle = Time.format(new Date(model.last_modified));
|
|
1757
1849
|
}
|
|
1758
1850
|
modified.textContent = modText;
|
|
1759
1851
|
modified.title = modTitle;
|
|
@@ -1896,7 +1988,7 @@ var Private;
|
|
|
1896
1988
|
* Sort a list of items by sort state as a new array.
|
|
1897
1989
|
*/
|
|
1898
1990
|
function sort(items, state) {
|
|
1899
|
-
const copy =
|
|
1991
|
+
const copy = Array.from(items);
|
|
1900
1992
|
const reverse = state.direction === 'descending' ? 1 : -1;
|
|
1901
1993
|
if (state.key === 'last_modified') {
|
|
1902
1994
|
// Sort by last modified (grouping directories first)
|
|
@@ -1908,6 +2000,15 @@ var Private;
|
|
|
1908
2000
|
return t1 - t2 || (valA - valB) * reverse;
|
|
1909
2001
|
});
|
|
1910
2002
|
}
|
|
2003
|
+
else if (state.key === 'file_size') {
|
|
2004
|
+
// Sort by size (grouping directories first)
|
|
2005
|
+
copy.sort((a, b) => {
|
|
2006
|
+
var _a, _b;
|
|
2007
|
+
const t1 = a.type === 'directory' ? 0 : 1;
|
|
2008
|
+
const t2 = b.type === 'directory' ? 0 : 1;
|
|
2009
|
+
return t1 - t2 || (((_a = a.size) !== null && _a !== void 0 ? _a : 0) - ((_b = b.size) !== null && _b !== void 0 ? _b : 0)) * reverse;
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
1911
2012
|
else {
|
|
1912
2013
|
// Sort by name (grouping directories first)
|
|
1913
2014
|
copy.sort((a, b) => {
|
|
@@ -1933,10 +2034,10 @@ var Private;
|
|
|
1933
2034
|
function formatFileSize(bytes, decimalPoint, k) {
|
|
1934
2035
|
// https://www.codexworld.com/how-to/convert-file-size-bytes-kb-mb-gb-javascript/
|
|
1935
2036
|
if (bytes === 0) {
|
|
1936
|
-
return '0
|
|
2037
|
+
return '0 B';
|
|
1937
2038
|
}
|
|
1938
2039
|
const dm = decimalPoint || 2;
|
|
1939
|
-
const sizes = ['
|
|
2040
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
1940
2041
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1941
2042
|
if (i >= 0 && i < sizes.length) {
|
|
1942
2043
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|