diff --git a/app/styles.css b/app/styles.css index 8bd8a9e..8175939 100644 --- a/app/styles.css +++ b/app/styles.css @@ -1135,6 +1135,7 @@ --frame-base-gap: clamp(2px, 0.3vw, 6px); --frame-gap: calc(var(--frame-base-gap) * var(--frame-grid-zoom-scale)); display: grid; + grid-template-columns: minmax(0, 1fr); gap: 14px; padding: 18px; border: 1px solid #27272a; @@ -1152,7 +1153,8 @@ align-items: flex-start; justify-content: space-between; gap: 12px; - min-width: max-content; + min-width: 0; + flex-wrap: wrap; } .tarot-frame-panel-title { @@ -1183,8 +1185,10 @@ } .tarot-frame-grid-viewport { - width: 100%; - max-width: 100%; + width: min(100%, calc(100vw - 52px)); + max-width: calc(100vw - 52px); + margin-left: auto; + margin-right: auto; overflow: auto; overscroll-behavior: contain; min-width: 0; @@ -1481,7 +1485,8 @@ } .tarot-frame-panel { - --frame-base-cell-width: 26px; + --frame-base-gap: 2px; + --frame-base-cell-width: min(26px, calc((100vw - 78px) / 14)); } .tarot-frame-card-badge { diff --git a/app/ui-tarot-frame.js b/app/ui-tarot-frame.js index e4ee209..65df5f2 100644 --- a/app/ui-tarot-frame.js +++ b/app/ui-tarot-frame.js @@ -991,7 +991,46 @@ return getCardOverlayDate(card) || formatMonthDay(getRelation(card, "decan")?.data?.dateStart) || getDisplayCardName(card); } - function centerGridViewport() { + function getOccupiedGridBounds(gridTrackEl) { + if (!(gridTrackEl instanceof HTMLElement)) { + return null; + } + + const filledSlots = Array.from(gridTrackEl.querySelectorAll(".tarot-frame-slot:not(.is-empty-slot)")); + if (!filledSlots.length) { + return null; + } + + const trackRect = gridTrackEl.getBoundingClientRect(); + return filledSlots.reduce((bounds, slotEl) => { + if (!(slotEl instanceof HTMLElement)) { + return bounds; + } + + const slotRect = slotEl.getBoundingClientRect(); + const left = slotRect.left - trackRect.left; + const right = slotRect.right - trackRect.left; + if (!bounds) { + return { left, right }; + } + + return { + left: Math.min(bounds.left, left), + right: Math.max(bounds.right, right) + }; + }, null); + } + + function resetFrameSectionScroll() { + const sectionEl = document.getElementById("tarot-frame-section"); + if (!(sectionEl instanceof HTMLElement)) { + return; + } + + sectionEl.scrollTop = 0; + } + + function centerGridViewport(attempt = 0) { const { tarotFrameBoardEl } = getElements(); const gridViewportEl = tarotFrameBoardEl?.querySelector(".tarot-frame-grid-viewport"); const gridTrackEl = tarotFrameBoardEl?.querySelector(".tarot-frame-grid-track"); @@ -1004,16 +1043,44 @@ return; } - const overflowX = Math.max(0, gridTrackEl.offsetWidth - gridViewportEl.clientWidth); - gridViewportEl.scrollLeft = overflowX > 0 ? overflowX / 2 : 0; + const contentWidth = gridTrackEl.scrollWidth || gridTrackEl.offsetWidth; + const viewportWidth = gridViewportEl.clientWidth; + if (!contentWidth || !viewportWidth) { + if (attempt < 6) { + centerGridViewport(attempt + 1); + } + return; + } + + const occupiedBounds = getOccupiedGridBounds(gridTrackEl); + const targetCenter = occupiedBounds + ? (occupiedBounds.left + occupiedBounds.right) / 2 + : contentWidth / 2; + const maxScrollLeft = Math.max(0, contentWidth - viewportWidth); + const targetScrollLeft = Math.min(Math.max(targetCenter - (viewportWidth / 2), 0), maxScrollLeft); + gridViewportEl.scrollLeft = targetScrollLeft; requestAnimationFrame(() => { if (!(gridViewportEl instanceof HTMLElement) || !(gridTrackEl instanceof HTMLElement)) { return; } - const nextOverflowX = Math.max(0, gridTrackEl.offsetWidth - gridViewportEl.clientWidth); - gridViewportEl.scrollLeft = nextOverflowX > 0 ? nextOverflowX / 2 : 0; + const nextContentWidth = gridTrackEl.scrollWidth || gridTrackEl.offsetWidth; + const nextViewportWidth = gridViewportEl.clientWidth; + if (!nextContentWidth || !nextViewportWidth) { + if (attempt < 6) { + centerGridViewport(attempt + 1); + } + return; + } + + const nextOccupiedBounds = getOccupiedGridBounds(gridTrackEl); + const nextTargetCenter = nextOccupiedBounds + ? (nextOccupiedBounds.left + nextOccupiedBounds.right) / 2 + : nextContentWidth / 2; + const nextMaxScrollLeft = Math.max(0, nextContentWidth - nextViewportWidth); + const nextTargetScrollLeft = Math.min(Math.max(nextTargetCenter - (nextViewportWidth / 2), 0), nextMaxScrollLeft); + gridViewportEl.scrollLeft = nextTargetScrollLeft; }); }); } @@ -2061,6 +2128,8 @@ await config.ensureTarotSection(referenceData, magickDataset); } + resetFrameSectionScroll(); + const cards = getCards(); if (!cards.length) { setStatus("Tarot cards are still loading..."); diff --git a/index.html b/index.html index eea55be..4fa741f 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ - +
@@ -1178,7 +1178,7 @@ - +