ovuse
Version:
WPF-inspired Web UI framework
605 lines (604 loc) • 27.1 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
const _1 = require(".");
const __1 = require("..");
var GridUnitType;
(function (GridUnitType) {
/// The value indicates that content should be calculated without constraints.
GridUnitType[GridUnitType["Auto"] = 0] = "Auto";
/// The value is expressed as a pixel.
GridUnitType[GridUnitType["Pixel"] = 1] = "Pixel";
/// The value is expressed as a weighted proportion of available space.
GridUnitType[GridUnitType["Star"] = 2] = "Star";
})(GridUnitType = exports.GridUnitType || (exports.GridUnitType = {}));
class GridLength {
constructor(value, type = GridUnitType.Pixel) {
if (value.isCloseTo(0))
value = 0;
this._value = value;
this._type = type;
}
static parseString(value) {
//parse a string in the form of:
// ([number],){0.1},[*|Auto|number],([number],){0.1} --> min*,[...],max*
// a string that define a series of row/col definition in the form of ([min len)*,(gridlen value),(max len])*
//ex:
//Auto [100,2*,200] [,Auto,2000]
//defines 3 row/col definition:
//1) auto row/column
//2) 2* row/column with min 100 pixel and max 200 pixel
//3) Auto row/olumn with max 2000 pixel
//TODO: use a regex instead
value = value.trim();
var tokens = value.split(" ");
return tokens
.filter((token) => token.trim().length > 0)
.map((token) => {
token = token.trim();
if (token[0] == '[') {
//Case "[100,*,]" or "[,Auto,200]"
if (token.length < 3 || token[token.length - 1] != ']')
throw new Error("GridLength definition error");
var subTokens = token.substr(1, token.length - 2).split(",");
if (subTokens.length == 1)
return {
length: GridLength.fromString(subTokens[0])
};
else if (subTokens.length == 3) {
var minSubToken = subTokens[0].trim();
var min = minSubToken.length == 0 ? 0 : parseFloat(minSubToken);
var maxSubToken = subTokens[2].trim();
var max = maxSubToken.length == 0 ? +Infinity : parseFloat(maxSubToken);
return {
length: GridLength.fromString(subTokens[1])
};
}
else
throw new Error("GridLength definition error");
}
else {
//case "*" or "Auto" or "12.3"
return {
length: GridLength.fromString(token)
};
}
});
}
static fromString(value) {
if (value == "Auto")
return new GridLength(1, GridUnitType.Auto);
if (value.substr(value.length - 1, 1) == "*") {
var starLen = value.length == 1 ? 1 : parseFloat(value.substr(0, value.length - 1));
return new GridLength(starLen, GridUnitType.Star);
}
return new GridLength(parseFloat(value), GridUnitType.Pixel);
}
get value() {
return this._value;
}
get type() {
return this._type;
}
get isAuto() {
return this._type == GridUnitType.Auto;
}
get isFixed() {
return this._type == GridUnitType.Pixel;
}
get isStar() {
return this._type == GridUnitType.Star;
}
}
exports.GridLength = GridLength;
class GridRow {
constructor(height = new GridLength(1, GridUnitType.Star), minHeight = 0, maxHeight = +Infinity) {
this.height = height;
this.minHeight = minHeight;
this.maxHeight = maxHeight;
}
}
exports.GridRow = GridRow;
class GridColumn {
constructor(width = new GridLength(1, GridUnitType.Star), minWidth = 0, maxWidth = +Infinity) {
this.width = width;
this.minWidth = minWidth;
this.maxWidth = maxWidth;
}
}
exports.GridColumn = GridColumn;
class RowDef {
constructor(row, index, vSizeToContent) {
this.row = row;
this.index = index;
this.availHeight = Infinity;
this._desHeight = 0;
this._finalHeight = 0;
this.elements = [];
this._isAuto = this.row.height.isAuto || (vSizeToContent && this.row.height.isStar);
this._isStar = this.row.height.isStar && !vSizeToContent;
this._isFixed = this.row.height.isFixed;
}
get desHeight() {
return this._desHeight;
}
set desHeight(newValue) {
this._desHeight = newValue.minMax(this.row.minHeight, this.row.maxHeight);
}
get finalHeight() {
return this._finalHeight;
}
set finalHeight(newValue) {
this._finalHeight = newValue.minMax(this.row.minHeight, this.row.maxHeight);
}
get isAuto() {
return this._isAuto;
}
get isStar() {
return this._isStar;
}
get isFixed() {
return this._isFixed;
}
}
class ColumnDef {
constructor(column, index, hSizeToContent) {
this.column = column;
this.index = index;
this.availWidth = Infinity;
this._desWidth = 0;
this._finalWidth = 0;
this.elements = [];
this._isAuto = this.column.width.isAuto || (hSizeToContent && this.column.width.isStar);
this._isStar = this.column.width.isStar && !hSizeToContent;
this._isFixed = this.column.width.isFixed;
}
get desWidth() {
return this._desWidth;
}
set desWidth(newValue) {
this._desWidth = newValue.minMax(this.column.minWidth, this.column.maxWidth);
}
get finalWidth() {
return this._finalWidth;
}
set finalWidth(newValue) {
this._finalWidth = newValue.minMax(this.column.minWidth, this.column.maxWidth);
}
get isAuto() {
return this._isAuto;
}
get isStar() {
return this._isStar;
}
get isFixed() {
return this._isFixed;
}
}
class ElementDef {
constructor(element, row, column, rowSpan, columnSpan) {
this.element = element;
this.row = row;
this.column = column;
this.rowSpan = rowSpan;
this.columnSpan = columnSpan;
this.desWidth = NaN;
this.desHeight = NaN;
this.measuredWidthFirstPass = false;
this.measuredHeightFirstPass = false;
this.cellTopOffset = 0;
this.cellLeftOffset = 0;
this._availWidth = new Array(columnSpan);
for (var i = 0; i < this._availWidth.length; i++)
this._availWidth[i] = Infinity;
this._availHeight = new Array(rowSpan);
for (var i = 0; i < this._availHeight.length; i++)
this._availHeight[i] = Infinity;
}
getAvailWidth(column) {
return this._availWidth[column - this.column];
}
getAllAvailWidth() {
let sum = 0;
for (var i = 0; i < this._availWidth.length; i++) {
if (!isFinite(this._availWidth[i]))
return Infinity;
sum += this._availWidth[i];
}
return sum;
}
setAvailWidth(column, value) {
this._availWidth[column - this.column] = value;
}
getAvailHeight(row) {
return this._availHeight[row - this.row];
}
getAllAvailHeight() {
let sum = 0;
for (var i = 0; i < this._availHeight.length; i++) {
if (!isFinite(this._availHeight[i]))
return Infinity;
sum += this._availHeight[i];
}
return sum;
}
setAvailHeight(row, value) {
this._availHeight[row - this.row] = value;
}
}
let Grid = Grid_1 = class Grid extends _1.Panel {
constructor() {
super(...arguments);
this._rowDefs = null;
this._columnDefs = null;
this._elementDefs = null;
this._lastDesiredSize = new _1.Size();
}
measureOverride(constraint) {
var desideredSize = new _1.Size();
var hSizeToContent = !isFinite(constraint.width);
var vSizeToContent = !isFinite(constraint.height);
var childrenCount = this.children == null ? 0 : this.children.count;
var rows = this.getRows();
var columns = this.getColumns();
this._rowDefs = new Array(Math.max(rows.count, 1));
this._columnDefs = new Array(Math.max(this.columns.count, 1));
this._elementDefs = new Array(childrenCount);
if (rows.count > 0)
rows.forEach((row, i) => {
if (this._rowDefs == null)
return;
this._rowDefs[i] = new RowDef(row, i, vSizeToContent);
});
else
this._rowDefs[0] = new RowDef(new GridRow(new GridLength(1, GridUnitType.Star)), 0, vSizeToContent);
if (columns.count > 0)
columns.forEach((column, i) => {
if (this._columnDefs == null)
return;
this._columnDefs[i] = new ColumnDef(column, i, hSizeToContent);
});
else
this._columnDefs[0] = new ColumnDef(new GridColumn(new GridLength(1, GridUnitType.Star)), 0, hSizeToContent);
for (var iElement = 0; iElement < childrenCount; iElement++) {
var child = this.children.at(iElement);
var elRow = Grid_1.getRow(child).minMax(0, this._rowDefs.length - 1);
var elColumn = Grid_1.getColumn(child).minMax(0, this._columnDefs.length - 1);
var elRowSpan = Grid_1.getRowSpan(child).minMax(1, this._rowDefs.length - elRow);
var elColumnSpan = Grid_1.getColumnSpan(child).minMax(1, this._columnDefs.length - elColumn);
this._elementDefs[iElement] = new ElementDef(child, elRow, elColumn, elRowSpan, elColumnSpan);
if (elRowSpan == 1) {
for (var row = elRow; row < elRow + elRowSpan; row++)
this._rowDefs[row].elements.push(this._elementDefs[iElement]);
}
if (elColumnSpan == 1) {
for (var col = elColumn; col < elColumn + elColumnSpan; col++)
this._columnDefs[col].elements.push(this._elementDefs[iElement]);
}
}
//measure children full contained in auto and fixed size row/column (exclude only children that are fully contained in star w/h cells)
for (var iRow = 0; iRow < this._rowDefs.length; iRow++) {
var rowDef = this._rowDefs[iRow];
var elements = rowDef.elements;
if (rowDef.isAuto) {
elements.forEach((el) => el.setAvailHeight(iRow, Infinity));
}
else if (rowDef.isFixed) {
rowDef.desHeight = rowDef.row.height.value;
elements.forEach((el) => el.setAvailHeight(iRow, rowDef.desHeight));
}
else {
elements.forEach((el) => el.measuredWidthFirstPass = true); //elements in this group can still be measured by the other dimension (width or height)
}
}
for (var iColumn = 0; iColumn < this._columnDefs.length; iColumn++) {
var columnDef = this._columnDefs[iColumn];
var elements = columnDef.elements;
if (columnDef.isAuto) {
elements.forEach((el) => el.setAvailWidth(iColumn, Infinity));
}
else if (columnDef.isFixed) {
columnDef.desWidth = columnDef.column.width.value;
elements.forEach((el) => el.setAvailWidth(iColumn, columnDef.desWidth));
}
else {
elements.forEach((el) => el.measuredHeightFirstPass = true); //elements in this group can still be measured by the other dimension (width or height)
}
}
this._elementDefs.forEach((el) => {
if (!el.measuredHeightFirstPass ||
!el.measuredWidthFirstPass) {
el.element.measure(new _1.Size(el.getAllAvailWidth(), el.getAllAvailHeight()));
if (isNaN(el.desWidth))
el.desWidth = el.element.desiredSize == null ? 0 : el.element.desiredSize.width;
if (isNaN(el.desHeight))
el.desHeight = el.element.desiredSize == null ? 0 : el.element.desiredSize.height;
}
el.measuredWidthFirstPass = el.measuredHeightFirstPass = true;
});
//than get max of any auto/fixed measured row/column
this._rowDefs.forEach(rowDef => {
if (!rowDef.isStar)
rowDef.elements.forEach((el) => rowDef.desHeight = Math.max(rowDef.desHeight, el.element.desiredSize == null ? 0 : el.element.desiredSize.height));
});
this._columnDefs.forEach(columnDef => {
if (!columnDef.isStar)
columnDef.elements.forEach((el) => columnDef.desWidth = Math.max(columnDef.desWidth, el.element.desiredSize == null ? 0 : el.element.desiredSize.width));
});
//now measure any fully contained star size row/column
var elementToMeasure = [];
var notStarRowsHeight = 0;
this._rowDefs.forEach((r) => notStarRowsHeight += r.desHeight);
var sumRowStars = 0;
this._rowDefs.forEach(r => { if (r.isStar)
sumRowStars += r.row.height.value; });
var vRowMultiplier = (constraint.height - notStarRowsHeight) / sumRowStars;
this._rowDefs.forEach(rowDef => {
if (!rowDef.isStar)
return;
var elements = rowDef.elements;
//if size to content horizontally, star rows are treat just like auto rows (same apply to columns of course)
if (!vSizeToContent) {
var availHeight = vRowMultiplier * rowDef.row.height.value;
rowDef.desHeight = availHeight;
elements.forEach((el) => { el.setAvailHeight(rowDef.index, availHeight); el.measuredHeightFirstPass = false; });
}
elementToMeasure.push.apply(elementToMeasure, elements);
});
var notStarColumnsHeight = 0;
this._columnDefs.forEach((c) => notStarColumnsHeight += c.desWidth);
var sumColumnStars = 0;
this._columnDefs.forEach(c => { if (c.isStar)
sumColumnStars += c.column.width.value; });
var vColumnMultiplier = (constraint.width - notStarColumnsHeight) / sumColumnStars;
this._columnDefs.forEach(columnDef => {
if (!columnDef.isStar)
return;
var elements = columnDef.elements;
if (!hSizeToContent) {
var availWidth = vColumnMultiplier * columnDef.column.width.value;
columnDef.desWidth = availWidth;
elements.forEach((el) => { el.setAvailWidth(columnDef.index, availWidth); el.measuredWidthFirstPass = false; });
}
elementToMeasure.push.apply(elementToMeasure, elements);
});
elementToMeasure.forEach(e => {
if (!e.measuredHeightFirstPass ||
!e.measuredWidthFirstPass) {
e.element.measure(new _1.Size(e.getAllAvailWidth(), e.getAllAvailHeight()));
e.desWidth = e.element.desiredSize == null ? 0 : e.element.desiredSize.width;
e.desHeight = e.element.desiredSize == null ? 0 : e.element.desiredSize.height;
e.measuredWidthFirstPass = true;
e.measuredHeightFirstPass = true;
}
});
//than adjust width and height to fit children that spans over columns or rows containing auto rows or auto columns
for (var iElement = 0; iElement < this._elementDefs.length; iElement++) {
var elementDef = this._elementDefs[iElement];
if (elementDef.rowSpan > 1) {
if (this._rowDefs
.slice(elementDef.row, elementDef.row + elementDef.rowSpan)
.every((v, i, a) => v.isAuto || v.isFixed)) {
var concatHeight = 0;
this._rowDefs.slice(elementDef.row, elementDef.row + elementDef.rowSpan).forEach((el) => concatHeight += el.desHeight);
if (concatHeight < elementDef.desHeight) {
var diff = elementDef.desHeight - concatHeight;
var autoRows = this._rowDefs.filter(r => r.isAuto);
if (autoRows.length > 0) {
autoRows.forEach(c => c.desHeight += diff / autoRows.length);
}
else {
var starRows = this._rowDefs.filter(r => r.isStar);
if (starRows.length > 0) {
starRows.forEach(c => c.desHeight += diff / autoColumns.length);
}
}
}
else if (concatHeight > elementDef.desHeight) {
elementDef.cellTopOffset = (concatHeight - elementDef.desHeight) / 2;
}
}
}
if (elementDef.columnSpan > 1) {
if (this._columnDefs
.slice(elementDef.column, elementDef.column + elementDef.columnSpan)
.every((v, i, a) => v.isAuto || v.isFixed)) {
var concatWidth = 0;
this._columnDefs.slice(elementDef.column, elementDef.column + elementDef.columnSpan).forEach((el) => concatWidth += el.desWidth);
if (concatWidth < elementDef.desWidth) {
var diff = elementDef.desWidth - concatWidth;
var autoColumns = this._columnDefs.filter(c => c.isAuto);
if (autoColumns.length > 0) {
autoColumns.forEach(c => c.desWidth += diff / autoColumns.length);
}
else {
var starColumns = this._columnDefs.filter(c => c.isStar);
if (starColumns.length > 0) {
starColumns.forEach(c => c.desWidth += diff / autoColumns.length);
}
}
}
else if (concatWidth > elementDef.desWidth) {
elementDef.cellLeftOffset = (concatWidth - elementDef.desWidth) / 2;
}
}
}
}
//finally sum up the desidered size
this._rowDefs.forEach(r => desideredSize.height += r.desHeight);
this._columnDefs.forEach(c => desideredSize.width += c.desWidth);
this._lastDesiredSize = desideredSize;
return desideredSize;
}
arrangeOverride(finalSize) {
//if finalSize != this.desideredSize we have to
//to correct row/column with star values to take extra space or viceversa
//remove space no more available from measure pass
var xDiff = finalSize.width - this._lastDesiredSize.width;
var yDiff = finalSize.height - this._lastDesiredSize.height;
//rd.isStar/cd.isStar take in count also sizeToContent stuff
//we need here only to know if column is star or not
//this why we are using rd.row.height.isStar or cd.column.width.isStar
if (this._rowDefs != null && this._columnDefs != null && this._elementDefs != null) {
var starRowCount = 0;
this._rowDefs.forEach(rd => {
if (rd.row.height.isStar)
starRowCount++;
});
var starColumnCount = 0;
this._columnDefs.forEach(cd => {
if (cd.column.width.isStar)
starColumnCount++;
});
this._rowDefs.forEach(rd => {
//rd.isStar takes in count also sizeToContent stuff
//we need here only to know if column is star or not
if (rd.row.height.isStar)
rd.finalHeight = rd.desHeight + yDiff / starRowCount;
else
rd.finalHeight = rd.desHeight;
});
this._columnDefs.forEach(cd => {
if (cd.column.width.isStar)
cd.finalWidth = cd.desWidth + xDiff / starColumnCount;
else
cd.finalWidth = cd.desWidth;
});
this._elementDefs.forEach(el => {
if (this._columnDefs == null ||
this._rowDefs == null)
return;
let finalLeft = 0;
this._columnDefs.slice(0, el.column).forEach(c => finalLeft += c.finalWidth);
let finalWidth = 0;
this._columnDefs.slice(el.column, el.column + el.columnSpan).forEach(c => finalWidth += c.finalWidth);
finalWidth -= (el.cellLeftOffset * 2);
let finalTop = 0;
this._rowDefs.slice(0, el.row).forEach(c => finalTop += c.finalHeight);
let finalHeight = 0;
this._rowDefs.slice(el.row, el.row + el.rowSpan).forEach(r => finalHeight += r.finalHeight);
finalHeight += (el.cellTopOffset * 2);
el.element.arrange(new _1.Rect(finalLeft + el.cellLeftOffset, finalTop + el.cellTopOffset, finalWidth, finalHeight));
});
}
return finalSize;
}
getRowFinalHeight(rowIndex) {
if (this._columnDefs == null ||
this._rowDefs == null)
throw new Error("Operation not valid in this state!");
return this._rowDefs[rowIndex].finalHeight;
}
getColumnFinalWidth(colIndex) {
if (this._columnDefs == null ||
this._rowDefs == null)
throw new Error("Operation not valid in this state!");
return this._columnDefs[colIndex].finalWidth;
}
get rows() {
return this.getValue(Grid_1.rowsProperty);
}
set rows(value) {
this.setValue(Grid_1.rowsProperty, value);
}
getRows() {
var rows = this.rows;
if (rows == null) {
this.rows = rows = new __1.ObservableCollection();
rows.onChangeNotify(this);
}
return rows;
}
static rowsFromString(rows) {
var listOfRows = new Array();
GridLength.parseString(rows).forEach((rowDef) => {
listOfRows.push(new GridRow(rowDef.length, rowDef.min, rowDef.max));
});
return new __1.ObservableCollection(listOfRows);
}
get columns() {
return this.getValue(Grid_1.columnsProperty);
}
set columns(value) {
this.setValue(Grid_1.columnsProperty, value);
}
getColumns() {
var columns = this.columns;
if (columns == null) {
this.columns = columns = new __1.ObservableCollection();
columns.onChangeNotify(this);
}
return columns;
}
static columnsFromString(columns) {
var listOfColumns = new Array();
GridLength.parseString(columns).forEach((columnDef) => {
listOfColumns.push(new GridColumn(columnDef.length, columnDef.min, columnDef.max));
});
return new __1.ObservableCollection(listOfColumns);
}
onCollectionChanged(collection, added, removed, startRemoveIndex) {
super.invalidateMeasure();
}
static getRow(target) {
return target.getValue(Grid_1.rowProperty);
}
static setRow(target, value) {
target.setValue(Grid_1.rowProperty, value);
}
static getColumn(target) {
return target.getValue(Grid_1.columnProperty);
}
static setColumn(target, value) {
target.setValue(Grid_1.columnProperty, value);
}
static fromString(value) {
var intValue = parseInt(value);
if (isNaN(intValue) ||
!isFinite(intValue))
return 0;
return intValue;
}
static getRowSpan(target) {
return target.getValue(Grid_1.rowSpanProperty);
}
static setRowSpan(target, value) {
target.setValue(Grid_1.rowSpanProperty, value);
}
static getColumnSpan(target) {
return target.getValue(Grid_1.columnSpanProperty);
}
static setColumnSpan(target, value) {
target.setValue(Grid_1.columnSpanProperty, value);
}
static spanFromString(value) {
var intValue = parseInt(value);
if (isNaN(intValue) ||
!isFinite(intValue))
return 1;
return intValue;
}
};
///Dependency properties
//rows
Grid.rowsProperty = __1.DependencyObject.registerProperty(Grid_1, "Rows", null, _1.FrameworkPropertyMetadataOptions.AffectsMeasure | _1.FrameworkPropertyMetadataOptions.AffectsRender, (v) => Grid_1.rowsFromString(v));
//columns
Grid.columnsProperty = __1.DependencyObject.registerProperty(Grid_1, "Columns", null, _1.FrameworkPropertyMetadataOptions.AffectsMeasure | _1.FrameworkPropertyMetadataOptions.AffectsRender, (v) => Grid_1.columnsFromString(v));
//Grid.Row property
Grid.rowProperty = __1.DependencyObject.registerProperty(Grid_1, "Grid#Row", 0, _1.FrameworkPropertyMetadataOptions.AffectsMeasure, (v) => Grid_1.fromString(v));
//Grid.Column property
Grid.columnProperty = __1.DependencyObject.registerProperty(Grid_1, "Grid#Column", 0, _1.FrameworkPropertyMetadataOptions.AffectsMeasure, (v) => Grid_1.fromString(v));
//Grid.RowSpan property
Grid.rowSpanProperty = __1.DependencyObject.registerProperty(Grid_1, "Grid#RowSpan", 1, _1.FrameworkPropertyMetadataOptions.AffectsMeasure, (v) => Grid_1.spanFromString(v));
//Grid.ColumnSpan property
Grid.columnSpanProperty = __1.DependencyObject.registerProperty(Grid_1, "Grid#ColumnSpan", 1, _1.FrameworkPropertyMetadataOptions.AffectsMeasure, (v) => Grid_1.spanFromString(v));
Grid = Grid_1 = __decorate([
__1.TypeId("ovuse.Controls.Grid")
], Grid);
exports.Grid = Grid;
var Grid_1;