devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
261 lines (246 loc) • 9.17 kB
JavaScript
/**
* DevExtreme (esm/viz/core/layout.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import {
normalizeEnum as _normalizeEnum
} from "./utils";
const _min = Math.min;
const _max = Math.max;
const _round = Math.round;
const ALIGN_START = 0;
const ALIGN_MIDDLE = 1;
const ALIGN_END = 2;
const horizontalAlignmentMap = {
left: 0,
center: 1,
right: 2
};
const verticalAlignmentMap = {
top: 0,
center: 1,
bottom: 2
};
const sideMap = {
horizontal: 0,
vertical: 1
};
const slicersMap = {};
const BBOX_CEIL_CORRECTION = 2;
slicersMap[0] = function(a, b, size) {
return [a, _min(b, a + size)]
};
slicersMap[1] = function(a, b, size) {
return [_max(a, (a + b - size) / 2), _min(b, (a + b + size) / 2)]
};
slicersMap[2] = function(a, b, size) {
return [_max(a, b - size), b]
};
function pickValue(value, map, defaultValue) {
const val = _normalizeEnum(value);
return val in map ? map[val] : defaultValue
}
function normalizeLayoutOptions(options) {
const side = pickValue(options.side, sideMap, 1);
const alignment = [pickValue(options.horizontalAlignment, horizontalAlignmentMap, 1), pickValue(options.verticalAlignment, verticalAlignmentMap, 0)];
return {
side: side,
primary: bringToEdge(alignment[side]),
secondary: alignment[1 - side],
weak: options.weak,
priority: options.priority || 0,
header: options.header,
position: options.position
}
}
function bringToEdge(primary) {
return primary < 2 ? 0 : 2
}
function getConjugateSide(side) {
return 1 - side
}
function getSlice(alignment, a, b, size) {
return slicersMap[alignment](a, b, size)
}
function getShrink(alignment, size) {
return (alignment > 0 ? -1 : 1) * size
}
function processForward(item, rect, minSize) {
const side = item.side;
const size = item.element.measure([rect[2] - rect[0], rect[3] - rect[1]]);
const minSide = "indside" === item.position ? 0 : minSize[side];
const isValid = size[side] < rect[2 + side] - rect[side] - minSide;
if (isValid) {
if ("inside" !== item.position) {
rect[item.primary + side] += getShrink(item.primary, size[side])
}
item.size = size
}
return isValid
}
function processRectBackward(item, rect, alignmentRect) {
const primarySide = item.side;
const secondarySide = getConjugateSide(primarySide);
const itemRect = [];
const secondary = getSlice(item.secondary, alignmentRect[secondarySide], alignmentRect[2 + secondarySide], item.size[secondarySide]);
itemRect[primarySide] = _round(itemRect[2 + primarySide] = rect[item.primary + primarySide] + ("inside" === item.position ? getShrink(item.primary, item.size[primarySide]) : 0));
itemRect[item.primary + primarySide] = _round(rect[item.primary + primarySide] - getShrink(item.primary, item.size[primarySide]));
if ("inside" !== item.position) {
rect[item.primary + primarySide] = itemRect[item.primary + primarySide]
}
itemRect[secondarySide] = _round(secondary[0]);
itemRect[2 + secondarySide] = _round(secondary[1]);
return itemRect
}
function processBackward(item, rect, alignmentRect, fitRect, size, targetRect) {
const itemRect = processRectBackward(item, rect, alignmentRect);
const itemFitRect = processRectBackward(item, fitRect, fitRect);
if (size[item.side] > 0) {
size[item.side] -= item.size[item.side];
targetRect[item.primary + item.side] = itemRect[item.primary + item.side];
item.element.freeSpace()
} else {
item.element.move(itemRect, itemFitRect)
}
}
function Layout() {
this._targets = []
}
Layout.prototype = {
constructor: Layout,
dispose: function() {
this._targets = null
},
add: function(target) {
this._targets.push(target)
},
forward: function(targetRect, minSize) {
const rect = targetRect.slice();
const targets = createTargets(this._targets);
let i;
const ii = targets.length;
const cache = [];
for (i = 0; i < ii; ++i) {
if (processForward(targets[i], rect, minSize)) {
cache.push(targets[i])
} else {
targets[i].element.freeSpace()
}
}
this._cache = cache.reverse();
return rect
},
backward: function(targetRect, alignmentRect) {
let size = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : [0, 0];
let backwardRect = targetRect.slice();
const fitRect = targetRect.slice();
const targets = this._cache;
let targetSide = 0;
let target;
let i;
const ii = targets.length;
for (i = 0; i < ii; ++i) {
target = targets[i];
if (target.side !== targetSide) {
backwardRect = targetRect.slice()
}
processBackward(target, backwardRect, alignmentRect, fitRect, size, targetRect);
targetSide = target.side
}
return size
}
};
function createTargets(targets) {
let i;
const ii = targets.length;
let collection = [];
let layout;
for (i = 0; i < ii; ++i) {
layout = targets[i].layoutOptions();
if (layout) {
layout = normalizeLayoutOptions(layout);
layout.element = targets[i];
collection.push(layout)
}
}
collection.sort((function(a, b) {
return b.side - a.side || a.priority - b.priority
}));
collection = processWeakItems(collection);
return collection
}
function processWeakItems(collection) {
const weakItem = collection.filter((function(item) {
return true === item.weak
}))[0];
let headerItem;
if (weakItem) {
headerItem = collection.filter((function(item) {
return weakItem.primary === item.primary && item.side === weakItem.side && item !== weakItem
}))[0]
}
if (weakItem && headerItem) {
return [makeHeader(headerItem, weakItem)].concat(collection.filter((function(item) {
return !(item === headerItem || item === weakItem)
})))
}
return collection
}
function processBackwardHeaderRect(element, rect) {
const rectCopy = rect.slice();
const itemRect = processRectBackward(element, rectCopy, rectCopy);
itemRect[element.side] = rect[element.side];
itemRect[2 + element.side] = rect[2 + element.side];
return itemRect
}
function makeHeader(header, weakElement) {
const side = header.side;
const primary = header.primary;
const secondary = header.secondary;
return {
side: side,
primary: primary,
secondary: secondary,
priority: 0,
element: {
measure: function(targetSize) {
const result = targetSize.slice();
const weakSize = weakElement.element.measure(targetSize.slice());
targetSize[primary] -= weakSize[primary];
const headerSize = header.element.measure(targetSize.slice());
result[side] = weakSize[side] = headerSize[side] = Math.max(headerSize[side], weakSize[side]);
weakElement.size = weakSize;
header.size = headerSize;
return result
},
move: function(rect, fitRect) {
if (fitRect[2] - fitRect[0] < header.size[0] + weakElement.size[0] - 2) {
this.freeSpace();
return
}
const weakRect = processBackwardHeaderRect(weakElement, fitRect);
fitRect[2 + weakElement.primary] = weakRect[weakElement.primary];
const headerFitReact = processBackwardHeaderRect(header, fitRect);
if (fitRect[2 + weakElement.primary] < rect[2 + weakElement.primary] && header.size[header.primary] > rect[2 + header.primary] - rect[header.primary]) {
rect[2 + weakElement.primary] = fitRect[2 + weakElement.primary]
}
let headerRect = processBackwardHeaderRect(header, rect);
if (headerRect[2 + weakElement.primary] > fitRect[2 + weakElement.primary]) {
rect[2 + weakElement.primary] = fitRect[2 + weakElement.primary];
headerRect = processBackwardHeaderRect(header, rect)
}
weakElement.element.move(weakRect);
header.element.move(headerRect, headerFitReact)
},
freeSpace: function() {
header.element.freeSpace();
weakElement.element.freeSpace()
}
}
}
}
export default Layout;