Selaa lähdekoodia

HUE-4964 [editor] Make it possible to edit tags in the context-popover

Johan Ahlen 9 vuotta sitten
vanhempi
commit
d3d820b

+ 67 - 21
desktop/core/src/desktop/static/desktop/ext/css/selectize.css

@@ -1,5 +1,5 @@
 /**
- * selectize.css (v0.12.1)
+ * selectize.css (v0.12.3)
  * Copyright (c) 2013–2015 Brian Reavis & contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
@@ -22,14 +22,17 @@
   -webkit-box-shadow: inset 0 0 12px 4px #ffffff;
   box-shadow: inset 0 0 12px 4px #ffffff;
 }
+
 .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
   content: '!';
   visibility: hidden;
 }
+
 .selectize-control.plugin-drag_drop .ui-sortable-helper {
   -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
 }
+
 .selectize-dropdown-header {
   position: relative;
   padding: 5px 8px;
@@ -39,6 +42,7 @@
   -moz-border-radius: 3px 3px 0 0;
   border-radius: 3px 3px 0 0;
 }
+
 .selectize-dropdown-header-close {
   position: absolute;
   right: 8px;
@@ -49,9 +53,11 @@
   line-height: 20px;
   font-size: 20px !important;
 }
+
 .selectize-dropdown-header-close:hover {
   color: #000000;
 }
+
 .selectize-dropdown.plugin-optgroup_columns .optgroup {
   border-right: 1px solid #f2f2f2;
   border-top: 0 none;
@@ -60,19 +66,24 @@
   -moz-box-sizing: border-box;
   box-sizing: border-box;
 }
+
 .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
   border-right: 0 none;
 }
+
 .selectize-dropdown.plugin-optgroup_columns .optgroup:before {
   display: none;
 }
+
 .selectize-dropdown.plugin-optgroup_columns .optgroup-header {
   border-top: 0 none;
 }
+
 .selectize-control.plugin-remove_button [data-value] {
   position: relative;
-  padding-right: 24px !important;
+  padding-right: 16px !important;
 }
+
 .selectize-control.plugin-remove_button [data-value] .remove {
   z-index: 1;
   /* fixes ie bug (see #392) */
@@ -89,7 +100,6 @@
   vertical-align: middle;
   display: inline-block;
   padding: 2px 0 0 0;
-  border-left: 1px solid #d0d0d0;
   -webkit-border-radius: 0 2px 2px 0;
   -moz-border-radius: 0 2px 2px 0;
   border-radius: 0 2px 2px 0;
@@ -97,21 +107,18 @@
   -moz-box-sizing: border-box;
   box-sizing: border-box;
 }
-.selectize-control.plugin-remove_button [data-value] .remove:hover {
-  background: rgba(0, 0, 0, 0.05);
-}
-.selectize-control.plugin-remove_button [data-value].active .remove {
-  border-left-color: #cacaca;
-}
-.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
-  background: none;
-}
-.selectize-control.plugin-remove_button .disabled [data-value] .remove {
-  border-left-color: #ffffff;
+
+.selectize-control.plugin-remove_button .remove-single {
+  position: absolute;
+  right: 28px;
+  top: 6px;
+  font-size: 23px;
 }
+
 .selectize-control {
   position: relative;
 }
+
 .selectize-dropdown,
 .selectize-input,
 .selectize-input input {
@@ -121,12 +128,14 @@
   line-height: 18px;
   -webkit-font-smoothing: inherit;
 }
+
 .selectize-input,
 .selectize-control.single .selectize-input.input-active {
   background: #ffffff;
   cursor: text;
   display: inline-block;
 }
+
 .selectize-input {
   border: 1px solid #d0d0d0;
   padding: 8px 8px;
@@ -140,26 +149,37 @@
   box-sizing: border-box;
   -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
 }
+
 .selectize-control.multi .selectize-input.has-items {
   padding: 6px 8px 3px;
+  box-shadow: none;
+  border-radius: 2px;
 }
+
 .selectize-input.full {
   background-color: #ffffff;
 }
+
 .selectize-input.disabled,
 .selectize-input.disabled * {
   cursor: default !important;
 }
+
 .selectize-input.focus {
   -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
   box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
 }
+
 .selectize-input.dropdown-active {
   -webkit-border-radius: 3px 3px 0 0;
   -moz-border-radius: 3px 3px 0 0;
   border-radius: 3px 3px 0 0;
 }
+
 .selectize-input > * {
   vertical-align: baseline;
   display: -moz-inline-stack;
@@ -167,25 +187,30 @@
   zoom: 1;
   *display: inline;
 }
+
 .selectize-control.multi .selectize-input > div {
   cursor: pointer;
   margin: 0 3px 3px 0;
   padding: 2px 6px;
-  background: #f2f2f2;
-  color: #303030;
-  border: 0 solid #d0d0d0;
+  background: #DBE8F1;
+  color: #338BB8;
+  border: 0 solid #DBE8F1;
+  border-radius: 5px
 }
+
 .selectize-control.multi .selectize-input > div.active {
-  background: #e8e8e8;
-  color: #303030;
-  border: 0 solid #cacaca;
+  background: #338BB8;
+  color: #DBE8F1;
+  border: 0 solid #DBE8F1;
 }
+
 .selectize-control.multi .selectize-input.disabled > div,
 .selectize-control.multi .selectize-input.disabled > div.active {
   color: #7d7d7d;
   background: #ffffff;
   border: 0 solid #ffffff;
 }
+
 .selectize-input > input {
   display: inline-block !important;
   padding: 0 !important;
@@ -201,17 +226,21 @@
   -webkit-box-shadow: none !important;
   box-shadow: none !important;
 }
+
 .selectize-input > input::-ms-clear {
   display: none;
 }
+
 .selectize-input > input:focus {
   outline: none !important;
 }
+
 .selectize-input::after {
   content: ' ';
   display: block;
   clear: left;
 }
+
 .selectize-input.dropdown-active::before {
   content: ' ';
   display: block;
@@ -222,6 +251,7 @@
   left: 0;
   right: 0;
 }
+
 .selectize-dropdown {
   position: absolute;
   z-index: 10;
@@ -238,51 +268,63 @@
   -moz-border-radius: 0 0 3px 3px;
   border-radius: 0 0 3px 3px;
 }
+
 .selectize-dropdown [data-selectable] {
   cursor: pointer;
   overflow: hidden;
 }
+
 .selectize-dropdown [data-selectable] .highlight {
   background: rgba(125, 168, 208, 0.2);
   -webkit-border-radius: 1px;
   -moz-border-radius: 1px;
   border-radius: 1px;
 }
+
 .selectize-dropdown [data-selectable],
 .selectize-dropdown .optgroup-header {
   padding: 5px 8px;
 }
+
 .selectize-dropdown .optgroup:first-child .optgroup-header {
   border-top: 0 none;
 }
+
 .selectize-dropdown .optgroup-header {
   color: #303030;
   background: #ffffff;
   cursor: default;
 }
+
 .selectize-dropdown .active {
   background-color: #f5fafd;
   color: #495c68;
 }
+
 .selectize-dropdown .active.create {
   color: #495c68;
 }
+
 .selectize-dropdown .create {
   color: rgba(48, 48, 48, 0.5);
 }
+
 .selectize-dropdown-content {
   overflow-y: auto;
   overflow-x: hidden;
   max-height: 200px;
 }
+
 .selectize-control.single .selectize-input,
 .selectize-control.single .selectize-input input {
   cursor: pointer;
 }
+
 .selectize-control.single .selectize-input.input-active,
 .selectize-control.single .selectize-input.input-active input {
   cursor: text;
 }
+
 .selectize-control.single .selectize-input:after {
   content: ' ';
   display: block;
@@ -296,19 +338,23 @@
   border-width: 5px 5px 0 5px;
   border-color: #808080 transparent transparent transparent;
 }
+
 .selectize-control.single .selectize-input.dropdown-active:after {
   margin-top: -4px;
   border-width: 0 5px 5px 5px;
   border-color: transparent transparent #808080 transparent;
 }
+
 .selectize-control.rtl.single .selectize-input:after {
   left: 15px;
   right: auto;
 }
+
 .selectize-control.rtl .selectize-input > input {
   margin: 0 4px 0 -2px !important;
 }
+
 .selectize-control .selectize-input.disabled {
   opacity: 0.5;
   background-color: #fafafa;
-}
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 1
desktop/core/src/desktop/static/desktop/ext/js/selectize.min.js


+ 28 - 13
desktop/core/src/desktop/static/desktop/js/apiHelper.js

@@ -33,6 +33,8 @@
   var IMPALA_INVALIDATE_API = '/impala/api/invalidate';
   var CONFIG_SAVE_API = '/desktop/api/configurations/save/';
   var CONFIG_APPS_API = '/desktop/api/configurations';
+  var NAV_ADD_TAGS_API = '/metadata/api/navigator/add_tags';
+  var NAV_DELETE_TAGS_API = '/metadata/api/navigator/delete_tags';
   var NAV_LIST_TAGS_API = '/metadata/api/navigator/list_tags';
   var NAV_FIND_ENTITY_API = '/metadata/api/navigator/find_entity';
 
@@ -1096,15 +1098,27 @@
       }
     }
 
-    fetchAssistData.bind(self)($.extend({}, options, {
+    fetchAssistData.bind(self)($.extend({ sourceType: 'nav' }, options, {
       url: url,
       errorCallback: self.assistErrorCallback(options),
-      cacheCondition: function (data) {
-        return data.status === 0 && typeof data.entity !== 'undefined';
-      }
+      noCache: true
     }));
   };
 
+  ApiHelper.prototype.addNavTags = function (entityId, tags) {
+    return $.post(NAV_ADD_TAGS_API, {
+      id: ko.mapping.toJSON(entityId),
+      tags: ko.mapping.toJSON(tags)
+    });
+  };
+
+  ApiHelper.prototype.deleteNavTags = function (entityId, tags) {
+    return $.post(NAV_DELETE_TAGS_API, {
+      id: ko.mapping.toJSON(entityId),
+      tags: ko.mapping.toJSON(tags)
+    });
+  };
+
   /**
    * Lists all available navigator tags
    *
@@ -1115,12 +1129,10 @@
    */
   ApiHelper.prototype.listNavTags = function (options) {
     var self = this;
-    fetchAssistData.bind(self)($.extend({}, options, {
+    fetchAssistData.bind(self)($.extend({ sourceType: 'nav' }, options, {
       url: NAV_LIST_TAGS_API,
       errorCallback: self.assistErrorCallback(options),
-      cacheCondition: function (data) {
-        return (data.status === 0 && typeof data.tags !== 'undefined' && Object.keys(data.tags).length > 0);
-      }
+      noCache: true
     }));
   };
 
@@ -1128,6 +1140,7 @@
    * @param {Object} options
    * @param {string} options.sourceType
    * @param {string} options.url
+   * @param {boolean} [options.noCache]
    * @param {Function} options.cacheCondition - Determines whether it should be cached or not
    * @param {Function} options.successCallback
    * @param {Function} options.errorCallback
@@ -1140,10 +1153,12 @@
       return
     }
 
-    var cachedData = $.totalStorage("hue.assist." + self.getTotalStorageUserPrefix(options.sourceType)) || {};
-    if (typeof cachedData[options.url] !== "undefined" && ! self.hasExpired(cachedData[options.url].timestamp)) {
-      options.successCallback(cachedData[options.url].data);
-      return;
+    if (!options.noCache) {
+      var cachedData = $.totalStorage("hue.assist." + self.getTotalStorageUserPrefix(options.sourceType)) || {};
+      if (typeof cachedData[options.url] !== "undefined" && ! self.hasExpired(cachedData[options.url].timestamp)) {
+        options.successCallback(cachedData[options.url].data);
+        return;
+      }
     }
     if (typeof options.editor !== 'undefined' && options.editor !== null) {
       options.editor.showSpinner();
@@ -1182,7 +1197,7 @@
       timeout: options.timeout
     }).success(function (data) {
       // Safe to assume all requests in the queue have the same cacheCondition
-      if (data.status === 0 && !self.successResponseIsError(data) && options.cacheCondition(data)) {
+      if (!options.noCache && data.status === 0 && !self.successResponseIsError(data) && options.cacheCondition(data)) {
         cachedData = $.totalStorage("hue.assist." + self.getTotalStorageUserPrefix(options.sourceType)) || {};
         cachedData[options.url] = {
           timestamp: (new Date()).getTime(),

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

@@ -22,6 +22,30 @@
   }
 }(function (ko) {
 
+  ko.bindingHandlers.tagEditor = {
+    init: function (element, valueAccessor) {
+      var options = valueAccessor();
+
+      options = $.extend({
+        plugins: ['restore_on_backspace', 'remove_button'],
+        options: $.map(options.initialTags, function (value) { return { value: value, text: value } }),
+        delimiter: ',',
+        items: options.initialTags,
+        closeAfterSelect: true,
+        persist: true,
+        preload: true,
+        create: function(input) {
+          return {
+            value: input.replace(/\s/g, '-'),
+            text: input.replace(/\s/g, '-')
+          }
+        }
+      }, options);
+
+      $(element).selectize(options)[0].selectize;
+    }
+  };
+
   ko.bindingHandlers.toggleOverflow = {
     render: function ($element) {
       if (hueUtils.isOverflowing($element.find('.toggle-overflow'))) {

+ 30 - 28
desktop/core/src/desktop/templates/sql_context_popover.mako

@@ -1033,21 +1033,18 @@ from metadata.conf import has_navigator
     }));
   </script>
 
+  <link href="${ static('desktop/ext/css/selectize.css') }" rel="stylesheet">
+
   <script type="text/html" id="sql-nav-tags-template">
     <div class="sql-context-flex">
       <div class="sql-context-flex-header">
         <div style="margin: 10px 5px 0 10px;">
           <span style="font-size: 15px; font-weight: 300;">${_('Tags')}</span>
-          <a href="#" data-bind="toggle: searchVisible"><i class="snippet-icon fa fa-search inactive-action margin-left-10" data-bind="css: { 'blue': searchVisible }"></i></a>
-          <input class="input-large sql-context-inline-search" type="text" data-bind="visible: searchVisible, hasFocus: searchFocus, clearable: searchInput, valueUpdate:'afterkeydown'" placeholder="${ _('Filter tags...') }">
         </div>
       </div>
       <div class="sql-context-flex-fill sql-columns-table" style="position:relative; height: 100%; overflow-y: auto;">
         <div style="margin: 10px">
-          <!-- ko foreach: filteredTags -->
-          <span class="label label-info" data-bind="text: $data"></span>
-          <!-- /ko -->
-          <div class="sql-context-empty-columns" data-bind="visible: filteredTags().length === 0">${_('No tags found')}</div>
+          <textarea style="width: 100%" data-bind="tagEditor: { placeholder: '${_ko('Enter tags...')}', initialTags: currentTags, onItemAdd: onTagAdd, onItemRemove: onTagRemove, load: loadTags  }"></textarea>
         </div>
       </div>
     </div>
@@ -1057,38 +1054,43 @@ from metadata.conf import has_navigator
     (function (factory) {
       if(typeof require === "function") {
         require([
-          'knockout'
+          'knockout',
+          'desktop/js/apiHelper'
         ], factory);
       } else {
-        factory(ko);
+        factory(ko, ApiHelper);
       }
-    }(function (ko) {
+    }(function (ko, ApiHelper) {
 
       function SqlNavTags(params) {
         var self = this;
-        var tags = params.entity.tags;
+        var apiHelper = ApiHelper.getInstance();
 
-        self.searchInput = ko.observable('');
-        self.searchVisible = ko.observable(false);
-        self.searchFocus = ko.observable(false);
+        self.currentTags = params.entity.tags;
+        self.allTags = ko.observableArray();
 
-        self.searchVisible.subscribe(function (newValue) {
-          if (newValue) {
-            self.searchFocus(true);
-          }
-        });
+        var fetchAllTags = function () {
+          apiHelper.listNavTags({
+            successCallback: function (data) {
+              self.allTags(Object.keys(data.tags))
+            },
+            silenceErrors: true
+          });
+        };
+        fetchAllTags();
 
-        self.filteredTags = ko.pureComputed(function () {
-          if (self.searchInput() === '') {
-            return tags;
-          }
-          var query = self.searchInput().toLowerCase();
-          return tags.filter(function (tag) {
-            return tag.toLowerCase().indexOf(query) != -1;
-          })
-        });
+        self.loadTags = function (query, callback) {
+          console.log(self.allTags());
+          callback($.map(self.allTags(), function (tag) { return { value: tag, text: tag }}));
+        };
 
-        // TODO: Have this one fetch the tags based on params instead
+        self.onTagAdd = function (value) {
+          apiHelper.addNavTags(params.entity.identity, [value]);
+        };
+
+        self.onTagRemove = function (value) {
+          apiHelper.deleteNavTags(params.entity.identity, [value]);
+        };
       }
 
       ko.components.register('sql-nav-tags', {

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä