gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
193 lines (175 loc) • 6.25 kB
JavaScript
// 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.
import $ from 'jquery';
import * as ko from 'knockout';
import huePubSub from '../../utils/huePubSub';
ko.bindingHandlers.templatePopover = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
const options = ko.unwrap(valueAccessor());
const clickTrigger = options.trigger === 'click';
let $container = $('#popover-container');
if (!$container.length) {
$container = $('<div>').attr('id', 'popover-container').appendTo('body');
$('<div>').addClass('temp-content').hide().appendTo($container);
$('<div>').addClass('temp-title').hide().appendTo($container);
}
const $content = $container.find('.temp-content');
const $title = $container.find('.temp-title');
$.extend(options, { html: true, trigger: 'manual', container: '#popover-container' });
const $element = $(element);
const visible = options.visible || ko.observable(false);
let trackElementInterval = -1;
const hidePopover = function () {
if (visible()) {
window.clearInterval(trackElementInterval);
$element.popover('hide');
visible(false);
$(document).off('click', hideOnClickOutside);
}
};
const closeSub = huePubSub.subscribe('close.popover', hidePopover);
const hideOnClickOutside = function (event) {
if (
visible() &&
$element.data('popover') &&
!$.contains($element.data('popover').$tip[0], event.target)
) {
hidePopover();
}
};
ko.utils.domNodeDisposal.addDisposeCallback(element, () => {
if (visible && $element.data('popover')) {
hidePopover();
}
closeSub.remove();
});
const afterRender = function () {
options.content = $content.html();
options.title = $title.html();
let triggerTitle;
if ($element.attr('title')) {
triggerTitle = $element.attr('title');
$element.attr('title', null);
}
$element.popover(options);
$element.popover('show');
if (triggerTitle) {
$element.attr('title', triggerTitle);
}
const $tip = $element.data('popover').$tip;
ko.cleanNode($tip.get(0));
ko.applyBindings(viewModel, $tip.get(0));
$tip.find('.close-popover, .close-template-popover').click(event => {
hidePopover();
event.stopPropagation();
});
if (options.minWidth) {
const heightBefore = $tip.outerHeight(true);
const widthBefore = $tip.outerWidth(true);
$tip.css('min-width', options.minWidth);
// The min-width might affect the height/width in which case we reposition the popover depending on placement
const heightDiff = (heightBefore - $tip.outerHeight(true)) / 2;
const widthDiff = (widthBefore - $tip.outerWidth(true)) / 2;
if (
(!options.placement || options.placement === 'left' || options.placement === 'right') &&
heightDiff !== 0
) {
$tip.css('top', $tip.position().top + heightDiff + 'px');
} else if (
options.placement &&
(options.placement === 'bottom' || options.placement === 'top') &&
widthDiff !== 0
) {
$tip.css('left', $tip.position().left + widthDiff + 'px');
}
}
let lastWidth = $element.outerWidth(true);
let lastOffset = $element.offset();
let lastHeight = $element.outerHeight(true);
trackElementInterval = window.setInterval(() => {
const elementWidth = $element.outerWidth(true);
const elementHeight = $element.outerHeight(true);
const elementOffset = $element.offset();
if (
lastHeight !== elementHeight ||
lastWidth !== $element.outerWidth(true) ||
lastOffset.top !== elementOffset.top ||
lastOffset.left !== elementOffset.left
) {
$tip.css({
left: elementOffset.left + elementWidth / 2 - $tip.outerWidth(true) / 2,
top: elementOffset.top + elementHeight + 10
});
lastWidth = elementWidth;
lastOffset = elementOffset;
lastHeight = elementHeight;
}
}, 50);
$content.empty();
$title.empty();
$(document).on('click', hideOnClickOutside);
visible(true);
};
const showPopover = function (preventClose) {
if (!preventClose) {
huePubSub.publish('close.popover');
}
ko.renderTemplate(
options.contentTemplate,
viewModel,
{
afterRender: function () {
if (options.titleTemplate) {
ko.renderTemplate(
options.titleTemplate,
viewModel,
{
afterRender: function () {
afterRender();
}
},
$title.get(0),
'replaceChildren'
);
} else {
afterRender();
}
}
},
$content.get(0),
'replaceChildren'
);
};
if (visible()) {
window.setTimeout(() => {
showPopover(true);
}, 0);
}
if (clickTrigger) {
$element.click(e => {
if (visible()) {
hidePopover();
} else {
showPopover();
}
e.stopPropagation();
});
} else {
$element.mouseenter(showPopover);
$element.mouseleave(hidePopover);
}
}
};