Bläddra i källkod

HUE-7758 [dashboard] Multi select and single select text facet

jdesjean 7 år sedan
förälder
incheckning
541e5ea

+ 1 - 0
desktop/libs/dashboard/src/dashboard/api.py

@@ -422,6 +422,7 @@ def _create_facet(collection, user, facet_id, facet_label, facet_field, widget_t
       facet['field'] = facet_field
       facet['limit'] = 10
       facet['fieldLabel'] = facet_field
+      facet['multiselect'] = True
 
       if range_properties:
         # TODO: timeline still uses properties from top properties

+ 2 - 0
desktop/libs/dashboard/src/dashboard/models.py

@@ -147,6 +147,8 @@ class Collection2(object):
         for facet_facet in properties['facets']:
           if 'fieldLabel' not in facet_facet:
             facet_facet['fieldLabel'] = facet_facet['field']
+          if 'multiselect' not in facet_facet:
+            facet_facet['multiselect'] = True
 
       if facet['widgetType'] == 'histogram-widget':
         if 'timelineChartType' not in properties:

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/css/common_dashboard.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/css/search.css


+ 83 - 9
desktop/libs/dashboard/src/dashboard/static/dashboard/js/search.ko.js

@@ -179,10 +179,22 @@ var Query = function (vm, query) {
     vm.search();
   });
 
-  self.toggleFacet = function (data) {
+  self.toggleFacetClear = function (data) {
+    var fqs = self.fqs();
+    for (var i = fqs.length - 1; i >= 0; i--) { // Backward iteration to delete
+      fq = fqs[i];
+      if (fq.id() == data.widget_id) {
+        self.fqs.remove(fq);
+      }
+    }
+    self.start(0);
+    vm.search();
+  };
+
+  self.toggleFacet = function (data, bSingle) {
     var fq = self.getFacetFilter(data.widget_id);
 
-    if (fq == null) {
+    if (!fq) {
       self.fqs.push(ko.mapping.fromJS({
         'id': data.widget_id,
         'field': data.facet.cat,
@@ -190,22 +202,28 @@ var Query = function (vm, query) {
         'type': 'field'
       }));
     } else {
-      $.each(self.fqs(), function (index, fq) {
+      var fqs = self.fqs();
+      var fFilter = function(f) { return ko.toJSON(f.value()) == ko.toJSON(data.facet.value); };
+      for (var i = fqs.length - 1; i >= 0; i--) { // Backward iteration to delete
+        fq = fqs[i];
         if (fq.id() == data.widget_id) {
-          var f = $.grep(fq.filter(), function(f) { return ko.toJSON(f.value()) == ko.toJSON(data.facet.value); });
-          if (f.length > 0) {
+          var f = $.grep(fq.filter(), fFilter);
+          if (f.length && !bSingle) {
             fq.filter.remove(f[0]);
-            if (fq.filter().length == 0) {
+            if (!fq.filter().length) {
               self.fqs.remove(fq);
             }
           } else {
+            if (bSingle) {
+              fq.filter.removeAll();
+            }
             fq.filter.push(ko.mapping.fromJS({'exclude': data.exclude ? true : false, 'value': data.facet.value}));
           }
         }
-      });
+      }
     }
 
-    if (vm.selectedQDefinition() != null){
+    if (vm.selectedQDefinition()) {
       vm.selectedQDefinition().hasChanged(true);
     }
 
@@ -1847,7 +1865,8 @@ var SearchViewModel = function (collection_json, query_json, initial_json, has_g
           hasRetrievedResults: true, // Temp
           results: [],
           response: '',
-          fieldAnalysesName: ''
+          fieldAnalysesName: '',
+          querySpec: ''
         });
       }
 
@@ -2342,7 +2361,62 @@ var SearchViewModel = function (collection_json, query_json, initial_json, has_g
       var _hash = ko.mapping.toJSON(new_facet);
 
       if (!facet.has_data() || facet.resultHash() != _hash) {
+        if (facet.countsSelectedSubscription) {
+          facet.countsSelectedSubscription.dispose();
+        }
         facet.counts(new_facet.counts);
+        facet.autocompleteFromEntries = function (nonPartial, partial) {
+          var result = [];
+          var partialLower = partial.toLowerCase();
+          facet.counts().forEach(function (entry) {
+            var value = typeof entry.value == "string" ? entry.value : entry.value.toString();
+            if (value.toLowerCase().indexOf(partialLower) === 0) {
+              result.push(nonPartial + partial + value.substring(partial.length));
+            }
+          });
+          return result;
+        };
+        $.each(facet.counts(), function (index, item) {
+          item.text = item.value + ' (' + item.count + ')';
+        });
+        var countsSelected = (function() {
+          var _fq;
+          var fqs = self.query.fqs();
+          for (var i = 0; i < fqs.length; i++) {
+            var fq = fqs[i];
+            if (fq.id() == new_facet.id) {
+              _fq = fq;
+              break;
+            }
+          }
+          return _fq && _fq.filter()[0].value();
+        })();
+
+        if (!facet.countsFiltered) {
+          facet.countsSelected = ko.observable(countsSelected);
+          facet.countsFiltered = ko.pureComputed(function() {
+            var querySpec = facet.querySpec();
+            if (!querySpec || !querySpec.query) return facet.counts();
+            var text = querySpec.query.toLowerCase();
+            return facet.counts().filter(function (entry) {
+              var value = typeof entry.value == "string" ? entry.value : entry.value.toString();
+              return value.toLowerCase().indexOf(text) >= 0;
+            });
+          });
+        }
+        setTimeout(function () { // Delay the execution, because setting facet.counts() above sets the value of countsSelected to ""
+          facet.countsSelected(countsSelected);
+          facet.countsSelectedSubscription = facet.countsSelected.subscribe(function (value) {
+            var counts = facet.counts();
+            for (var i = 0; i < counts.length; i++) {
+              if (counts[i].value == value && value !== '') {
+                self.query.toggleFacet({ facet: counts[i], widget_id: new_facet.id }, true);
+                return;
+              }
+            }
+            self.query.toggleFacetClear({ widget_id: new_facet.id });
+          });
+        },1);
 
         if (typeof new_facet.docs != 'undefined') {
           var _docs = [];

+ 13 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/less/common_dashboard.less

@@ -455,6 +455,19 @@
     margin-left: 65px;
   }
 
+  .text-widget-filter {
+    .inline-autocomplete-container {
+      height: 25px;
+    }
+    input {
+      -webkit-box-sizing: border-box;
+      -moz-box-sizing: border-box;
+      box-sizing: border-box;
+      height: 25px;
+      border-radius: 4px;
+    }
+  }
+
   .singleValued.form-horizontal .control-label {
     text-align: left;
     margin-left: 0px;

+ 5 - 0
desktop/libs/dashboard/src/dashboard/static/dashboard/less/search.less

@@ -798,6 +798,11 @@
     vertical-align: middle;
   }
 
+  .facet-count {
+    overflow: auto;
+    max-height: 110px;
+  }
+
   .gridster {
     .remove-widget, .remove-empty-gridster {
       opacity: 0;

+ 61 - 35
desktop/libs/dashboard/src/dashboard/templates/common_search.mako

@@ -743,47 +743,63 @@ ${ dashboard.layout_skeleton(suffix='search') }
     <div class="clearfix"></div>
     <div data-bind="with: $root.collection.getFacetById($parent.id())">
       <!-- ko if: properties.facets()[0].type() == 'field' -->
-        <div data-bind="foreach: $parent.counts">
-          <div class="trigger-exclude">
-              <!-- ko if: $index() < $parent.properties.limit() -->
-                <!-- ko if: ! $data.selected -->
-                  <a class="exclude pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id(), 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
-                  <div class="hellip">
-                    <a class="pointer" dir="ltr" data-bind="html: prettifyDate($data.value), click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }, attr: {'title': $data.value + ' (' + $data.count + ')'}"></a>
-                    <span class="pointer counter" dir="rtl" data-bind="text: ' (' + $data.count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }"></span>
-                  </div>
-                <!-- /ko -->
-                <!-- ko if: $data.selected -->
-                  <span class="pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }">
-                    <a class="include pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
-                    <a class="include pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+        <!-- ko if: properties.facets()[0].multiselect -->
+          <div class="text-widget-filter">
+            <!-- ko component: {
+              name: 'inline-autocomplete',
+              params: {
+                querySpec: $parent.querySpec,
+                facets: [],
+                knownFacetValues: {},
+                autocompleteFromEntries: $parent.autocompleteFromEntries
+              }
+            } --><!-- /ko -->
+          </div>
+          <div data-bind="foreach: $parent.countsFiltered" class="facet-count">
+            <div class="trigger-exclude">
+                <!-- ko if: $index() < $parent.properties.limit() -->
+                  <!-- ko if: ! $data.selected -->
+                    <a class="exclude pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id(), 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
                     <div class="hellip">
-                      <strong data-bind="html: prettifyDate($data.value), attr: {'title': $data.value}"></strong>
+                      <a class="pointer" dir="ltr" data-bind="html: prettifyDate($data.value), click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }, attr: {'title': $data.value + ' (' + $data.count + ')'}"></a>
+                      <span class="pointer counter" dir="rtl" data-bind="text: ' (' + $data.count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }"></span>
                     </div>
-                  </span>
-                <!-- /ko -->
-              <!-- /ko -->
-              <!-- ko if: $index() == $parent.properties.limit() -->
-                <!-- ko if: $parent.properties.prevLimit == undefined || $parent.properties.prevLimit == $parent.properties.limit() -->
-                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
-                    ${ _('Show more...') }
-                  </a>
+                  <!-- /ko -->
+                  <!-- ko if: $data.selected -->
+                    <span class="pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }">
+                      <a class="include pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
+                      <a class="include pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+                      <div class="hellip">
+                        <strong data-bind="html: prettifyDate($data.value), attr: {'title': $data.value}"></strong>
+                      </div>
+                    </span>
+                  <!-- /ko -->
                 <!-- /ko -->
-                <!-- ko if: $parent.properties.prevLimit != undefined && $parent.properties.prevLimit != $parent.properties.limit() -->
-                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
-                    ${ _('Show more') }
-                  </a>
-                  /
-                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'down') }">
-                    ${ _('less...') }
-                  </a>
+                <!-- ko if: $index() == $parent.properties.limit() -->
+                  <!-- ko if: $parent.properties.prevLimit == undefined || $parent.properties.prevLimit == $parent.properties.limit() -->
+                    <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
+                      ${ _('Show more...') }
+                    </a>
+                  <!-- /ko -->
+                  <!-- ko if: $parent.properties.prevLimit != undefined && $parent.properties.prevLimit != $parent.properties.limit() -->
+                    <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
+                      ${ _('Show more') }
+                    </a>
+                    /
+                    <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'down') }">
+                      ${ _('less...') }
+                    </a>
+                  <!-- /ko -->
                 <!-- /ko -->
-              <!-- /ko -->
+            </div>
           </div>
-        </div>
+       <!-- /ko -->
+       <!-- ko ifnot: properties.facets()[0].multiselect -->
+        <select data-bind="selectize: $parent.counts, optionsText: 'text', optionsValue:'value', value: $parent.countsSelected"/>
+       <!-- /ko -->
       <!-- /ko -->
       <!-- ko if: properties.facets()[0].type() == 'range' -->
-        <div data-bind="foreach: $parent.counts">
+        <div data-bind="foreach: $parent.counts" class="facet-count">
           <div class="trigger-exclude">
               <!-- ko if: ! selected -->
                 <a class="exclude pointer" data-bind="click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
@@ -805,7 +821,7 @@ ${ dashboard.layout_skeleton(suffix='search') }
         </div>
       <!-- /ko -->
       <!-- ko if: properties.facets()[0].type() == 'range-up' -->
-        <div data-bind="foreach: $parent.counts">
+        <div data-bind="foreach: $parent.counts" class="facet-count">
           <div class="trigger-exclude">
               <!-- ko if: ! selected -->
                 <a class="exclude pointer" data-bind="click: function(){ $root.query.selectRangeUpFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, 'exclude': true, is_up: $data.is_up}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
@@ -2165,6 +2181,16 @@ ${ dashboard.layout_skeleton(suffix='search') }
 ##            </span>
 ##          </div>
 ##        <!-- /ko -->
+        <!-- ko if: $parentContext.$data.type() == 'field' -->
+        <div class="facet-field-cnt" data-bind="visible: $parent == $parentContext.$parentContext.$data.properties.facets()[0]"> <!-- visible on first element only -->
+          <span class="spinedit-cnt">
+            <span class="facet-field-label facet-field-label-fixed-width">
+              ${ _('Multi select') }
+            </span>
+            <input type="checkbox" data-bind="checked: $parent.multiselect"/>
+          </span>
+        </div>
+        <!-- /ko -->
       <!-- /ko -->
       <!-- /ko -->
 

Vissa filer visades inte eftersom för många filer har ändrats