Files
DeltaGreen/module/utils/skill-layout.js
T
2026-06-21 13:44:54 -07:00

145 lines
3.4 KiB
JavaScript

/**
* Splits a flat list into column arrays for vertical stacking (top-to-bottom per column).
*
* @template T
* @param {T[]} arr
* @param {number} numCols
* @returns {T[][]}
*/
export function splitIntoColumns(arr, numCols) {
if (numCols <= 1) return [arr];
const columns = Array.from({ length: numCols }, () => /** @type {T[]} */ ([]));
const perCol = Math.ceil(arr.length / numCols);
for (let col = 0; col < numCols; col++) {
columns[col] = arr.slice(col * perCol, (col + 1) * perCol);
}
return columns;
}
/**
* Reorders a flat array into column-major order for grid display.
*
* @param {object[]} arr
* @param {number} numCols
* @returns {object[]}
*/
export function reorderForColumnSorting(arr, numCols) {
const numRows = Math.ceil(arr.length / numCols);
const reordered = new Array(arr.length);
const baseRowCount = Math.floor(arr.length / numCols);
const extraColumns = arr.length % numCols;
const colHeights = new Array(numCols).fill(baseRowCount);
for (let i = 0; i < extraColumns; i++) {
colHeights[i] += 1;
}
let index = 0;
for (let col = 0; col < numCols; col++) {
const rowCount = colHeights[col];
for (let row = 0; row < rowCount; row++) {
const newIndex = numCols * row + col;
if (newIndex < arr.length) {
reordered[newIndex] = arr[index];
index += 1;
}
}
}
return reordered;
}
/**
* @param {object} column
* @param {object} group
* @returns {void}
*/
function appendSkillGroupToColumn(column, group) {
column.blocks.push({
type: "groupHeader",
group: group.group,
label: group.label,
...(group.isSpecialTraining ? { isSpecialTraining: true } : {}),
});
const skillType = group.isSpecialTraining ? "training" : "typedSkill";
for (const skill of group.skills) {
column.blocks.push({ ...skill, type: skillType });
}
column.height += group.rowCount;
}
/**
* @param {object[]} groups
* @param {number} numCols
* @returns {object[][]}
*/
function packSkillGroupsByColumn(groups, numCols) {
const columns = Array.from({ length: numCols }, () => ({
blocks: [],
height: 0,
}));
if (groups.length === 0) {
return columns.map((col) => col.blocks);
}
const totalHeight = groups.reduce((sum, group) => sum + group.rowCount, 0);
const targetHeight = totalHeight / numCols;
let colIndex = 0;
for (const group of groups) {
const col = columns[colIndex];
if (
colIndex < numCols - 1 &&
col.blocks.length > 0 &&
col.height + group.rowCount > targetHeight
) {
colIndex += 1;
}
appendSkillGroupToColumn(columns[colIndex], group);
}
return columns.map((col) => col.blocks);
}
/**
* @param {object[]} groups
* @param {number} numCols
* @returns {object[][]}
*/
function packSkillGroupsByRow(groups, numCols) {
const columns = Array.from({ length: numCols }, () => ({
blocks: [],
height: 0,
}));
for (let i = 0; i < groups.length; i++) {
appendSkillGroupToColumn(columns[i % numCols], groups[i]);
}
return columns.map((col) => col.blocks);
}
/**
* Packs skill groups into columns without splitting a group across columns.
*
* @param {object[]} groups
* @param {number} numCols
* @param {boolean} sortByColumn
* @returns {object[][]}
*/
export function packSkillGroupsIntoColumns(groups, numCols, sortByColumn) {
if (sortByColumn) {
return packSkillGroupsByColumn(groups, numCols);
}
return packSkillGroupsByRow(groups, numCols);
}