@iobroker/adapter-react-v5 7.0.2 → 7.1.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/Components/404.d.ts +3 -2
- package/Components/404.js +3 -2
- package/Components/ColorPicker.d.ts +22 -8
- package/Components/ColorPicker.js +34 -17
- package/Components/ComplexCron.js +24 -24
- package/Components/CopyToClipboard.d.ts +10 -1
- package/Components/CopyToClipboard.js +17 -8
- package/Components/CustomModal.d.ts +1 -1
- package/Components/CustomModal.js +8 -8
- package/Components/FileBrowser.d.ts +11 -11
- package/Components/FileBrowser.js +135 -152
- package/Components/FileViewer.js +34 -23
- package/Components/Icon.d.ts +16 -2
- package/Components/Icon.js +19 -8
- package/Components/IconPicker.js +10 -14
- package/Components/IconSelector.d.ts +1 -1
- package/Components/IconSelector.js +64 -74
- package/Components/Image.d.ts +8 -4
- package/Components/Image.js +13 -32
- package/Components/Loader.d.ts +2 -2
- package/Components/Loader.js +21 -18
- package/Components/Loaders/MV.d.ts +6 -1
- package/Components/Loaders/MV.js +23 -7
- package/Components/Loaders/PT.d.ts +7 -2
- package/Components/Loaders/PT.js +20 -7
- package/Components/Loaders/Vendor.d.ts +2 -2
- package/Components/Loaders/Vendor.js +15 -7
- package/Components/Logo.js +16 -18
- package/Components/MDUtils.d.ts +1 -1
- package/Components/MDUtils.js +8 -4
- package/Components/ObjectBrowser.d.ts +40 -39
- package/Components/ObjectBrowser.js +550 -435
- package/Components/Router.d.ts +1 -3
- package/Components/Router.js +3 -1
- package/Components/SaveCloseButtons.d.ts +3 -3
- package/Components/SaveCloseButtons.js +3 -3
- package/Components/Schedule.d.ts +15 -15
- package/Components/Schedule.js +177 -154
- package/Components/SelectWithIcon.d.ts +2 -2
- package/Components/SelectWithIcon.js +45 -34
- package/Components/SimpleCron/index.js +83 -43
- package/Components/TabContainer.js +2 -2
- package/Components/TabContent.js +1 -1
- package/Components/TabHeader.js +1 -1
- package/Components/TableResize.d.ts +2 -2
- package/Components/TableResize.js +5 -5
- package/Components/TextWithIcon.d.ts +1 -1
- package/Components/TextWithIcon.js +10 -8
- package/Components/ToggleThemeMenu.d.ts +2 -2
- package/Components/ToggleThemeMenu.js +3 -3
- package/Components/TreeTable.d.ts +18 -18
- package/Components/TreeTable.js +76 -72
- package/Components/UploadImage.d.ts +2 -2
- package/Components/UploadImage.js +25 -21
- package/Components/Utils.d.ts +42 -22
- package/Components/Utils.js +66 -65
- package/Components/withWidth.d.ts +2 -2
- package/Components/withWidth.js +10 -6
- package/Dialogs/ComplexCron.d.ts +2 -2
- package/Dialogs/ComplexCron.js +3 -3
- package/Dialogs/Confirm.d.ts +4 -4
- package/Dialogs/Confirm.js +18 -8
- package/Dialogs/Cron.d.ts +3 -3
- package/Dialogs/Cron.js +21 -17
- package/Dialogs/Error.d.ts +3 -3
- package/Dialogs/Error.js +6 -4
- package/Dialogs/Message.d.ts +3 -3
- package/Dialogs/Message.js +6 -4
- package/Dialogs/SelectFile.d.ts +4 -4
- package/Dialogs/SelectFile.js +6 -4
- package/Dialogs/SelectID.d.ts +12 -10
- package/Dialogs/SelectID.js +12 -8
- package/Dialogs/SimpleCron.d.ts +2 -2
- package/Dialogs/SimpleCron.js +2 -2
- package/Dialogs/TextInput.d.ts +2 -2
- package/Dialogs/TextInput.js +3 -3
- package/GenericApp.d.ts +19 -13
- package/GenericApp.js +128 -85
- package/LegacyConnection.d.ts +240 -248
- package/LegacyConnection.js +500 -525
- package/README.md +1264 -1171
- package/Theme.d.ts +1 -1
- package/Theme.js +9 -12
- package/assets/devices.json +1 -0
- package/assets/rooms.json +1 -0
- package/craco-module-federation.js +3 -12
- package/i18n/de.json +434 -434
- package/i18n/en.json +434 -434
- package/i18n/es.json +434 -434
- package/i18n/fr.json +434 -434
- package/i18n/it.json +434 -434
- package/i18n/nl.json +434 -434
- package/i18n/pl.json +434 -434
- package/i18n/pt.json +434 -434
- package/i18n/ru.json +434 -434
- package/i18n/uk.json +434 -434
- package/i18n/zh-cn.json +434 -434
- package/i18n.d.ts +26 -19
- package/i18n.js +28 -22
- package/icons/IconAdapter.js +2 -2
- package/icons/IconAlias.js +2 -2
- package/icons/IconChannel.js +2 -2
- package/icons/IconClearFilter.js +2 -2
- package/icons/IconClosed.js +2 -2
- package/icons/IconCopy.js +2 -2
- package/icons/IconDevice.js +2 -2
- package/icons/IconDocument.js +2 -2
- package/icons/IconDocumentReadOnly.js +2 -2
- package/icons/IconExpert.js +2 -2
- package/icons/IconFx.js +2 -2
- package/icons/IconInstance.js +2 -2
- package/icons/IconLogout.js +2 -2
- package/icons/IconNoIcon.js +2 -2
- package/icons/IconOpen.d.ts +2 -2
- package/icons/IconOpen.js +2 -2
- package/icons/IconProps.d.ts +4 -3
- package/icons/IconState.d.ts +2 -2
- package/icons/IconState.js +2 -2
- package/index.css +3 -2
- package/package.json +4 -4
- package/src/Components/404.tsx +32 -31
- package/src/Components/ColorPicker.tsx +142 -114
- package/src/Components/ComplexCron.tsx +174 -137
- package/src/Components/CopyToClipboard.tsx +22 -9
- package/src/Components/CustomModal.tsx +76 -69
- package/src/Components/FileBrowser.tsx +959 -852
- package/src/Components/FileViewer.tsx +146 -127
- package/src/Components/Icon.tsx +80 -52
- package/src/Components/IconPicker.tsx +83 -67
- package/src/Components/IconSelector.tsx +159 -141
- package/src/Components/Image.tsx +43 -26
- package/src/Components/Loader.tsx +56 -32
- package/src/Components/Logo.tsx +62 -52
- package/src/Components/MDUtils.tsx +10 -6
- package/src/Components/ObjectBrowser.tsx +3198 -2478
- package/src/Components/Router.tsx +11 -11
- package/src/Components/SaveCloseButtons.tsx +43 -39
- package/src/Components/Schedule.tsx +1091 -853
- package/src/Components/SelectWithIcon.tsx +135 -93
- package/src/Components/TabContainer.tsx +22 -20
- package/src/Components/TabContent.tsx +13 -12
- package/src/Components/TabHeader.tsx +10 -9
- package/src/Components/TableResize.tsx +52 -37
- package/src/Components/TextWithIcon.tsx +30 -19
- package/src/Components/ToggleThemeMenu.tsx +31 -13
- package/src/Components/TreeTable.tsx +468 -385
- package/src/Components/UploadImage.tsx +153 -121
- package/src/Components/Utils.tsx +135 -127
- package/src/Components/loader.css +40 -31
- package/src/Components/withWidth.tsx +23 -12
- package/src/Connection.tsx +1 -3
- package/src/Dialogs/ComplexCron.tsx +55 -61
- package/src/Dialogs/Confirm.tsx +88 -65
- package/src/Dialogs/Cron.tsx +122 -112
- package/src/Dialogs/Error.tsx +37 -42
- package/src/Dialogs/Message.tsx +39 -37
- package/src/Dialogs/SelectFile.tsx +95 -85
- package/src/Dialogs/SelectID.tsx +141 -129
- package/src/Dialogs/SimpleCron.tsx +44 -44
- package/src/Dialogs/TextInput.tsx +60 -68
- package/src/GenericApp.tsx +342 -242
- package/src/LegacyConnection.tsx +972 -842
- package/src/Prompt.tsx +3 -1
- package/src/Theme.tsx +19 -26
- package/src/icons/IconAdapter.tsx +16 -14
- package/src/icons/IconAlias.tsx +16 -14
- package/src/icons/IconChannel.tsx +55 -16
- package/src/icons/IconClearFilter.tsx +17 -15
- package/src/icons/IconClosed.tsx +16 -11
- package/src/icons/IconCopy.tsx +16 -11
- package/src/icons/IconDevice.tsx +121 -22
- package/src/icons/IconDocument.tsx +16 -11
- package/src/icons/IconDocumentReadOnly.tsx +21 -12
- package/src/icons/IconExpert.tsx +20 -12
- package/src/icons/IconFx.tsx +16 -14
- package/src/icons/IconInstance.tsx +16 -14
- package/src/icons/IconLogout.tsx +20 -18
- package/src/icons/IconNoIcon.tsx +16 -14
- package/src/icons/IconOpen.tsx +17 -12
- package/src/icons/IconProps.tsx +4 -3
- package/src/icons/IconState.tsx +34 -13
- package/src/index.css +3 -2
- package/tasks.js +91 -0
- package/types.d.ts +141 -0
- package/Components/Loaders/PT.css +0 -109
- package/Components/Loaders/Vendor.css +0 -13
- package/Components/loader.css +0 -222
- package/Components/types.d.ts +0 -82
- package/assets/devices/Alarm Systems.svg +0 -19
- package/assets/devices/Amplifier.svg +0 -22
- package/assets/devices/Awnings.svg +0 -5
- package/assets/devices/Battery Status.svg +0 -5
- package/assets/devices/Ceiling Spotlights.svg +0 -16
- package/assets/devices/Chandelier.svg +0 -7
- package/assets/devices/Climate.svg +0 -12
- package/assets/devices/Coffee Makers.svg +0 -6
- package/assets/devices/Cold Water.svg +0 -31
- package/assets/devices/Computer.svg +0 -21
- package/assets/devices/Consumption.svg +0 -8
- package/assets/devices/Curtains.svg +0 -43
- package/assets/devices/Dishwashers.svg +0 -12
- package/assets/devices/Doors.svg +0 -6
- package/assets/devices/Doorstep.svg +0 -35
- package/assets/devices/Dryer.svg +0 -14
- package/assets/devices/Fan.svg +0 -20
- package/assets/devices/Floor Lamps.svg +0 -5
- package/assets/devices/Garage Doors.svg +0 -9
- package/assets/devices/Gates.svg +0 -32
- package/assets/devices/Hairdryer.svg +0 -23
- package/assets/devices/Handle.svg +0 -6
- package/assets/devices/Hanging Lamps.svg +0 -9
- package/assets/devices/Heater.svg +0 -44
- package/assets/devices/Hoods.svg +0 -12
- package/assets/devices/Hot Water.svg +0 -10
- package/assets/devices/Humidity.svg +0 -41
- package/assets/devices/Iron.svg +0 -5
- package/assets/devices/Irrigation.svg +0 -23
- package/assets/devices/Led Strip.svg +0 -31
- package/assets/devices/Light.svg +0 -30
- package/assets/devices/Lightings.svg +0 -46
- package/assets/devices/Lock.svg +0 -19
- package/assets/devices/Louvre.svg +0 -7
- package/assets/devices/Mowing Machine.svg +0 -9
- package/assets/devices/Music.svg +0 -13
- package/assets/devices/Outdoor Blinds.svg +0 -7
- package/assets/devices/People.svg +0 -19
- package/assets/devices/Pool.svg +0 -8
- package/assets/devices/Power Consumption.svg +0 -13
- package/assets/devices/Printer.svg +0 -10
- package/assets/devices/Pump.svg +0 -10
- package/assets/devices/Receiver.svg +0 -19
- package/assets/devices/Sconces.svg +0 -10
- package/assets/devices/Security.svg +0 -34
- package/assets/devices/Shading.svg +0 -5
- package/assets/devices/Shutters.svg +0 -11
- package/assets/devices/SmokeDetector.svg +0 -13
- package/assets/devices/Sockets.svg +0 -13
- package/assets/devices/Speaker.svg +0 -35
- package/assets/devices/Stove.svg +0 -12
- package/assets/devices/Table Lamps.svg +0 -12
- package/assets/devices/Temperature Sensors.svg +0 -28
- package/assets/devices/Tv.svg +0 -8
- package/assets/devices/Vacuum Cleaner.svg +0 -16
- package/assets/devices/Ventilation.svg +0 -12
- package/assets/devices/Washing Machines.svg +0 -16
- package/assets/devices/Water Consumption.svg +0 -6
- package/assets/devices/Water Heater.svg +0 -8
- package/assets/devices/Water.svg +0 -40
- package/assets/devices/Weather.svg +0 -28
- package/assets/devices/Window.svg +0 -8
- package/assets/rooms/Anteroom.svg +0 -53
- package/assets/rooms/Attic.svg +0 -21
- package/assets/rooms/Balcony.svg +0 -13
- package/assets/rooms/Barn.svg +0 -6
- package/assets/rooms/Basement.svg +0 -5
- package/assets/rooms/Bathroom.svg +0 -38
- package/assets/rooms/Bedroom.svg +0 -5
- package/assets/rooms/Boiler Room.svg +0 -13
- package/assets/rooms/Carport.svg +0 -17
- package/assets/rooms/Cellar.svg +0 -89
- package/assets/rooms/Chamber.svg +0 -9
- package/assets/rooms/Corridor.svg +0 -53
- package/assets/rooms/Dining Area.svg +0 -37
- package/assets/rooms/Dining Room.svg +0 -37
- package/assets/rooms/Dining.svg +0 -37
- package/assets/rooms/Dressing Room.svg +0 -5
- package/assets/rooms/Driveway.svg +0 -15
- package/assets/rooms/Entrance.svg +0 -44
- package/assets/rooms/Equipment Room.svg +0 -15
- package/assets/rooms/Front Yard.svg +0 -64
- package/assets/rooms/Gallery.svg +0 -14
- package/assets/rooms/Garage.svg +0 -20
- package/assets/rooms/Garden.svg +0 -13
- package/assets/rooms/Ground Floor.svg +0 -95
- package/assets/rooms/Guest Bathroom.svg +0 -33
- package/assets/rooms/Guest Room.svg +0 -5
- package/assets/rooms/Gym.svg +0 -5
- package/assets/rooms/Hall.svg +0 -19
- package/assets/rooms/Home Theater.svg +0 -8
- package/assets/rooms/Kitchen.svg +0 -18
- package/assets/rooms/Laundry Room.svg +0 -12
- package/assets/rooms/Living Area.svg +0 -11
- package/assets/rooms/Living Room.svg +0 -10
- package/assets/rooms/Locker Room.svg +0 -17
- package/assets/rooms/Nursery.svg +0 -5
- package/assets/rooms/Office.svg +0 -8
- package/assets/rooms/Outdoors.svg +0 -7
- package/assets/rooms/Playroom.svg +0 -6
- package/assets/rooms/Pool.svg +0 -8
- package/assets/rooms/Rear Wall.svg +0 -30
- package/assets/rooms/Second Floor.svg +0 -95
- package/assets/rooms/Shed.svg +0 -16
- package/assets/rooms/Sleeping Area.svg +0 -22
- package/assets/rooms/Stairway.svg +0 -5
- package/assets/rooms/Stairwell.svg +0 -15
- package/assets/rooms/Storeroom.svg +0 -5
- package/assets/rooms/Summer House.svg +0 -27
- package/assets/rooms/Swimming Pool.svg +0 -21
- package/assets/rooms/Terrace.svg +0 -7
- package/assets/rooms/Toilet.svg +0 -10
- package/assets/rooms/Upstairs.svg +0 -6
- package/assets/rooms/Wardrobe.svg +0 -60
- package/assets/rooms/Washroom.svg +0 -20
- package/assets/rooms/Wc.svg +0 -10
- package/assets/rooms/Windscreen.svg +0 -60
- package/assets/rooms/Workshop.svg +0 -23
- package/assets/rooms/Workspace.svg +0 -8
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* MIT License
|
|
5
5
|
*
|
|
6
|
-
|
|
6
|
+
*/
|
|
7
7
|
import React, { Component } from 'react';
|
|
8
8
|
import Dropzone from 'react-dropzone';
|
|
9
9
|
|
|
@@ -71,17 +71,14 @@ import IconNoIcon from '../icons/IconNoIcon';
|
|
|
71
71
|
import Icon from './Icon';
|
|
72
72
|
|
|
73
73
|
import withWidth from './withWidth';
|
|
74
|
-
import {
|
|
75
|
-
ThemeName, ThemeType,
|
|
76
|
-
Translate, IobTheme,
|
|
77
|
-
} from '../types';
|
|
74
|
+
import type { ThemeName, ThemeType, Translate, IobTheme } from '../types';
|
|
78
75
|
|
|
79
76
|
import FileViewer, { EXTENSIONS } from './FileViewer';
|
|
80
77
|
|
|
81
|
-
const ROW_HEIGHT
|
|
78
|
+
const ROW_HEIGHT = 32;
|
|
82
79
|
const BUTTON_WIDTH = 32;
|
|
83
|
-
const TILE_HEIGHT
|
|
84
|
-
const TILE_WIDTH
|
|
80
|
+
const TILE_HEIGHT = 120;
|
|
81
|
+
const TILE_WIDTH = 64;
|
|
85
82
|
|
|
86
83
|
const NOT_FOUND = 'Not found';
|
|
87
84
|
|
|
@@ -268,9 +265,7 @@ const styles: Record<string, any> = {
|
|
|
268
265
|
width: 30,
|
|
269
266
|
height: 30,
|
|
270
267
|
},
|
|
271
|
-
itemFolderTable: {
|
|
272
|
-
|
|
273
|
-
},
|
|
268
|
+
itemFolderTable: {},
|
|
274
269
|
itemFolderTemp: {
|
|
275
270
|
opacity: 0.4,
|
|
276
271
|
},
|
|
@@ -505,7 +500,6 @@ export interface FileBrowserProps {
|
|
|
505
500
|
|
|
506
501
|
restrictToFolder?: string;
|
|
507
502
|
|
|
508
|
-
// eslint-disable-next-line no-use-before-define
|
|
509
503
|
modalEditOfAccessControl?: (obj: FileBrowserClass) => React.JSX.Element | null;
|
|
510
504
|
|
|
511
505
|
allowNonRestricted?: boolean;
|
|
@@ -534,9 +528,9 @@ export interface FolderOrFileItem {
|
|
|
534
528
|
|
|
535
529
|
export type Folders = Record<string, FolderOrFileItem[]>;
|
|
536
530
|
|
|
537
|
-
function sortFolders(a: FolderOrFileItem, b: FolderOrFileItem) {
|
|
531
|
+
function sortFolders(a: FolderOrFileItem, b: FolderOrFileItem): number {
|
|
538
532
|
if (a.folder && b.folder) {
|
|
539
|
-
return a.name > b.name ? 1 :
|
|
533
|
+
return a.name > b.name ? 1 : a.name < b.name ? -1 : 0;
|
|
540
534
|
}
|
|
541
535
|
if (a.folder) {
|
|
542
536
|
return -1;
|
|
@@ -544,7 +538,7 @@ function sortFolders(a: FolderOrFileItem, b: FolderOrFileItem) {
|
|
|
544
538
|
if (b.folder) {
|
|
545
539
|
return 1;
|
|
546
540
|
}
|
|
547
|
-
return a.name > b.name ? 1 :
|
|
541
|
+
return a.name > b.name ? 1 : a.name < b.name ? -1 : 0;
|
|
548
542
|
}
|
|
549
543
|
|
|
550
544
|
interface FileBrowserState {
|
|
@@ -619,7 +613,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
619
613
|
constructor(props: FileBrowserProps) {
|
|
620
614
|
super(props);
|
|
621
615
|
|
|
622
|
-
this.localStorage = (
|
|
616
|
+
this.localStorage = (window as any)._localStorage || window.localStorage;
|
|
623
617
|
const expandedStr = this.localStorage.getItem('files.expanded') || '[]';
|
|
624
618
|
|
|
625
619
|
if (this.props.limitPath) {
|
|
@@ -635,8 +629,12 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
635
629
|
try {
|
|
636
630
|
expanded = JSON.parse(expandedStr);
|
|
637
631
|
if (this.limitToPath) {
|
|
638
|
-
expanded = expanded.filter(
|
|
639
|
-
id
|
|
632
|
+
expanded = expanded.filter(
|
|
633
|
+
id =>
|
|
634
|
+
id.startsWith(`${this.limitToPath}/`) ||
|
|
635
|
+
id === this.limitToPath ||
|
|
636
|
+
this.limitToPath?.startsWith(`${id}/`),
|
|
637
|
+
);
|
|
640
638
|
}
|
|
641
639
|
} catch {
|
|
642
640
|
expanded = [];
|
|
@@ -649,10 +647,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
649
647
|
viewType = TABLE;
|
|
650
648
|
}
|
|
651
649
|
|
|
652
|
-
let selected =
|
|
653
|
-
this.props.selected ||
|
|
654
|
-
this.localStorage.getItem('files.selected') ||
|
|
655
|
-
USER_DATA;
|
|
650
|
+
let selected = this.props.selected || this.localStorage.getItem('files.selected') || USER_DATA;
|
|
656
651
|
|
|
657
652
|
let currentDir: string;
|
|
658
653
|
|
|
@@ -678,8 +673,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
678
673
|
currentDir = selected;
|
|
679
674
|
}
|
|
680
675
|
}
|
|
681
|
-
const backgroundImage =
|
|
682
|
-
this.localStorage.getItem('files.backgroundImage') || null;
|
|
676
|
+
const backgroundImage = this.localStorage.getItem('files.backgroundImage') || null;
|
|
683
677
|
|
|
684
678
|
this.state = {
|
|
685
679
|
viewType,
|
|
@@ -733,7 +727,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
733
727
|
return null;
|
|
734
728
|
}
|
|
735
729
|
|
|
736
|
-
async loadFolders() {
|
|
730
|
+
async loadFolders(): Promise<void> {
|
|
737
731
|
this.initialReadFinished = false;
|
|
738
732
|
|
|
739
733
|
let folders = (await this.browseFolder('/')) as unknown as Folders;
|
|
@@ -768,17 +762,16 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
768
762
|
});
|
|
769
763
|
}
|
|
770
764
|
|
|
771
|
-
scrollToSelected() {
|
|
765
|
+
scrollToSelected(): void {
|
|
772
766
|
if (this.mounted) {
|
|
773
767
|
const el = document.getElementById(this.state.selected);
|
|
774
768
|
el?.scrollIntoView();
|
|
775
769
|
}
|
|
776
770
|
}
|
|
777
771
|
|
|
778
|
-
async componentDidMount() {
|
|
772
|
+
async componentDidMount(): Promise<void> {
|
|
779
773
|
this.mounted = true;
|
|
780
|
-
this.loadFolders()
|
|
781
|
-
.catch(error => console.error(`Cannot load folders: ${error}`));
|
|
774
|
+
this.loadFolders().catch(error => console.error(`Cannot load folders: ${error}`));
|
|
782
775
|
|
|
783
776
|
this.supportSubscribes = await this.props.socket.checkFeatureSupported('BINARY_STATE_EVENT');
|
|
784
777
|
if (this.supportSubscribes) {
|
|
@@ -786,30 +779,25 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
786
779
|
}
|
|
787
780
|
}
|
|
788
781
|
|
|
789
|
-
componentWillUnmount() {
|
|
782
|
+
componentWillUnmount(): void {
|
|
790
783
|
if (this.supportSubscribes) {
|
|
791
784
|
this.props.socket.unsubscribeFiles('*', '*', this.onFileChange);
|
|
792
785
|
}
|
|
793
786
|
this.mounted = false;
|
|
794
787
|
this.browseList = null;
|
|
795
788
|
this.browseListRunning = false;
|
|
796
|
-
Object.values(this._tempTimeout)
|
|
797
|
-
.forEach(timer => timer && clearTimeout(timer));
|
|
789
|
+
Object.values(this._tempTimeout).forEach(timer => timer && clearTimeout(timer));
|
|
798
790
|
this._tempTimeout = {};
|
|
799
791
|
}
|
|
800
792
|
|
|
801
|
-
browseFoldersCb(
|
|
802
|
-
foldersList: string[],
|
|
803
|
-
newFoldersNotNull: Folders,
|
|
804
|
-
cb: (folders: Folders) => void,
|
|
805
|
-
): void {
|
|
793
|
+
browseFoldersCb(foldersList: string[], newFoldersNotNull: Folders, cb: (folders: Folders) => void): void {
|
|
806
794
|
if (!foldersList?.length) {
|
|
807
795
|
cb(newFoldersNotNull);
|
|
808
796
|
} else {
|
|
809
797
|
const folder = foldersList.shift();
|
|
810
798
|
if (folder) {
|
|
811
|
-
this.browseFolder(folder, newFoldersNotNull)
|
|
812
|
-
.catch((e: Error) => console.error(`Cannot read folder ${folder}: ${e}`))
|
|
799
|
+
void this.browseFolder(folder, newFoldersNotNull)
|
|
800
|
+
.catch((e: Error) => console.error(`Cannot read folder ${folder}: ${e.message}`))
|
|
813
801
|
.then(() => {
|
|
814
802
|
setTimeout(() => this.browseFoldersCb(foldersList, newFoldersNotNull, cb), 0);
|
|
815
803
|
});
|
|
@@ -819,10 +807,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
819
807
|
}
|
|
820
808
|
}
|
|
821
809
|
|
|
822
|
-
browseFolders(
|
|
823
|
-
foldersList: string[],
|
|
824
|
-
_newFolders?: Folders | null,
|
|
825
|
-
): Promise<Folders> {
|
|
810
|
+
browseFolders(foldersList: string[], _newFolders?: Folders | null): Promise<Folders> {
|
|
826
811
|
let newFoldersNotNull: Folders;
|
|
827
812
|
if (!_newFolders) {
|
|
828
813
|
newFoldersNotNull = {};
|
|
@@ -878,7 +863,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
878
863
|
if (item) {
|
|
879
864
|
const resolve = item.resolve;
|
|
880
865
|
item.resolve = null;
|
|
881
|
-
item.reject
|
|
866
|
+
item.reject = null;
|
|
882
867
|
item.adapter = null;
|
|
883
868
|
item.relPath = null;
|
|
884
869
|
if (resolve) {
|
|
@@ -949,11 +934,13 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
949
934
|
if (newFoldersNotNull[folderId] && !force) {
|
|
950
935
|
if (!_checkEmpty) {
|
|
951
936
|
return new Promise((resolve, reject) => {
|
|
952
|
-
Promise.all(
|
|
953
|
-
|
|
954
|
-
.
|
|
937
|
+
Promise.all(
|
|
938
|
+
newFoldersNotNull[folderId]
|
|
939
|
+
.filter(item => item.folder)
|
|
940
|
+
.map(item => this.browseFolder(item.id, newFoldersNotNull, true).catch(() => undefined)),
|
|
941
|
+
)
|
|
955
942
|
.then(() => resolve(newFoldersNotNull))
|
|
956
|
-
.catch(error => reject(error));
|
|
943
|
+
.catch(error => reject(new Error(error)));
|
|
957
944
|
});
|
|
958
945
|
}
|
|
959
946
|
|
|
@@ -986,17 +973,17 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
986
973
|
}
|
|
987
974
|
|
|
988
975
|
const item: FolderOrFileItem = {
|
|
989
|
-
id:
|
|
990
|
-
name:
|
|
991
|
-
title:
|
|
992
|
-
meta:
|
|
993
|
-
from:
|
|
994
|
-
ts:
|
|
995
|
-
color:
|
|
996
|
-
icon:
|
|
976
|
+
id: obj._id,
|
|
977
|
+
name: obj._id,
|
|
978
|
+
title: (obj.common && obj.common.name) || obj._id,
|
|
979
|
+
meta: true,
|
|
980
|
+
from: obj.from,
|
|
981
|
+
ts: obj.ts,
|
|
982
|
+
color: obj.common && obj.common.color,
|
|
983
|
+
icon: obj.common && obj.common.icon,
|
|
997
984
|
folder: true,
|
|
998
|
-
acl:
|
|
999
|
-
level:
|
|
985
|
+
acl: obj.acl,
|
|
986
|
+
level: 0,
|
|
1000
987
|
};
|
|
1001
988
|
|
|
1002
989
|
if (item.id === USER_DATA) {
|
|
@@ -1007,7 +994,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1007
994
|
}
|
|
1008
995
|
});
|
|
1009
996
|
|
|
1010
|
-
_folders.sort((a, b) => (a.id > b.id ? 1 :
|
|
997
|
+
_folders.sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0));
|
|
1011
998
|
if (!this.limitToObjectID || this.limitToObjectID === USER_DATA) {
|
|
1012
999
|
if (userData) {
|
|
1013
1000
|
_folders.unshift(userData);
|
|
@@ -1017,15 +1004,16 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1017
1004
|
newFoldersNotNull[folderId || '/'] = _folders;
|
|
1018
1005
|
|
|
1019
1006
|
if (!_checkEmpty) {
|
|
1020
|
-
return Promise.all(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1007
|
+
return Promise.all(
|
|
1008
|
+
_folders
|
|
1009
|
+
.filter(item => item.folder)
|
|
1010
|
+
.map(item => this.browseFolder(item.id, newFoldersNotNull, true).catch(() => undefined)),
|
|
1011
|
+
).then(() => newFoldersNotNull);
|
|
1025
1012
|
}
|
|
1026
|
-
} catch (e) {
|
|
1013
|
+
} catch (e: unknown) {
|
|
1014
|
+
const knownError = e as Error;
|
|
1027
1015
|
if (this.initialReadFinished) {
|
|
1028
|
-
window.alert(`Cannot read meta items: ${
|
|
1016
|
+
window.alert(`Cannot read meta items: ${knownError.message}`);
|
|
1029
1017
|
}
|
|
1030
1018
|
newFoldersNotNull[folderId || '/'] = [];
|
|
1031
1019
|
}
|
|
@@ -1044,13 +1032,13 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1044
1032
|
|
|
1045
1033
|
files.forEach(file => {
|
|
1046
1034
|
const item: FolderOrFileItem = {
|
|
1047
|
-
id:
|
|
1048
|
-
ext:
|
|
1049
|
-
folder:
|
|
1050
|
-
name:
|
|
1051
|
-
size:
|
|
1035
|
+
id: `${folderId}/${file.file}`,
|
|
1036
|
+
ext: Utils.getFileExtension(file.file),
|
|
1037
|
+
folder: file.isDir,
|
|
1038
|
+
name: file.file,
|
|
1039
|
+
size: file.stats?.size,
|
|
1052
1040
|
modified: file.modifiedAt,
|
|
1053
|
-
acl:
|
|
1041
|
+
acl: file.acl,
|
|
1054
1042
|
level,
|
|
1055
1043
|
};
|
|
1056
1044
|
|
|
@@ -1085,14 +1073,16 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1085
1073
|
newFoldersNotNull[folderId] = _folders;
|
|
1086
1074
|
|
|
1087
1075
|
if (!_checkEmpty) {
|
|
1088
|
-
return Promise.all(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1076
|
+
return Promise.all(
|
|
1077
|
+
_folders
|
|
1078
|
+
.filter(item => item.folder)
|
|
1079
|
+
.map(item => this.browseFolder(item.id, newFoldersNotNull, true)),
|
|
1080
|
+
).then(() => newFoldersNotNull);
|
|
1092
1081
|
}
|
|
1093
|
-
} catch (e) {
|
|
1082
|
+
} catch (e: unknown) {
|
|
1083
|
+
const knownError = e as Error;
|
|
1094
1084
|
if (this.initialReadFinished) {
|
|
1095
|
-
window.alert(`Cannot read ${adapter}${relPath ? `/${relPath}` : ''}: ${
|
|
1085
|
+
window.alert(`Cannot read ${adapter}${relPath ? `/${relPath}` : ''}: ${knownError?.message}`);
|
|
1096
1086
|
}
|
|
1097
1087
|
newFoldersNotNull[folderId] = [];
|
|
1098
1088
|
}
|
|
@@ -1100,7 +1090,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1100
1090
|
return newFoldersNotNull;
|
|
1101
1091
|
}
|
|
1102
1092
|
|
|
1103
|
-
toggleFolder(item: FolderOrFileItem, e: React.MouseEvent<Element>) {
|
|
1093
|
+
toggleFolder(item: FolderOrFileItem, e: React.MouseEvent<Element>): void {
|
|
1104
1094
|
e?.stopPropagation();
|
|
1105
1095
|
const expanded = [...this.state.expanded];
|
|
1106
1096
|
const pos = expanded.indexOf(item.id);
|
|
@@ -1113,11 +1103,13 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1113
1103
|
if (!item.temp) {
|
|
1114
1104
|
this.browseFolder(item.id)
|
|
1115
1105
|
.then(folders => this.setState({ expanded, folders }))
|
|
1116
|
-
.catch(err =>
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1106
|
+
.catch(err =>
|
|
1107
|
+
window.alert(
|
|
1108
|
+
err === NOT_FOUND
|
|
1109
|
+
? this.props.t('ra_Cannot find "%s"', item.id)
|
|
1110
|
+
: this.props.t('ra_Cannot read "%s"', item.id),
|
|
1111
|
+
),
|
|
1112
|
+
);
|
|
1121
1113
|
} else {
|
|
1122
1114
|
this.setState({ expanded });
|
|
1123
1115
|
}
|
|
@@ -1128,7 +1120,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1128
1120
|
}
|
|
1129
1121
|
}
|
|
1130
1122
|
|
|
1131
|
-
onFileChange = (id: string, fileName: string, size: number | null) => {
|
|
1123
|
+
onFileChange = (id: string, fileName: string, size: number | null): void => {
|
|
1132
1124
|
const key = `${id}/${fileName}`;
|
|
1133
1125
|
const pos = key.lastIndexOf('/');
|
|
1134
1126
|
const folder = key.substring(0, pos);
|
|
@@ -1143,12 +1135,13 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1143
1135
|
delete this._tempTimeout[folder];
|
|
1144
1136
|
|
|
1145
1137
|
this.browseFolder(folder, null, false, true)
|
|
1146
|
-
.then(folders => this.setState({ folders }))
|
|
1138
|
+
.then(folders => this.setState({ folders }))
|
|
1139
|
+
.catch(e => console.error(`Cannot read folder: ${e.message}`));
|
|
1147
1140
|
}, 300);
|
|
1148
1141
|
}
|
|
1149
1142
|
};
|
|
1150
1143
|
|
|
1151
|
-
changeFolder(e: React.MouseEvent<HTMLDivElement>, folder?: string) {
|
|
1144
|
+
changeFolder(e: React.MouseEvent<HTMLDivElement>, folder?: string): void {
|
|
1152
1145
|
if (e) {
|
|
1153
1146
|
e.stopPropagation();
|
|
1154
1147
|
}
|
|
@@ -1168,17 +1161,21 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1168
1161
|
}
|
|
1169
1162
|
|
|
1170
1163
|
if (_folder && !this.state.folders[_folder]) {
|
|
1171
|
-
|
|
1172
|
-
.then(folders =>
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1164
|
+
this.browseFolder(_folder)
|
|
1165
|
+
.then(folders =>
|
|
1166
|
+
this.setState(
|
|
1167
|
+
{
|
|
1168
|
+
folders,
|
|
1169
|
+
path: _folder,
|
|
1170
|
+
currentDir: _folder,
|
|
1171
|
+
selected: _folder,
|
|
1172
|
+
pathFocus: false,
|
|
1173
|
+
},
|
|
1174
|
+
() => this.props.onSelect && this.props.onSelect(''),
|
|
1175
|
+
),
|
|
1176
|
+
)
|
|
1177
|
+
.catch(_e => console.error(`Cannot read folder: ${_e.message}`));
|
|
1178
|
+
return;
|
|
1182
1179
|
}
|
|
1183
1180
|
|
|
1184
1181
|
return this.setState(
|
|
@@ -1192,7 +1189,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1192
1189
|
);
|
|
1193
1190
|
}
|
|
1194
1191
|
|
|
1195
|
-
select(id: string, e?: React.MouseEvent<HTMLDivElement> | null, cb?: () => void) {
|
|
1192
|
+
select(id: string, e?: React.MouseEvent<HTMLDivElement> | null, cb?: () => void): void {
|
|
1196
1193
|
if (e) {
|
|
1197
1194
|
e.stopPropagation();
|
|
1198
1195
|
}
|
|
@@ -1244,123 +1241,147 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1244
1241
|
const isUserData = item.name === USER_DATA;
|
|
1245
1242
|
const isSpecialData = isUserData || item.name === 'vis.0' || item.name === 'vis-2.0';
|
|
1246
1243
|
|
|
1247
|
-
return
|
|
1248
|
-
component="div"
|
|
1249
|
-
key={item.id}
|
|
1250
|
-
id={item.id}
|
|
1251
|
-
style={this.state.viewType === TABLE ? { marginLeft: padding, width: `calc(100% - ${padding}px` } : {}}
|
|
1252
|
-
onClick={e => (this.state.viewType === TABLE ? this.select(item.id, e) : this.changeFolder(e, item.id))}
|
|
1253
|
-
onDoubleClick={e => this.state.viewType === TABLE && this.toggleFolder(item, e)}
|
|
1254
|
-
title={this.getText(item.title)}
|
|
1255
|
-
className="browserItem"
|
|
1256
|
-
sx={Utils.getStyle(
|
|
1257
|
-
this.props.theme,
|
|
1258
|
-
styles[`item${this.state.viewType}`],
|
|
1259
|
-
styles[`itemFolder${this.state.viewType}`],
|
|
1260
|
-
this.state.selected === item.id ? styles.itemSelected : {},
|
|
1261
|
-
item.temp ? styles.itemFolderTemp : {},
|
|
1262
|
-
)}
|
|
1263
|
-
>
|
|
1264
|
-
<IconEl
|
|
1265
|
-
style={Utils.getStyle(this.props.theme, styles[`itemFolderIcon${this.state.viewType}`], isSpecialData && styles.specialFolder)}
|
|
1266
|
-
onClick={this.state.viewType === TABLE ? (e: React.MouseEvent<Element>) => this.toggleFolder(item, e) : undefined}
|
|
1267
|
-
/>
|
|
1268
|
-
|
|
1244
|
+
return (
|
|
1269
1245
|
<Box
|
|
1270
1246
|
component="div"
|
|
1247
|
+
key={item.id}
|
|
1248
|
+
id={item.id}
|
|
1249
|
+
style={this.state.viewType === TABLE ? { marginLeft: padding, width: `calc(100% - ${padding}px` } : {}}
|
|
1250
|
+
onClick={e => (this.state.viewType === TABLE ? this.select(item.id, e) : this.changeFolder(e, item.id))}
|
|
1251
|
+
onDoubleClick={e => this.state.viewType === TABLE && this.toggleFolder(item, e)}
|
|
1252
|
+
title={this.getText(item.title)}
|
|
1253
|
+
className="browserItem"
|
|
1271
1254
|
sx={Utils.getStyle(
|
|
1272
1255
|
this.props.theme,
|
|
1273
|
-
styles[`
|
|
1274
|
-
styles[`
|
|
1256
|
+
styles[`item${this.state.viewType}`],
|
|
1257
|
+
styles[`itemFolder${this.state.viewType}`],
|
|
1258
|
+
this.state.selected === item.id ? styles.itemSelected : {},
|
|
1259
|
+
item.temp ? styles.itemFolderTemp : {},
|
|
1275
1260
|
)}
|
|
1276
1261
|
>
|
|
1277
|
-
|
|
1278
|
-
|
|
1262
|
+
<IconEl
|
|
1263
|
+
style={Utils.getStyle(
|
|
1264
|
+
this.props.theme,
|
|
1265
|
+
styles[`itemFolderIcon${this.state.viewType}`],
|
|
1266
|
+
isSpecialData && styles.specialFolder,
|
|
1267
|
+
)}
|
|
1268
|
+
onClick={
|
|
1269
|
+
this.state.viewType === TABLE
|
|
1270
|
+
? (e: React.MouseEvent<Element>) => this.toggleFolder(item, e)
|
|
1271
|
+
: undefined
|
|
1272
|
+
}
|
|
1273
|
+
/>
|
|
1279
1274
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1275
|
+
<Box
|
|
1276
|
+
component="div"
|
|
1277
|
+
sx={Utils.getStyle(
|
|
1278
|
+
this.props.theme,
|
|
1279
|
+
styles[`itemName${this.state.viewType}`],
|
|
1280
|
+
styles[`itemNameFolder${this.state.viewType}`],
|
|
1281
|
+
)}
|
|
1282
|
+
>
|
|
1283
|
+
{isUserData ? this.props.t('ra_User files') : item.name}
|
|
1284
|
+
</Box>
|
|
1289
1285
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1286
|
+
<Box
|
|
1287
|
+
component="div"
|
|
1288
|
+
style={styles[`itemSize${this.state.viewType}`]}
|
|
1289
|
+
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1290
|
+
>
|
|
1291
|
+
{this.state.viewType === TABLE && this.state.folders[item.id]
|
|
1292
|
+
? this.state.folders[item.id].length
|
|
1293
|
+
: ''}
|
|
1294
|
+
</Box>
|
|
1295
|
+
|
|
1296
|
+
<Box
|
|
1297
|
+
component="div"
|
|
1298
|
+
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1299
|
+
>
|
|
1300
|
+
{this.state.viewType === TABLE && this.props.expertMode ? this.formatAcl(item.acl) : null}
|
|
1301
|
+
</Box>
|
|
1296
1302
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1303
|
+
{this.state.viewType === TABLE && this.props.expertMode ? (
|
|
1304
|
+
<Box
|
|
1305
|
+
component="div"
|
|
1306
|
+
sx={{ ...styles.itemDeleteButtonTable, display: { md: 'inline-block', sm: 'none' } }}
|
|
1307
|
+
/>
|
|
1308
|
+
) : null}
|
|
1299
1309
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1310
|
+
{this.state.viewType === TABLE && this.props.allowDownload ? (
|
|
1311
|
+
<div style={styles[`itemDownloadEmpty${this.state.viewType}`]} />
|
|
1312
|
+
) : null}
|
|
1302
1313
|
|
|
1303
|
-
|
|
1314
|
+
{this.state.viewType === TABLE &&
|
|
1304
1315
|
this.props.allowDelete &&
|
|
1305
1316
|
this.state.folders[item.id] &&
|
|
1306
|
-
this.state.folders[item.id].length ?
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
e
|
|
1310
|
-
|
|
1311
|
-
this.
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1317
|
+
this.state.folders[item.id].length ? (
|
|
1318
|
+
<IconButton
|
|
1319
|
+
aria-label="delete"
|
|
1320
|
+
onClick={e => {
|
|
1321
|
+
e.stopPropagation();
|
|
1322
|
+
if (this.suppressDeleteConfirm > Date.now()) {
|
|
1323
|
+
this.deleteItem(item.id);
|
|
1324
|
+
} else {
|
|
1325
|
+
this.setState({ deleteItem: item.id });
|
|
1326
|
+
}
|
|
1327
|
+
}}
|
|
1328
|
+
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1329
|
+
size="large"
|
|
1330
|
+
>
|
|
1331
|
+
<DeleteIcon fontSize="small" />
|
|
1332
|
+
</IconButton>
|
|
1333
|
+
) : this.state.viewType === TABLE && this.props.allowDelete ? (
|
|
1334
|
+
<Box
|
|
1335
|
+
component="div"
|
|
1336
|
+
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1337
|
+
/>
|
|
1338
|
+
) : null}
|
|
1339
|
+
</Box>
|
|
1340
|
+
);
|
|
1324
1341
|
}
|
|
1325
1342
|
|
|
1326
|
-
renderBackFolder() {
|
|
1327
|
-
return
|
|
1328
|
-
component="div"
|
|
1329
|
-
key={this.state.currentDir}
|
|
1330
|
-
id={this.state.currentDir}
|
|
1331
|
-
onClick={e => this.changeFolder(e)}
|
|
1332
|
-
title={this.props.t('ra_Back to %s', getParentDir(this.state.currentDir))}
|
|
1333
|
-
className="browserItem"
|
|
1334
|
-
sx={Utils.getStyle(
|
|
1335
|
-
this.props.theme,
|
|
1336
|
-
styles[`item${this.state.viewType}`],
|
|
1337
|
-
styles[`itemFolder${this.state.viewType}`],
|
|
1338
|
-
)}
|
|
1339
|
-
>
|
|
1340
|
-
<IconClosed style={styles[`itemFolderIcon${this.state.viewType}`]} />
|
|
1341
|
-
<IconBack sx={styles.itemFolderIconBack} />
|
|
1342
|
-
|
|
1343
|
+
renderBackFolder(): React.JSX.Element {
|
|
1344
|
+
return (
|
|
1343
1345
|
<Box
|
|
1344
1346
|
component="div"
|
|
1347
|
+
key={this.state.currentDir}
|
|
1348
|
+
id={this.state.currentDir}
|
|
1349
|
+
onClick={e => this.changeFolder(e)}
|
|
1350
|
+
title={this.props.t('ra_Back to %s', getParentDir(this.state.currentDir))}
|
|
1351
|
+
className="browserItem"
|
|
1345
1352
|
sx={Utils.getStyle(
|
|
1346
1353
|
this.props.theme,
|
|
1347
|
-
styles[`
|
|
1348
|
-
styles[`
|
|
1354
|
+
styles[`item${this.state.viewType}`],
|
|
1355
|
+
styles[`itemFolder${this.state.viewType}`],
|
|
1349
1356
|
)}
|
|
1350
1357
|
>
|
|
1351
|
-
|
|
1358
|
+
<IconClosed style={styles[`itemFolderIcon${this.state.viewType}`]} />
|
|
1359
|
+
<IconBack sx={styles.itemFolderIconBack} />
|
|
1360
|
+
|
|
1361
|
+
<Box
|
|
1362
|
+
component="div"
|
|
1363
|
+
sx={Utils.getStyle(
|
|
1364
|
+
this.props.theme,
|
|
1365
|
+
styles[`itemName${this.state.viewType}`],
|
|
1366
|
+
styles[`itemNameFolder${this.state.viewType}`],
|
|
1367
|
+
)}
|
|
1368
|
+
>
|
|
1369
|
+
..
|
|
1370
|
+
</Box>
|
|
1352
1371
|
</Box>
|
|
1353
|
-
|
|
1372
|
+
);
|
|
1354
1373
|
}
|
|
1355
1374
|
|
|
1356
|
-
formatSize(size: number | null | undefined) {
|
|
1357
|
-
return
|
|
1358
|
-
{
|
|
1359
|
-
|
|
1375
|
+
formatSize(size: number | null | undefined): React.JSX.Element {
|
|
1376
|
+
return (
|
|
1377
|
+
<div style={styles[`itemSize${this.state.viewType}`]}>
|
|
1378
|
+
{size || size === 0 ? Utils.formatBytes(size) : ''}
|
|
1379
|
+
</div>
|
|
1380
|
+
);
|
|
1360
1381
|
}
|
|
1361
1382
|
|
|
1362
|
-
formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined) {
|
|
1363
|
-
const access: number = acl ? (
|
|
1383
|
+
formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined): React.JSX.Element {
|
|
1384
|
+
const access: number = acl ? (acl as ioBroker.EvaluatedFileACL).permissions || (acl as MetaACL).file : 0;
|
|
1364
1385
|
let accessStr: string;
|
|
1365
1386
|
if (access) {
|
|
1366
1387
|
accessStr = access.toString(16).padStart(3, '0');
|
|
@@ -1368,18 +1389,24 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1368
1389
|
accessStr = '';
|
|
1369
1390
|
}
|
|
1370
1391
|
|
|
1371
|
-
return
|
|
1372
|
-
{this.
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1392
|
+
return (
|
|
1393
|
+
<div style={styles[`itemAccess${this.state.viewType}`]}>
|
|
1394
|
+
{this.props.modalEditOfAccessControl ? (
|
|
1395
|
+
<IconButton
|
|
1396
|
+
size="large"
|
|
1397
|
+
onClick={() => this.setState({ modalEditOfAccess: true })}
|
|
1398
|
+
sx={styles[`itemAclButton${this.state.viewType}`]}
|
|
1399
|
+
>
|
|
1400
|
+
{accessStr || '---'}
|
|
1401
|
+
</IconButton>
|
|
1402
|
+
) : (
|
|
1403
|
+
accessStr || '---'
|
|
1404
|
+
)}
|
|
1405
|
+
</div>
|
|
1406
|
+
);
|
|
1380
1407
|
}
|
|
1381
1408
|
|
|
1382
|
-
getFileIcon(ext: string | null) {
|
|
1409
|
+
getFileIcon(ext: string | null): React.JSX.Element {
|
|
1383
1410
|
switch (ext) {
|
|
1384
1411
|
case 'json':
|
|
1385
1412
|
case 'json5':
|
|
@@ -1424,14 +1451,15 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1424
1451
|
}
|
|
1425
1452
|
}
|
|
1426
1453
|
|
|
1427
|
-
setStateBackgroundImage = () => {
|
|
1454
|
+
setStateBackgroundImage = (): void => {
|
|
1428
1455
|
const array = ['light', 'dark', 'colored', 'delete'];
|
|
1429
1456
|
this.setState(({ backgroundImage }) => {
|
|
1430
|
-
if (
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1457
|
+
if (
|
|
1458
|
+
backgroundImage &&
|
|
1459
|
+
array.indexOf(backgroundImage) !== -1 &&
|
|
1460
|
+
array.length - 1 !== array.indexOf(backgroundImage)
|
|
1461
|
+
) {
|
|
1462
|
+
this.localStorage.setItem('files.backgroundImage', array[array.indexOf(backgroundImage) + 1]);
|
|
1435
1463
|
return { backgroundImage: array[array.indexOf(backgroundImage) + 1] };
|
|
1436
1464
|
}
|
|
1437
1465
|
this.localStorage.setItem('files.backgroundImage', array[0]);
|
|
@@ -1439,7 +1467,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1439
1467
|
});
|
|
1440
1468
|
};
|
|
1441
1469
|
|
|
1442
|
-
getStyleBackgroundImage = () => {
|
|
1470
|
+
getStyleBackgroundImage = (): React.CSSProperties | null => {
|
|
1443
1471
|
// ['light', 'dark', 'colored', 'delete']
|
|
1444
1472
|
switch (this.state.backgroundImage) {
|
|
1445
1473
|
case 'light':
|
|
@@ -1459,137 +1487,156 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1459
1487
|
const padding = this.state.viewType === TABLE ? item.level * this.levelPadding : 0;
|
|
1460
1488
|
const ext = Utils.getFileExtension(item.name);
|
|
1461
1489
|
|
|
1462
|
-
return
|
|
1463
|
-
component="div"
|
|
1464
|
-
key={item.id}
|
|
1465
|
-
id={item.id}
|
|
1466
|
-
onDoubleClick={e => {
|
|
1467
|
-
e.stopPropagation();
|
|
1468
|
-
if (!this.props.onSelect) {
|
|
1469
|
-
this.setState({ viewer: this.imagePrefix + item.id, formatEditFile: ext });
|
|
1470
|
-
} else if (
|
|
1471
|
-
(!this.props.filterFiles || (item.ext && this.props.filterFiles.includes(item.ext))) &&
|
|
1472
|
-
(!this.state.filterByType ||
|
|
1473
|
-
(item.ext &&
|
|
1474
|
-
(EXTENSIONS as Record<string, string[]>)[this.state.filterByType].includes(item.ext)))
|
|
1475
|
-
) {
|
|
1476
|
-
this.props.onSelect(item.id, true, !!this.state.folders[item.id]);
|
|
1477
|
-
}
|
|
1478
|
-
}}
|
|
1479
|
-
onClick={e => this.select(item.id, e)}
|
|
1480
|
-
style={this.state.viewType === TABLE ? { marginLeft: padding, width: `calc(100% - ${padding}px)` } : {}}
|
|
1481
|
-
className="browserItem"
|
|
1482
|
-
sx={Utils.getStyle(
|
|
1483
|
-
this.props.theme,
|
|
1484
|
-
styles[`item${this.state.viewType}`],
|
|
1485
|
-
styles[`itemFile${this.state.viewType}`],
|
|
1486
|
-
this.state.selected === item.id ? styles.itemSelected : undefined,
|
|
1487
|
-
)}
|
|
1488
|
-
>
|
|
1489
|
-
{ext && EXTENSIONS.images.includes(ext) ?
|
|
1490
|
-
this.state.fileErrors.includes(item.id) ?
|
|
1491
|
-
<IconNoIcon
|
|
1492
|
-
style={{
|
|
1493
|
-
...styles[`itemImage${this.state.viewType}`],
|
|
1494
|
-
...this.getStyleBackgroundImage(),
|
|
1495
|
-
...styles[`itemNoImage${this.state.viewType}`],
|
|
1496
|
-
}}
|
|
1497
|
-
/> :
|
|
1498
|
-
<Icon
|
|
1499
|
-
onError={e => {
|
|
1500
|
-
(e.target as HTMLImageElement).onerror = null;
|
|
1501
|
-
const fileErrors = [...this.state.fileErrors];
|
|
1502
|
-
if (!fileErrors.includes(item.id)) {
|
|
1503
|
-
fileErrors.push(item.id);
|
|
1504
|
-
this.setState({ fileErrors });
|
|
1505
|
-
}
|
|
1506
|
-
}}
|
|
1507
|
-
style={{ ...styles[`itemImage${this.state.viewType}`], ...this.getStyleBackgroundImage() }}
|
|
1508
|
-
src={this.imagePrefix + item.id}
|
|
1509
|
-
alt={item.name}
|
|
1510
|
-
/>
|
|
1511
|
-
:
|
|
1512
|
-
this.getFileIcon(ext)}
|
|
1513
|
-
<Box component="div" sx={styles[`itemName${this.state.viewType}`]}>{item.name}</Box>
|
|
1514
|
-
<Box
|
|
1515
|
-
component="div"
|
|
1516
|
-
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1517
|
-
>
|
|
1518
|
-
{this.formatSize(item.size)}
|
|
1519
|
-
</Box>
|
|
1490
|
+
return (
|
|
1520
1491
|
<Box
|
|
1521
1492
|
component="div"
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
{
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1493
|
+
key={item.id}
|
|
1494
|
+
id={item.id}
|
|
1495
|
+
onDoubleClick={e => {
|
|
1496
|
+
e.stopPropagation();
|
|
1497
|
+
if (!this.props.onSelect) {
|
|
1498
|
+
this.setState({ viewer: this.imagePrefix + item.id, formatEditFile: ext });
|
|
1499
|
+
} else if (
|
|
1500
|
+
(!this.props.filterFiles || (item.ext && this.props.filterFiles.includes(item.ext))) &&
|
|
1501
|
+
(!this.state.filterByType ||
|
|
1502
|
+
(item.ext &&
|
|
1503
|
+
(EXTENSIONS as Record<string, string[]>)[this.state.filterByType].includes(item.ext)))
|
|
1504
|
+
) {
|
|
1505
|
+
this.props.onSelect(item.id, true, !!this.state.folders[item.id]);
|
|
1506
|
+
}
|
|
1507
|
+
}}
|
|
1508
|
+
onClick={e => this.select(item.id, e)}
|
|
1509
|
+
style={this.state.viewType === TABLE ? { marginLeft: padding, width: `calc(100% - ${padding}px)` } : {}}
|
|
1510
|
+
className="browserItem"
|
|
1511
|
+
sx={Utils.getStyle(
|
|
1512
|
+
this.props.theme,
|
|
1513
|
+
styles[`item${this.state.viewType}`],
|
|
1514
|
+
styles[`itemFile${this.state.viewType}`],
|
|
1515
|
+
this.state.selected === item.id ? styles.itemSelected : undefined,
|
|
1516
|
+
)}
|
|
1529
1517
|
>
|
|
1530
|
-
{
|
|
1518
|
+
{ext && EXTENSIONS.images.includes(ext) ? (
|
|
1519
|
+
this.state.fileErrors.includes(item.id) ? (
|
|
1520
|
+
<IconNoIcon
|
|
1521
|
+
style={{
|
|
1522
|
+
...styles[`itemImage${this.state.viewType}`],
|
|
1523
|
+
...this.getStyleBackgroundImage(),
|
|
1524
|
+
...styles[`itemNoImage${this.state.viewType}`],
|
|
1525
|
+
}}
|
|
1526
|
+
/>
|
|
1527
|
+
) : (
|
|
1528
|
+
<Icon
|
|
1529
|
+
onError={e => {
|
|
1530
|
+
(e.target as HTMLImageElement).onerror = null;
|
|
1531
|
+
const fileErrors = [...this.state.fileErrors];
|
|
1532
|
+
if (!fileErrors.includes(item.id)) {
|
|
1533
|
+
fileErrors.push(item.id);
|
|
1534
|
+
this.setState({ fileErrors });
|
|
1535
|
+
}
|
|
1536
|
+
}}
|
|
1537
|
+
style={{ ...styles[`itemImage${this.state.viewType}`], ...this.getStyleBackgroundImage() }}
|
|
1538
|
+
src={this.imagePrefix + item.id}
|
|
1539
|
+
alt={item.name}
|
|
1540
|
+
/>
|
|
1541
|
+
)
|
|
1542
|
+
) : (
|
|
1543
|
+
this.getFileIcon(ext)
|
|
1544
|
+
)}
|
|
1545
|
+
<Box
|
|
1546
|
+
component="div"
|
|
1547
|
+
sx={styles[`itemName${this.state.viewType}`]}
|
|
1548
|
+
>
|
|
1549
|
+
{item.name}
|
|
1550
|
+
</Box>
|
|
1551
|
+
<Box
|
|
1552
|
+
component="div"
|
|
1553
|
+
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1554
|
+
>
|
|
1555
|
+
{this.formatSize(item.size)}
|
|
1556
|
+
</Box>
|
|
1557
|
+
<Box
|
|
1558
|
+
component="div"
|
|
1559
|
+
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1560
|
+
>
|
|
1561
|
+
{this.state.viewType === TABLE && this.props.expertMode ? this.formatAcl(item.acl) : null}
|
|
1562
|
+
</Box>
|
|
1563
|
+
<Box
|
|
1564
|
+
component="div"
|
|
1565
|
+
sx={{ display: { md: 'inline-block', sm: 'none' } }}
|
|
1566
|
+
>
|
|
1567
|
+
{this.state.viewType === TABLE && this.props.expertMode && FileBrowserClass.getEditFile(ext) ? (
|
|
1568
|
+
<IconButton
|
|
1569
|
+
aria-label="edit"
|
|
1570
|
+
onClick={e => {
|
|
1571
|
+
e.stopPropagation();
|
|
1572
|
+
if (!this.props.onSelect) {
|
|
1573
|
+
this.setState({ viewer: this.imagePrefix + item.id, formatEditFile: ext });
|
|
1574
|
+
} else if (
|
|
1575
|
+
(!this.props.filterFiles ||
|
|
1576
|
+
(item.ext && this.props.filterFiles.includes(item.ext))) &&
|
|
1577
|
+
(!this.state.filterByType ||
|
|
1578
|
+
(item.ext &&
|
|
1579
|
+
(EXTENSIONS as Record<string, string[]>)[this.state.filterByType].includes(
|
|
1580
|
+
item.ext,
|
|
1581
|
+
)))
|
|
1582
|
+
) {
|
|
1583
|
+
this.props.onSelect(item.id, true, !!this.state.folders[item.id]);
|
|
1584
|
+
}
|
|
1585
|
+
}}
|
|
1586
|
+
sx={styles.itemDeleteButtonTable}
|
|
1587
|
+
size="large"
|
|
1588
|
+
>
|
|
1589
|
+
<EditIcon fontSize="small" />
|
|
1590
|
+
</IconButton>
|
|
1591
|
+
) : (
|
|
1592
|
+
<Box
|
|
1593
|
+
component="div"
|
|
1594
|
+
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1595
|
+
/>
|
|
1596
|
+
)}
|
|
1597
|
+
</Box>
|
|
1598
|
+
{this.state.viewType === TABLE && this.props.allowDownload ? (
|
|
1599
|
+
<Box
|
|
1600
|
+
component="a"
|
|
1601
|
+
className="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeLarge"
|
|
1602
|
+
sx={styles.itemDownloadButtonTable}
|
|
1603
|
+
tabIndex={0}
|
|
1604
|
+
download={item.id}
|
|
1605
|
+
href={this.imagePrefix + item.id}
|
|
1606
|
+
onClick={e => e.stopPropagation()}
|
|
1607
|
+
>
|
|
1608
|
+
<DownloadIcon />
|
|
1609
|
+
</Box>
|
|
1610
|
+
) : null}
|
|
1611
|
+
|
|
1612
|
+
{this.state.viewType === TABLE &&
|
|
1613
|
+
this.props.allowDelete &&
|
|
1614
|
+
item.id !== 'vis.0/' &&
|
|
1615
|
+
item.id !== 'vis-2.0/' &&
|
|
1616
|
+
item.id !== USER_DATA ? (
|
|
1531
1617
|
<IconButton
|
|
1532
|
-
aria-label="
|
|
1618
|
+
aria-label="delete"
|
|
1533
1619
|
onClick={e => {
|
|
1534
1620
|
e.stopPropagation();
|
|
1535
|
-
if (
|
|
1536
|
-
this.
|
|
1537
|
-
} else
|
|
1538
|
-
|
|
1539
|
-
(item.ext && this.props.filterFiles.includes(item.ext))) &&
|
|
1540
|
-
(!this.state.filterByType ||
|
|
1541
|
-
(item.ext &&
|
|
1542
|
-
(EXTENSIONS as Record<string, string[]>)[this.state.filterByType].includes(
|
|
1543
|
-
item.ext,
|
|
1544
|
-
)))
|
|
1545
|
-
) {
|
|
1546
|
-
this.props.onSelect(item.id, true, !!this.state.folders[item.id]);
|
|
1621
|
+
if (this.suppressDeleteConfirm > Date.now()) {
|
|
1622
|
+
this.deleteItem(item.id);
|
|
1623
|
+
} else {
|
|
1624
|
+
this.setState({ deleteItem: item.id });
|
|
1547
1625
|
}
|
|
1548
1626
|
}}
|
|
1549
|
-
sx={styles.
|
|
1627
|
+
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1550
1628
|
size="large"
|
|
1551
1629
|
>
|
|
1552
|
-
<
|
|
1630
|
+
<DeleteIcon fontSize="small" />
|
|
1553
1631
|
</IconButton>
|
|
1554
|
-
|
|
1555
|
-
<Box
|
|
1632
|
+
) : this.state.viewType === TABLE && this.props.allowDelete ? (
|
|
1633
|
+
<Box
|
|
1634
|
+
component="div"
|
|
1635
|
+
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1636
|
+
/>
|
|
1637
|
+
) : null}
|
|
1556
1638
|
</Box>
|
|
1557
|
-
|
|
1558
|
-
{this.state.viewType === TABLE && this.props.allowDownload ? <Box
|
|
1559
|
-
component="a"
|
|
1560
|
-
className="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeLarge"
|
|
1561
|
-
sx={styles.itemDownloadButtonTable}
|
|
1562
|
-
tabIndex={0}
|
|
1563
|
-
download={item.id}
|
|
1564
|
-
href={this.imagePrefix + item.id}
|
|
1565
|
-
onClick={e => e.stopPropagation()}
|
|
1566
|
-
>
|
|
1567
|
-
<DownloadIcon />
|
|
1568
|
-
</Box> : null}
|
|
1569
|
-
|
|
1570
|
-
{this.state.viewType === TABLE &&
|
|
1571
|
-
this.props.allowDelete &&
|
|
1572
|
-
item.id !== 'vis.0/' &&
|
|
1573
|
-
item.id !== 'vis-2.0/' &&
|
|
1574
|
-
item.id !== USER_DATA ? <IconButton
|
|
1575
|
-
aria-label="delete"
|
|
1576
|
-
onClick={e => {
|
|
1577
|
-
e.stopPropagation();
|
|
1578
|
-
if (this.suppressDeleteConfirm > Date.now()) {
|
|
1579
|
-
this.deleteItem(item.id);
|
|
1580
|
-
} else {
|
|
1581
|
-
this.setState({ deleteItem: item.id });
|
|
1582
|
-
}
|
|
1583
|
-
}}
|
|
1584
|
-
sx={styles[`itemDeleteButton${this.state.viewType}`]}
|
|
1585
|
-
size="large"
|
|
1586
|
-
>
|
|
1587
|
-
<DeleteIcon fontSize="small" />
|
|
1588
|
-
</IconButton>
|
|
1589
|
-
:
|
|
1590
|
-
(this.state.viewType === TABLE && this.props.allowDelete ?
|
|
1591
|
-
<Box component="div" sx={styles[`itemDeleteButton${this.state.viewType}`]} /> : null)}
|
|
1592
|
-
</Box>;
|
|
1639
|
+
);
|
|
1593
1640
|
}
|
|
1594
1641
|
|
|
1595
1642
|
renderItems(folderId: string): React.JSX.Element | (React.JSX.Element | null)[] {
|
|
@@ -1647,240 +1694,264 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1647
1694
|
return totalResult;
|
|
1648
1695
|
}
|
|
1649
1696
|
|
|
1650
|
-
return
|
|
1651
|
-
<
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1697
|
+
return (
|
|
1698
|
+
<div style={{ position: 'relative' }}>
|
|
1699
|
+
<CircularProgress
|
|
1700
|
+
key={folderId}
|
|
1701
|
+
color="secondary"
|
|
1702
|
+
size={24}
|
|
1703
|
+
/>
|
|
1704
|
+
<div
|
|
1705
|
+
style={{
|
|
1706
|
+
position: 'absolute',
|
|
1707
|
+
zIndex: 2,
|
|
1708
|
+
top: 4,
|
|
1709
|
+
width: 24,
|
|
1710
|
+
textAlign: 'center',
|
|
1711
|
+
}}
|
|
1712
|
+
>
|
|
1713
|
+
{this.state.queueLength}
|
|
1714
|
+
</div>
|
|
1662
1715
|
</div>
|
|
1663
|
-
|
|
1716
|
+
);
|
|
1664
1717
|
}
|
|
1665
1718
|
|
|
1666
|
-
renderToolbar() {
|
|
1667
|
-
const IconType: React.FC<
|
|
1668
|
-
? FILE_TYPE_ICONS[this.state.filterByType || 'all'] ||
|
|
1669
|
-
FILE_TYPE_ICONS.all
|
|
1719
|
+
renderToolbar(): React.JSX.Element {
|
|
1720
|
+
const IconType: React.FC<{ fontSize?: 'small' }> | null = this.props.showTypeSelector
|
|
1721
|
+
? FILE_TYPE_ICONS[this.state.filterByType || 'all'] || FILE_TYPE_ICONS.all
|
|
1670
1722
|
: null;
|
|
1671
1723
|
|
|
1672
1724
|
const isInFolder = this.findFirstFolder(this.state.selected);
|
|
1673
1725
|
|
|
1674
|
-
return
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
this.state.restrictToFolder
|
|
1679
|
-
? this.props.t('ra_Show all folders')
|
|
1680
|
-
: this.props.t('ra_Restrict to folder')
|
|
1681
|
-
}
|
|
1682
|
-
style={{
|
|
1683
|
-
...styles.menuButton,
|
|
1684
|
-
...(this.state.restrictToFolder ? styles.menuButtonRestrictActive : undefined),
|
|
1685
|
-
}}
|
|
1686
|
-
aria-label="restricted to folder"
|
|
1687
|
-
onClick={() =>
|
|
1688
|
-
this.setState({
|
|
1689
|
-
restrictToFolder:
|
|
1690
|
-
(this.state.restrictToFolder ? '' : this.props.restrictToFolder) || '',
|
|
1691
|
-
loadAllFolders: true,
|
|
1692
|
-
})}
|
|
1693
|
-
size="small"
|
|
1694
|
-
>
|
|
1695
|
-
<RestrictedIcon fontSize="small" />
|
|
1696
|
-
</IconButton> : null}
|
|
1697
|
-
{this.props.showExpertButton ? <IconButton
|
|
1698
|
-
edge="start"
|
|
1699
|
-
title={this.props.t('ra_Toggle expert mode')}
|
|
1700
|
-
style={{
|
|
1701
|
-
...styles.menuButton,
|
|
1702
|
-
...(this.state.expertMode ? styles.menuButtonExpertActive : undefined),
|
|
1703
|
-
}}
|
|
1704
|
-
aria-label="expert mode"
|
|
1705
|
-
onClick={() => this.setState({ expertMode: !this.state.expertMode })}
|
|
1706
|
-
size="small"
|
|
1726
|
+
return (
|
|
1727
|
+
<Toolbar
|
|
1728
|
+
key="toolbar"
|
|
1729
|
+
variant="dense"
|
|
1707
1730
|
>
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
onClick={() => {
|
|
1716
|
-
const viewType = this.state.viewType === TABLE ? TILE : TABLE;
|
|
1717
|
-
this.localStorage.setItem('files.viewType', viewType);
|
|
1718
|
-
let currentDir = this.state.selected;
|
|
1719
|
-
if (isFile(currentDir)) {
|
|
1720
|
-
currentDir = getParentDir(currentDir);
|
|
1721
|
-
}
|
|
1722
|
-
this.setState({ viewType, currentDir }, () => {
|
|
1723
|
-
if (this.state.viewType === TABLE) {
|
|
1724
|
-
this.scrollToSelected();
|
|
1731
|
+
{this.props.allowNonRestricted && this.props.restrictToFolder ? (
|
|
1732
|
+
<IconButton
|
|
1733
|
+
edge="start"
|
|
1734
|
+
title={
|
|
1735
|
+
this.state.restrictToFolder
|
|
1736
|
+
? this.props.t('ra_Show all folders')
|
|
1737
|
+
: this.props.t('ra_Restrict to folder')
|
|
1725
1738
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
!isInFolder ||
|
|
1765
|
-
(!!this.limitToPath &&
|
|
1766
|
-
!this.state.selected.startsWith(`${this.limitToPath}/`) &&
|
|
1767
|
-
this.limitToPath !== this.state.selected)
|
|
1768
|
-
}
|
|
1769
|
-
title={this.props.t('ra_Create folder')}
|
|
1770
|
-
style={styles.menuButton}
|
|
1771
|
-
color="inherit"
|
|
1772
|
-
aria-label="add folder"
|
|
1773
|
-
onClick={() => this.setState({ addFolder: true })}
|
|
1774
|
-
size="small"
|
|
1775
|
-
>
|
|
1776
|
-
<AddFolderIcon fontSize="small" />
|
|
1777
|
-
</IconButton> : null}
|
|
1778
|
-
{this.props.allowUpload ? <IconButton
|
|
1779
|
-
edge="start"
|
|
1780
|
-
disabled={
|
|
1781
|
-
!this.state.selected ||
|
|
1782
|
-
!isInFolder ||
|
|
1783
|
-
(!!this.limitToPath &&
|
|
1784
|
-
!this.state.selected.startsWith(`${this.limitToPath}/`) &&
|
|
1785
|
-
this.limitToPath !== this.state.selected)
|
|
1786
|
-
}
|
|
1787
|
-
title={this.props.t('ra_Upload file')}
|
|
1788
|
-
style={styles.menuButton}
|
|
1789
|
-
color="inherit"
|
|
1790
|
-
aria-label="upload file"
|
|
1791
|
-
onClick={() => this.setState({ uploadFile: true })}
|
|
1792
|
-
size="small"
|
|
1793
|
-
>
|
|
1794
|
-
<UploadIcon fontSize="small" />
|
|
1795
|
-
</IconButton> : null}
|
|
1796
|
-
{this.props.showTypeSelector && IconType ? <Tooltip title={this.props.t('ra_Filter files')} slotProps={{ popper: { sx: styles.tooltip } }}>
|
|
1797
|
-
<IconButton size="small" onClick={e => this.setState({ showTypesMenu: e.target as HTMLButtonElement })}>
|
|
1798
|
-
<IconType fontSize="small" />
|
|
1799
|
-
</IconButton>
|
|
1800
|
-
</Tooltip> : null}
|
|
1801
|
-
{this.state.showTypesMenu ? <Menu
|
|
1802
|
-
open={!0}
|
|
1803
|
-
anchorEl={this.state.showTypesMenu}
|
|
1804
|
-
onClose={() => this.setState({ showTypesMenu: null })}
|
|
1805
|
-
>
|
|
1806
|
-
{Object.keys(FILE_TYPE_ICONS).map(type => {
|
|
1807
|
-
const MyIcon: React.FC<{ fontSize?: 'small' }> = FILE_TYPE_ICONS[type];
|
|
1808
|
-
return <MenuItem
|
|
1809
|
-
key={type}
|
|
1810
|
-
selected={this.state.filterByType === type}
|
|
1739
|
+
style={{
|
|
1740
|
+
...styles.menuButton,
|
|
1741
|
+
...(this.state.restrictToFolder ? styles.menuButtonRestrictActive : undefined),
|
|
1742
|
+
}}
|
|
1743
|
+
aria-label="restricted to folder"
|
|
1744
|
+
onClick={() =>
|
|
1745
|
+
this.setState({
|
|
1746
|
+
restrictToFolder:
|
|
1747
|
+
(this.state.restrictToFolder ? '' : this.props.restrictToFolder) || '',
|
|
1748
|
+
loadAllFolders: true,
|
|
1749
|
+
})
|
|
1750
|
+
}
|
|
1751
|
+
size="small"
|
|
1752
|
+
>
|
|
1753
|
+
<RestrictedIcon fontSize="small" />
|
|
1754
|
+
</IconButton>
|
|
1755
|
+
) : null}
|
|
1756
|
+
{this.props.showExpertButton ? (
|
|
1757
|
+
<IconButton
|
|
1758
|
+
edge="start"
|
|
1759
|
+
title={this.props.t('ra_Toggle expert mode')}
|
|
1760
|
+
style={{
|
|
1761
|
+
...styles.menuButton,
|
|
1762
|
+
...(this.state.expertMode ? styles.menuButtonExpertActive : undefined),
|
|
1763
|
+
}}
|
|
1764
|
+
aria-label="expert mode"
|
|
1765
|
+
onClick={() => this.setState({ expertMode: !this.state.expertMode })}
|
|
1766
|
+
size="small"
|
|
1767
|
+
>
|
|
1768
|
+
<IconExpert />
|
|
1769
|
+
</IconButton>
|
|
1770
|
+
) : null}
|
|
1771
|
+
{this.props.showViewTypeButton ? (
|
|
1772
|
+
<IconButton
|
|
1773
|
+
edge="start"
|
|
1774
|
+
title={this.props.t('ra_Toggle view mode')}
|
|
1775
|
+
style={styles.menuButton}
|
|
1776
|
+
aria-label="view mode"
|
|
1811
1777
|
onClick={() => {
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
} else {
|
|
1818
|
-
this.localStorage.setItem(
|
|
1819
|
-
'files.filterByType',
|
|
1820
|
-
type,
|
|
1821
|
-
);
|
|
1822
|
-
this.setState({ filterByType: type, showTypesMenu: null });
|
|
1778
|
+
const viewType = this.state.viewType === TABLE ? TILE : TABLE;
|
|
1779
|
+
this.localStorage.setItem('files.viewType', viewType);
|
|
1780
|
+
let currentDir = this.state.selected;
|
|
1781
|
+
if (isFile(currentDir)) {
|
|
1782
|
+
currentDir = getParentDir(currentDir);
|
|
1823
1783
|
}
|
|
1784
|
+
this.setState({ viewType, currentDir }, () => {
|
|
1785
|
+
if (this.state.viewType === TABLE) {
|
|
1786
|
+
this.scrollToSelected();
|
|
1787
|
+
}
|
|
1788
|
+
});
|
|
1824
1789
|
}}
|
|
1790
|
+
size="small"
|
|
1825
1791
|
>
|
|
1826
|
-
<
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
<ListItemText>{this.props.t(`ra_fileType_${type}`)}</ListItemText>
|
|
1830
|
-
</MenuItem>;
|
|
1831
|
-
})}
|
|
1832
|
-
</Menu> : null}
|
|
1833
|
-
<Tooltip
|
|
1834
|
-
title={this.props.t('ra_Background image')}
|
|
1835
|
-
slotProps={{ popper: { sx: styles.tooltip } }}
|
|
1836
|
-
>
|
|
1792
|
+
{this.state.viewType !== TABLE ? <IconList fontSize="small" /> : <IconTile fontSize="small" />}
|
|
1793
|
+
</IconButton>
|
|
1794
|
+
) : null}
|
|
1837
1795
|
<IconButton
|
|
1838
|
-
color="inherit"
|
|
1839
1796
|
edge="start"
|
|
1797
|
+
title={this.props.t('ra_Hide empty folders')}
|
|
1840
1798
|
style={styles.menuButton}
|
|
1841
|
-
|
|
1799
|
+
color={this.state.filterEmpty ? 'secondary' : 'inherit'}
|
|
1800
|
+
aria-label="filter empty"
|
|
1801
|
+
onClick={() => {
|
|
1802
|
+
this.localStorage.setItem('file.empty', this.state.filterEmpty ? 'false' : 'true');
|
|
1803
|
+
this.setState({ filterEmpty: !this.state.filterEmpty });
|
|
1804
|
+
}}
|
|
1842
1805
|
size="small"
|
|
1843
1806
|
>
|
|
1844
|
-
<
|
|
1807
|
+
<EmptyFilterIcon fontSize="small" />
|
|
1845
1808
|
</IconButton>
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1809
|
+
<IconButton
|
|
1810
|
+
edge="start"
|
|
1811
|
+
title={this.props.t('ra_Reload files')}
|
|
1812
|
+
style={styles.menuButton}
|
|
1813
|
+
color="inherit"
|
|
1814
|
+
aria-label="reload files"
|
|
1815
|
+
onClick={() => this.setState({ folders: {} }, () => this.loadFolders())}
|
|
1816
|
+
size="small"
|
|
1817
|
+
>
|
|
1818
|
+
<RefreshIcon fontSize="small" />
|
|
1819
|
+
</IconButton>
|
|
1820
|
+
{this.props.allowCreateFolder ? (
|
|
1852
1821
|
<IconButton
|
|
1853
|
-
|
|
1822
|
+
edge="start"
|
|
1854
1823
|
disabled={
|
|
1855
1824
|
!this.state.selected ||
|
|
1856
|
-
|
|
1857
|
-
this.
|
|
1858
|
-
|
|
1825
|
+
!isInFolder ||
|
|
1826
|
+
(!!this.limitToPath &&
|
|
1827
|
+
!this.state.selected.startsWith(`${this.limitToPath}/`) &&
|
|
1828
|
+
this.limitToPath !== this.state.selected)
|
|
1859
1829
|
}
|
|
1830
|
+
title={this.props.t('ra_Create folder')}
|
|
1831
|
+
style={styles.menuButton}
|
|
1860
1832
|
color="inherit"
|
|
1833
|
+
aria-label="add folder"
|
|
1834
|
+
onClick={() => this.setState({ addFolder: true })}
|
|
1835
|
+
size="small"
|
|
1836
|
+
>
|
|
1837
|
+
<AddFolderIcon fontSize="small" />
|
|
1838
|
+
</IconButton>
|
|
1839
|
+
) : null}
|
|
1840
|
+
{this.props.allowUpload ? (
|
|
1841
|
+
<IconButton
|
|
1861
1842
|
edge="start"
|
|
1843
|
+
disabled={
|
|
1844
|
+
!this.state.selected ||
|
|
1845
|
+
!isInFolder ||
|
|
1846
|
+
(!!this.limitToPath &&
|
|
1847
|
+
!this.state.selected.startsWith(`${this.limitToPath}/`) &&
|
|
1848
|
+
this.limitToPath !== this.state.selected)
|
|
1849
|
+
}
|
|
1850
|
+
title={this.props.t('ra_Upload file')}
|
|
1862
1851
|
style={styles.menuButton}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
this.deleteItem(this.state.selected);
|
|
1867
|
-
} else {
|
|
1868
|
-
this.setState({ deleteItem: this.state.selected });
|
|
1869
|
-
}
|
|
1870
|
-
}}
|
|
1852
|
+
color="inherit"
|
|
1853
|
+
aria-label="upload file"
|
|
1854
|
+
onClick={() => this.setState({ uploadFile: true })}
|
|
1871
1855
|
size="small"
|
|
1872
1856
|
>
|
|
1873
|
-
<
|
|
1857
|
+
<UploadIcon fontSize="small" />
|
|
1874
1858
|
</IconButton>
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1859
|
+
) : null}
|
|
1860
|
+
{this.props.showTypeSelector && IconType ? (
|
|
1861
|
+
<Tooltip
|
|
1862
|
+
title={this.props.t('ra_Filter files')}
|
|
1863
|
+
slotProps={{ popper: { sx: styles.tooltip } }}
|
|
1864
|
+
>
|
|
1865
|
+
<IconButton
|
|
1866
|
+
size="small"
|
|
1867
|
+
onClick={e => this.setState({ showTypesMenu: e.target as HTMLButtonElement })}
|
|
1868
|
+
>
|
|
1869
|
+
<IconType fontSize="small" />
|
|
1870
|
+
</IconButton>
|
|
1871
|
+
</Tooltip>
|
|
1872
|
+
) : null}
|
|
1873
|
+
{this.state.showTypesMenu ? (
|
|
1874
|
+
<Menu
|
|
1875
|
+
open={!0}
|
|
1876
|
+
anchorEl={this.state.showTypesMenu}
|
|
1877
|
+
onClose={() => this.setState({ showTypesMenu: null })}
|
|
1878
|
+
>
|
|
1879
|
+
{Object.keys(FILE_TYPE_ICONS).map(type => {
|
|
1880
|
+
const MyIcon: React.FC<{ fontSize?: 'small' }> = FILE_TYPE_ICONS[type];
|
|
1881
|
+
return (
|
|
1882
|
+
<MenuItem
|
|
1883
|
+
key={type}
|
|
1884
|
+
selected={this.state.filterByType === type}
|
|
1885
|
+
onClick={() => {
|
|
1886
|
+
if (type === 'all') {
|
|
1887
|
+
this.localStorage.removeItem('files.filterByType');
|
|
1888
|
+
this.setState({ filterByType: '', showTypesMenu: null });
|
|
1889
|
+
} else {
|
|
1890
|
+
this.localStorage.setItem('files.filterByType', type);
|
|
1891
|
+
this.setState({ filterByType: type, showTypesMenu: null });
|
|
1892
|
+
}
|
|
1893
|
+
}}
|
|
1894
|
+
>
|
|
1895
|
+
<ListItemIcon>
|
|
1896
|
+
<MyIcon fontSize="small" />
|
|
1897
|
+
</ListItemIcon>
|
|
1898
|
+
<ListItemText>{this.props.t(`ra_fileType_${type}`)}</ListItemText>
|
|
1899
|
+
</MenuItem>
|
|
1900
|
+
);
|
|
1901
|
+
})}
|
|
1902
|
+
</Menu>
|
|
1903
|
+
) : null}
|
|
1904
|
+
<Tooltip
|
|
1905
|
+
title={this.props.t('ra_Background image')}
|
|
1906
|
+
slotProps={{ popper: { sx: styles.tooltip } }}
|
|
1907
|
+
>
|
|
1908
|
+
<IconButton
|
|
1909
|
+
color="inherit"
|
|
1910
|
+
edge="start"
|
|
1911
|
+
style={styles.menuButton}
|
|
1912
|
+
onClick={this.setStateBackgroundImage}
|
|
1913
|
+
size="small"
|
|
1914
|
+
>
|
|
1915
|
+
<Brightness5Icon fontSize="small" />
|
|
1916
|
+
</IconButton>
|
|
1917
|
+
</Tooltip>
|
|
1918
|
+
{this.state.viewType !== TABLE && this.props.allowDelete ? (
|
|
1919
|
+
<Tooltip
|
|
1920
|
+
title={this.props.t('ra_Delete')}
|
|
1921
|
+
slotProps={{ popper: { sx: styles.tooltip } }}
|
|
1922
|
+
>
|
|
1923
|
+
<span>
|
|
1924
|
+
<IconButton
|
|
1925
|
+
aria-label="delete"
|
|
1926
|
+
disabled={
|
|
1927
|
+
!this.state.selected ||
|
|
1928
|
+
this.state.selected === 'vis.0/' ||
|
|
1929
|
+
this.state.selected === 'vis-2.0/' ||
|
|
1930
|
+
this.state.selected === USER_DATA
|
|
1931
|
+
}
|
|
1932
|
+
color="inherit"
|
|
1933
|
+
edge="start"
|
|
1934
|
+
style={styles.menuButton}
|
|
1935
|
+
onClick={e => {
|
|
1936
|
+
e.stopPropagation();
|
|
1937
|
+
if (this.suppressDeleteConfirm > Date.now()) {
|
|
1938
|
+
this.deleteItem(this.state.selected);
|
|
1939
|
+
} else {
|
|
1940
|
+
this.setState({ deleteItem: this.state.selected });
|
|
1941
|
+
}
|
|
1942
|
+
}}
|
|
1943
|
+
size="small"
|
|
1944
|
+
>
|
|
1945
|
+
<DeleteIcon fontSize="small" />
|
|
1946
|
+
</IconButton>
|
|
1947
|
+
</span>
|
|
1948
|
+
</Tooltip>
|
|
1949
|
+
) : null}
|
|
1950
|
+
</Toolbar>
|
|
1951
|
+
);
|
|
1878
1952
|
}
|
|
1879
1953
|
|
|
1880
|
-
findItem(
|
|
1881
|
-
id: string,
|
|
1882
|
-
folders?: Folders | null,
|
|
1883
|
-
) {
|
|
1954
|
+
findItem(id: string, folders?: Folders | null): null | FolderOrFileItem {
|
|
1884
1955
|
folders = folders || this.state.folders;
|
|
1885
1956
|
if (!folders) {
|
|
1886
1957
|
return null;
|
|
@@ -1891,10 +1962,10 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1891
1962
|
if (!folders[parentFolder]) {
|
|
1892
1963
|
return null;
|
|
1893
1964
|
}
|
|
1894
|
-
return folders[parentFolder].find(item => item.id === id);
|
|
1965
|
+
return folders[parentFolder].find(item => item.id === id) || null;
|
|
1895
1966
|
}
|
|
1896
1967
|
|
|
1897
|
-
renderInputDialog() {
|
|
1968
|
+
renderInputDialog(): React.JSX.Element | null {
|
|
1898
1969
|
if (this.state.addFolder) {
|
|
1899
1970
|
const parentFolder = this.findFirstFolder(this.state.selected);
|
|
1900
1971
|
|
|
@@ -1903,59 +1974,59 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1903
1974
|
return null;
|
|
1904
1975
|
}
|
|
1905
1976
|
|
|
1906
|
-
return
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
(
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
if (name) {
|
|
1921
|
-
const folders: Folders = {};
|
|
1922
|
-
Object.keys(this.state.folders).forEach(
|
|
1923
|
-
folder => (folders[folder] = this.state.folders[folder]),
|
|
1924
|
-
);
|
|
1925
|
-
const parent = this.findItem(parentFolder);
|
|
1926
|
-
const id = `${parentFolder}/${name}`;
|
|
1927
|
-
folders[parentFolder].push({
|
|
1928
|
-
id,
|
|
1929
|
-
level: (parent?.level || 0) + 1,
|
|
1930
|
-
name,
|
|
1931
|
-
folder: true,
|
|
1932
|
-
temp: true,
|
|
1933
|
-
});
|
|
1934
|
-
|
|
1935
|
-
folders[parentFolder].sort(sortFolders);
|
|
1936
|
-
|
|
1937
|
-
folders[id] = [];
|
|
1938
|
-
const expanded = [...this.state.expanded];
|
|
1939
|
-
if (!expanded.includes(parentFolder)) {
|
|
1940
|
-
expanded.push(parentFolder);
|
|
1941
|
-
expanded.sort();
|
|
1942
|
-
}
|
|
1943
|
-
this.localStorage.setItem(
|
|
1944
|
-
'files.expanded',
|
|
1945
|
-
JSON.stringify(expanded),
|
|
1946
|
-
);
|
|
1947
|
-
this.setState({ addFolder: false, folders, expanded }, () => this.select(id));
|
|
1948
|
-
} else {
|
|
1949
|
-
this.setState({ addFolder: false });
|
|
1977
|
+
return (
|
|
1978
|
+
<TextInputDialog
|
|
1979
|
+
key="inputDialog"
|
|
1980
|
+
applyText={this.props.t('ra_Create')}
|
|
1981
|
+
cancelText={this.props.t('ra_Cancel')}
|
|
1982
|
+
titleText={this.props.t('ra_Create new folder in %s', this.state.selected)}
|
|
1983
|
+
promptText={this.props.t(
|
|
1984
|
+
'ra_If no file will be created in the folder, it will disappear after the browser closed',
|
|
1985
|
+
)}
|
|
1986
|
+
labelText={this.props.t('ra_Folder name')}
|
|
1987
|
+
verify={(text: string) =>
|
|
1988
|
+
this.state.folders[parentFolder].find(item => item.name === text)
|
|
1989
|
+
? ''
|
|
1990
|
+
: this.props.t('ra_Duplicate name')
|
|
1950
1991
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1992
|
+
onClose={(name: string | null) => {
|
|
1993
|
+
if (name) {
|
|
1994
|
+
const folders: Folders = {};
|
|
1995
|
+
Object.keys(this.state.folders).forEach(
|
|
1996
|
+
folder => (folders[folder] = this.state.folders[folder]),
|
|
1997
|
+
);
|
|
1998
|
+
const parent = this.findItem(parentFolder);
|
|
1999
|
+
const id = `${parentFolder}/${name}`;
|
|
2000
|
+
folders[parentFolder].push({
|
|
2001
|
+
id,
|
|
2002
|
+
level: (parent?.level || 0) + 1,
|
|
2003
|
+
name,
|
|
2004
|
+
folder: true,
|
|
2005
|
+
temp: true,
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
folders[parentFolder].sort(sortFolders);
|
|
2009
|
+
|
|
2010
|
+
folders[id] = [];
|
|
2011
|
+
const expanded = [...this.state.expanded];
|
|
2012
|
+
if (!expanded.includes(parentFolder)) {
|
|
2013
|
+
expanded.push(parentFolder);
|
|
2014
|
+
expanded.sort();
|
|
2015
|
+
}
|
|
2016
|
+
this.localStorage.setItem('files.expanded', JSON.stringify(expanded));
|
|
2017
|
+
this.setState({ addFolder: false, folders, expanded }, () => this.select(id));
|
|
2018
|
+
} else {
|
|
2019
|
+
this.setState({ addFolder: false });
|
|
2020
|
+
}
|
|
2021
|
+
}}
|
|
2022
|
+
replace={(text: string) => text.replace(/[^-_\w]/, '_')}
|
|
2023
|
+
/>
|
|
2024
|
+
);
|
|
1954
2025
|
}
|
|
1955
2026
|
return null;
|
|
1956
2027
|
}
|
|
1957
2028
|
|
|
1958
|
-
componentDidUpdate(/* prevProps , prevState, snapshot */) {
|
|
2029
|
+
componentDidUpdate(/* prevProps , prevState, snapshot */): void {
|
|
1959
2030
|
if (this.setOpacityTimer) {
|
|
1960
2031
|
clearTimeout(this.setOpacityTimer);
|
|
1961
2032
|
}
|
|
@@ -1968,7 +2039,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1968
2039
|
}, 100);
|
|
1969
2040
|
}
|
|
1970
2041
|
|
|
1971
|
-
findFirstFolder(id: string) {
|
|
2042
|
+
findFirstFolder(id: string): string | null {
|
|
1972
2043
|
let parentFolder = id;
|
|
1973
2044
|
const item = this.findItem(parentFolder);
|
|
1974
2045
|
// find folder
|
|
@@ -1997,12 +2068,13 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
1997
2068
|
const adapterName = parts.shift();
|
|
1998
2069
|
try {
|
|
1999
2070
|
await this.props.socket.writeFile64(adapterName || '', parts.join('/'), data);
|
|
2000
|
-
} catch (e) {
|
|
2001
|
-
|
|
2071
|
+
} catch (e: unknown) {
|
|
2072
|
+
const knownError = e as Error;
|
|
2073
|
+
window.alert(`Cannot write file: ${knownError?.message}`);
|
|
2002
2074
|
}
|
|
2003
2075
|
}
|
|
2004
2076
|
|
|
2005
|
-
renderUpload() {
|
|
2077
|
+
renderUpload(): React.JSX.Element[] | null {
|
|
2006
2078
|
if (this.state.uploadFile) {
|
|
2007
2079
|
return [
|
|
2008
2080
|
<Fab
|
|
@@ -2034,46 +2106,47 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2034
2106
|
} else {
|
|
2035
2107
|
const id = `${parentFolder}/${file.name}`;
|
|
2036
2108
|
|
|
2037
|
-
this.uploadFile(id, reader.result as string)
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
this.
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
JSON.stringify(expanded),
|
|
2050
|
-
);
|
|
2051
|
-
}
|
|
2052
|
-
this.setState({ expanded }, () => this.select(id));
|
|
2053
|
-
} else {
|
|
2054
|
-
setTimeout(
|
|
2055
|
-
() =>
|
|
2056
|
-
this.browseFolder(parentFolder, null, false, true)
|
|
2057
|
-
.then(folders => {
|
|
2058
|
-
// open current folder
|
|
2059
|
-
const expanded = [...this.state.expanded];
|
|
2060
|
-
if (!expanded.includes(parentFolder)) {
|
|
2061
|
-
expanded.push(parentFolder);
|
|
2062
|
-
expanded.sort();
|
|
2063
|
-
this.localStorage.setItem(
|
|
2064
|
-
'files.expanded',
|
|
2065
|
-
JSON.stringify(expanded),
|
|
2066
|
-
);
|
|
2067
|
-
}
|
|
2068
|
-
this.setState({ folders, expanded }, () =>
|
|
2069
|
-
this.select(id));
|
|
2070
|
-
}),
|
|
2071
|
-
500,
|
|
2109
|
+
void this.uploadFile(id, reader.result as string).then(() => {
|
|
2110
|
+
if (!--count) {
|
|
2111
|
+
this.setState({ uploadFile: false }, () => {
|
|
2112
|
+
if (this.supportSubscribes) {
|
|
2113
|
+
// open current folder
|
|
2114
|
+
const expanded = [...this.state.expanded];
|
|
2115
|
+
if (!expanded.includes(parentFolder)) {
|
|
2116
|
+
expanded.push(parentFolder);
|
|
2117
|
+
expanded.sort();
|
|
2118
|
+
this.localStorage.setItem(
|
|
2119
|
+
'files.expanded',
|
|
2120
|
+
JSON.stringify(expanded),
|
|
2072
2121
|
);
|
|
2073
2122
|
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2123
|
+
this.setState({ expanded }, () => this.select(id));
|
|
2124
|
+
} else {
|
|
2125
|
+
setTimeout(
|
|
2126
|
+
() =>
|
|
2127
|
+
this.browseFolder(parentFolder, null, false, true).then(
|
|
2128
|
+
folders => {
|
|
2129
|
+
// open current folder
|
|
2130
|
+
const expanded = [...this.state.expanded];
|
|
2131
|
+
if (!expanded.includes(parentFolder)) {
|
|
2132
|
+
expanded.push(parentFolder);
|
|
2133
|
+
expanded.sort();
|
|
2134
|
+
this.localStorage.setItem(
|
|
2135
|
+
'files.expanded',
|
|
2136
|
+
JSON.stringify(expanded),
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
this.setState({ folders, expanded }, () =>
|
|
2140
|
+
this.select(id),
|
|
2141
|
+
);
|
|
2142
|
+
},
|
|
2143
|
+
),
|
|
2144
|
+
500,
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2077
2150
|
}
|
|
2078
2151
|
};
|
|
2079
2152
|
|
|
@@ -2081,27 +2154,32 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2081
2154
|
});
|
|
2082
2155
|
}}
|
|
2083
2156
|
>
|
|
2084
|
-
{({ getRootProps, getInputProps }) =>
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
<
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2157
|
+
{({ getRootProps, getInputProps }) => (
|
|
2158
|
+
<div
|
|
2159
|
+
style={{
|
|
2160
|
+
...styles.uploadDiv,
|
|
2161
|
+
...(this.state.uploadFile === 'dragging' ? styles.uploadDivDragging : undefined),
|
|
2162
|
+
}}
|
|
2163
|
+
{...getRootProps()}
|
|
2164
|
+
>
|
|
2165
|
+
<input {...getInputProps()} />
|
|
2166
|
+
<Box
|
|
2167
|
+
component="div"
|
|
2168
|
+
sx={styles.uploadCenterDiv}
|
|
2169
|
+
>
|
|
2170
|
+
<div style={styles.uploadCenterTextAndIcon}>
|
|
2171
|
+
<UploadIcon style={styles.uploadCenterIcon} />
|
|
2172
|
+
<div style={styles.uploadCenterText}>
|
|
2173
|
+
{this.state.uploadFile === 'dragging'
|
|
2174
|
+
? this.props.t('ra_Drop file here')
|
|
2175
|
+
: this.props.t(
|
|
2176
|
+
'ra_Place your files here or click here to open the browse dialog',
|
|
2177
|
+
)}
|
|
2178
|
+
</div>
|
|
2101
2179
|
</div>
|
|
2102
|
-
</
|
|
2103
|
-
</
|
|
2104
|
-
|
|
2180
|
+
</Box>
|
|
2181
|
+
</div>
|
|
2182
|
+
)}
|
|
2105
2183
|
</Dropzone>,
|
|
2106
2184
|
];
|
|
2107
2185
|
}
|
|
@@ -2115,30 +2193,29 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2115
2193
|
this.state.folders[id]
|
|
2116
2194
|
? Promise.all(this.state.folders[id].map(_item => this.deleteRecursive(_item.id)))
|
|
2117
2195
|
: Promise.resolve()
|
|
2118
|
-
)
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
parentFolder.splice(pos, 1);
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
this.select(parentId, null, () => this.setState({ folders }));
|
|
2196
|
+
).then(() => {
|
|
2197
|
+
// If it is a folder of second level
|
|
2198
|
+
if (item.level >= 1) {
|
|
2199
|
+
const parts = id.split('/');
|
|
2200
|
+
const adapter = parts.shift();
|
|
2201
|
+
void this.props.socket.deleteFolder(adapter || '', parts.join('/')).then(() => {
|
|
2202
|
+
// remove this folder
|
|
2203
|
+
const folders = JSON.parse(JSON.stringify(this.state.folders));
|
|
2204
|
+
delete folders[item.id];
|
|
2205
|
+
// delete folder from parent item
|
|
2206
|
+
const parentId = getParentDir(item.id);
|
|
2207
|
+
const parentFolder = folders[parentId];
|
|
2208
|
+
if (parentFolder) {
|
|
2209
|
+
const pos = parentFolder.findIndex((f: FolderOrFileItem) => f.id === item.id);
|
|
2210
|
+
if (pos !== -1) {
|
|
2211
|
+
parentFolder.splice(pos, 1);
|
|
2138
2212
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2213
|
+
|
|
2214
|
+
this.select(parentId, null, () => this.setState({ folders }));
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2142
2219
|
}
|
|
2143
2220
|
|
|
2144
2221
|
const parts = id.split('/');
|
|
@@ -2151,128 +2228,135 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2151
2228
|
return Promise.resolve();
|
|
2152
2229
|
}
|
|
2153
2230
|
|
|
2154
|
-
deleteItem(deleteItem: string) {
|
|
2231
|
+
deleteItem(deleteItem: string): void {
|
|
2155
2232
|
deleteItem = deleteItem || this.state.deleteItem;
|
|
2156
2233
|
|
|
2157
2234
|
this.setState({ deleteItem: '' }, () =>
|
|
2158
|
-
this.deleteRecursive(deleteItem)
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
}
|
|
2235
|
+
this.deleteRecursive(deleteItem).then(() => {
|
|
2236
|
+
const newState: Partial<FileBrowserState> = {};
|
|
2237
|
+
const pos = this.state.expanded.indexOf(deleteItem);
|
|
2238
|
+
if (pos !== -1) {
|
|
2239
|
+
const expanded = [...this.state.expanded];
|
|
2240
|
+
expanded.splice(pos, 1);
|
|
2241
|
+
this.localStorage.setItem('files.expanded', JSON.stringify(expanded));
|
|
2242
|
+
newState.expanded = expanded;
|
|
2243
|
+
}
|
|
2168
2244
|
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2245
|
+
if (this.state.selected === deleteItem) {
|
|
2246
|
+
const parts = this.state.selected.split('/');
|
|
2247
|
+
parts.pop();
|
|
2248
|
+
newState.selected = parts.join('/');
|
|
2249
|
+
}
|
|
2174
2250
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2251
|
+
if (!this.supportSubscribes) {
|
|
2252
|
+
const parentFolder = this.findFirstFolder(deleteItem);
|
|
2253
|
+
const folders: Folders = {};
|
|
2178
2254
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2255
|
+
Object.keys(this.state.folders).forEach(name => {
|
|
2256
|
+
if (name !== parentFolder && !name.startsWith(`${parentFolder}/`)) {
|
|
2257
|
+
folders[name] = this.state.folders[name];
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2184
2260
|
|
|
2185
|
-
|
|
2261
|
+
newState.folders = folders;
|
|
2186
2262
|
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
}
|
|
2263
|
+
this.setState(newState as FileBrowserState, () =>
|
|
2264
|
+
setTimeout(() => {
|
|
2265
|
+
this.browseFolders([...this.state.expanded], folders)
|
|
2266
|
+
.then(_folders => this.setState({ folders: _folders }))
|
|
2267
|
+
.catch(e => console.error(e));
|
|
2268
|
+
}, 200),
|
|
2269
|
+
);
|
|
2270
|
+
} else {
|
|
2271
|
+
this.setState(newState as FileBrowserState);
|
|
2272
|
+
}
|
|
2273
|
+
}),
|
|
2274
|
+
);
|
|
2197
2275
|
}
|
|
2198
2276
|
|
|
2199
|
-
renderDeleteDialog() {
|
|
2277
|
+
renderDeleteDialog(): React.JSX.Element | null {
|
|
2200
2278
|
if (this.state.deleteItem) {
|
|
2201
|
-
return
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
<
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
<
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2279
|
+
return (
|
|
2280
|
+
<Dialog
|
|
2281
|
+
key="deleteDialog"
|
|
2282
|
+
open={!0}
|
|
2283
|
+
onClose={() => this.setState({ deleteItem: '' })}
|
|
2284
|
+
aria-labelledby="ar_dialog_file_delete_title"
|
|
2285
|
+
>
|
|
2286
|
+
<DialogTitle id="ar_dialog_file_delete_title">
|
|
2287
|
+
{this.props.t('ra_Confirm deletion of %s', this.state.deleteItem.split('/').pop() as string)}
|
|
2288
|
+
</DialogTitle>
|
|
2289
|
+
<DialogContent>
|
|
2290
|
+
<DialogContentText>{this.props.t('ra_Are you sure?')}</DialogContentText>
|
|
2291
|
+
</DialogContent>
|
|
2292
|
+
<DialogActions>
|
|
2293
|
+
<Button
|
|
2294
|
+
color="grey"
|
|
2295
|
+
variant="contained"
|
|
2296
|
+
onClick={() => {
|
|
2297
|
+
this.suppressDeleteConfirm = Date.now() + 60000 * 5;
|
|
2298
|
+
this.deleteItem('');
|
|
2299
|
+
}}
|
|
2300
|
+
>
|
|
2301
|
+
{this.props.t('ra_Delete (no confirm for 5 mins)')}
|
|
2302
|
+
</Button>
|
|
2303
|
+
<Button
|
|
2304
|
+
variant="contained"
|
|
2305
|
+
onClick={() => this.deleteItem('')}
|
|
2306
|
+
color="primary"
|
|
2307
|
+
autoFocus
|
|
2308
|
+
>
|
|
2309
|
+
{this.props.t('ra_Delete')}
|
|
2310
|
+
</Button>
|
|
2311
|
+
<Button
|
|
2312
|
+
variant="contained"
|
|
2313
|
+
onClick={() => this.setState({ deleteItem: '' })}
|
|
2314
|
+
color="grey"
|
|
2315
|
+
>
|
|
2316
|
+
{this.props.t('ra_Cancel')}
|
|
2317
|
+
</Button>
|
|
2318
|
+
</DialogActions>
|
|
2319
|
+
</Dialog>
|
|
2320
|
+
);
|
|
2241
2321
|
}
|
|
2242
|
-
return
|
|
2322
|
+
return null;
|
|
2243
2323
|
}
|
|
2244
2324
|
|
|
2245
|
-
renderViewDialog() {
|
|
2246
|
-
return this.state.viewer ?
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2325
|
+
renderViewDialog(): React.JSX.Element | null {
|
|
2326
|
+
return this.state.viewer ? (
|
|
2327
|
+
<FileViewer
|
|
2328
|
+
supportSubscribes={this.supportSubscribes}
|
|
2329
|
+
key={this.state.viewer}
|
|
2330
|
+
href={this.state.viewer}
|
|
2331
|
+
formatEditFile={this.state.formatEditFile}
|
|
2332
|
+
themeType={this.props.themeType}
|
|
2333
|
+
setStateBackgroundImage={this.setStateBackgroundImage}
|
|
2334
|
+
getStyleBackgroundImage={this.getStyleBackgroundImage}
|
|
2335
|
+
t={this.props.t}
|
|
2336
|
+
socket={this.props.socket}
|
|
2337
|
+
lang={this.props.lang}
|
|
2338
|
+
expertMode={this.state.expertMode}
|
|
2339
|
+
onClose={() => this.setState({ viewer: '', formatEditFile: '' })}
|
|
2340
|
+
/>
|
|
2341
|
+
) : null;
|
|
2260
2342
|
}
|
|
2261
2343
|
|
|
2262
|
-
renderError() {
|
|
2344
|
+
renderError(): React.JSX.Element | null {
|
|
2263
2345
|
if (this.state.errorText) {
|
|
2264
|
-
return
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2346
|
+
return (
|
|
2347
|
+
<ErrorDialog
|
|
2348
|
+
key="errorDialog"
|
|
2349
|
+
text={this.state.errorText}
|
|
2350
|
+
onClose={() => this.setState({ errorText: '' })}
|
|
2351
|
+
/>
|
|
2352
|
+
);
|
|
2269
2353
|
}
|
|
2270
2354
|
return null;
|
|
2271
2355
|
}
|
|
2272
2356
|
|
|
2273
2357
|
// used in tabs/Files
|
|
2274
2358
|
// eslint-disable-next-line react/no-unused-class-component-methods
|
|
2275
|
-
updateItemsAcl(info: FolderOrFileItem[]) {
|
|
2359
|
+
updateItemsAcl(info: FolderOrFileItem[]): void {
|
|
2276
2360
|
this.cacheFolders = this.cacheFolders || JSON.parse(JSON.stringify(this.state.folders));
|
|
2277
2361
|
let changed;
|
|
2278
2362
|
|
|
@@ -2296,7 +2380,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2296
2380
|
}
|
|
2297
2381
|
}
|
|
2298
2382
|
|
|
2299
|
-
changeToPath() {
|
|
2383
|
+
changeToPath(): void {
|
|
2300
2384
|
setTimeout(() => {
|
|
2301
2385
|
if (this.state.path !== this.state.selected && (!this.lastSelect || Date.now() - this.lastSelect > 100)) {
|
|
2302
2386
|
let folder = this.state.path;
|
|
@@ -2313,79 +2397,100 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2313
2397
|
err === NOT_FOUND
|
|
2314
2398
|
? this.props.t('ra_Cannot find "%s"', folder)
|
|
2315
2399
|
: this.props.t('ra_Cannot read "%s"', folder),
|
|
2316
|
-
})
|
|
2400
|
+
}),
|
|
2401
|
+
);
|
|
2317
2402
|
} else {
|
|
2318
2403
|
resolve(true);
|
|
2319
2404
|
}
|
|
2320
2405
|
})
|
|
2321
|
-
.then(
|
|
2322
|
-
result
|
|
2406
|
+
.then(
|
|
2407
|
+
result =>
|
|
2408
|
+
result &&
|
|
2409
|
+
this.setState({ selected: this.state.path, currentDir: folder, pathFocus: false }),
|
|
2410
|
+
)
|
|
2411
|
+
.catch(e => console.error(e));
|
|
2323
2412
|
} else if (!this.lastSelect || Date.now() - this.lastSelect > 100) {
|
|
2324
2413
|
this.setState({ pathFocus: false });
|
|
2325
2414
|
}
|
|
2326
2415
|
}, 100);
|
|
2327
2416
|
}
|
|
2328
2417
|
|
|
2329
|
-
renderBreadcrumb() {
|
|
2418
|
+
renderBreadcrumb(): React.JSX.Element {
|
|
2330
2419
|
const parts = this.state.currentDir.startsWith('/')
|
|
2331
2420
|
? this.state.currentDir.split('/')
|
|
2332
2421
|
: `/${this.state.currentDir}`.split('/');
|
|
2333
2422
|
const p: string[] = [];
|
|
2334
|
-
return
|
|
2335
|
-
{
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2423
|
+
return (
|
|
2424
|
+
<Breadcrumbs style={{ paddingLeft: 8 }}>
|
|
2425
|
+
{parts.map((part, i) => {
|
|
2426
|
+
if (part) {
|
|
2427
|
+
p.push(part);
|
|
2428
|
+
}
|
|
2429
|
+
const path = p.join('/');
|
|
2430
|
+
if (i < parts.length - 1) {
|
|
2431
|
+
return (
|
|
2432
|
+
<Box
|
|
2433
|
+
component="div"
|
|
2434
|
+
key={`${this.state.selected}_${i}`}
|
|
2435
|
+
sx={styles.pathDivBreadcrumbDir}
|
|
2436
|
+
onClick={e => this.changeFolder(e, path || '/')}
|
|
2437
|
+
>
|
|
2438
|
+
{part || this.props.t('ra_Root')}
|
|
2439
|
+
</Box>
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2350
2442
|
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2443
|
+
return (
|
|
2444
|
+
<div
|
|
2445
|
+
style={styles.pathDivBreadcrumbSelected}
|
|
2446
|
+
key={`${this.state.selected}_${i}`}
|
|
2447
|
+
onClick={() => this.setState({ pathFocus: true })}
|
|
2448
|
+
>
|
|
2449
|
+
{part}
|
|
2450
|
+
</div>
|
|
2451
|
+
);
|
|
2452
|
+
})}
|
|
2453
|
+
</Breadcrumbs>
|
|
2454
|
+
);
|
|
2360
2455
|
}
|
|
2361
2456
|
|
|
2362
|
-
renderPath() {
|
|
2363
|
-
return
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2457
|
+
renderPath(): React.JSX.Element {
|
|
2458
|
+
return (
|
|
2459
|
+
<Box
|
|
2460
|
+
component="div"
|
|
2461
|
+
key="path"
|
|
2462
|
+
sx={styles.pathDiv}
|
|
2463
|
+
>
|
|
2464
|
+
{this.state.pathFocus ? (
|
|
2465
|
+
<Input
|
|
2466
|
+
value={this.state.path}
|
|
2467
|
+
onKeyDown={e => {
|
|
2468
|
+
if (e.key === 'Enter') {
|
|
2469
|
+
this.changeToPath();
|
|
2470
|
+
} else if (e.key === 'Escape') {
|
|
2471
|
+
this.setState({ pathFocus: false });
|
|
2472
|
+
}
|
|
2473
|
+
}}
|
|
2474
|
+
endAdornment={
|
|
2475
|
+
<IconButton
|
|
2476
|
+
size="small"
|
|
2477
|
+
onClick={() => this.changeToPath()}
|
|
2478
|
+
>
|
|
2479
|
+
<EnterIcon />
|
|
2480
|
+
</IconButton>
|
|
2372
2481
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
/>
|
|
2383
|
-
:
|
|
2384
|
-
this.renderBreadcrumb()}
|
|
2385
|
-
</Box>;
|
|
2482
|
+
onBlur={() => this.changeToPath()}
|
|
2483
|
+
onChange={e => this.setState({ path: e.target.value })}
|
|
2484
|
+
style={styles.pathDivInput}
|
|
2485
|
+
/>
|
|
2486
|
+
) : (
|
|
2487
|
+
this.renderBreadcrumb()
|
|
2488
|
+
)}
|
|
2489
|
+
</Box>
|
|
2490
|
+
);
|
|
2386
2491
|
}
|
|
2387
2492
|
|
|
2388
|
-
render() {
|
|
2493
|
+
render(): React.JSX.Element {
|
|
2389
2494
|
if (!this.props.ready) {
|
|
2390
2495
|
return <LinearProgress />;
|
|
2391
2496
|
}
|
|
@@ -2395,48 +2500,50 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
|
|
|
2395
2500
|
setTimeout(() => {
|
|
2396
2501
|
this.setState({ loadAllFolders: false, folders: {} }, () => {
|
|
2397
2502
|
this.foldersLoading = false;
|
|
2398
|
-
this.loadFolders()
|
|
2399
|
-
.catch(error => console.error(`Cannot load folders: ${error}`));
|
|
2503
|
+
this.loadFolders().catch(error => console.error(`Cannot load folders: ${error}`));
|
|
2400
2504
|
});
|
|
2401
2505
|
}, 300);
|
|
2402
2506
|
}
|
|
2403
2507
|
|
|
2404
|
-
return
|
|
2405
|
-
style={{ ...styles.root, ...this.props.style }}
|
|
2406
|
-
className={this.props.className}
|
|
2407
|
-
>
|
|
2408
|
-
{this.props.showToolbar ? this.renderToolbar() : null}
|
|
2409
|
-
{this.state.viewType === TILE ? this.renderPath() : null}
|
|
2508
|
+
return (
|
|
2410
2509
|
<div
|
|
2411
|
-
style={{
|
|
2412
|
-
|
|
2413
|
-
...styles[`filesDiv${this.state.viewType}`],
|
|
2414
|
-
}}
|
|
2415
|
-
onClick={e => {
|
|
2416
|
-
if (this.state.viewType !== TABLE) {
|
|
2417
|
-
if (this.state.selected !== (this.state.currentDir || '/')) {
|
|
2418
|
-
this.changeFolder(e, this.state.currentDir || '/');
|
|
2419
|
-
} else {
|
|
2420
|
-
e.stopPropagation();
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
}}
|
|
2510
|
+
style={{ ...styles.root, ...this.props.style }}
|
|
2511
|
+
className={this.props.className}
|
|
2424
2512
|
>
|
|
2425
|
-
{this.
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2513
|
+
{this.props.showToolbar ? this.renderToolbar() : null}
|
|
2514
|
+
{this.state.viewType === TILE ? this.renderPath() : null}
|
|
2515
|
+
<div
|
|
2516
|
+
style={{
|
|
2517
|
+
...styles.filesDiv,
|
|
2518
|
+
...styles[`filesDiv${this.state.viewType}`],
|
|
2519
|
+
}}
|
|
2520
|
+
onClick={e => {
|
|
2521
|
+
if (this.state.viewType !== TABLE) {
|
|
2522
|
+
if (this.state.selected !== (this.state.currentDir || '/')) {
|
|
2523
|
+
this.changeFolder(e, this.state.currentDir || '/');
|
|
2524
|
+
} else {
|
|
2525
|
+
e.stopPropagation();
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}}
|
|
2529
|
+
>
|
|
2530
|
+
{this.state.viewType === TABLE
|
|
2531
|
+
? this.renderItems('/')
|
|
2532
|
+
: this.renderItems(this.state.currentDir || '/')}
|
|
2533
|
+
{this.state.viewType !== TABLE ? (
|
|
2534
|
+
<div style={styles.filesDivHint}>{this.props.t('ra_select_folder_hint')}</div>
|
|
2535
|
+
) : null}
|
|
2536
|
+
</div>
|
|
2537
|
+
{this.props.allowUpload ? this.renderInputDialog() : null}
|
|
2538
|
+
{this.props.allowUpload ? this.renderUpload() : null}
|
|
2539
|
+
{this.props.allowDelete ? this.renderDeleteDialog() : null}
|
|
2540
|
+
{this.props.allowView ? this.renderViewDialog() : null}
|
|
2541
|
+
{this.state.modalEditOfAccess && this.props.modalEditOfAccessControl
|
|
2542
|
+
? this.props.modalEditOfAccessControl(this)
|
|
2543
|
+
: null}
|
|
2544
|
+
{this.renderError()}
|
|
2430
2545
|
</div>
|
|
2431
|
-
|
|
2432
|
-
{this.props.allowUpload ? this.renderUpload() : null}
|
|
2433
|
-
{this.props.allowDelete ? this.renderDeleteDialog() : null}
|
|
2434
|
-
{this.props.allowView ? this.renderViewDialog() : null}
|
|
2435
|
-
{this.state.modalEditOfAccess && this.props.modalEditOfAccessControl
|
|
2436
|
-
? this.props.modalEditOfAccessControl(this)
|
|
2437
|
-
: null}
|
|
2438
|
-
{this.renderError()}
|
|
2439
|
-
</div>;
|
|
2546
|
+
);
|
|
2440
2547
|
}
|
|
2441
2548
|
}
|
|
2442
2549
|
|