gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
151 lines (136 loc) • 4.69 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';
/**
* This binding can be used to show a custom context menu on right-click,
* It assumes that the context menu is nested within the bound element and
* the selector for the menu has to be supplied as a parameter.
*
* Example:
*
* <div data-bind="contextMenu: {
* menuSelector: '.hue-context-menu',
* beforeOpen: function () { ... }
* }">
* <ul class="hue-context-menu">...</ul>
* </div>
*
*/
ko.bindingHandlers.contextMenu = {
initContextMenu: function ($menu, $scrollContainer, onShow, onHide) {
let active = false;
let currentLeft = 0;
let currentTop = 0;
let openScrollTop = 0;
let openScrollLeft = 0;
const adjustForScroll = function () {
$menu.css('top', currentTop - $scrollContainer.scrollTop() + openScrollTop);
$menu.css('left', currentLeft - $scrollContainer.scrollLeft() + openScrollLeft);
};
return {
show: function (event) {
$menu.css('top', 0);
$menu.css('left', 0);
$menu.css('opacity', 0);
$menu.show();
openScrollTop = $scrollContainer.scrollTop();
openScrollLeft = $scrollContainer.scrollLeft();
const menuWidth = $menu.outerWidth(true);
if (event.clientX + menuWidth > $(window).width()) {
currentLeft = $(window).width() - menuWidth;
} else {
currentLeft = event.clientX;
}
$menu.css('left', currentLeft);
const menuHeight = $menu.outerHeight(true);
if (event.clientY + menuHeight > $(window).height()) {
currentTop = $(window).height() - menuHeight;
} else {
currentTop = event.clientY;
}
$menu.css('top', currentTop);
$menu.css('opacity', 1);
active = true;
$scrollContainer.on('scroll', adjustForScroll);
if (onShow) {
onShow();
}
},
hide: function () {
if (active) {
$scrollContainer.off('scroll', adjustForScroll);
$menu.css('opacity', 0);
window.setTimeout(() => {
$menu.hide();
}, 300);
active = false;
}
if (onHide) {
onHide();
}
}
};
},
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
const $element = $(element);
const options = valueAccessor();
const $menu = $element.find(options.menuSelector);
bindingContext.$altDown = ko.observable(false);
window.addEventListener('keydown', e => {
bindingContext.$altDown(e.altKey);
});
window.addEventListener('keyup', () => {
bindingContext.$altDown(false);
});
const $scrollContainer =
$(options.scrollContainer).length > 0 ? $(options.scrollContainer) : $(window);
const menu = ko.bindingHandlers.contextMenu.initContextMenu($menu, $scrollContainer);
element.addEventListener('contextmenu', event => {
if (document.selection && document.selection.empty) {
document.selection.empty();
} else if (window.getSelection) {
const sel = window.getSelection();
sel.removeAllRanges();
}
if (typeof options.beforeOpen === 'function') {
options.beforeOpen.bind(viewModel)();
}
menu.show(event);
huePubSub.publish('contextmenu-active', element);
event.preventDefault();
event.stopPropagation();
});
huePubSub.subscribe('contextmenu-active', origin => {
if (origin !== element) {
menu.hide();
}
});
document.addEventListener('contextmenu', () => {
menu.hide();
});
$menu.click(e => {
menu.hide();
e.stopPropagation();
});
$(document).click(event => {
if ($element.find($(event.target)).length === 0) {
menu.hide();
}
});
}
};