Pārlūkot izejas kodu

HUE-7049 [assist] Extract a generic context popover component

Johan Ahlen 8 gadi atpakaļ
vecāks
revīzija
70b9764

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


+ 1 - 0
desktop/core/src/desktop/static/desktop/less/hue.less

@@ -784,6 +784,7 @@ a:visited {
   background-color: @cui-gray-200;
 }
 
+#contextPopover .hue-popover,
 #sqlContextPopover .hue-popover {
   position: fixed;
 }

+ 403 - 0
desktop/core/src/desktop/templates/context_popover.mako

@@ -0,0 +1,403 @@
+## 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 django.utils.translation import ugettext as _
+%>
+
+<%def name="contextPopover()">
+
+  <script type="text/html" id="context-popover-contents">
+    <div class="context-popover-content">Hola</div>
+  </script>
+
+  <script type="text/html" id="context-popover-template">
+    <div class="hue-popover" data-bind="css: orientationClass, style: { 'left': left() + 'px', 'top': top() + 'px', 'width': width() + 'px', height: height() + 'px' }, resizable: { containment: 'document', handles: resizeHelper.resizableHandles, start: resizeHelper.resizeStart, stop: resizeHelper.resizeStop, resize: resizeHelper.resize }">
+      <div class="hue-popover-arrow" data-bind="style: { 'margin-left': leftAdjust() + 'px',  'margin-top': topAdjust() + 'px' }"></div>
+      <div class="hue-popover-title" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 30px;">
+        <i class="fa muted" data-bind="css: iconClass" style="margin-top: 3px"></i> <span style="padding-left: 4px;" data-bind="text: title"></span>
+        <div style="position: absolute; right: 6px; top: 8px;">
+          <a class="pointer inactive-action" data-bind="click: close"><i class="fa fa-fw fa-times"></i></a>
+        </div>
+      </div>
+      <!-- ko template: 'context-popover-contents' --><!-- /ko -->
+    </div>
+  </script>
+
+  <script type="text/javascript">
+    (function () {
+
+      var CONTEXT_POPOVER_ID = 'contextPopover';
+      var HALF_SIZE_LIMIT_X = 130;
+      var HALF_SIZE_LIMIT_Y = 100;
+      var HALF_ARROW = 6;
+
+      var preventHide = false;
+      var intervals = [];
+      var pubSubs = [];
+
+      var hidePopover = function () {
+        if (! preventHide) {
+          if ($('#' + CONTEXT_POPOVER_ID).length > 0) {
+            ko.cleanNode($('#' + CONTEXT_POPOVER_ID)[0]);
+            huePubSub.publish('context.popover.dispose');
+            $('#' + CONTEXT_POPOVER_ID).remove();
+            $(document).off('click', hideOnClickOutside);
+            while (intervals.length > 0) {
+              window.clearInterval(intervals.pop());
+            }
+            while (pubSubs.length > 0) {
+              pubSubs.pop().remove();
+            };
+            huePubSub.publish('context.popover.hidden');
+          }
+        }
+      };
+
+      var hideOnClickOutside = function (event) {
+        if (jQuery.contains(document, event.target) && !$.contains($('#' + CONTEXT_POPOVER_ID)[0], event.target) && ($('.modal')[0].length === 0 || !$.contains($('.modal')[0], event.target))) {
+          hidePopover();
+        }
+      };
+
+      function ResizeHelper(orientation, leftAdjust, topAdjust) {
+        var self = this;
+
+        var apiHelper = ApiHelper.getInstance();
+
+        var originalMidX, originalWidth, originalRightX, originalLeftX, originalMidY, originalHeight, originalTopY, originalBottomY;
+        var rightX, leftX, leftDiff, rightDiff, topY, bottomY, topDiff, bottomDiff;
+        var redrawHeaders = false;
+
+        window.setTimeout(function () {
+          var offset = $('.hue-popover').offset();
+          if (orientation === 'right') {
+            offset.left -= 5;
+          } else if (orientation === 'bottom') {
+            offset.top -= 5;
+          }
+          originalHeight = $('.hue-popover').height();
+          originalWidth = $('.hue-popover').width();
+          originalMidX = offset.left + originalWidth / 2;
+          originalMidY = offset.top + originalHeight / 2;
+          originalLeftX = offset.left;
+          originalRightX = offset.left + originalWidth;
+          originalTopY = offset.top;
+          originalBottomY = offset.top + originalHeight;
+        }, 0);
+
+        self.saveSize = function () {
+          apiHelper.setInTotalStorage('assist', 'popover.size', {
+            width: $('.hue-popover').width(),
+            height: $('.hue-popover').height()
+          });
+        };
+
+        self.resizeStart = function (event, ui) {
+          preventHide = true;
+        };
+
+        self.resizeStop = function (event, ui) {
+          if (redrawHeaders) {
+            huePubSub.publish('table.extender.redraw', 'sampleTab');
+            redrawHeaders = false;
+          }
+
+          huePubSub.publish('sql.context.popover.resized');
+
+          // Delay or it will close the popover when releasing at the window borders
+          window.setTimeout(function () {
+            preventHide = false;
+          }, 300);
+
+          self.saveSize();
+        };
+
+        var resizeTopBottomHorizontal = function (event, ui) {
+          leftX = ui.position.left;
+          rightX = ui.position.left + ui.size.width;
+
+          if (rightX < originalMidX + HALF_SIZE_LIMIT_X) {
+            ui.size.width = originalMidX + HALF_SIZE_LIMIT_X - ui.position.left;
+            rightX = ui.position.left + ui.size.width;
+            $('.hue-popover').css('width', ui.size.width + 'px');
+          }
+
+          if (leftX > originalMidX - HALF_SIZE_LIMIT_X) {
+            ui.position.left = originalMidX - HALF_SIZE_LIMIT_X;
+            ui.size.width = ui.originalSize.width - (ui.position.left - ui.originalPosition.left);
+            leftX = ui.position.left;
+            rightX = ui.position.left + ui.size.width;
+            $('.hue-popover').css('left', ui.position.left + 'px');
+            $('.hue-popover').css('width', ui.size.width + 'px');
+          }
+
+          leftDiff = originalLeftX - leftX;
+          rightDiff = originalRightX - rightX;
+          $('.hue-popover-arrow').css('margin-left', (leftDiff + rightDiff) / 2 + leftAdjust() + 'px');
+        };
+
+        var resizeLeftRightVertical = function (event, ui) {
+          if (!redrawHeaders && ui.originalPosition.top !== ui.position.top) {
+            redrawHeaders = true;
+            huePubSub.publish('table.extender.hide', 'sampleTab');
+          }
+          topY = ui.position.top;
+          bottomY = ui.position.top + ui.size.height;
+
+          if (bottomY < originalMidY + HALF_SIZE_LIMIT_Y) {
+            ui.size.height = originalMidY + HALF_SIZE_LIMIT_Y - ui.position.top;
+            bottomY = ui.position.top + ui.size.height;
+            $('.hue-popover').css('height', ui.size.height + 'px');
+          }
+
+          if (topY > originalMidY - HALF_SIZE_LIMIT_Y) {
+            ui.position.top = originalMidY - HALF_SIZE_LIMIT_Y;
+            ui.size.height = ui.originalSize.height - (ui.position.top - ui.originalPosition.top);
+            topY = ui.position.top;
+            bottomY = ui.position.top + ui.size.height;
+            $('.hue-popover').css('top', ui.position.top + 'px');
+            $('.hue-popover').css('height', ui.size.height + 'px');
+          }
+
+          topDiff = originalTopY - topY;
+          bottomDiff = originalBottomY - bottomY;
+          $('.hue-popover-arrow').css('margin-top', (topDiff + bottomDiff) / 2 + topAdjust() + 'px');
+        };
+
+        switch(orientation) {
+          case 'top':
+            self.resizableHandles = "w, nw, n, ne, e";
+            self.resize = function (event, ui) {
+              resizeTopBottomHorizontal(event, ui);
+              // TODO: Implement resize height limits when popover is above
+            };
+            break;
+          case 'right':
+            self.resizableHandles = "n, ne, e, se, s";
+            self.resize = function (event, ui) {
+              resizeLeftRightVertical(event, ui);
+              if (ui.size.width < 350) {
+                ui.size.width = 350;
+                $('.hue-popover').css('width', 350 + 'px');
+              }
+            };
+            break;
+          case 'bottom':
+            self.resizableHandles = "e, se, s, sw, w";
+            self.resize = function (event, ui) {
+              resizeTopBottomHorizontal(event, ui);
+              if (ui.size.height < 200) {
+                ui.size.height = 200;
+                $('.hue-popover').css('height', 200 + 'px');
+              }
+            };
+            break;
+          case 'left':
+            self.resizableHandles = "s, sw, w, nw, n";
+            self.resize = function (event, ui) {
+              resizeLeftRightVertical(event, ui);
+              // TODO: Implement resize width limits when popover is on the left
+            };
+            break;
+        }
+      }
+
+      function ContextPopoverViewModel(params) {
+        var self = this;
+        self.disposalFunctions = [];
+
+        var apiHelper = ApiHelper.getInstance();
+
+        self.left = ko.observable(0);
+        self.top = ko.observable(0);
+
+        self.iconClass = 'fa-info';
+        self.title = "title";
+
+        self.showInAssistEnabled = typeof params.showInAssistEnabled !== 'undefined' ? params.showInAssistEnabled : true;
+
+        var popoverSize = apiHelper.getFromTotalStorage('assist', 'popover.size', {
+          width: 450,
+          height: 400
+        });
+
+        self.width = ko.observable(popoverSize.width);
+        self.height = ko.observable(popoverSize.height);
+
+        self.leftAdjust = ko.observable(0);
+        self.topAdjust = ko.observable(0);
+        self.data = params.data;
+        self.sourceType = params.sourceType;
+        self.defaultDatabase = params.defaultDatabase;
+        self.close = hidePopover;
+        var orientation = params.orientation || 'bottom';
+        self.contents = null;
+        self.resizeHelper = new ResizeHelper(orientation, self.leftAdjust, self.topAdjust);
+
+        if (typeof params.source.element !== 'undefined') {
+          // Track the source element and close the popover if moved
+          var $source = $(params.source.element);
+          var originalSourceOffset = $source.offset();
+          var currentSourceOffset;
+          intervals.push(window.setInterval(function () {
+            currentSourceOffset = $source.offset();
+            if (currentSourceOffset.left !== originalSourceOffset.left || currentSourceOffset.top !== originalSourceOffset.top) {
+              hidePopover();
+            }
+          }, 200));
+        }
+
+        var windowWidth = $(window).width();
+        var fitHorizontally = function () {
+          var left = params.source.left + Math.round((params.source.right - params.source.left) / 2) - (self.width() / 2);
+          if (left + self.width() > windowWidth - 10) {
+            self.leftAdjust(left + self.width() - windowWidth + 5);
+            left = windowWidth - self.width() - 10;
+          } else if (left < 10) {
+            self.leftAdjust(left - 10 - HALF_ARROW);
+            left = 10;
+          } else {
+            self.leftAdjust(-HALF_ARROW);
+          }
+          self.left(left);
+        };
+
+        var windowHeight = $(window).height();
+        var fitVertically = function () {
+          var top = params.source.top + Math.round((params.source.bottom - params.source.top) / 2) - (self.height() / 2);
+          if (top + self.height() > windowHeight - 10) {
+            self.topAdjust(top + self.height() - windowHeight + 5);
+            top = windowHeight - self.height() - 10;
+          } else if (top < 10) {
+            self.topAdjust(top - 10 - HALF_ARROW);
+            top = 10;
+          } else {
+            self.topAdjust(-HALF_ARROW);
+          }
+          self.top(top);
+        };
+
+        switch (orientation) {
+          case 'top':
+            fitHorizontally();
+            self.top(params.source.top - self.height());
+            break;
+          case 'right':
+            fitVertically();
+            self.left(params.source.right);
+            break;
+          case 'bottom':
+            fitHorizontally();
+            self.top(params.source.bottom);
+            break;
+          case 'left':
+            fitVertically();
+            self.left(params.source.left - self.width());
+        }
+
+        self.orientationClass = 'hue-popover-' + orientation;
+
+        if (params.delayedHide) {
+          var hideTimeout = -1;
+          var onLeave = function () {
+            hideTimeout = window.setTimeout(function () {
+              $('.hue-popover').fadeOut(200, function () {
+                hidePopover();
+              })
+            }, 1000);
+          };
+
+          var onEnter = function () {
+            window.clearTimeout(hideTimeout);
+          };
+
+          $(params.delayedHide).add($('.hue-popover')).on('mouseleave', onLeave).on('mouseenter', onEnter);
+
+          var keepPopoverOpenOnClick = function () {
+            window.clearTimeout(hideTimeout);
+            $(params.delayedHide).add($('.hue-popover')).off('mouseleave', onLeave).off('mouseenter', onEnter);
+          };
+
+          $('.hue-popover').on('click', keepPopoverOpenOnClick);
+
+          self.disposalFunctions.push(function () {
+            $(params.delayedHide).add($('.hue-popover')).off('mouseleave', onLeave).off('mouseenter', onEnter);
+            $('.hue-popover').off('click', keepPopoverOpenOnClick);
+          });
+        }
+
+        var closeOnEsc = function (e) {
+          if (e.keyCode === 27) {
+            hidePopover();
+          }
+        };
+
+        $(document).on('keyup', closeOnEsc);
+
+        self.disposalFunctions.push(function () {
+          $(document).off('keyup', closeOnEsc);
+        });
+
+        window.setTimeout(function() {
+          $(document).on('click', hideOnClickOutside);
+        }, 0);
+
+        self.disposalFunctions.push(function () {
+          $(document).off('click', hideOnClickOutside);
+        })
+      }
+
+      ContextPopoverViewModel.prototype.dispose = function() {
+        var self = this;
+        self.disposalFunctions.forEach(function (fn) {
+          fn();
+        })
+      };
+
+      ko.components.register('context-popover', {
+        viewModel: ContextPopoverViewModel,
+        template: { element: 'context-popover-template' }
+      });
+
+      huePubSub.subscribe('context.popover.hide', hidePopover);
+
+      huePubSub.subscribe('context.popover.show', function (details) {
+        hidePopover();
+        var $contextPopover = $('<div id="' + CONTEXT_POPOVER_ID + '" data-bind="component: { name: \'context-popover\', params: $data }" />');
+        $('body').append($contextPopover);
+        ko.applyBindings(details, $contextPopover[0]);
+        huePubSub.publish('context.popover.shown');
+      });
+
+
+      // TODO: Drop once sql context popover is replaced
+      huePubSub.subscribe('context.popover.dispose', function () {
+        huePubSub.publish('sql.context.popover.dispose');
+      });
+      huePubSub.subscribe('context.popover.hidden', function () {
+        huePubSub.publish('sql.context.popover.hidden');
+      });
+      huePubSub.subscribe('context.popover.shown', function () {
+        huePubSub.publish('sql.context.popover.shown');
+      });
+      huePubSub.subscribe('sql.context.popover.hide', hidePopover);
+      huePubSub.subscribe('sql.context.popover.show', function (details) {
+        huePubSub.publish('context.popover.show', details);
+      });
+    })();
+  </script>
+</%def>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels