more card grid fixes

This commit is contained in:
Guy Sandler 2026-10-04 13:44:51 -07:00
parent 030a500609
commit ade39d6014

View File

@ -1109,9 +1109,9 @@ function applyOptionsChanges(changes) {
case "gpa_calc_cumulative": case "gpa_calc_cumulative":
if (!grades) getGrades(); if (!grades) getGrades();
setupGPACalc(); setupGPACalc();
// The GPA elements are placed on dedicated grid rows when Card // The GPA elements participate in the card grid layout (see
// Grid is on (see applyAestheticChanges), so toggling GPA on/off // applyAestheticChanges), so toggling GPA on/off has to
// has to regenerate the grid CSS too. // regenerate the grid CSS too.
if (options.card_grid === true) debouncedApplyAestheticChanges(); if (options.card_grid === true) debouncedApplyAestheticChanges();
break; break;
case "gpa_calc_bounds": case "gpa_calc_bounds":
@ -6741,6 +6741,10 @@ function setupGPACalc() {
container.style.display = "none"; container.style.display = "none";
editBtn.textContent = "Edit Calculator"; editBtn.textContent = "Edit Calculator";
} }
// Opening/closing changes the card grid's row composition
// (the expanded calculator claims a full-width row), so
// uneven-row centering has to recompute either way.
requestAnimationFrame(centerUnevenGridRows);
}); });
container2.dataset.canvasrefinedGpaRendered = "true"; container2.dataset.canvasrefinedGpaRendered = "true";
@ -7232,11 +7236,12 @@ function applyAestheticChanges() {
// justify-content:center. // justify-content:center.
// //
// The GPA calculator elements live in this same container (appended by // The GPA calculator elements live in this same container (appended by
// setupGPACalc), so when they're visible they get definite full-width // setupGPACalc). Two spare auto-height template rows are reserved so the
// rows past the card rows — otherwise they'd take grid cells, land in // GPA card and its expanded calculator still get real rows when the
// the middle of the card flow (they can be prepended), and throw off the // course cards fill the whole card area (otherwise they'd land in the
// row math. Two spare auto-height template rows host them; when the GPA // zero-height implicit rows that overflow-hidden clips away). When the
// feature is off they're display:none and the spare rows collapse to 0. // GPA card instead flows into an open slot inside the card area, the
// spare rows stay empty and collapse to 0.
if (options.card_grid === true) { if (options.card_grid === true) {
const gridCols = Math.max(1, parseInt(options.card_grid_columns, 10) || 4); const gridCols = Math.max(1, parseInt(options.card_grid_columns, 10) || 4);
const gridRows = Math.max(1, parseInt(options.card_grid_rows, 10) || 3); const gridRows = Math.max(1, parseInt(options.card_grid_rows, 10) || 3);
@ -7283,13 +7288,11 @@ function applyAestheticChanges() {
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-row:1!important;grid-column:1!important}`; style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-row:1!important;grid-column:1!important}`;
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-row:2!important;grid-column:1/-1!important}`; style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-row:2!important;grid-column:1/-1!important}`;
} else { } else {
// Bottom mode: no fixed spare rows (column count varies // Bottom mode: the GPA card auto-places in DOM order, so it
// with width). `order` places the GPA card after every // flows right after the last course card — filling the next
// course card in the auto-placement sequence — it flows // open slot instead of stranding below the grid — and the
// right after the last card — and the expanded calculator // expanded calculator follows on a full-width row.
// follows on a full-width row. style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-column:1/-1!important}`;
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{order:1!important}`;
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-column:1/-1!important;order:2!important}`;
} }
} }
// Step the column count down: m columns apply once m+1 no longer // Step the column count down: m columns apply once m+1 no longer
@ -7308,20 +7311,24 @@ function applyAestheticChanges() {
} else { } else {
const totalRows = gridRows + (gpaOn ? 2 : 0); const totalRows = gridRows + (gpaOn ? 2 : 0);
style.textContent += `.ic-DashboardCard__box__container{display:grid!important;grid-template-columns:repeat(${gridCols * 2},minmax(0,max-content))!important;grid-template-rows:repeat(${totalRows},minmax(0,auto))!important;grid-auto-rows:0!important;column-gap:${gridColGap}px!important;row-gap:${gridRowGap}px!important;align-items:start!important;overflow:hidden!important${centerGrid ? ";justify-content:center!important" : ""}}`; style.textContent += `.ic-DashboardCard__box__container{display:grid!important;grid-template-columns:repeat(${gridCols * 2},minmax(0,max-content))!important;grid-template-rows:repeat(${totalRows},minmax(0,auto))!important;grid-auto-rows:0!important;column-gap:${gridColGap}px!important;row-gap:${gridRowGap}px!important;align-items:start!important;overflow:hidden!important${centerGrid ? ";justify-content:center!important" : ""}}`;
style.textContent += `.ic-DashboardCard__box__container > .ic-DashboardCard{grid-column:span 2!important}`; style.textContent += `.ic-DashboardCard__box__container > .ic-DashboardCard,.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-column:span 2!important}`;
if (gpaOn) { if (gpaOn) {
// "Move to top" (gpa_calc_prepend) decides where the GPA card sits // "Move to top" (gpa_calc_prepend) decides where the GPA card
// in the grid: on -> first row, leftmost slot (the calculator row // sits: on -> pinned to the first slot (the expanded calculator
// goes right below it, and cards auto-place around both); off -> // takes the full second row, and cards auto-place around both);
// spare rows past the card rows, anchored to the rightmost slot. // off -> the GPA card is a regular auto-placed item (one card
// In both cases the card spans one card width (2 sub-columns) and // slot wide via the span rule above), so it flows into the next
// the expanded calculator spans the full grid width. // open slot after the course cards — filling the leftover
// spaces of a partial last row instead of squatting alone in a
// dedicated row below the grid. The expanded calculator always
// takes a full-width row: auto-placed right after the GPA card
// in bottom mode, and display:none while collapsed so it
// consumes no cells.
if (gpaTop) { if (gpaTop) {
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-row:1!important;grid-column:1/span 2!important}`; style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-row:1!important;grid-column:1/span 2!important}`;
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-row:2!important;grid-column:1/-1!important}`; style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-row:2!important;grid-column:1/-1!important}`;
} else { } else {
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa-card{grid-row:${gridRows + 1}!important;grid-column:-3/span 2!important}`; style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-column:1/-1!important}`;
style.textContent += `.ic-DashboardCard__box__container > .canvasrefined-gpa{grid-row:${gridRows + 2}!important;grid-column:1/-1!important}`;
} }
} }
} }
@ -7402,19 +7409,32 @@ hidden/unhidden, dashboard re-render, setting changes) start clean.
function centerUnevenGridRows() { function centerUnevenGridRows() {
const container = document.querySelector(".ic-DashboardCard__box__container"); const container = document.querySelector(".ic-DashboardCard__box__container");
if (!container) return; if (!container) return;
const cards = Array.from(container.children).filter(el => el.classList && el.classList.contains("ic-DashboardCard")); // Flow items: course cards plus the GPA card (it occupies a regular card
cards.forEach(card => card.style.removeProperty("grid-column")); // slot — pinned first by "Move to top", otherwise auto-placed after the
// cards). DOM order matches auto-placement order, so chunking this list
// into rows of `cols` matches the real grid rows — including when the
// expanded calculator (a full-width row between chunks) is open.
const items = Array.from(container.children).filter(el => el.classList && (el.classList.contains("ic-DashboardCard") || el.classList.contains("canvasrefined-gpa-card")));
items.forEach(el => el.style.removeProperty("grid-column"));
if (options.card_grid !== true || options.card_grid_center_rows !== true) return; if (options.card_grid !== true || options.card_grid_center_rows !== true) return;
// Flexible grid: the live column count changes with the window width, so // Flexible grid: the live column count changes with the window width, so
// fixed sub-column offsets computed here would be wrong — skip centering. // fixed sub-column offsets computed here would be wrong — skip centering.
if (options.card_grid_flex === true) return; if (options.card_grid_flex === true) return;
const cols = Math.max(1, parseInt(options.card_grid_columns, 10) || 4); const cols = Math.max(1, parseInt(options.card_grid_columns, 10) || 4);
const rows = Math.max(1, parseInt(options.card_grid_rows, 10) || 3); const rows = Math.max(1, parseInt(options.card_grid_rows, 10) || 3);
const visible = cards.filter(card => getComputedStyle(card).display !== "none"); const visible = items.filter(el => getComputedStyle(el).display !== "none");
const count = visible.length; const count = visible.length;
// Overflowing the template: extra cards land in clipped implicit rows and // Capacity: the explicit template rows (cards + the two GPA spare rows),
// the last explicit row is full, so there's nothing to center. // minus a full row while the expanded calculator is open (it claims one
if (count === 0 || count >= cols * rows) return; // for itself). Overflowing the template: extra items land in clipped
// implicit rows and the last explicit row is full, so there's nothing to
// center.
const gpaExpanded = options.gpa_calc === true && (() => {
const expanded = container.querySelector(":scope > .canvasrefined-gpa");
return !!expanded && getComputedStyle(expanded).display !== "none";
})();
const capacity = cols * (rows + (options.gpa_calc === true ? 2 : 0)) - (gpaExpanded ? cols : 0);
if (count === 0 || count > capacity) return;
const lastRowStart = Math.floor((count - 1) / cols) * cols; const lastRowStart = Math.floor((count - 1) / cols) * cols;
const inLastRow = count - lastRowStart; const inLastRow = count - lastRowStart;
if (inLastRow >= cols) return; if (inLastRow >= cols) return;