/**
* This file exports utility functions that are used across the layout module
*/
import { Symbols } from 'muze-utils';
import { PRIMARY, SECONDARY, HEIGHT, WIDTH } from '../enums/constants';
const nest = Symbols.nest;
/**
*
*
* @param {*} isTransposed
*
*/
const getMeasureInfo = (isTransposed) => {
if (isTransposed) {
return {
firstMeasure: HEIGHT,
secondMeasure: WIDTH
};
}
return {
firstMeasure: WIDTH,
secondMeasure: HEIGHT
};
};
/**
*
*
* @param {*} i
* @param {*} page
*
*/
const findInPage = (i, page) => {
let count = 0;
for (const x in page) {
if (i < page[x]) {
count = x;
break;
}
}
return count;
};
/**
* Creates a single matrix from two matrices
*
* @param {Array<Array>} matrix Input matrix
* @return {Array<Array>} Joined matrix
* @memberof VisualMatrix
*/
export const combineMatrices = (matrix, config) => {
const { isTransposed } = config;
let joinedMatrix = matrix[0].length > 0 ? matrix[0] : matrix[1];
if (isTransposed) {
joinedMatrix = matrix[0].length > 0 ? [...matrix[0]] : [];
joinedMatrix = matrix[1].length > 0 ? [...joinedMatrix, ...matrix[1]] : joinedMatrix;
} else {
joinedMatrix = joinedMatrix.map((row, rowIndex) => {
let rowData = [];
if (matrix[0].length > 0) {
rowData = [...matrix[0][rowIndex]];
}
if (matrix[1].length > 0) {
rowData = [...rowData, ...matrix[1][rowIndex]];
}
return rowData;
});
}
return joinedMatrix;
};
/**
* Creates a hirachical tree from the context
*
* @param {Object} context context for creating tree
* @return {Object} nested tree
*/
export const createTree = (context) => {
let matrixTree = {};
let lastLevelKey = 0;
let facet = PRIMARY;
const nestFn = nest();
const keys = [];
const layoutMatrix = context._layoutMatrix;
const primaryMatrix = context.primaryMatrix();
const secondaryMatrix = context.secondaryMatrix();
const { isTransposed, breakPage } = context.config();
if (!isTransposed) {
if (primaryMatrix.length === 0) {
facet = PRIMARY;
} else if (secondaryMatrix.length === 0) {
facet = SECONDARY;
} else {
facet = primaryMatrix.length >= secondaryMatrix.length ? PRIMARY : SECONDARY;
}
if (primaryMatrix.length > 0) {
for (let i = 0; i < primaryMatrix[0].length - 1; i++) {
keys.push(i);
}
keys.push(primaryMatrix[0].length - 1);
if (facet === SECONDARY || secondaryMatrix.length === 0) {
lastLevelKey = primaryMatrix[0].length - 1;
}
}
if (secondaryMatrix.length > 0) {
const keyLength = primaryMatrix.length > 0 ? primaryMatrix[0].length : 0;
for (let i = secondaryMatrix[0].length - 1; i > 0; i--) {
keys.push(i + keyLength);
}
keys.push(keyLength);
if (facet === PRIMARY) {
lastLevelKey = keyLength;
}
} else {
lastLevelKey = primaryMatrix.length > 0 ? primaryMatrix[0].length - 1 : 0;
}
keys.forEach((key) => {
let counter = -1;
return nestFn.key((d) => {
counter++;
return `${d[key].valueOf()}-${findInPage(counter, breakPage)}`;
});
});
matrixTree = nestFn.entries(layoutMatrix);
} else {
if (primaryMatrix.length === 0) {
facet = PRIMARY;
} else if (secondaryMatrix.length === 0) {
facet = SECONDARY;
} else {
facet = primaryMatrix.length >= secondaryMatrix.length ? PRIMARY : SECONDARY;
}
if (primaryMatrix.length > 0) {
for (let i = 0; i < primaryMatrix.length - 1; i++) {
keys.push(i);
}
// if (facet === PRIMARY) {
keys.push(primaryMatrix.length - 1);
// } else {
if (facet === SECONDARY) {
lastLevelKey = primaryMatrix.length - 1;
}
}
if (secondaryMatrix.length > 0) {
const primaryMatrixLength = primaryMatrix.length;
for (let i = secondaryMatrix.length - 1; i > 0; i--) {
keys.push(i + primaryMatrixLength);
}
// if (facet === SECONDARY) {
keys.push(primaryMatrixLength);
// }
if (facet === PRIMARY) {
lastLevelKey = primaryMatrixLength;
}
}
keys.forEach((key) => {
let counter = -1;
return nestFn.key((d) => {
counter++;
return `${d[key].valueOf()}-${findInPage(counter, breakPage)}`;
});
});
let newMatrix = [];
newMatrix = layoutMatrix[0].map((col, colIndex) => layoutMatrix.map(row => row[colIndex]));
matrixTree = nestFn.entries(newMatrix);
}
return { tree: matrixTree, lastLevelKey };
};
/**
* Gives the min measues
*
* @param {boolean} isTransposed is column matrix
* @param {Object} unitMeasures min measures to be given to cells
* @return {Object} dimension min cell
*/
export const getMinMeasures = (isTransposed, unitMeasures) => {
if (!isTransposed) {
return {
height: unitMeasures.height,
width: 0
};
}
return {
height: 0,
width: unitMeasures.width
};
};
/**
* Get the logical space from the tree
*
* @param {Node} item tree to be calculated
* @param {number} measures width and height
* @param {Array} minMeasures min measures for a cell
* @param {Array} maxMeasure max measures for a col/row
* @return {Object} dimension
*/
export const getLogicalSpace = (item, measures, minMeasures, maxMeasure = []) => {
const { firstMeasure, secondMeasure } = measures;
let firstMeasureValue = 0;
let secondMeasureValue = 0;
item.values.forEach((valueArray) => {
let fMeasure = 0;
let sMeasure = 0;
valueArray.forEach((placeholder, colIndex) => {
placeholder.logicalSpace(null);
const space = placeholder.getLogicalSpace();
sMeasure = Math.max(sMeasure, +space[secondMeasure], minMeasures[secondMeasure]);
maxMeasure[colIndex] = Math.max(maxMeasure[colIndex] || 0, space[firstMeasure]);
fMeasure += +maxMeasure[colIndex];
});
secondMeasureValue += sMeasure;
firstMeasureValue = Math.max(firstMeasureValue, fMeasure);
item.space = {
[secondMeasure]: Math.ceil(secondMeasureValue),
[firstMeasure]: Math.ceil(firstMeasureValue)
};
});
return {
[secondMeasure]: secondMeasureValue,
[firstMeasure]: firstMeasureValue
};
};
/**
* Computes the logical spcae taken by the matrix tree
*
* @param {*} [item={}] tree to be viewed
* @param {boolean} [isTransposed=false] is column matrix
* @param {*} unitMeasures min measues for a cell
* @param {Array} maxMeasure max measures for a col/row
* @return {Object} logical space taken
*/
export const computeLogicalSpace = (item = {}, config, maxMeasures) => {
const { isTransposed = false, unitMeasures } = config;
const { firstMeasure, secondMeasure } = getMeasureInfo(isTransposed);
const { values } = item;
const minMeasures = getMinMeasures(isTransposed, unitMeasures);
if (values[0].key) {
const logicalSpace = { [firstMeasure]: 0, [secondMeasure]: 0 };
values.forEach((valueItem) => {
// Compute logical space for lowest level
const space = computeLogicalSpace(valueItem, config, maxMeasures);
// Set logical space for first measure
logicalSpace[firstMeasure] = Math.max(logicalSpace[firstMeasure], space[firstMeasure],
minMeasures[firstMeasure]);
// Set logical space for second measure
logicalSpace[secondMeasure] += +space[secondMeasure];
});
item.space = logicalSpace;
return logicalSpace;
}
return getLogicalSpace(item, { firstMeasure, secondMeasure }, minMeasures, maxMeasures);
};
/**
* Gives the space taken by a row
*
* @param {Array} row matrix array of rows
* @return {Object} dimension of the row
*/
export const spaceTakenByRow = (row) => {
let height = 0;
let width = 0;
row.forEach((col) => {
const spaces = col.getLogicalSpace();
height = Math.max(height, spaces.height);
width += spaces.width;
});
return {
width,
height
};
};
/**
* Gives the space taken by a column
*
* @param {Array<Array>} matrix column matrix
* @param {number} colIndex column index
* @return {Object} dimension of the column
*/
export const spaceTakenByColumn = (matrix, colIndex) => {
let height = 0;
let width = 0;
matrix.forEach((row) => {
const col = row[colIndex];
const spaces = col.getLogicalSpace();
width = Math.max(width, spaces.width);
height += spaces.height;
});
return {
width,
height
};
};
/**
* Creates different level matrices
*
* @param {Object} item matrix tree
* @param {boolean} isTransposed is column matrix
* @return {Object} matrix of each level
*/
export const createMatrixEachLevel = (item, isTransposed) => {
if (item.values[0].key) {
const arr = [];
item.values.forEach((child) => {
if (!isTransposed) {
arr.push(...createMatrixEachLevel(child, isTransposed));
} else {
const eachLevel = createMatrixEachLevel(child, isTransposed);
eachLevel.forEach((e, i) => {
arr[i] = arr[i] || [];
arr[i].push(...e);
});
}
});
item.matrix = arr;
return arr;
}
if (!isTransposed) {
item.matrix = item.values;
} else {
item.matrix = item.values[0].map((col, colIndex) => item.values.map(row => row[colIndex]));
}
return item.matrix;
};
/**
* Breaks the matrix into two part
*
* @param {Array<Array>} matrix input matrix
* @param {boolean} isTransposed is column matrix
* @param {number} breakPointer point in matrix where it is to be broken
* @return {Array} two broken matrix
*/
export const breakMatrix = (matrix, isTransposed, breakPointer) => {
const primaryMatrix = [];
const secondaryMatrix = [];
if (isTransposed) {
matrix.forEach((row, rowIndex) => {
if (rowIndex >= breakPointer) {
secondaryMatrix.push(row);
} else {
primaryMatrix.push(row);
}
});
} else {
matrix.forEach((row, rowIndex) => {
row.forEach((column, columnIndex) => {
if (columnIndex >= breakPointer) {
secondaryMatrix[rowIndex] = secondaryMatrix[rowIndex] || [];
secondaryMatrix[rowIndex].push(column);
} else {
primaryMatrix[rowIndex] = primaryMatrix[rowIndex] || [];
primaryMatrix[rowIndex].push(column);
}
});
});
}
return [primaryMatrix, secondaryMatrix];
};
/**
* Distributed width returned
*
* @param {Object} context context for the width distibution
* @return {number} distributed widths
*/
export const getDistributedWidth = (context, layoutConfig) => {
const {
availableWidth,
width,
row
} = context;
const {
isDistributionEqual,
isTransposed,
distribution
} = layoutConfig;
let distSum = 0;
if (distribution && distribution[0]) {
distSum = distribution.reduce((t, n) => {
t += n;
return t;
});
}
return row.map((col, colIndex) => {
const space = col.getLogicalSpace().width;
let distWidth = (space + (availableWidth - width) * (space / width));
if (isTransposed) {
if (distribution.length > 0) {
distWidth = (availableWidth * distribution[colIndex] / distSum);
} else if (isDistributionEqual || width === 0) {
const rowLen = row.length;
distWidth = (availableWidth / rowLen);
}
}
return Math.floor(distWidth);
});
};
/**
* Distributeed heights returned
*
* @param {Object} context input for the height distribution
* @return {Object} distribured heights
*/
export const getDistributedHeight = (context) => {
let distSum = 0;
let gutterSum = 0;
let heightWithoutGutter = 0;
const {
isTransposed,
distribution,
availableHeight,
height,
isDistributionEqual,
gutter,
matrix,
cIdx
} = context;
if (distribution && distribution[0] !== undefined) {
distSum = distribution.reduce((t, n) => {
t += n;
return t;
});
}
if (gutter && gutter[0] !== undefined) {
gutterSum = gutter.reduce((t, n) => {
t += n;
return t;
});
}
heightWithoutGutter = availableHeight - Math.floor(availableHeight * gutterSum);
const colLen = matrix.length;
return matrix.map((row, rIdx) => {
const col = row[cIdx];
const space = col.getLogicalSpace().height;
let distHeight = (space + (heightWithoutGutter - height) * (space / height));
if (!isTransposed) {
if (distribution.length > 0 && colLen === distribution.length) {
distHeight = (heightWithoutGutter * distribution[rIdx] / distSum);
} else if (isDistributionEqual || context.height === 0) {
distHeight = (heightWithoutGutter / colLen);
}
}
return Math.floor(distHeight);
});
};
/**
*
*
* @param {*} arr
* @param {*} beg
* @param {*} end
*/
export const extraCellsRemover = (arr, beg, end) => arr.slice(beg, -end);
/**
* Creates matrix instancess
*
* @param {Array} [arr=[]] mutated arry
* @param {number} depth depth of the tree
* @param {Array} matrixInfo Details about the matrix(tree, etc) to be inserted
* @param {boolean} layout Instance of layout
*/
export const createMatrixInstances = (arr = [], depth, matrixInfo, layout) => {
const breakPointer = layout._breakPointer;
const config = layout.config();
const {
isTransposed
} = config;
const {
tree,
layoutMatrix
} = matrixInfo;
if (depth === 0) {
const brokenMatrix = breakMatrix(tree.matrix, isTransposed, breakPointer);
arr.push({
matrix: tree.matrix,
primaryMatrix: brokenMatrix[0],
secondaryMatrix: brokenMatrix[1],
space: tree.space
});
return arr;
}
const nextLevel = depth - 1;
tree.values.forEach((e) => {
createMatrixInstances(arr, nextLevel, {
tree: e,
layoutMatrix
}, layout);
});
return arr;
};