Kaynağa Gözat

HUE-4144 [editor] Add search functionality to snippet DB selection

Johan Ahlen 9 yıl önce
ebeveyn
işleme
502479c

+ 1 - 0
desktop/core/src/desktop/static/desktop/css/hue3.css

@@ -2414,6 +2414,7 @@ body {
 }
 
 .hue-inner-drop-down > li > a:hover,
+.hue-inner-drop-down > li.active,
 .hue-context-menu > li > a:not(.disabled):hover {
   background-color: #DBE8F1;
 }

+ 1 - 0
desktop/libs/notebook/src/notebook/templates/editor.mako

@@ -42,6 +42,7 @@ ${ tableStats.tableStats() }
 ${ assist.assistPanel() }
 ${ configKoComponents.config() }
 ${ notebookKoComponents.downloadSnippetResults() }
+${ notebookKoComponents.snippetDbSelection() }
 
 ${ editorComponents.commonJS() }
 

+ 2 - 7
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1297,13 +1297,8 @@ ${ hueIcons.symbols() }
 <script type="text/html" id="editor-snippet-header">
   <div class="hover-actions inline pull-right" style="font-size: 15px; position: relative;">
     <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
-    <!-- ko if: availableDatabases().length > 0 -->
-    <a class="inactive-action active-database margin-left-10" href="javascript:void(0)" data-toggle="dropdown" data-bind="toggle: dbSelectionVisible, css: { 'blue': dbSelectionVisible }"><span data-bind="visible: isSqlDialect, text: database"  title="${ _('Selected database') }"></span> <i class="fa fa-caret-down"></i></a>
-    <div class="dropdown-menu" style="overflow-y: scroll; min-width: 170px; min-height: 34px; max-height: 200px;">
-      <ul class="hue-inner-drop-down" data-bind="foreachVisible: { data: availableDatabases, minHeight: 34, container: '.dropdown-menu' }">
-        <li><a href="javascript:void(0)" data-bind="text: $data, click: function () { $parent.database($data); }"></a></li>
-      </ul>
-    </div>
+    <!-- ko if: availableDatabases().length > 0 && isSqlDialect() -->
+    <div data-bind="component: { name: 'snippet-db-selection', params: { selectedDatabase: database, availableDatabases: availableDatabases } }" style="display: inline-block"></div>
     <!-- /ko -->
     <a class="inactive-action margin-left-10" href="javascript:void(0)" data-bind="visible: status() != 'ready' && status() != 'loading', click: function() { hideFixedHeaders(); $data.showLogs(!$data.showLogs());}, css: {'blue': $data.showLogs}" title="${ _('Show Logs') }"><i class="fa fa-file-text-o"></i></a>
     <a class="inactive-action margin-left-10" href="javascript:void(0)" data-bind="toggle: settingsVisible, visible: hasProperties, css: { 'blue' : settingsVisible }" title="${ _('Settings and properties') }"><i class="fa fa-cog"></i></a>

+ 1 - 0
desktop/libs/notebook/src/notebook/templates/notebook.mako

@@ -53,6 +53,7 @@ ${ assist.assistPanel() }
 ${ configKoComponents.config() }
 ${ notebookKoComponents.addSnippetMenu() }
 ${ notebookKoComponents.downloadSnippetResults() }
+${ notebookKoComponents.snippetDbSelection() }
 
 ${ editorComponents.commonJS() }
 

+ 148 - 0
desktop/libs/notebook/src/notebook/templates/notebook_ko_components.mako

@@ -21,6 +21,154 @@ from django.utils.translation import ugettext as _
 from desktop.views import _ko
 %>
 
+<%def name="snippetDbSelection()">
+  <style>
+    .db-selection-input {
+      width: 175px !important;
+      height: 12px !important;
+      font-size: 12px !important;
+      border: 1px solid rgba(0,0,0,0.2) !important;
+      border-radius: 2px !important;
+      box-shadow: none !important;
+      margin-left: -1px;
+    }
+  </style>
+
+  <script type="text/html" id="snippet-db-selection-template">
+    <!-- ko ifnot: dbSelectionVisible -->
+    <a class="inactive-action active-database margin-left-10" href="javascript:void(0)" data-bind="toggle: dbSelectionVisible, css: { 'blue': dbSelectionVisible }">
+      <span data-bind="text: selectedDatabase, visible: ! dbSelectionVisible()"  title="${ _('Selected database') }"></span>
+      <i class="fa fa-caret-down"></i>
+    </a>
+    <!-- /ko -->
+    <!-- ko if: dbSelectionVisible -->
+    <input class="db-selection-input" type="text" data-bind="textInput: filter, attr: { 'placeHolder': selectedDatabase }, visible: dbSelectionVisible, style: { color: filterEdited() ? '#000' : '#AAA' }"/>
+    <i class="fa fa-caret-down"></i>
+    <!-- /ko -->
+    <div data-bind="css: { 'open' : dbSelectionVisible }" style="display: inline-block;">
+      <div class="dropdown-menu" data-bind="visible: filteredDatabases().length > 0" style="overflow-y: scroll; width: 190px; min-height: 34px; max-height: 200px;">
+        <ul class="hue-inner-drop-down" data-bind="foreachVisible: { data: filteredDatabases, minHeight: 34, container: '.dropdown-menu' }">
+          <li><a href="javascript:void(0)" data-bind="text: $data, click: function () { $parent.selectedDatabase($data); }"></a></li>
+        </ul>
+      </div>
+    </div>
+  </script>
+
+  <script type="text/javascript" charset="utf-8">
+    (function (factory) {
+      if(typeof require === "function") {
+        require(['knockout'], factory);
+      } else {
+        factory(ko);
+      }
+    }(function (ko) {
+
+      var SnippetDbSelection = function (params, element) {
+        var self = this;
+        self.dbSelectionVisible = ko.observable(false);
+        self.selectedDatabase = params.selectedDatabase;
+        self.availableDatabases = params.availableDatabases;
+
+        var closeOnOutsideClick = function (e) {
+          var $input = $(element).find('.db-selection-input');
+          if (!$input.is($(e.target))) {
+            self.dbSelectionVisible(false);
+          }
+        };
+
+        var inputKeyup = function (e) {
+          var $currentActive = $(element).find('.hue-inner-drop-down > .active');
+          var activeTop = $currentActive.length !== 0 ? $currentActive.position().top : 0;
+          var activeHeight = $currentActive.length !== 0 ? $currentActive.outerHeight(true) : $(element).find('.hue-inner-drop-down li:first-child').outerHeight(true);
+          var containerHeight = $(element).find('.dropdown-menu').innerHeight();
+          var containerScrollTop = $(element).find('.dropdown-menu').scrollTop();
+
+          $currentActive.removeClass('active');
+          if (e.keyCode === 27) {
+            // esc
+            self.dbSelectionVisible(false);
+          } else if (e.keyCode === 38) {
+            // up
+            if ($currentActive.length !== 0 && $currentActive.prev().length !== 0) {
+              if (activeTop < containerScrollTop + activeHeight) {
+                $(element).find('.dropdown-menu').scrollTop(activeTop - containerHeight/2);
+              }
+              $currentActive.prev().addClass('active');
+            }
+          } else if (e.keyCode === 40) {
+            // down
+            if ($currentActive.length === 0) {
+              $(element).find('.hue-inner-drop-down li:first-child').addClass('active');
+            } else if ($currentActive.next().length !== 0) {
+              if ((activeTop + activeHeight *3) > containerHeight - containerScrollTop) {
+                $(element).find('.dropdown-menu').scrollTop(activeTop - containerHeight/2);
+              }
+              $currentActive.next().addClass('active');
+            } else {
+              $currentActive.addClass('active');
+            }
+          } else if (e.keyCode === 13) {
+            // enter
+            if ($currentActive.length > 0) {
+              self.selectedDatabase(ko.dataFor($currentActive[0]));
+              self.dbSelectionVisible(false);
+              $(element).find('.dropdown-menu').scrollTop(0)
+            }
+          } else {
+            $(element).find('.dropdown-menu').scrollTop(0)
+          }
+        };
+        self.filter = ko.observable('');
+
+        self.selectedDatabase.subscribe(function () {
+          self.dbSelectionVisible(false);
+        });
+
+        self.filterEdited = ko.observable(false);
+
+        self.filter.subscribe(function () {
+          self.filterEdited(true);
+          $(element).find('.hue-inner-drop-down > .active').removeClass('.active');
+        });
+        self.dbSelectionVisible.subscribe(function (newValue) {
+          self.filterEdited(false);
+          if (newValue) {
+            window.setTimeout(function () {
+              self.filter('');
+              $(window).on('click', closeOnOutsideClick);
+              $(element).find('.db-selection-input').on('keyup', inputKeyup);
+              $(element).find('.db-selection-input').focus();
+            }, 0);
+          } else {
+            $(element).find('.hue-inner-drop-down > .active').removeClass('.active');
+            $(window).off('click', closeOnOutsideClick);
+            $(element).find('.db-selection-input').off('keyup', inputKeyup);
+          }
+        });
+        self.filteredDatabases = ko.pureComputed(function () {
+          if (self.filter() === '' || ! self.filterEdited()) {
+            return self.availableDatabases();
+          } else {
+            var lowerFilter = self.filter().toLowerCase();
+            return self.availableDatabases().filter(function (database) {
+              return database.toLowerCase().indexOf(lowerFilter) !== -1;
+            });
+          }
+        });
+      };
+
+      ko.components.register('snippet-db-selection', {
+        viewModel: {
+          createViewModel: function(params, componentInfo) {
+            return new SnippetDbSelection(params, componentInfo.element);
+          }
+        },
+        template: { element: 'snippet-db-selection-template' }
+      });
+    }));
+  </script>
+</%def>
+
 <%def name="addSnippetMenu()">
   <script type="text/html" id="add-snippet-menu-template">
     <div class="add-snippet-button" style="position:relative; width:65px; text-align: center;">