gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
262 lines (230 loc) • 9.51 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';
import { getFromLocalStorage, setInLocalStorage } from '../../utils/storageUtils';
ko.bindingHandlers.assistVerticalResizer = {
init: function (element, valueAccessor) {
const $container = $(element);
const options = ko.unwrap(valueAccessor());
const panelDefinitions = options.panels;
const checkForElements = function () {
const $allPanels = $container.children('.assist-inner-panel');
const $allExtras = $container.children('.assist-fixed-height');
if (
panelDefinitions().length === $allPanels.length &&
($allExtras.length > 0 || options.noFixedHeights)
) {
ko.bindingHandlers.assistVerticalResizer.updateWhenRendered(element, valueAccessor);
} else {
timeout = window.setTimeout(checkForElements, 10);
}
};
checkForElements();
let timeout = -1;
panelDefinitions.subscribe(() => {
window.clearTimeout(timeout);
timeout = window.setTimeout(checkForElements, 10);
});
},
updateWhenRendered: function (element, valueAccessor) {
const options = ko.unwrap(valueAccessor());
const panelDefinitions = options.panels;
const $container = $(element);
let $allPanels = $container.children('.assist-inner-panel');
const $allResizers = $container.children('.assist-resizer');
const $allExtras = $container.children('.assist-fixed-height');
let allExtrasHeight = 0;
$allExtras.each((idx, extra) => {
allExtrasHeight += $(extra).outerHeight(true);
});
window.clearInterval($container.data('height_interval'));
if (panelDefinitions().length === 0) {
$allExtras.show();
return;
}
if (panelDefinitions().length === 1) {
const adjustHeightSingle = function () {
$allPanels.height($container.innerHeight() - allExtrasHeight);
};
const heightAdjustInterval = window.setInterval(adjustHeightSingle, 800);
adjustHeightSingle();
$container.data('height_interval', heightAdjustInterval);
$(window).resize(adjustHeightSingle);
huePubSub.subscribe('assist.forceRender', () => {
window.setTimeout(adjustHeightSingle, 200);
});
$allExtras.show();
$allPanels.show();
return;
}
const panelRatios = getFromLocalStorage('assist.innerPanelRatios', {});
let totalRatios = 0;
$.each($allPanels, (idx, panel) => {
const panelDef = panelDefinitions()[idx];
if (!panelRatios[panelDef.type]) {
panelRatios[panelDef.type] = 1 / panelDefinitions().length;
}
totalRatios += panelRatios[panelDef.type];
$(panel).data('minHeight', panelDef.minHeight);
});
// Normalize the ratios in case new panels were added or removed.
if (totalRatios !== 1) {
const diff = 1 / totalRatios;
$.each(panelDefinitions(), (idx, panel) => {
panelRatios[panel.type] = panelRatios[panel.type] * diff;
});
}
let totalHeight = -1;
let containerTop = $container.offset().top;
// Resizes all containers according to the set ratios
const resizeByRatio = function () {
if (totalHeight === $container.innerHeight()) {
return;
}
$allPanels = $container.children('.assist-inner-panel');
totalHeight = $container.innerHeight();
containerTop = $container.offset().top;
$.each($allPanels, (idx, panel) => {
const panelDef = panelDefinitions()[idx];
if (!panelRatios[panelDef.type] || $allPanels.length === 1) {
panelRatios[panelDef.type] = 1 / panelDefinitions().length;
}
totalRatios += panelRatios[panelDef.type];
$(panel).data('minHeight', panelDef.minHeight);
});
const availableForPanels = totalHeight - allExtrasHeight;
let leftoverSpace = 0;
$allPanels.each((idx, panel) => {
const $panel = $(panel);
const desiredHeight = availableForPanels * panelRatios[panelDefinitions()[idx].type];
const newHeight = Math.max($panel.data('minHeight'), desiredHeight);
$panel.height(newHeight);
leftoverSpace += newHeight - desiredHeight;
});
// The minheight is greater than the ratio so we shrink where possible
if (leftoverSpace > 0) {
$allPanels.each((idx, panel) => {
if (leftoverSpace === 0) {
return false;
}
const $panel = $(panel);
const currentHeight = $panel.height();
const possibleContribution = Math.min(
currentHeight - $panel.data('minHeight'),
leftoverSpace
);
if (possibleContribution > 0) {
$panel.height(currentHeight - possibleContribution);
leftoverSpace -= possibleContribution;
}
});
}
};
resizeByRatio();
$(window).resize(resizeByRatio);
window.setTimeout(resizeByRatio, 1000);
huePubSub.subscribe('assist.forceRender', () => {
window.setTimeout(resizeByRatio, 200);
});
$allExtras.show();
$allPanels.show();
const fitPanelHeights = function ($panelsToResize, desiredTotalHeight) {
let currentHeightOfPanels = 0;
let noMoreSpace = true;
$panelsToResize.each((idx, panel) => {
const $panel = $(panel);
const panelHeight = $panel.outerHeight(true);
noMoreSpace = noMoreSpace && panelHeight <= $panel.data('minHeight');
currentHeightOfPanels += panelHeight;
});
let distanceToGo = desiredTotalHeight - currentHeightOfPanels;
if (noMoreSpace && distanceToGo < 0) {
return;
}
// Add all to the first panel if expanding (distanceToGo is positive
if (distanceToGo >= 0) {
$panelsToResize.first().height($panelsToResize.first().height() + distanceToGo + 'px');
return;
}
// Remove as much as possible on each panel if shrinking (distanceToGo is negative)
$panelsToResize.each((idx, panel) => {
const $panel = $(panel);
const initialHeight = $panel.height();
const newHeight = Math.max($panel.data('minHeight'), initialHeight + distanceToGo);
if (initialHeight === newHeight) {
return true;
}
$panel.height(newHeight);
distanceToGo += initialHeight - newHeight;
if (distanceToGo >= 0) {
return false;
}
});
};
$allResizers.each((idx, resizer) => {
const $resizer = $(resizer);
let extrasBeforeHeight = 0;
$resizer.prevAll('.assist-fixed-height').each((idx, extra) => {
extrasBeforeHeight += $(extra).outerHeight(true);
});
const $panelsBefore = $resizer.prevAll('.assist-inner-panel');
let limitBefore = extrasBeforeHeight;
$panelsBefore.each((idx, panel) => {
limitBefore += $(panel).data('minHeight');
});
const extrasAfterHeight = allExtrasHeight - extrasBeforeHeight;
const $panelsAfter = $resizer.nextAll('.assist-inner-panel');
let requiredSpaceAfter = extrasAfterHeight;
$panelsAfter.each((idx, panel) => {
requiredSpaceAfter += $(panel).data('minHeight');
});
$resizer.draggable({
axis: 'y',
drag: function (event, ui) {
const limitAfter = totalHeight - requiredSpaceAfter;
const position = ui.offset.top - containerTop;
if (position > limitBefore && position < limitAfter) {
fitPanelHeights($panelsBefore, position - extrasBeforeHeight);
fitPanelHeights($panelsAfter, totalHeight - extrasAfterHeight - position);
} else if (position > limitAfter) {
fitPanelHeights($panelsBefore, limitAfter - extrasBeforeHeight);
fitPanelHeights($panelsAfter, totalHeight - extrasAfterHeight - limitAfter);
} else if (position < limitBefore) {
fitPanelHeights($panelsBefore, limitBefore - extrasBeforeHeight);
fitPanelHeights($panelsAfter, totalHeight - extrasAfterHeight - limitBefore);
}
ui.offset.top = 0;
ui.position.top = 0;
},
stop: function (event, ui) {
ui.offset.top = 0;
ui.position.top = 0;
let totalHeightForPanels = 0;
$allPanels.each((idx, panel) => {
totalHeightForPanels += $(panel).outerHeight(true);
});
$allPanels.each((idx, panel) => {
panelRatios[panelDefinitions()[idx].type] =
$(panel).outerHeight(true) / totalHeightForPanels;
});
setInLocalStorage('assist.innerPanelRatios', panelRatios);
}
});
});
}
};