Эх сурвалжийг харах

HUE-4032 [editor] Skeleton for context aware popover

Johan Ahlen 9 жил өмнө
parent
commit
6037913da2

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

@@ -2377,13 +2377,37 @@ body {
   padding: 6px 1px;
   list-style: none;
   font-size: 13px;
+
   opacity: 0;
+  transition: opacity .3s;
+}
 
+.hue-context-menu, .sql-context-popover-bottom {
   border-radius: 2px;
   background-color: #FFF;
   -webkit-box-shadow: 0 2px 8px rgba(0,0,0,.18);
   box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18), 0 2px 8px 0 rgba(0, 0, 0, 0.13);
-  transition: opacity .3s;
+}
+
+.sql-context-popover-top {
+  border-radius: 2px;
+  background-color: #FFF;
+  -webkit-box-shadow: 0 -2px 8px rgba(0,0,0,.18);
+  box-shadow: 0 -2px 6px 0 rgba(0, 0, 0, 0.18), 0 -2px 8px 0 rgba(0, 0, 0, 0.13);
+}
+
+.sql-context-popover-right {
+  border-radius: 2px;
+  background-color: #FFF;
+  -webkit-box-shadow: 2px 0 8px rgba(0,0,0,.18);
+  box-shadow: 2px 0 6px 0 rgba(0, 0, 0, 0.18), 2px 0 8px 0 rgba(0, 0, 0, 0.13);
+}
+
+.sql-context-popover-left {
+  border-radius: 2px;
+  background-color: #FFF;
+  -webkit-box-shadow: -2px 0 8px rgba(0,0,0,.18);
+  box-shadow: -2px 0 6px 0 rgba(0, 0, 0, 0.18), -2px 0 8px 0 rgba(0, 0, 0, 0.13);
 }
 
 .hue-inner-drop-down {

+ 14 - 15
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -2748,9 +2748,7 @@
           }
           e.data.locations.forEach(function (location) {
             var token = editor.session.getTokenAt(location.location.first_line - 1, location.location.first_column);
-            if (token && location.type === 'table') {
-              token.parseLocation = location;
-            }
+            token.parseLocation = location;
           });
         };
 
@@ -3012,7 +3010,7 @@
                 if ($.totalStorage("hue.ace.showLinkTooltips") == null || $.totalStorage("hue.ace.showLinkTooltips")) {
                   this.show(null, this.x + 10, this.y + 10);
                 }
-                this.link = token.parseLocation;
+                this.link = { token: token, marker: this.marker };
                 this.isClearable = true
               } else if (token.error) {
                 this.setText(this.createErrorMessage(token.error));
@@ -3101,23 +3099,24 @@
 
       HueLink = ace.require("huelink").HueLink;
       editor.hueLink = new HueLink(editor);
-      editor.hueLink.on("open", function (token) {
-        if (token.location) {
-          if (token.type === 'table') {
-            if (token.identifierChain.length === 1) {
-              window.open("/metastore/table/" + snippet.database() + "/" + token.identifierChain[0].name, '_blank');
-            } else if (token.identifierChain.length === 2) {
-              window.open("/metastore/table/" + token.identifierChain[0].name + '/' + token.identifierChain[1].name, '_blank');
-            }
-          }
+      editor.hueLink.on("open", function (link) {
+        var marker = editor.session.getMarkers()[link.marker];
+        var startCoordinates = editor.renderer.textToScreenCoordinates(marker.range.start.row, marker.range.start.column);
+        var endCoordinates = editor.renderer.textToScreenCoordinates(marker.range.end.row, marker.range.end.column);
+        var token = link.token;
+        if (token.parseLocation) {
+          huePubSub.publish('sql.context.popover.show', { data: token.parseLocation, source: {
+            left: startCoordinates.pageX,
+            top: startCoordinates.pageY,
+            right: endCoordinates.pageX,
+            bottom: endCoordinates.pageY + editor.renderer.lineHeight
+          }});
         } else if (token.value === " * " && token.columns.length > 0) {
           editor.session.replace(token.range, token.columns.join(", "))
         } else if (token.value.indexOf("'/") == 0 && token.value.lastIndexOf("'") == token.value.length - 1) {
           window.open("/filebrowser/#" + token.value.replace(/'/gi, ""), '_blank');
         } else if (token.value.indexOf("\"/") == 0 && token.value.lastIndexOf("\"") == token.value.length - 1) {
           window.open("/filebrowser/#" + token.value.replace(/\"/gi, ""), '_blank');
-        } else {
-          window.open("/metastore/table/" + snippet.database() + "/" + token.value, '_blank');
         }
       });
 

+ 248 - 0
desktop/core/src/desktop/templates/sql_context_popover.mako

@@ -0,0 +1,248 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+from desktop import conf
+from desktop.lib.i18n import smart_unicode
+from django.utils.translation import ugettext as _
+from desktop.views import _ko
+from metadata.conf import has_navigator
+%>
+
+<%def name="sqlContextPopover()">
+  <style>
+    .sql-context-popover {
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 1060;
+      display: block;
+      width: 450px;
+      height: 300px;
+      padding: 1px;
+      text-align: left;
+      text-align: start;
+      background-color: #fff;
+      -webkit-background-clip: padding-box;
+      background-clip: padding-box;
+      border: 1px solid transparent;
+    }
+
+    .sql-context-popover.sql-context-popover-top {
+      margin-top: -5px;
+    }
+
+    .sql-context-popover.sql-context-popover-top .sql-context-popover-arrow {
+      bottom: -6px;
+      left: 50%;
+      margin-left: -6px;
+      border-top-color: #338BB8;
+      border-bottom-width: 0;
+    }
+
+    .sql-context-popover.sql-context-popover-top .sql-context-popover-arrow::after {
+      bottom: 1px;
+      margin-left: -3px;
+      content: "";
+      border-top-color: #338BB8;
+      border-bottom-width: 0;
+    }
+
+    .sql-context-popover.sql-context-popover-right {
+      margin-left: 5px;
+    }
+
+    .sql-context-popover.sql-context-popover-right .sql-context-popover-arrow {
+      top: 50%;
+      left: -6px;
+      margin-top: -6px;
+      border-right-color: #338BB8;
+      border-left-width: 0;
+    }
+
+    .sql-context-popover.sql-context-popover-right .sql-context-popover-arrow::after {
+      bottom: -3px;
+      left: 1px;
+      content: "";
+      border-right-color: #338BB8;
+      border-left-width: 0;
+    }
+
+    .sql-context-popover.sql-context-popover-bottom {
+      margin-top: 7px;
+    }
+
+    .sql-context-popover.sql-context-popover-bottom .sql-context-popover-arrow {
+      top: -6px;
+      left: 50%;
+      margin-left: -6px;
+      border-top-width: 0;
+      border-bottom-color: #338BB8;
+    }
+
+    .sql-context-popover.sql-context-popover-bottom .sql-context-popover-arrow::after {
+      top: 3px;
+      margin-left: -3px;
+      content: "";
+      border-top-width: 0;
+      border-bottom-color: #338BB8;
+    }
+
+    .sql-context-popover.sql-context-popover-left {
+      margin-left: -5px;
+    }
+
+    .sql-context-popover.sql-context-popover-left .sql-context-popover-arrow {
+      top: 50%;
+      right: -6px;
+      margin-top: -3px;
+      border-right-width: 0;
+      border-left-color: #338BB8;
+    }
+
+    .sql-context-popover.sql-context-popover-left .sql-context-popover-arrow::after {
+      right: 2px;
+      bottom: -3px;
+      content: "";
+      border-right-width: 0;
+      border-left-color: #338BB8;
+    }
+
+    .sql-context-popover-title {
+      padding: 6px 10px;
+      margin: 0;
+      font-size: 0.9rem;
+      background-color: #fff;
+      border-bottom: 1px solid #ebebeb;
+    }
+
+    .sql-context-popover-title:empty {
+      display: none;
+    }
+
+    .sql-context-popover-content {
+      padding: 9px 14px;
+    }
+
+    .sql-context-popover-arrow, .sql-context-popover-arrow::after {
+      position: absolute;
+      display: block;
+      width: 0;
+      height: 0;
+      border-color: transparent;
+      border-style: solid;
+    }
+
+    .sql-context-popover-arrow {
+      border-width: 6px;
+    }
+
+    .popover-arrow::after {
+      content: "";
+      border-width: 5px;
+    }
+  </style>
+
+  <script type="text/html" id="sql-context-popover-template">
+    <div class="sql-context-popover sql-context-popover-bottom" data-bind="css: orientationClass, style: { left: left() + 'px', top: top() + 'px' }">
+      <div class="sql-context-popover-arrow"></div>
+      <div class="sql-context-popover-title">
+        <i class="pull-left fa muted" data-bind="css: iconClass" style="margin-top: 3px"></i> <span data-bind="text: title"></span>
+      </div>
+      <div class="sql-context-popover-content">
+        <pre data-bind="text: ko.mapping.toJSON(data)"></pre>
+      </div>
+    </div>
+  </script>
+
+  <script type="text/javascript" charset="utf-8">
+    require(['knockout'], function (ko) {
+
+      var hidePopover = function () {
+        $('#sqlContextPopover').remove();
+        $(document).off('click', hideOnClickOutside);
+      };
+
+      var hideOnClickOutside = function (event) {
+        if (!$.contains($('#sqlContextPopover')[0], event.target)) {
+          hidePopover();
+        }
+      };
+
+      function sqlContextPopoverViewModel(params) {
+        var self = this;
+        self.left = ko.observable();
+        self.top = ko.observable();
+        self.data = params.data;
+        var orientation = params.orientation || 'bottom';
+
+        switch (orientation) {
+          case 'left':
+            break;
+          case 'top':
+            self.left(params.source.left + Math.round((params.source.right - params.source.left) / 2) - 225);
+            self.top(params.source.top - 300);
+            break;
+          case 'right':
+            break;
+          case 'bottom':
+            self.left(params.source.left + Math.round((params.source.right - params.source.left) / 2) - 225);
+            self.top(params.source.bottom);
+        }
+
+        self.isTable = params.data.type === 'table';
+        self.isColumn = params.data.type === 'column';
+        self.isFunction = params.data.type === 'function';
+
+        if (self.isTable) {
+          self.title = params.data.identifierChain[params.data.identifierChain.length - 1].name;
+          self.iconClass = 'fa-table'
+        } else if (self.isColumn) {
+          self.title = params.data.identifierChain[params.data.identifierChain.length - 1].name;
+          self.iconClass = 'fa-columns'
+        } else if (self.isFunction) {
+          self.title = params.data.function;
+          self.iconClass = 'fa-superscript'
+        } else {
+          self.title = '';
+          self.iconClass = 'fa-info'
+        }
+        self.orientationClass = 'sql-context-popover-' + orientation;
+      }
+
+      sqlContextPopoverViewModel.prototype.dispose = function () {
+        hidePopover();
+      };
+
+      ko.components.register('sql-context-popover', {
+        viewModel: sqlContextPopoverViewModel,
+        template: { element: 'sql-context-popover-template' }
+      });
+
+      huePubSub.subscribe('sql.context.popover.hide', hidePopover);
+
+      huePubSub.subscribe('sql.context.popover.show', function (details) {
+        hidePopover();
+        var $sqlContextPopover = $('<div id="sqlContextPopover" data-bind="component: { name: \'sql-context-popover\', params: { data: data, source: source } }" />');
+        $('body').append($sqlContextPopover);
+        ko.applyBindings(details, $sqlContextPopover[0]);
+        window.setTimeout(function() {
+          $(document).on('click', hideOnClickOutside);
+        }, 0);
+      })
+    });
+  </script>
+</%def>

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

@@ -21,6 +21,7 @@
 
 <%namespace name="configKoComponents" file="/config_ko_components.mako" />
 <%namespace name="tableStats" file="/table_stats.mako" />
+<%namespace name="sqlContextPopover" file="/sql_context_popover.mako" />
 <%namespace name="assist" file="/assist.mako" />
 <%namespace name="editorComponents" file="editor_components.mako" />
 <%namespace name="notebookKoComponents" file="notebook_ko_components.mako" />
@@ -39,6 +40,7 @@ ${ editorComponents.topBar() }
 ${ editorComponents.commonHTML() }
 
 ${ tableStats.tableStats() }
+${ sqlContextPopover.sqlContextPopover() }
 ${ assist.assistPanel() }
 ${ configKoComponents.config() }
 ${ notebookKoComponents.downloadSnippetResults() }

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

@@ -1525,7 +1525,7 @@ ${ hueIcons.symbols() }
     <div class="editor span12" data-bind="css: {'single-snippet-editor ace-container-resizable' : $root.editorMode() }, clickForAceFocus: ace">
       <div class="ace-editor" data-bind="css: {'single-snippet-editor ace-editor-resizable' : $root.editorMode(), 'active-editor': inFocus }, attr: { id: id() }, delayedOverflow, aceEditor: {
         snippet: $data,
-        openIt: '${ _ko("Shift + Click to open it") }',
+        openIt: '${ _ko("Shift + Click for details") }',
         expandStar: '${ _ko("Shift + Click to replace with all columns") }',
         onBlur: saveTemporarySnippet,
         highlightedRange: result.statement_range,

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

@@ -20,6 +20,7 @@
 %>
 
 <%namespace name="tableStats" file="/table_stats.mako" />
+<%namespace name="sqlContextPopover" file="/sql_context_popover.mako" />
 <%namespace name="assist" file="/assist.mako" />
 <%namespace name="configKoComponents" file="/config_ko_components.mako" />
 <%namespace name="editorComponents" file="editor_components.mako" />
@@ -50,6 +51,7 @@ ${ editorComponents.topBar() }
 
 ${ tableStats.tableStats() }
 ${ assist.assistPanel() }
+${ sqlContextPopover.sqlContextPopover() }
 ${ configKoComponents.config() }
 ${ notebookKoComponents.addSnippetMenu() }
 ${ notebookKoComponents.downloadSnippetResults() }