Эх сурвалжийг харах

HUE-2272 [fb] Improved UX of the new jHueHdfsTree plugin

Implemented Create folder and path autocomplete
Enrico Berti 11 жил өмнө
parent
commit
77fad49

+ 12 - 0
apps/filebrowser/src/filebrowser/static/css/listdir_components.css

@@ -200,3 +200,15 @@
   -webkit-animation-fill-mode: both;
   animation-fill-mode: both;
 }
+
+#copyModal .modal-body, #moveModal .modal-body {
+  max-height: 300px;
+}
+
+#copyModal .modal-footer div, #moveModal .modal-footer div {
+  position: absolute;
+  left: 10px;
+  width: 400px;
+  text-align: left;
+  display: none;
+}

+ 35 - 24
apps/filebrowser/src/filebrowser/templates/listdir_components.mako

@@ -161,10 +161,10 @@ from django.utils.translation import ugettext as _
         <label>${_('New name')} <input id="newNameInput" name="dest_path" value="" type="text" class="input-xlarge"/></label>
       </div>
       <div class="modal-footer">
-        <div id="renameNameRequiredAlert" class="hide" style="position: absolute; left: 10;">
+        <div id="renameNameRequiredAlert" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important">${_('Name is required.')}</span>
         </div>
-        <div id="renameNameExistsAlert" class="hide" style="position: absolute; left: 10;">
+        <div id="renameNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
         </div>
         <input id="renameSrcPath" type="hidden" name="src_path" type="text">
@@ -199,7 +199,7 @@ from django.utils.translation import ugettext as _
       </div>
 
       <div class="modal-footer" style="padding-top: 10px;">
-        <div id="chownRequired" class="hide" style="position: absolute; left: 10;">
+        <div id="chownRequired" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important">${_('Name is required.')}</span>
         </div>
         <a class="btn" onclick="$('#changeOwnerModal').modal('hide');">${_('Cancel')}</a>
@@ -277,18 +277,18 @@ from django.utils.translation import ugettext as _
     <form id="moveForm" action="/filebrowser/move" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
       <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
-        <h3>${_('Move to:')}</h3>
+        <h3>${_('Move to')}</h3>
       </div>
       <div class="modal-body">
         <div id="moveHdfsTree" style="padding-left: 15px;"></div>
       </div>
       <div class="modal-footer">
-        <div style="position: absolute; left: 10;">
-          <input type="text" class="input-xlarge" value="" name="dest_path" id="moveDestination" placeholder="${_('Select a folder or paste a path...')}" />
+        <div>
+          <input type="text" class="input-xlarge disable-autsofocus" value="" name="dest_path" id="moveDestination" placeholder="${_('Select a folder or paste a path...')}" />
           <span id="moveNameRequiredAlert" class="hide label label-important">${_('Required')}</span>
         </div>
         <a class="btn" onclick="$('#moveModal').modal('hide');">${_('Cancel')}</a>
-        <input class="btn btn-primary" type="submit" value="${_('Move')}"/>
+        <input class="btn btn-primary disable-enter" type="submit" value="${_('Move')}"/>
       </div>
     </form>
   </div>
@@ -298,21 +298,18 @@ from django.utils.translation import ugettext as _
     <form id="copyForm" action="/filebrowser/copy" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
       <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
-        <h3>${_('Copy to:')}</h3>
+        <h3>${_('Copy to')}</h3>
       </div>
       <div class="modal-body">
         <div id="copyHdfsTree"></div>
       </div>
       <div class="modal-footer">
-        <div id="copyNameRequiredAlert" class="hide" style="position: absolute; left: 10;">
-          <span class="label label-important">${_('Please select a folder.')}</span>
-        </div>
-        <div style="position: absolute; left: 10;">
-          <input type="text" class="input-xlarge" value="" name="dest_path" id="copyDestination" placeholder="${_('Select a folder or paste a path...')}" />
+        <div>
+          <input type="text" class="input-xlarge disable-autofocus" value="" name="dest_path" id="copyDestination" placeholder="${_('Select a folder or paste a path...')}" />
           <span id="copyNameRequiredAlert" class="hide label label-important">${_('Required')}</span>
         </div>
         <a class="btn" onclick="$('#copyModal').modal('hide');">${_('Cancel')}</a>
-        <input class="btn btn-primary" type="submit" value="${_('Copy')}"/>
+        <input class="btn btn-primary disable-enter" type="submit" value="${_('Copy')}"/>
       </div>
     </form>
   </div>
@@ -362,10 +359,10 @@ from django.utils.translation import ugettext as _
         <input type="hidden" name="path" type="text" data-bind="value: currentPath"/>
       </div>
       <div class="modal-footer">
-        <div id="directoryNameRequiredAlert" class="hide" style="position: absolute; left: 10;">
+        <div id="directoryNameRequiredAlert" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important">${_('Directory name is required.')}</span>
         </div>
-        <div id="directoryNameExistsAlert" class="hide" style="position: absolute; left: 10;">
+        <div id="directoryNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
         </div>
         <a class="btn" href="#" data-dismiss="modal">${_('Cancel')}</a>
@@ -386,10 +383,10 @@ from django.utils.translation import ugettext as _
         <input type="hidden" name="path" type="text" data-bind="value: currentPath"/>
       </div>
       <div class="modal-footer">
-         <div id="fileNameRequiredAlert" class="alert-message error hide" style="position: absolute; left: 10;">
+         <div id="fileNameRequiredAlert" class="alert-message error hide" style="position: absolute; left: 10px;">
           <span class="label label-important">${_('File name is required.')}</span>
         </div>
-        <div id="fileNameExistsAlert" class="hide" style="position: absolute; left: 10;">
+        <div id="fileNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
           <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
         </div>
         <a class="btn" href="#" data-dismiss="modal">${_('Cancel')}</a>
@@ -878,6 +875,7 @@ from django.utils.translation import ugettext as _
         });
 
         $("#moveModal").on("shown", function(){
+          $("#moveModal .modal-footer div").show();
           $("#moveHdfsTree").remove();
           $("<div>").attr("id", "moveHdfsTree").appendTo($("#moveModal .modal-body"));
           $("#moveHdfsTree").jHueHdfsTree({
@@ -907,6 +905,7 @@ from django.utils.translation import ugettext as _
         });
 
         $("#copyModal").on("shown", function(){
+          $("#copyModal .modal-footer div").show();
           $("#copyHdfsTree").remove();
           $("<div>").attr("id", "copyHdfsTree").appendTo($("#copyModal .modal-body"));
           $("#copyHdfsTree").jHueHdfsTree({
@@ -918,6 +917,7 @@ from django.utils.translation import ugettext as _
           });
         });
 
+
       };
 
       self.changeOwner = function () {
@@ -1410,9 +1410,14 @@ from django.utils.translation import ugettext as _
         return true;
       });
 
-      $("#moveForm").on("focus", "input[name='dest_path']", function () {
-        $("#moveNameRequiredAlert").hide();
-        $("#moveForm").find("input[name='dest_path']").removeClass("fieldError");
+      $("#moveForm").bind("keypress", function(e) {
+        if (e.keyCode == 13) {
+           return false;
+        }
+       });
+
+      $("#moveDestination").jHueHdfsAutocomplete({
+        showOnFocus: true
       });
 
       $("#copyForm").on("submit", function () {
@@ -1425,9 +1430,15 @@ from django.utils.translation import ugettext as _
         return true;
       });
 
-      $("#copyForm").find("input[name='dest_path']").on("focus", function () {
-        $("#copyNameRequiredAlert").hide();
-        $("#copyForm").find("input[name='dest_path']").removeClass("fieldError");
+      $("#copyForm").bind("keypress", function(e) {
+        if (e.keyCode == 13) {
+           return false;
+        }
+       });
+
+
+      $("#copyDestination").jHueHdfsAutocomplete({
+        showOnFocus: true
       });
 
       $(".create-directory-link").click(function () {

+ 1 - 12
desktop/core/src/desktop/templates/common_footer.mako

@@ -117,17 +117,6 @@ from django.template.defaultfilters import escape, escapejs
           return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
         }
     %endif
-
-
-##    if (window.devicePixelRatio > 1) {
-##        $('img').each(function(i) {
-##            var lowres = $(this).attr('src');
-##            var highres = lowres.replace(".", "@2x.");
-##            $(this).attr('src', highres).attr("width", "24").attr("height", "24");
-##        });
-##    }
-
-
   });
 
   function resetPrimaryButtonsStatus() {
@@ -139,7 +128,7 @@ from django.template.defaultfilters import escape, escapejs
   $(".modal").on("shown", function () {
     _catchEnterKeyOnModals = true;
     // safe ux enhancement: focus on the first editable input
-    $(".modal:visible").find("input:visible:first").focus();
+    $(".modal:visible").find("input:not(.disable-autofocus):visible:first").focus();
   });
 
   $(".modal").on("hidden", function () {

+ 8 - 0
desktop/core/src/desktop/templates/common_header.mako

@@ -103,6 +103,14 @@ from django.utils.translation import ugettext as _
       user: "${ user.username }"
     };
 
+    jHueHdfsTreeGlobals = {
+      labels: {
+        CREATE_FOLDER: "${_('Create folder')}",
+        FOLDER_NAME: "${_('Folder name')}",
+        CANCEL: "${_('Cancel')}"
+      }
+    };
+
     jHueTableExtenderGlobals = {
       labels: {
         GO_TO_COLUMN: "${_('Go to column:')}",

+ 6 - 1
desktop/core/static/css/hue3.css

@@ -1836,4 +1836,9 @@ input.no-margin {
 
 .jHueHdfsTree a.selected {
   font-weight: bold;
-}
+}
+
+form .modal-header .close {
+  top: 0px;
+  right: -11px;
+}

+ 10 - 1
desktop/core/static/js/jquery.hdfsautocomplete.js

@@ -29,7 +29,8 @@
         onPathChange: function () {
         },
         smartTooltip: "",
-        smartTooltipThreshold: 10 // needs 10 up/down or click actions and no tab to activate the smart tooltip
+        smartTooltipThreshold: 10, // needs 10 up/down or click actions and no tab to activate the smart tooltip
+        showOnFocus: false
       };
 
   function Plugin(element, options) {
@@ -41,8 +42,10 @@
   }
 
   Plugin.prototype.init = function () {
+
     var _this = this;
     var _el = $(_this.element);
+    _el.attr("autocomplete", "off"); // prevents default browser behavior
 
     // creates autocomplete popover
     if ($("#jHueHdfsAutocomplete").length == 0) {
@@ -173,6 +176,12 @@
       }
     });
 
+    if (_this.options.showOnFocus){
+      _el.on("focus", function(){
+        showHdfsAutocomplete();
+      });
+    }
+
     var _pauseBlur = false;
 
     _el.blur(function () {

+ 83 - 14
desktop/core/static/js/jquery.hdfstree.js

@@ -30,19 +30,45 @@
       defaults = {
         home: "/",
         onPathChange: function () {
+        },
+        createFolder: true,
+        labels: {
+          CREATE_FOLDER: "Create folder",
+          FOLDER_NAME: "Folder name",
+          CANCEL: "Cancel"
         }
-      };
+      },
+      STORAGE_PREFIX = "hueFileBrowserLastPathForUser_";
 
   function Plugin(element, options) {
     this.element = element;
-    this.options = $.extend({}, defaults, options);
+    if (typeof jHueHdfsTreeGlobals != 'undefined') {
+      var extendedDefaults = $.extend({}, defaults, jHueHdfsTreeGlobals);
+      extendedDefaults.labels = $.extend({}, defaults.labels, jHueHdfsTreeGlobals.labels);
+      this.options = $.extend({}, extendedDefaults, options);
+      if (options != null) {
+        this.options.labels = $.extend({}, extendedDefaults.labels, options.labels);
+      }
+    }
+    else {
+      this.options = $.extend({}, defaults, options);
+      if (options != null) {
+        this.options.labels = $.extend({}, defaults.labels, options.labels);
+      }
+    }
     this._defaults = defaults;
     this._name = pluginName;
+    this.lastPath = "";
+    this.previousPath = "";
     this.init();
   }
 
-  Plugin.prototype.init = function () {
+  Plugin.prototype.init = function (optionalPath) {
     var _this = this;
+
+    if (typeof optionalPath != "undefined") {
+      _this.options.home = optionalPath;
+    }
     var _el = $(_this.element);
     _el.empty();
     _el.addClass("jHueHdfsTree");
@@ -62,19 +88,21 @@
     var _currentFiles = [];
 
     function showHdfsLeaf(options) {
-      var autocompleteUrl = BASE_PATH;
-      if (options.paths != null) {
-        autocompleteUrl += options.paths.shift();
+      var autocompleteUrl = BASE_PATH,
+          currentPath = "";
+
+      if (options.paths != null && options.paths.length > 0) {
+        currentPath = options.paths.shift();
       }
       else {
-        autocompleteUrl += (options.leaf != null ? options.leaf : "");
+        currentPath = (options.leaf != null ? options.leaf : "");
       }
+      autocompleteUrl += currentPath;
       $.getJSON(autocompleteUrl + "?pagesize=1000&format=json", function (data) {
         _currentFiles = [];
         if (data.error == null) {
-          if (options.leaf != null) {
-            _el.find("[data-path='" + options.leaf + "']").attr("data-loaded", true);
-          }
+          _el.find("[data-path='" + currentPath + "']").attr("data-loaded", true);
+          _el.find("[data-path='" + currentPath + "']").siblings("a").find(".fa-folder-o").removeClass("fa-folder-o").addClass("fa-folder-open-o");
           $(data.files).each(function (cnt, item) {
             if (item.name != "." && item.name != ".." && item.type == "dir") {
               var _path = item.path;
@@ -83,10 +111,10 @@
               _li.appendTo(_el.find("[data-path='" + _destination + "']"));
               _li.find("a").on("click", function () {
                 _this.options.onPathChange(_path);
+                _this.lastPath = _path;
                 _tree.find("a").removeClass("selected");
                 _li.find("a:eq(0)").addClass("selected");
                 if (_li.find(".content").attr("data-loaded") == "false") {
-                  _li.find(".fa-folder-o").removeClass("fa-folder-o").addClass("fa-folder-open-o");
                   showHdfsLeaf({
                     leaf: _path
                   });
@@ -102,7 +130,49 @@
               });
             }
           });
-          if (options.paths.length > 0) {
+          if (_this.options.createFolder) {
+            var _createFolderLi = $("<li>").html('<a class="pointer"><i class="fa fa-plus-square-o"></i> ' + _this.options.labels.CREATE_FOLDER + '</a>');
+            _createFolderLi.appendTo(_el.find("[data-path='" + currentPath + "']"));
+
+            var _createFolderDetails = $("<form>").css("margin-top", "10px").addClass("form-inline");
+            _createFolderDetails.hide();
+            var _folderName = $("<input>").attr("type", "text").attr("placeholder", _this.options.labels.FOLDER_NAME).appendTo(_createFolderDetails);
+            $("<span> </span>").appendTo(_createFolderDetails);
+            var _folderBtn = $("<input>").attr("type", "button").attr("value", _this.options.labels.CREATE_FOLDER).addClass("btn primary").appendTo(_createFolderDetails);
+            $("<span> </span>").appendTo(_createFolderDetails);
+            var _folderCancel = $("<input>").attr("type", "button").attr("value", _this.options.labels.CANCEL).addClass("btn").appendTo(_createFolderDetails);
+            _folderCancel.click(function () {
+              _createFolderDetails.slideUp();
+            });
+            _folderBtn.click(function () {
+              $.ajax({
+                type: "POST",
+                url: "/filebrowser/mkdir",
+                data: {
+                  name: _folderName.val(),
+                  path: currentPath
+                },
+                beforeSend: function (xhr) {
+                  xhr.setRequestHeader("X-Requested-With", "Hue"); // need to override the default one because otherwise Django returns HTTP 500
+                },
+                success: function (xhr, status) {
+                  if (status == "success") {
+                    _createFolderDetails.slideUp();
+                    var _newFolder = currentPath + "/" + _folderName.val();
+                    _this.init(_newFolder);
+                    _this.options.onPathChange(_newFolder);
+                  }
+                }
+              });
+
+            });
+            _createFolderDetails.appendTo(_el.find("[data-path='" + currentPath + "']"));
+
+            _createFolderLi.find("a").on("click", function () {
+              _createFolderDetails.slideDown();
+            });
+          }
+          if (options.paths != null && options.paths.length > 0) {
             showHdfsLeaf({
               paths: options.paths
             });
@@ -118,13 +188,12 @@
         _paths.push(_this.options.home.substr(0, match.index));
       }
       _paths.push(_this.options.home);
-      console.log(_paths)
     }
+
     showHdfsLeaf({
       paths: _paths
     });
 
-
   };
 
   Plugin.prototype.setOptions = function (options) {