250 regels
8.1 KiB
JavaScript
Executable File
250 regels
8.1 KiB
JavaScript
Executable File
/**
|
|
* FlexLayout.js
|
|
*
|
|
* Copyright, Moxiecode Systems AB
|
|
* Released under LGPL License.
|
|
*
|
|
* License: http://www.tinymce.com/license
|
|
* Contributing: http://www.tinymce.com/contributing
|
|
*/
|
|
|
|
/**
|
|
* This layout manager works similar to the CSS flex box.
|
|
*
|
|
* @setting {String} direction row|row-reverse|column|column-reverse
|
|
* @setting {Number} flex A positive-number to flex by.
|
|
* @setting {String} align start|end|center|stretch
|
|
* @setting {String} pack start|end|justify
|
|
*
|
|
* @class tinymce.ui.FlexLayout
|
|
* @extends tinymce.ui.AbsoluteLayout
|
|
*/
|
|
define("tinymce/ui/FlexLayout", [
|
|
"tinymce/ui/AbsoluteLayout"
|
|
], function(AbsoluteLayout) {
|
|
"use strict";
|
|
|
|
return AbsoluteLayout.extend({
|
|
/**
|
|
* Recalculates the positions of the controls in the specified container.
|
|
*
|
|
* @method recalc
|
|
* @param {tinymce.ui.Container} container Container instance to recalc.
|
|
*/
|
|
recalc: function(container) {
|
|
// A ton of variables, needs to be in the same scope for performance
|
|
var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
|
|
var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
|
|
var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, afterName, deltaSizeName, contentSizeName;
|
|
var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignMaxSizeName, alignBeforeName, alignAfterName;
|
|
var alignDeltaSizeName, alignContentSizeName;
|
|
var max = Math.max, min = Math.min;
|
|
|
|
// Get container items, properties and settings
|
|
items = container.items().filter(':visible');
|
|
contLayoutRect = container.layoutRect();
|
|
contPaddingBox = container._paddingBox;
|
|
contSettings = container.settings;
|
|
direction = contSettings.direction;
|
|
align = contSettings.align;
|
|
pack = contSettings.pack;
|
|
spacing = contSettings.spacing || 0;
|
|
|
|
if (direction == "row-reversed" || direction == "column-reverse") {
|
|
items = items.set(items.toArray().reverse());
|
|
direction = direction.split('-')[0];
|
|
}
|
|
|
|
// Setup axis variable name for row/column direction since the calculations is the same
|
|
if (direction == "column") {
|
|
posName = "y";
|
|
sizeName = "h";
|
|
minSizeName = "minH";
|
|
maxSizeName = "maxH";
|
|
innerSizeName = "innerH";
|
|
beforeName = 'top';
|
|
afterName = 'bottom';
|
|
deltaSizeName = "deltaH";
|
|
contentSizeName = "contentH";
|
|
|
|
alignBeforeName = "left";
|
|
alignSizeName = "w";
|
|
alignAxisName = "x";
|
|
alignInnerSizeName = "innerW";
|
|
alignMinSizeName = "minW";
|
|
alignMaxSizeName = "maxW";
|
|
alignAfterName = "right";
|
|
alignDeltaSizeName = "deltaW";
|
|
alignContentSizeName = "contentW";
|
|
} else {
|
|
posName = "x";
|
|
sizeName = "w";
|
|
minSizeName = "minW";
|
|
maxSizeName = "maxW";
|
|
innerSizeName = "innerW";
|
|
beforeName = 'left';
|
|
afterName = 'right';
|
|
deltaSizeName = "deltaW";
|
|
contentSizeName = "contentW";
|
|
|
|
alignBeforeName = "top";
|
|
alignSizeName = "h";
|
|
alignAxisName = "y";
|
|
alignInnerSizeName = "innerH";
|
|
alignMinSizeName = "minH";
|
|
alignMaxSizeName = "maxH";
|
|
alignAfterName = "bottom";
|
|
alignDeltaSizeName = "deltaH";
|
|
alignContentSizeName = "contentH";
|
|
}
|
|
|
|
// Figure out total flex, availableSpace and collect any max size elements
|
|
availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
|
|
maxAlignEndPos = totalFlex = 0;
|
|
for (i = 0, l = items.length; i < l; i++) {
|
|
ctrl = items[i];
|
|
ctrlLayoutRect = ctrl.layoutRect();
|
|
ctrlSettings = ctrl.settings;
|
|
flex = ctrlSettings.flex;
|
|
availableSpace -= (i < l - 1 ? spacing : 0);
|
|
|
|
if (flex > 0) {
|
|
totalFlex += flex;
|
|
|
|
// Flexed item has a max size then we need to check if we will hit that size
|
|
if (ctrlLayoutRect[maxSizeName]) {
|
|
maxSizeItems.push(ctrl);
|
|
}
|
|
|
|
ctrlLayoutRect.flex = flex;
|
|
}
|
|
|
|
availableSpace -= ctrlLayoutRect[minSizeName];
|
|
|
|
// Calculate the align end position to be used to check for overflow/underflow
|
|
size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
|
|
if (size > maxAlignEndPos) {
|
|
maxAlignEndPos = size;
|
|
}
|
|
}
|
|
|
|
// Calculate minW/minH
|
|
rect = {};
|
|
if (availableSpace < 0) {
|
|
rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
|
|
} else {
|
|
rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
|
|
}
|
|
|
|
rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
|
|
|
|
rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
|
|
rect[alignContentSizeName] = maxAlignEndPos;
|
|
rect.minW = min(rect.minW, contLayoutRect.maxW);
|
|
rect.minH = min(rect.minH, contLayoutRect.maxH);
|
|
rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
|
|
rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
|
|
|
|
// Resize container container if minSize was changed
|
|
if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
|
|
rect.w = rect.minW;
|
|
rect.h = rect.minH;
|
|
|
|
container.layoutRect(rect);
|
|
this.recalc(container);
|
|
|
|
// Forced recalc for example if items are hidden/shown
|
|
if (container._lastRect === null) {
|
|
var parentCtrl = container.parent();
|
|
if (parentCtrl) {
|
|
parentCtrl._lastRect = null;
|
|
parentCtrl.recalc();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle max size elements, check if they will become to wide with current options
|
|
ratio = availableSpace / totalFlex;
|
|
for (i = 0, l = maxSizeItems.length; i < l; i++) {
|
|
ctrl = maxSizeItems[i];
|
|
ctrlLayoutRect = ctrl.layoutRect();
|
|
maxSize = ctrlLayoutRect[maxSizeName];
|
|
size = ctrlLayoutRect[minSizeName] + Math.ceil(ctrlLayoutRect.flex * ratio);
|
|
|
|
if (size > maxSize) {
|
|
availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
|
|
totalFlex -= ctrlLayoutRect.flex;
|
|
ctrlLayoutRect.flex = 0;
|
|
ctrlLayoutRect.maxFlexSize = maxSize;
|
|
} else {
|
|
ctrlLayoutRect.maxFlexSize = 0;
|
|
}
|
|
}
|
|
|
|
// Setup new ratio, target layout rect, start position
|
|
ratio = availableSpace / totalFlex;
|
|
pos = contPaddingBox[beforeName];
|
|
rect = {};
|
|
|
|
// Handle pack setting moves the start position to end, center
|
|
if (totalFlex === 0) {
|
|
if (pack == "end") {
|
|
pos = availableSpace + contPaddingBox[beforeName];
|
|
} else if (pack == "center") {
|
|
pos = Math.round(
|
|
(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
|
|
) + contPaddingBox[beforeName];
|
|
|
|
if (pos < 0) {
|
|
pos = contPaddingBox[beforeName];
|
|
}
|
|
} else if (pack == "justify") {
|
|
pos = contPaddingBox[beforeName];
|
|
spacing = Math.floor(availableSpace / (items.length - 1));
|
|
}
|
|
}
|
|
|
|
// Default aligning (start) the other ones needs to be calculated while doing the layout
|
|
rect[alignAxisName] = contPaddingBox[alignBeforeName];
|
|
|
|
// Start laying out controls
|
|
for (i = 0, l = items.length; i < l; i++) {
|
|
ctrl = items[i];
|
|
ctrlLayoutRect = ctrl.layoutRect();
|
|
size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
|
|
|
|
// Align the control on the other axis
|
|
if (align === "center") {
|
|
rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
|
|
} else if (align === "stretch") {
|
|
rect[alignSizeName] = max(
|
|
ctrlLayoutRect[alignMinSizeName] || 0,
|
|
contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
|
|
);
|
|
rect[alignAxisName] = contPaddingBox[alignBeforeName];
|
|
} else if (align === "end") {
|
|
rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
|
|
}
|
|
|
|
// Calculate new size based on flex
|
|
if (ctrlLayoutRect.flex > 0) {
|
|
size += Math.ceil(ctrlLayoutRect.flex * ratio);
|
|
}
|
|
|
|
rect[sizeName] = size;
|
|
rect[posName] = pos;
|
|
ctrl.layoutRect(rect);
|
|
|
|
// Recalculate containers
|
|
if (ctrl.recalc) {
|
|
ctrl.recalc();
|
|
}
|
|
|
|
// Move x/y position
|
|
pos += size + spacing;
|
|
}
|
|
}
|
|
});
|
|
}); |