✨ Add Vue 3 composable for group sizing and print-safe layout generation
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* useGroupPrintLayout
|
||||
* --------------------------------------------------
|
||||
* Vue 3 composable for group sizing and
|
||||
* print-safe layout generation.
|
||||
*
|
||||
* Uses only Vue reactivity + vanilla JS.
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {number} options.rowsPerPage
|
||||
* @param {number} options.headerRows
|
||||
* @param {number} options.spacerRows
|
||||
*/
|
||||
export function useGroupPrintLayout(options) {
|
||||
// -----------------------------
|
||||
// Reactive state
|
||||
// -----------------------------
|
||||
const players = ref([]);
|
||||
const groupIds = ref([]);
|
||||
const droppedPlayerIds = ref([]);
|
||||
|
||||
// -----------------------------
|
||||
// Internal helpers (pure JS)
|
||||
// -----------------------------
|
||||
function calculateGroupSizes(totalPlayers, ids) {
|
||||
const baseSize = Math.floor(totalPlayers / ids.length);
|
||||
const remainder = totalPlayers % ids.length;
|
||||
|
||||
return ids.map((id, index) => ({
|
||||
id,
|
||||
players: [],
|
||||
size: baseSize + (index < remainder ? 1 : 0)
|
||||
}));
|
||||
}
|
||||
|
||||
function assignPlayersToGroups(playerList, groups) {
|
||||
let index = 0;
|
||||
|
||||
groups.forEach(group => {
|
||||
for (let i = 0; i < group.size; i++) {
|
||||
if (index >= playerList.length) break;
|
||||
|
||||
const player = { ...playerList[index] };
|
||||
player.groupId = group.id;
|
||||
group.players.push(player);
|
||||
index++;
|
||||
}
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
function applyDrops(groups, dropIds) {
|
||||
if (!dropIds.length) return groups;
|
||||
|
||||
const dropSet = new Set(dropIds);
|
||||
groups.forEach(group => {
|
||||
group.players = group.players.filter(player => !dropSet.has(player.id));
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
function calculateGroupPrintRows(group) {
|
||||
return options.headerRows + group.players.length + options.spacerRows;
|
||||
}
|
||||
|
||||
function generatePrintLayout(groups) {
|
||||
const pages = [];
|
||||
let pageNumber = 1;
|
||||
|
||||
let currentPage = {
|
||||
pageNumber,
|
||||
groups: [],
|
||||
usedRows: 0
|
||||
};
|
||||
|
||||
groups.forEach(group => {
|
||||
const groupRows = calculateGroupPrintRows(group);
|
||||
|
||||
if (currentPage.usedRows + groupRows > options.rowsPerPage) {
|
||||
pages.push(currentPage);
|
||||
pageNumber++;
|
||||
|
||||
currentPage = {
|
||||
pageNumber,
|
||||
groups: [],
|
||||
usedRows: 0
|
||||
};
|
||||
}
|
||||
|
||||
currentPage.groups.push(group);
|
||||
currentPage.usedRows += groupRows;
|
||||
});
|
||||
|
||||
pages.push(currentPage);
|
||||
return pages;
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Computed pipeline
|
||||
// -----------------------------
|
||||
const groupedData = computed(() => {
|
||||
if (!players.value.length || !groupIds.value.length) return [];
|
||||
|
||||
let groups = calculateGroupSizes(players.value.length, groupIds.value);
|
||||
|
||||
groups = assignPlayersToGroups(players.value, groups);
|
||||
groups = applyDrops(groups, droppedPlayerIds.value);
|
||||
|
||||
return groups;
|
||||
});
|
||||
|
||||
const printPages = computed(() => {
|
||||
if (!groupedData.value.length) return [];
|
||||
return generatePrintLayout(groupedData.value);
|
||||
});
|
||||
|
||||
// -----------------------------
|
||||
// Public API
|
||||
// -----------------------------
|
||||
function setPlayers(list) {
|
||||
players.value = Array.isArray(list) ? list : [];
|
||||
}
|
||||
|
||||
function setGroups(ids) {
|
||||
groupIds.value = Array.isArray(ids) ? ids : [];
|
||||
}
|
||||
|
||||
function dropPlayer(id) {
|
||||
if (!droppedPlayerIds.value.includes(id)) {
|
||||
droppedPlayerIds.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function restorePlayer(id) {
|
||||
droppedPlayerIds.value = droppedPlayerIds.value.filter(pid => pid !== id);
|
||||
}
|
||||
|
||||
function resetDrops() {
|
||||
droppedPlayerIds.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
// state
|
||||
players,
|
||||
groupIds,
|
||||
droppedPlayerIds,
|
||||
|
||||
// derived
|
||||
groupedData,
|
||||
printPages,
|
||||
|
||||
// actions
|
||||
setPlayers,
|
||||
setGroups,
|
||||
dropPlayer,
|
||||
restorePlayer,
|
||||
resetDrops
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user