浏览代码

HUE-2218 [search] Added typeahead to field stats

Added autoresize to the popover too
Enrico Berti 11 年之前
父节点
当前提交
ed5f4816b8

+ 3 - 2
apps/search/src/search/templates/search.mako

@@ -865,7 +865,7 @@ ${ dashboard.layout_skeleton() }
   <div data-bind="with: $root.getFieldAnalysis()">
     <div class="pull-right">
         <input type="text" data-bind="visible: section() == 'terms', clearable: terms.prefix, valueUpdate:'afterkeydown'" placeholder="${ _('Prefix filter...') }"/>
-        <input type="text" data-bind="visible: section() == 'stats', clearable: stats.facet" placeholder="${ _('Field name...') }"/>
+        <input type="text" data-bind="visible: section() == 'stats', clearable: stats.facet, typeahead: { target: stats.facet, source: $root.fieldsAnalysisAttributesNames, triggerOnFocus: true }" placeholder="${ _('Field name...') }"/>
     </div>
     <ul class="nav nav-tabs" role="tablist">
       <li class="active"><a href="#analysis-terms" role="tab" data-toggle="tab" data-bind="click: function() { section('terms'); }">${ _('Terms') }</a></li>
@@ -898,6 +898,7 @@ ${ dashboard.layout_skeleton() }
           <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
         </div>
         <div class="alert" data-bind="visible: ! $parent.isLoading() && $data.data().length > 0 && $data.data()[0].key.toLowerCase() == 'error'">${ _('This field does not support stats') }</div>
+        <div class="alert" data-bind="visible: ! $parent.isLoading() && $data.data().length == 0">${ _('There are no stats to be shown') }</div>
         <table style="width: 100%" data-bind="visible: ! $parent.isLoading() && $data.data().length > 0 && $data.data()[0].key.toLowerCase() != 'error'" class="table-striped">
           <tbody data-bind="foreach: $data.data">
           <tr>
@@ -905,7 +906,7 @@ ${ dashboard.layout_skeleton() }
             <!-- ko if: key == 'facets' -->
             <td>
               <!-- ko if: val[Object.keys(val)[0]] != null -->
-              <table style="width: 100%">
+              <table style="width: 400px">
                 <tbody data-bind="foreach: Object.keys(val[Object.keys(val)[0]])">
                   <tr>
                     <td style="vertical-align: top; padding-left: 4px; padding-right: 4px"><strong data-bind="text: $data"></strong></td>

+ 7 - 3
apps/search/static/css/search.css

@@ -300,12 +300,12 @@ em {
 }
 
 .mega-popover .popover-content {
-  width: 440px;
-  height: 300px;
+  min-width: 440px;
+  min-height: 360px;
 }
 
 .mega-popover .tab-pane {
-  height: 240px;
+  height: 300px;
   overflow-y: scroll;
 }
 
@@ -326,4 +326,8 @@ em {
 
 .fa.analysis:hover {
   color: #333;
+}
+
+.typeahead {
+  z-index: 10000;
 }

+ 24 - 7
apps/search/static/js/search.ko.js

@@ -217,8 +217,12 @@ var FieldAnalysis = function (vm, field_name) {
   });
   self.terms.prefix.extend({rateLimit: {timeout: 2000, method: "notifyWhenChangesStop"}});
   self.stats = ko.mapping.fromJS({'facet': '', 'data': []});
+
+  var _statsUpdateTimeout = -1;
   self.stats.facet.subscribe(function () {
-    self.getStats();
+    // this is to avoid false positives from typeahead blur
+    window.clearTimeout(_statsUpdateTimeout);
+    _statsUpdateTimeout = window.setTimeout(function(){self.getStats()}, 200);
   });
 
   self.update = function() {
@@ -241,9 +245,11 @@ var FieldAnalysis = function (vm, field_name) {
       analysis: ko.mapping.toJSON(self)
     }, function (data) {
       if (data.status == 0) {
-        $.each(data.terms, function(key, val) {
-          self.terms.data.push({'key': key, 'val': val});
-        });
+        if (data.terms != null) {
+          $.each(data.terms, function (key, val) {
+            self.terms.data.push({'key': key, 'val': val});
+          });
+        }
       }
       else if (data.status == 1) {
         self.terms.data.push({'key': 'Error', 'val': data.message});
@@ -266,9 +272,11 @@ var FieldAnalysis = function (vm, field_name) {
       analysis: ko.mapping.toJSON(self)
     }, function (data) {
       if (data.status == 0) {
-        $.each(data.stats.stats.stats_fields[self.name()], function(key, val) {
-          self.stats.data.push({'key': key, 'val': val});
-        });
+        if (data.stats.stats.stats_fields[self.name()] != null) {
+          $.each(data.stats.stats.stats_fields[self.name()], function (key, val) {
+            self.stats.data.push({'key': key, 'val': val});
+          });
+        }
       }
       else if (data.status == 1) {
         self.stats.data.push({'key': 'Error', 'val': data.message});
@@ -821,6 +829,15 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
   };
   self.fieldAnalyses = ko.observableArray([]);
   self.fieldAnalysesName = ko.observableArray("");
+  self.fieldsAnalysisAttributesNames = ko.computed(function () {
+    var _fields = [];
+    $.each(self.collection.template.fieldsAttributes(), function (index, field) {
+      if (field.name() != self.fieldAnalysesName()){
+        _fields.push(field.name())
+      }
+    });
+    return _fields;
+  });
 
   self.previewColumns = ko.observable("");
   self.columns = ko.observable([]);

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

@@ -156,6 +156,7 @@ from django.utils.translation import ugettext as _
   <script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js"></script>
   <script src="/static/js/jquery.datatables.sorting.js"></script>
   <script src="/static/ext/js/bootstrap.min.js"></script>
+  <script src="/static/ext/js/bootstrap-better-typeahead.min.js"></script>
   <script src="/static/ext/js/fileuploader.js"></script>
   <script src="/static/js/popover-extra-placements.js"></script>
 

+ 12 - 0
desktop/core/static/ext/js/bootstrap-better-typeahead.min.js

@@ -0,0 +1,12 @@
+/* =============================================================
+ * bootstrap-better-typeahead.js v1.0.0 by Philipp Nolte
+ * https://github.com/ptnplanet/Bootstrap-Better-Typeahead
+ * =============================================================
+ * This plugin makes use of twitter bootstrap typeahead
+ * http://twitter.github.com/bootstrap/javascript.html#typeahead
+ *
+ * Bootstrap is licensed under the Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * ============================================================ */
+ 
+ !function(e){"use strict";var t={lookup:function(t){var n;return this.query=this.$element.val()||"",this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?(this.query.length&&(t=t.slice(0,this.options.items)),this.render(t).show()):this.shown?this.hide():this},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),this.query.length>0&&t.first().addClass("active"),this.$menu.html(t),this},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]);if(t.keyCode===9){if(!this.shown)return;this.select()}else this.move(t)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0,this.mousedover||this.lookup(e)}};e.extend(e.fn.typeahead.Constructor.prototype,t)}(window.jQuery);

+ 38 - 0
desktop/core/static/js/ko.hue-bindings.js

@@ -680,4 +680,42 @@ ko.bindingHandlers.tooltip = {
       $(element).tooltip("destroy");
     });
   }
+};
+
+ko.bindingHandlers.typeahead = {
+  init: function (element, valueAccessor) {
+    var binding = this;
+    var elem = $(element);
+    var value = valueAccessor();
+
+    var _options = {
+      source: function () {
+        return ko.utils.unwrapObservable(value.source);
+      },
+      onselect: function (val) {
+        value.target(val);
+      }
+    }
+
+    if (value.triggerOnFocus){
+      _options.minLength =  0;
+    }
+
+    elem.typeahead(_options);
+
+    if (value.triggerOnFocus){
+      elem.on('focus', function(){
+        elem.trigger("keyup");
+      });
+    }
+
+    elem.blur(function () {
+      value.target(elem.val());
+    });
+  },
+  update: function (element, valueAccessor) {
+    var elem = $(element);
+    var value = valueAccessor();
+    elem.val(value.target());
+  }
 };