From da546998157a4b48a8a8a1db40c1ed6a1d42f4bc Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Mon, 13 Jul 2020 15:41:10 +0200 Subject: [PATCH] Add drag'n'drop to filemanager --- src/filemanager.html | 6 +-- src/js/filemanager.js | 111 ++++++++++++++++++++++++++++++++++++++++-- src/theme.scss | 21 ++++++++ 3 files changed, 132 insertions(+), 6 deletions(-) diff --git a/src/filemanager.html b/src/filemanager.html index aecb4d0a1..e8dbe799d 100644 --- a/src/filemanager.html +++ b/src/filemanager.html @@ -53,7 +53,7 @@ - + Cloudron is offline. Reconnecting... @@ -181,7 +181,7 @@
- +
@@ -195,7 +195,7 @@ - +
 
No files
diff --git a/src/js/filemanager.js b/src/js/filemanager.js index bfedd4adb..677d0351f 100644 --- a/src/js/filemanager.js +++ b/src/js/filemanager.js @@ -3,7 +3,7 @@ /* global angular, $, async */ // create main application module -var app = angular.module('Application', ['angular-md5', 'ui-notification']); +var app = angular.module('Application', ['angular-md5', 'ui-notification', 'ngDrag']); angular.module('Application').filter('prettyOwner', function () { return function (uid) { @@ -21,6 +21,35 @@ app.config(function ($sceProvider) { $sceProvider.enabled(false); }); +// https://stackoverflow.com/questions/25621321/angularjs-ng-drag +var ngDragEventDirectives = {}; +angular.forEach( + 'drag dragend dragenter dragexit dragleave dragover dragstart drop'.split(' '), + function(eventName) { + var directiveName = 'ng' + eventName.charAt(0).toUpperCase() + eventName.slice(1); + + ngDragEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { + return { + restrict: 'A', + compile: function($element, attr) { + var fn = $parse(attr[directiveName], null, true); + + return function ngDragEventHandler(scope, element) { + element.on(eventName, function(event) { + var callback = function() { + fn(scope, {$event: event}); + }; + + scope.$apply(callback); + }); + }; + } + }; + }]; + } +); +angular.module('ngDrag', []).directive(ngDragEventDirectives); + app.controller('FileManagerController', ['$scope', 'Client', function ($scope, Client) { var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {}); @@ -44,6 +73,80 @@ app.controller('FileManagerController', ['$scope', 'Client', function ($scope, C return '/' + filePath; } + $scope.dropToBody = false; + + $scope.dragEnter = function ($event, entry) { + $event.originalEvent.stopPropagation(); + $event.originalEvent.preventDefault(); + + if (entry && entry.isDirectory) entry.hovered = true; + else $scope.dropToBody = true; + + $event.originalEvent.dataTransfer.dropEffect = 'copy'; + } + + $scope.dragExit = function ($event, entry) { + $event.originalEvent.stopPropagation(); + $event.originalEvent.preventDefault(); + + if (entry && entry.isDirectory) entry.hovered = false; + $scope.dropToBody = false; + + $event.originalEvent.dataTransfer.dropEffect = 'copy'; + } + + $scope.drop = function (event, entry) { + event.originalEvent.stopPropagation(); + event.originalEvent.preventDefault(); + + if (!event.originalEvent.dataTransfer.items[0]) return; + + var targetFolder = entry && entry.isDirectory ? entry.fileName : ''; + + // figure if a folder was dropped on a modern browser, in this case the first would have to be a directory + var folderItem; + try { + folderItem = event.originalEvent.dataTransfer.items[0].webkitGetAsEntry(); + if (folderItem.isFile) return uploadFiles(event.originalEvent.dataTransfer.files, targetFolder); + } catch (e) { + return uploadFiles(event.originalEvent.dataTransfer.files, targetFolder); + } + + // if we got here we have a folder drop and a modern browser + // now traverse the folder tree and create a file list + $scope.uploadStatus.busy = true; + $scope.uploadStatus.count = 0; + + var fileList = []; + function traverseFileTree(item, path, callback) { + if (item.isFile) { + // Get file + item.file(function (file) { + fileList.push(file); + ++$scope.uploadStatus.count; + callback(); + }); + } else if (item.isDirectory) { + // Get folder contents + var dirReader = item.createReader(); + dirReader.readEntries(function (entries) { + async.each(entries, function (entry, callback) { + traverseFileTree(entry, path + item.name + '/', callback); + }, callback); + }); + } + } + + traverseFileTree(folderItem, '', function (error) { + $scope.uploadStatus.busy = false; + $scope.uploadStatus.count = 0; + + if (error) return console.error(error); + + uploadFiles(fileList, targetFolder); + }); + } + $scope.refresh = function () { Client.filesGet($scope.appId, $scope.cwd, function (error, result) { if (error) return Client.error(error); @@ -102,9 +205,11 @@ app.controller('FileManagerController', ['$scope', 'Client', function ($scope, C percentDone: 0 }; - function uploadFiles(files) { + function uploadFiles(files, targetFolder) { if (!files || !files.length) return; + targetFolder = targetFolder || ''; + // prevent it from getting closed $('#uploadModal').modal({ backdrop: 'static', @@ -123,7 +228,7 @@ app.controller('FileManagerController', ['$scope', 'Client', function ($scope, C } async.eachSeries(files, function (file, callback) { - var filePath = sanitize($scope.cwd + '/' + (file.webkitRelativePath || file.name)); + var filePath = sanitize($scope.cwd + '/' + targetFolder + '/' + (file.webkitRelativePath || file.name)); $scope.uploadStatus.fileName = file.name; diff --git a/src/theme.scss b/src/theme.scss index f4b63612c..85114c8ba 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -1681,6 +1681,27 @@ tag-input { } +// ---------------------------- +// FileManager +// ---------------------------- + +.filemanager { + + // those are for drag'n'drop + table { + border: 2px solid transparent; + } + + .entry-hovered { + border: 2px solid $brand-primary; + } + + .entry-hovered > td { + border: none !important; + } +} + + // ---------------------------- // Dark mode overrides // ----------------------------