Explorar o código

HUE-1392 [hbase] Searchbar Rewrite

Kevin Wang %!s(int64=12) %!d(string=hai) anos
pai
achega
a462d34

+ 4 - 4
apps/hbase/src/hbase/templates/app.mako

@@ -67,7 +67,7 @@ ${ commonheader(None, "hbase", user) | n,unicode }
         <span class="smartview-row-controls pull-right">
           <button class="btn" data-bind="click: $data.toggleSelectedCollapse, enable: $data.selected().length > 0, clickBubble: false" data-toggle="tooltip" title="${_('Toggle Collapse Selected')}"><i class="icon-resize-small"></i></button>
           <button class="btn" data-bind="click: $data.toggleSelectAllVisible, enable: $data.displayedItems().length > 0, clickBubble: false" data-toggle="tooltip" title="${_('Select All Visible')}"><i class="icon-check"></i></button>
-          <input type="text" placeholder="Filter Column Names/Family" data-bind="value: $data.searchQuery, valueUpdate: $data.items().length < 100 ? 'afterkeydown' : 'change', clickBubble: false"/>
+          <input type="text" placeholder="${('Filter Column Names/Family')}" data-bind="value: $data.searchQuery, valueUpdate: $data.items().length < 100 ? 'afterkeydown' : 'change', clickBubble: false"/>
           ${sortBtn('$data.sortDropDown')}
           % if user.is_superuser:
             <button class="btn" data-bind="enable: $data.selected().length > 0, click: $data.dropSelected, clickBubble: false"><i class="icon-trash"></i> Drop Columns</button>
@@ -198,10 +198,10 @@ ${ commonheader(None, "hbase", user) | n,unicode }
     <div class="subnav sticky">
       <div class="container-fluid">
         <div class="row-fluid">
-            <div id="searchbar-main" class="span4" data-bind="click: function(){search.focused(true)}">
-              <div id="search-tags" data-bind="html: search.renderedValue()">
+          <div class="span6">
+            <div id="searchbar-main" class="span10" data-bind="click: search.clickTagBar">
+              <div id="search-tags" contenteditable="true" data-bind="editableText: search.cur_input, hasfocus: search.focused, css: { 'active': search.cur_input() != '' }, event: { 'keydown': search.onKeyDown, click: search.updateMenu.bind(null) }" data-placeholder="${_('row_key, row_key_prefix* + scan_length, row_key [family:col1, family2:col2, family3:]')}">
               </div>
-              <input id="tag-input" class="tag-searchbar" type="text" autocomplete="off" placeholder="${_('row_key1, row_key2 + scan_length, row[ cf:column ], [ cf:col1 cf:col2 ]')}" title="${_('ie: row_key1, row_key2 + scan_length, row[ cf:column ], [ cf:col1 cf:col2 ]')}" data-bind="event: { keydown: search.onKeyDown, keyup: search.onKeyDown, change: search.onKeyDown }, value: search.cur_input, valueUpdate: 'afterkeydown', click: function(){$($element).attr('placeholder','').addClass('active')}, hasfocus: search.focused, css: { 'active': search.cur_input() != '' }">
             </div>
             <ul id="search-typeahead" data-bind="visible: search.focused()">
               <!-- ko if: search.mode() != 'idle' -->

+ 14 - 2
apps/hbase/static/css/hbase.css

@@ -382,10 +382,13 @@ ul#hbase-breadcrumbs li a
 }
 #search-tags
 {
-	display: inline;
+	display: block;
 	margin: 0px;
 	padding: 0px;
+	outline: none;
 	list-style: none;
+	width: 100%;
+	height: 100%;
 }
 
 #search-tags > span
@@ -501,13 +504,22 @@ ul#hbase-breadcrumbs li a
 	opacity: .1;
 }
 
+
+#searchbar-main small
+{
+	color: #bbb;
+}
+
 #searchbar-main i.tagIndicator
 {
 	color: #000;
 	padding: 0px !important;
 	border: 0px;
 	margin: 0px;
-	background: none !important;
+	background: black !important;
+	display: inline-block;
+	width: 1px;
+	height: 1em;
 	border:none !important;
 	animation: blink 1.5s steps(2, start) infinite;
 	-webkit-animation: blink 1.5s steps(2, start) infinite;

+ 35 - 16
apps/hbase/static/js/controls.js

@@ -20,8 +20,8 @@ var searchRenderers = {
      tag: /.+/g, //select the matches to wrap with tags
      strip: /,(?![^\[\]\:]+[^\]\[]+\])/g, //strip delimiters and post-process string to make nice
      nested: {
-       'scan': { select: /\+[0-9 ]+/g, tag: /.+/g, strip: /a^/g },
-        'columns': { select: /\[.+\]/g, tag: /[^:,\[\]]+:([^,\[\]]+|)/g, strip: /[\[\]]/g }, //forced to do this select due to lack of lookbehinds
+        'scan': { select: /\+[0-9 ]+/g, tag: /.+/g, strip: /a^/g },
+        'columns': { select: /\[.+\]/g, tag: /[^:,\[\]]+:([^,\[\]]+|)/g, strip: /a^/g }, //forced to do this select due to lack of lookbehinds /[\[\]]/g
         'prefix': { select: /[^\*]+\*/g, tag: /\*/g, strip: /a^/g }
      }
   }
@@ -675,7 +675,7 @@ var tagsearch = function()
         var processed = selected.replace(renderers[keys[i]].tag, function(tagged)
         {
           hasMatched = true;
-          return " <span class='" + keys[i] + " tagsearchTag' title='" + keys[i] + "' data-toggle='tooltip'>" + ('nested' in renderers[keys[i]] ? self.render(tagged, renderers[keys[i]].nested) : tagged).trim() + "</span> ";
+          return "<span class='" + keys[i] + " tagsearchTag' title='" + keys[i] + "' data-toggle='tooltip'>" + ('nested' in renderers[keys[i]] ? self.render(tagged, renderers[keys[i]].nested) : tagged) + "</span>";
         });
         if(hasMatched)
           processed = processed.replace(renderers[keys[i]].strip, '');
@@ -685,7 +685,6 @@ var tagsearch = function()
     return input;
   };
 
-
   self.updateMode = function(value)
   {
     var selection = value.slice(0, self.selectionEnd());
@@ -720,15 +719,6 @@ var tagsearch = function()
   self.selectionStart = ko.observable(0);
   self.selectionEnd = ko.observable(0);
 
-  self.renderedValue = ko.computed(function()
-  {
-    var pre = self.cur_input();
-    var s = self.selectionStart(), e = self.selectionEnd();
-    var indicator = (self.focused()) ? '<i class="tagIndicator">|</i>' : '';
-    self.updateMode(self.cur_input());
-    return self.render(pre.slice(0, e) + indicator + pre.slice(e), searchRenderers);
-  });
-
   self.hintText = ko.computed(function()
   {
     var value = self.cur_input();
@@ -747,19 +737,48 @@ var tagsearch = function()
 
   self.onKeyDown = function(target, ev)
   {
-    self.selectionStart($('#tag-input')[0].selectionStart);
-    self.selectionEnd($('#tag-input')[0].selectionEnd);
     if(ev.keyCode == 13 && self.cur_input().slice(self.cur_input().lastIndexOf(',')).trim() != ",")
     {
         self.evaluate();
+        return false;
     }
+    setTimeout(self.updateMenu, 1);
     return true;
-  }
+  };
+
+  self.updateMenu = function() {
+    try{
+    	var pos = getEditablePosition(document.getElementById('search-tags'));
+	  	self.selectionStart(pos);
+	  	self.selectionEnd(pos);
+    } catch (err) {}
+	self.updateMode(self.cur_input());
+  };
 
   self.evaluate = function()
   {
     app.views.tabledata.searchQuery(self.cur_input());
+  };
+
+  $('#search-tags').blur(function(){
+  	self.focused(false);
+  });
+
+  self.doBlur = function() {
+  	if(self.cur_input().trim() == "") {
+  	  function doClick() {
+        $('#search-tags').html('');
+        setTimeout(function() {
+        	$('#search-tags').focus();
+        }, 1);
+      }
+      $('#search-tags').html('<small>' + $('#search-tags').data("placeholder") + '</small>').one('click', doClick).find('small').on('mousedown', doClick);
+    }
   }
+
+  $('#search-tags').focus(function(){
+  	self.focused(true);
+  });
 };
 
 var CellHistoryPage = function(options)

+ 3 - 2
apps/hbase/static/js/nav.js

@@ -27,13 +27,14 @@ var Router =
     Router.setCluster(cluster);
     app.pageTitle(cluster + ' / ' + table);
     app.views.tabledata.name(table);
-      app.views.tabledata.name(table);
-      app.focusModel(app.views.tabledata);
+    app.views.tabledata.name(table);
+    app.focusModel(app.views.tabledata);
   },
   setCluster: function(cluster)
   {
     Router.set();
     app.cluster(cluster);
+    logGA();
   },
   set: function()
   {

+ 86 - 1
apps/hbase/static/js/utils.js

@@ -240,6 +240,91 @@ function logGA(postfix)
   if(postfix == null)
     postfix = ""
   if (typeof _gaq != 'undefined' && _gaq != null && _gaq != undefined){
-       _gaq.push(['_trackPageview', '/hbase/' + document.URL.slice(document.URL.indexOf('/hbase')) + postfix]);
+       _gaq.push(['_trackPageview', document.URL.slice(document.URL.indexOf('/hbase')) + postfix]);
     }
+};
+
+function getEditablePosition(contentEditable, trimWhitespaceNodes) {
+  var el = contentEditable;
+  if(window.getSelection().getRangeAt(0).startContainer == el) //raw reference for FF fix
+    return 0;
+  var index = window.getSelection().getRangeAt(0).startOffset; //ff
+  var cur_node = window.getSelection().getRangeAt(0).startContainer; //ff
+  while(cur_node != null && cur_node != el) {
+    var cur_sib = cur_node.previousSibling || cur_node.previousElementSibling;
+    while(cur_sib != null) {
+      var val = $(cur_sib).text() || cur_sib.nodeValue;
+      if(typeof val !== "undefined" && val != null) {
+        index += trimWhitespaceNodes ? val.length : val.length;
+      }
+      cur_sib = cur_sib.previousSibling;
+    }
+    cur_node = cur_node.parentNode;
+  }
+  return index;
+};
+
+function setCursor(node, pos, trimWhitespaceNodes){
+  var sel = window.getSelection();
+  var range = document.createRange();
+  node = function selectNode(node) {
+    var nodes = node.childNodes;
+    if(pos > 0) {
+      for(var i=0; i<nodes.length; i++) {
+        var val = trimWhitespaceNodes ? nodes[i].nodeValue.trim() : nodes[i].nodeValue;
+        if(val) {
+          if(val.length >= pos) {
+            return nodes[i];
+          } else {
+            pos -= val.length;
+          }
+        }
+        var n = selectNode(nodes[i]);
+        if (n) return n;
+      }
+    }
+    return false;
+  }(node);
+  try {
+    range.setStart(node, pos);
+    range.setEnd(node, pos);
+    range.collapse(true);
+    sel.removeAllRanges();
+    sel.addRange(range);
+  } catch (err) { }
+}
+
+window.selectIndex = null;
+var fallback = typeof window.getSelection === "undefined";
+ko.bindingHandlers.editableText = {
+  init: function(element, valueAccessor, allBindingsAccessor) {
+    $(element).on('keydown', function() {
+      setTimeout(function() {
+        var modelValue = valueAccessor();
+        var elementValue = $(element).text();
+        if (ko.isWriteableObservable(modelValue) && elementValue != modelValue()) {
+          if(!fallback)
+            window.selectIndex = getEditablePosition(element); //firefox does some tricky predictive stuff here
+          modelValue(elementValue);
+        }
+        else { //handle non-observable one-way binding
+            var allBindings = allBindingsAccessor();
+            if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers'].htmlValue) allBindings['_ko_property_writers'].htmlValue(elementValue);
+        }}, 1);
+      });
+  },
+  update: function(element, valueAccessor) {
+    var value = ko.utils.unwrapObservable(valueAccessor()) || "";
+    if(value.trim() == "" && !app.search.focused()) {
+      app.search.doBlur();
+    } else {
+      if(!fallback) {
+        element.innerHTML = app.search.render(value, searchRenderers);
+        if(window.selectIndex != null)
+        {
+          setCursor(element, window.selectIndex);
+        }
+      }
+    }
+  }
 };