phaser3-flex
Version:
Flex containers for Phaser
807 lines (803 loc) • 19.8 kB
JavaScript
// src/sharedtypes.ts
var AlignItems = /* @__PURE__ */ ((AlignItems2) => {
AlignItems2[AlignItems2["CENTER"] = 1] = "CENTER";
AlignItems2[AlignItems2["FLEX_END"] = 2] = "FLEX_END";
AlignItems2[AlignItems2["FLEX_START"] = 3] = "FLEX_START";
AlignItems2[AlignItems2["STRETCH"] = 4] = "STRETCH";
return AlignItems2;
})(AlignItems || {});
var FlexDirection = /* @__PURE__ */ ((FlexDirection2) => {
FlexDirection2[FlexDirection2["COLUMN"] = 1] = "COLUMN";
FlexDirection2[FlexDirection2["ROW"] = 2] = "ROW";
return FlexDirection2;
})(FlexDirection || {});
var JustifyContent = /* @__PURE__ */ ((JustifyContent2) => {
JustifyContent2[JustifyContent2["CENTER"] = 1] = "CENTER";
JustifyContent2[JustifyContent2["FLEX_END"] = 2] = "FLEX_END";
JustifyContent2[JustifyContent2["FLEX_START"] = 3] = "FLEX_START";
JustifyContent2[JustifyContent2["SPACE_AROUND"] = 4] = "SPACE_AROUND";
JustifyContent2[JustifyContent2["SPACE_BETWEEN"] = 5] = "SPACE_BETWEEN";
return JustifyContent2;
})(JustifyContent || {});
// src/helpers.ts
var SetPosName = {
width: "setX",
height: "setY"
};
function alignCrossCenter(f, dim) {
const setPos = SetPosName[dim];
const bound = dim == "width" ? getLeft(f) : getTop(f);
const center = f[dim] / 2 + bound;
f.items.forEach((item) => {
item.setOrigin(0, 0);
const position = center - item[dim] / 2;
item[setPos](position);
});
}
function alignCrossStretch(f, dim) {
const setPos = SetPosName[dim];
const bound = dim == "width" ? getLeft(f) : getTop(f);
let maxSize = f[dim] - 2 * f.padding;
let position = bound + f.padding;
f.items.forEach((item) => {
if (!item["_isFlex"])
return;
item[setPos](position);
if (item[dim] > maxSize) {
maxSize = item[dim];
}
});
f.items.forEach((item, index) => {
if (!item["_isFlex"])
return;
if (dim == "width") {
f._widths[index] = item[dim];
} else {
f._heights[index] = item[dim];
}
const size = dim == "width" ? [maxSize, item.height] : [item.width, maxSize];
setItemDisplaySize(item, ...size);
});
}
function isMainAxis(dim, dir) {
return dim == "width" && dir == 2 /* ROW */ || dim == "height" && dir == 1 /* COLUMN */;
}
function alignEnd(f, dim) {
const items = f.items;
const setPos = SetPosName[dim];
let position = f._bounds[dim == "width" ? "right" : "bottom"];
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
item.setOrigin(0, 0);
item[setPos](position - item[dim]);
if (isMainAxis(dim, f.flexDirection)) {
position -= item[dim] + f.itemsMargin;
}
}
}
function alignMainCenter(f, dim) {
const setPos = SetPosName[dim];
const bound = dim == "width" ? getLeft(f) : getTop(f);
const itemsSize = getItemsSize(f);
let center = f[dim] / 2 + bound;
let position = center - itemsSize / 2;
f.items.forEach((item) => {
item.setOrigin(0, 0);
item[setPos](position);
position += item[dim] + f.itemsMargin;
});
}
function alignStart(f, dim) {
const setPos = SetPosName[dim];
let position = f._bounds[dim == "width" ? "left" : "top"];
f.items.forEach((item) => {
item.setOrigin(0, 0);
item[setPos](position);
if (isMainAxis(dim, f.flexDirection)) {
position += item[dim] + f.itemsMargin;
}
});
}
function checkDimension(f, dim, value) {
const totalDimension = value + f.padding * 2;
if (totalDimension > f[dim]) {
f[dim] = totalDimension;
}
updateBounds(f);
}
function checkHeight(f, height) {
checkDimension(f, "height", height);
}
function checkWidth(f, width) {
checkDimension(f, "width", width);
}
function fill(f, dim) {
const freeSpace = getFreeSpace(f);
const setPos = SetPosName[dim];
let position = f._bounds[dim == "width" ? "left" : "top"];
f.items.forEach((item) => {
if (item["_isFlex"])
setItemSize(f, item, freeSpace);
item.setOrigin(0, 0);
item[setPos](position);
position += item[dim] + f.itemsMargin;
});
}
function fillH(f) {
fill(f, "width");
}
function fillV(f) {
fill(f, "height");
}
function fitDimension(f, dim) {
const max = f[`_${dim}s`].length ? Math.max(...f[`_${dim}s`]) : 0;
f[dim] = max + 2 * f.padding;
updateBounds(f);
}
function fitHeight(f) {
fitDimension(f, "height");
}
function fitTextToColumn(f, item) {
let w = f.width - f.padding * 2;
item["setWordWrapWidth"](w);
let b = item["getBounds"]();
item["setFixedSize"](w, b.height);
}
function fitWidth(f) {
fitDimension(f, "width");
}
function getFreeSpace(f) {
const dim = f.flexDirection == 2 /* ROW */ ? f.width : f.height;
return dim - getItemsSize(f) - 2 * f.padding;
}
function getItemsSize(f) {
const paddingsSum = (f.items.length - 1) * f.itemsMargin;
return f._basisSum + paddingsSum;
}
function getLeft(f) {
return f.x - f.width * f.origin.x;
}
function getTop(f) {
return f.y - f.height * f.origin.y;
}
function resetHeights(f) {
for (let i = 0; i < f.items.length; i++) {
let item = f.items[i];
setItemDisplaySize(item, item.width, f._heights[i]);
item.height = f._heights[i];
}
}
function resetWidths(f) {
for (let i = 0; i < f.items.length; i++) {
let item = f.items[i];
setItemDisplaySize(item, f._widths[i], item.height);
item.width = f._widths[i];
}
}
function setAlignH(f, alignment) {
if (alignment == 6 /* STRETCH */) {
alignCrossStretch(f, "width");
return;
}
if (f.flexDirection == 2 /* ROW */) {
let freeSpace = getFreeSpace(f);
if (f._growSum && freeSpace >= 0 || freeSpace < 0) {
fillH(f);
return;
}
}
if (alignment == 4 /* LEFT */) {
if (f.flexDirection == 2 /* ROW */) {
alignStart(f, "width");
return;
}
alignStart(f, "width");
return;
}
if (alignment == 5 /* RIGHT */) {
if (f.flexDirection == 2 /* ROW */) {
alignEnd(f, "width");
return;
}
alignEnd(f, "width");
return;
}
if (alignment == 2 /* CENTER */) {
if (f.flexDirection == 2 /* ROW */) {
alignMainCenter(f, "width");
} else {
alignCrossCenter(f, "width");
}
return;
}
}
function setAlignV(f, alignment) {
if (alignment == 6 /* STRETCH */) {
alignCrossStretch(f, "height");
return;
}
if (f.flexDirection == 1 /* COLUMN */) {
let freeSpace = getFreeSpace(f);
if (f._growSum && freeSpace >= 0 || freeSpace < 0) {
fillV(f);
return;
}
}
if (alignment == 7 /* TOP */) {
if (f.flexDirection == 1 /* COLUMN */) {
alignStart(f, "height");
return;
}
alignStart(f, "height");
return;
}
if (alignment == 1 /* BOTTOM */) {
if (f.flexDirection == 1 /* COLUMN */) {
alignEnd(f, "height");
return;
}
alignEnd(f, "height");
return;
}
if (alignment == 2 /* CENTER */) {
if (f.flexDirection == 1 /* COLUMN */) {
alignMainCenter(f, "height");
} else {
alignCrossCenter(f, "height");
}
return;
}
}
function setItemDisplaySize(item, width, height) {
if (item["type"] == "Text") {
if (item.height != height) {
return;
}
item["setWordWrapWidth"](width);
item["setFixedSize"](width, height);
return;
}
item.setSize(width, height);
}
function setItems(f) {
updateBounds(f);
f.setJustifyContent(f.justifyContent);
f.setAlignItems(f.alignItems);
}
function setItemSize(f, item, freeSpace) {
const isRow = f.flexDirection == 2 /* ROW */;
let dim = isRow ? "width" : "height";
let dimValue = 0;
if (freeSpace >= 0) {
dimValue = item.flexGrow / f._growSum * freeSpace + item.basis;
}
if (freeSpace < 0) {
const shrinkRatio = item.basis * item.flexShrink / f._shrinkSum;
dimValue = item.basis + freeSpace * shrinkRatio;
}
if (isRow) {
setItemDisplaySize(item, dimValue, item.height);
} else {
setItemDisplaySize(item, item.width, dimValue);
}
item[dim] = dimValue;
}
function setJustify(f) {
const dim = f.flexDirection == 2 /* ROW */ ? "width" : "height";
const setPos = SetPosName[dim];
let freeSpace = getFreeSpace(f);
let padding = 0;
let position = dim == "width" ? getLeft(f) : getTop(f);
if (f.justifyContent == 4 /* SPACE_AROUND */) {
padding = freeSpace / (f.items.length + 1);
position += f.padding + padding;
} else {
padding = freeSpace / (f.items.length - 1);
position += f.padding;
}
f.items.forEach((item) => {
item.setOrigin(0, 0);
item[setPos](position);
position += item[dim] + padding + f.itemsMargin;
});
}
function updateBounds(f) {
let x = getLeft(f);
let y = getTop(f);
f._bounds = {
left: x + f.padding,
right: x + f.width - f.padding,
top: y + f.padding,
bottom: y + f.height - f.padding
};
}
// src/flex.ts
var Flex = class {
/**
* X position. (Default = 0)
*/
x;
/**
* Y position. (Default = 0)
*/
y;
/**
* Width of this object. (Default = 0)
*/
width;
/**
* Height of this object. (Default = 0)
*/
height;
/**
* Minimum distance between this object content and its border. (Default = 10)
*/
padding;
/**
* Minimum distance between items contained inside this object. (Default = 4)
*/
itemsMargin;
/**
* Alignment of the items with respect to the cross axis. (Default = AlignItems.CENTER)
*/
alignItems;
/**
* Sets how items are placed in the flex object defining the main axis. (Default = FlexDirection.ROW)
*/
flexDirection;
/**
* Alignment of the items with respect to the main axis. (Default = JustifyContent.FLEX_START)
*/
justifyContent;
/**
* Array of all items managed by this object.
*/
items;
/**
* Position of this object anchor relative to its width and height. (x and y between 0 and 1).
* Sets how this object is placed.
*/
origin;
/**
* Original size of this object in the main axis.
*/
basis;
scene;
flexGrow;
flexShrink;
/**
* @private
*/
_fparent;
/**
* @private
*/
_scrollFactorX;
/**
* @private
*/
_scrollFactorY;
/**
* @private
*/
_isFlex;
/**
* @private
*/
_basisSum;
/**
* @private
*/
_heights;
/**
* @private
*/
_widths;
/**
* @private
*/
_growSum;
/**
* @private
*/
_shrinkSum;
/**
* @private
*/
_bounds;
/**
* Creates an instance of Flex class
* @param scene
* @param config
* @returns
*/
constructor(scene, config) {
this.scene = scene;
this.x = config.x || 0;
this.y = config.y || 0;
this.width = config.width == void 0 ? scene.scale.width : config.width;
this.height = config.height || 0;
this.padding = config.padding || 10;
this.itemsMargin = config.itemsMargin || 4;
this.alignItems = config.alignItems || 1 /* CENTER */;
this.flexDirection = config.flexDirection || 2 /* ROW */;
this.justifyContent = config.justifyContent || 3 /* FLEX_START */;
this.items = [];
this.origin = { x: 0, y: 0 };
this._scrollFactorX = 0;
this._scrollFactorY = 0;
this._fparent = null;
this._isFlex = true;
this._basisSum = 0;
this._heights = [];
this._widths = [];
this._growSum = 0;
this._shrinkSum = 0;
this._bounds = { left: 0, right: 0, top: 0, bottom: 0 };
this.scene.events.once("destroy", this.destroy, this);
return this;
}
/**
* Adds an item to the items list of this object. The position and size of this items
* are managed by this object.
*
* @param item
* @param flexGrow
* @param flexShrink
* @returns This Flex instance.
*/
add(item, flexGrow = 0, flexShrink = 1) {
item.setOrigin(0, 0);
item.setScrollFactor(this._scrollFactorX, this._scrollFactorY);
if (item["_isFlex"]) {
item.flexGrow = flexGrow;
item.flexShrink = flexShrink;
item["_fparent"] = this;
} else {
item.flexGrow = 0;
item.flexShrink = 0;
}
if (this.width && item.type == "Text" && this.flexDirection == 1 /* COLUMN */) {
fitTextToColumn(this, item);
}
item.basis = this.flexDirection == 2 /* ROW */ ? item.width : item.height;
this._basisSum += item.basis;
this.items.push(item);
this._heights.push(item.height);
this._widths.push(item.width);
this._growSum += item.flexGrow;
this._shrinkSum += item.flexShrink * item.basis;
if (this.flexDirection == 2 /* ROW */) {
checkHeight(this, item.height);
if (!item["_isFlex"])
checkWidth(this, getItemsSize(this));
}
if (this.flexDirection == 1 /* COLUMN */) {
checkWidth(this, item.width);
if (!item["_isFlex"])
checkHeight(this, getItemsSize(this));
}
setItems(this);
if (this._fparent) {
if (this._fparent.flexDirection == 2 /* ROW */) {
checkHeight(this._fparent, this.height);
} else {
checkWidth(this._fparent, this.width);
}
setItems(this._fparent);
}
return this;
}
/**
* Each item managed by this object are destroyed.
*
* @returns This Flex instance.
*/
clear() {
this.items.forEach((item) => {
if (item["_isFlex"]) {
item["clear"]();
}
});
this.items.forEach((item) => item.destroy());
this.items = [];
this._heights = [];
this._widths = [];
this._basisSum = 0;
return this;
}
/**
* An item is removed from the items list managed by this flex object.
*
* @param index Index of the item to be removed in the items array of this instance.
* @param destroy The item should be destroyed?.
* @returns This Flex instance.
*/
remove(index, destroy) {
if (this.items[index] == void 0) {
return;
}
let item = this.items[index];
item["_fparent"] = null;
this._basisSum -= item.basis;
this.items.splice(index, 1);
this._heights.splice(index, 1);
this._widths.splice(index, 1);
if (destroy) {
item.destroy();
}
if (this.flexDirection == 2 /* ROW */) {
fitHeight(this);
}
if (this.flexDirection == 1 /* COLUMN */) {
fitWidth(this);
}
setItems(this);
return this;
}
/**
* Sets the size of this object.
*
* @param width
* @param height
* @returns This Flex instance.
*/
setSize(width, height) {
this.setWidth(width);
this.setHeight(height);
return this;
}
/**
* Disposes all resources used by this object.
*
*/
destroy() {
this.clear();
this.items = null;
this._widths = null;
this._heights = null;
this._bounds = null;
this.origin = null;
this._fparent = null;
}
/**
* Sets the *alignItems* property of this object.
*
* @param alignItems
* @returns This Flex instance.
*/
setAlignItems(alignItems) {
if (this.alignItems == 4 /* STRETCH */ && alignItems != 4 /* STRETCH */) {
if (this.flexDirection == 2 /* ROW */) {
resetHeights(this);
} else {
resetWidths(this);
}
}
this.alignItems = alignItems;
switch (alignItems) {
case 1 /* CENTER */:
if (this.flexDirection == 2 /* ROW */) {
setAlignV(this, 2 /* CENTER */);
} else {
setAlignH(this, 2 /* CENTER */);
}
break;
case 3 /* FLEX_START */:
if (this.flexDirection == 2 /* ROW */) {
setAlignV(this, 7 /* TOP */);
} else {
setAlignH(this, 4 /* LEFT */);
}
break;
case 2 /* FLEX_END */:
if (this.flexDirection == 2 /* ROW */) {
setAlignV(this, 1 /* BOTTOM */);
} else {
setAlignH(this, 5 /* RIGHT */);
}
break;
case 4 /* STRETCH */:
if (this.flexDirection == 2 /* ROW */) {
setAlignV(this, 6 /* STRETCH */);
} else {
setAlignH(this, 6 /* STRETCH */);
}
break;
default:
break;
}
return this;
}
/**
* Sets the *height* of this object.
*
* @param height
* @returns This Flex instance.
*/
setHeight(height) {
this.height = height;
resetHeights(this);
setItems(this);
return this;
}
/**
* Sets the *width* of this object.
*
* @param width
* @returns This Flex instance.
*/
setWidth(width) {
this.width = width;
resetWidths(this);
if (this.flexDirection == 1 /* COLUMN */) {
for (let i = 0; i < this.items.length; i++) {
let item = this.items[i];
if (item.type == "Text") {
fitTextToColumn(this, item);
}
}
}
setItems(this);
return this;
}
/**
* Sets the *justifyContent* property of this object.
*
* @param justifyContent
* @returns This Flex instance.
*/
setJustifyContent(justifyContent) {
this.justifyContent = justifyContent;
switch (justifyContent) {
case 1 /* CENTER */:
if (this.flexDirection == 2 /* ROW */) {
setAlignH(this, 2 /* CENTER */);
} else {
setAlignV(this, 2 /* CENTER */);
}
break;
case 3 /* FLEX_START */:
if (this.flexDirection == 2 /* ROW */) {
setAlignH(this, 4 /* LEFT */);
} else {
setAlignV(this, 7 /* TOP */);
}
break;
case 2 /* FLEX_END */:
if (this.flexDirection == 2 /* ROW */) {
setAlignH(this, 5 /* RIGHT */);
} else {
setAlignV(this, 1 /* BOTTOM */);
}
break;
case 4 /* SPACE_AROUND */:
setJustify(this);
break;
case 5 /* SPACE_BETWEEN */:
setJustify(this);
break;
default:
break;
}
return this;
}
/**
* Sets the *origin* property of this object.
*
* @param x
* @param y
* @returns This Flex instance.
*/
setOrigin(x, y) {
if (y == void 0) {
y = x;
}
this.origin.x = x;
this.origin.y = y;
if (x > 1) {
this.origin.x = 1;
}
;
if (x < 0) {
this.origin.x = 0;
}
;
if (y > 1) {
this.origin.y = 1;
}
;
if (y < 0) {
this.origin.y = 0;
}
;
setItems(this);
return this;
}
/**
* Sets the *scrollFactor* property of this object.
*
* @param x
* @param y
* @returns This Flex instance.
*/
setScrollFactor(x, y) {
if (x > 1) {
x = 1;
}
if (x < 0) {
x = 0;
}
if (!y) {
y = x;
}
this._scrollFactorX = x;
this._scrollFactorY = y;
for (let i = 0; i < this.items.length; i++) {
this.items[i].setScrollFactor(x, y);
}
return this;
}
/**
* Sets the x position of this object.
*
* @param x
* @returns This Flex instance.
*/
setX(x) {
this.x = x;
setItems(this);
return this;
}
/**
* Sets the y position of this object.
*
* @param y
* @returns This Flex instance.
*/
setY(y) {
this.y = y;
setItems(this);
return this;
}
/**
* Updates items positions. Should be used only if any item have changed its size.
*/
update() {
let change = false;
if (this.flexDirection == 2 /* ROW */) {
this.items.forEach((item) => {
if (item.basis != item.width) {
this._basisSum += item.width - item.basis;
item.basis = item.width;
change = true;
}
});
} else {
this.items.forEach((item) => {
if (item.basis != item.height) {
this._basisSum += item.height - item.height;
item.basis = item.height;
change = true;
}
});
}
if (change)
setItems(this);
}
};
// src/index.ts
if (typeof window != "undefined") {
globalThis.Fbx = {
Flex,
AlignItems,
FlexDirection,
JustifyContent
};
}
export {
AlignItems,
Flex,
FlexDirection,
JustifyContent
};