bugfix and phone view optimizations
This commit is contained in:
212
app/styles.css
212
app/styles.css
@@ -42,6 +42,22 @@
|
||||
.topbar-home-button[aria-pressed="true"] {
|
||||
color: #fbbf24;
|
||||
}
|
||||
.topbar-menu-toggle {
|
||||
display: none;
|
||||
padding: 7px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #3f3f46;
|
||||
background: #27272a;
|
||||
color: #f4f4f5;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.topbar-menu-toggle:hover {
|
||||
background: #3f3f46;
|
||||
}
|
||||
.topbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -113,6 +129,104 @@
|
||||
.settings-trigger[aria-pressed="true"] {
|
||||
background: #3f3f46;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 10px 12px;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
background: rgba(24, 24, 27, 0.96);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
.topbar-home-button {
|
||||
font-size: 17px;
|
||||
min-height: 38px;
|
||||
}
|
||||
.topbar-menu-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 38px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.topbar-actions {
|
||||
display: none;
|
||||
flex: 1 0 100%;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin: 4px 0 0;
|
||||
border: 1px solid #2f2f39;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, rgba(24, 24, 34, 0.98), rgba(12, 12, 18, 0.98));
|
||||
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.34);
|
||||
overflow: visible;
|
||||
pointer-events: auto;
|
||||
gap: 10px;
|
||||
max-height: calc(100svh - 88px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.topbar.is-menu-open .topbar-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.topbar-actions > * {
|
||||
width: 100%;
|
||||
}
|
||||
.topbar-dropdown {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
}
|
||||
.topbar-dropdown-menu {
|
||||
position: static;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
padding: 6px;
|
||||
border-radius: 12px;
|
||||
border-color: #31313d;
|
||||
background: rgba(10, 10, 16, 0.92);
|
||||
box-shadow: none;
|
||||
}
|
||||
.topbar-dropdown:hover .topbar-dropdown-menu,
|
||||
.topbar-dropdown:focus-within .topbar-dropdown-menu {
|
||||
display: none;
|
||||
}
|
||||
.topbar-dropdown.is-open .topbar-dropdown-menu {
|
||||
display: grid;
|
||||
}
|
||||
.settings-trigger {
|
||||
width: 100%;
|
||||
min-height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.topbar-sub-trigger {
|
||||
min-height: 40px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.topbar {
|
||||
padding: 9px 10px;
|
||||
}
|
||||
.topbar-home-button {
|
||||
font-size: 16px;
|
||||
}
|
||||
.topbar-actions {
|
||||
padding: 10px;
|
||||
border-radius: 14px;
|
||||
max-height: calc(100svh - 78px);
|
||||
}
|
||||
.settings-trigger {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
body.connection-gated {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -475,6 +589,16 @@
|
||||
border: 1px solid #3f3f46;
|
||||
background: #09090b;
|
||||
}
|
||||
.tarot-detail-heading {
|
||||
position: relative;
|
||||
padding-right: 108px;
|
||||
}
|
||||
.tarot-detail-heading .detail-toggle-inline {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
.tarot-detail-heading h2 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
@@ -1160,6 +1284,13 @@
|
||||
.planet-detail-heading {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-right: 108px;
|
||||
}
|
||||
.planet-detail-heading .detail-toggle-inline {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
.planet-detail-heading h2 {
|
||||
margin: 0;
|
||||
@@ -3368,10 +3499,18 @@
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.planet-detail-heading,
|
||||
.tarot-detail-heading,
|
||||
.alpha-text-detail-heading {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.planet-detail-heading,
|
||||
.tarot-detail-heading {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.alpha-text-controls--heading {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -3380,10 +3519,29 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.planet-detail-heading .detail-toggle-inline,
|
||||
.tarot-detail-heading .detail-toggle-inline,
|
||||
.alpha-text-detail-heading .detail-toggle-inline {
|
||||
position: static;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.tarot-detail-top {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.tarot-detail-image {
|
||||
width: min(180px, 60vw);
|
||||
height: auto;
|
||||
aspect-ratio: 2 / 3;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.tarot-meanings,
|
||||
.tarot-meta-grid,
|
||||
#kab-detail-body {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.alpha-text-meta-grid {
|
||||
@@ -5244,9 +5402,61 @@
|
||||
.now-stats-planets {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
.planet-layout {
|
||||
.planet-layout,
|
||||
.tarot-layout {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
.kab-layout {
|
||||
grid-template-rows: minmax(220px, 42svh) minmax(0, 1fr);
|
||||
}
|
||||
.planet-list-panel,
|
||||
.tarot-list-panel {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #27272a;
|
||||
max-height: min(42svh, 420px);
|
||||
}
|
||||
.planet-list-item,
|
||||
.tarot-list-item {
|
||||
min-height: 48px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.planet-card-list,
|
||||
.tarot-card-list {
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
.planet-detail-panel,
|
||||
.tarot-detail-panel,
|
||||
.kab-detail-panel {
|
||||
padding: 14px 14px calc(84px + env(safe-area-inset-bottom, 0px));
|
||||
gap: 14px;
|
||||
}
|
||||
.sidebar-toggle-inline,
|
||||
.detail-toggle-inline {
|
||||
min-height: 38px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.sidebar-popout-open,
|
||||
.detail-popout-open {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: calc(14px + env(safe-area-inset-bottom, 0px));
|
||||
z-index: 35;
|
||||
min-height: 42px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.36);
|
||||
background: rgba(24, 24, 27, 0.96);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.sidebar-popout-open {
|
||||
right: 14px;
|
||||
}
|
||||
.detail-popout-open {
|
||||
left: 14px;
|
||||
}
|
||||
.planet-meta-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
let detailNameEl;
|
||||
let detailSubEl;
|
||||
let detailBodyEl;
|
||||
let textLayoutEl;
|
||||
let lexiconPopupEl;
|
||||
let lexiconPopupTitleEl;
|
||||
let lexiconPopupSubtitleEl;
|
||||
@@ -65,9 +66,21 @@
|
||||
detailNameEl = document.getElementById("alpha-text-detail-name");
|
||||
detailSubEl = document.getElementById("alpha-text-detail-sub");
|
||||
detailBodyEl = document.getElementById("alpha-text-detail-body");
|
||||
textLayoutEl = sourceListEl?.closest?.(".planet-layout") || detailBodyEl?.closest?.(".planet-layout") || null;
|
||||
ensureLexiconPopup();
|
||||
}
|
||||
|
||||
function showDetailOnlyMode() {
|
||||
if (!(textLayoutEl instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.TarotChromeUi?.initializeSidebarPopouts?.();
|
||||
window.TarotChromeUi?.initializeDetailPopouts?.();
|
||||
window.TarotChromeUi?.initializeSidebarAutoCollapse?.();
|
||||
window.TarotChromeUi?.showDetailOnly?.(textLayoutEl);
|
||||
}
|
||||
|
||||
function ensureLexiconPopup() {
|
||||
if (lexiconPopupEl instanceof HTMLElement) {
|
||||
return;
|
||||
@@ -597,6 +610,7 @@
|
||||
button.append(name, meta);
|
||||
button.addEventListener("click", () => {
|
||||
if (normalizeId(source.id) === normalizeId(state.selectedSourceId)) {
|
||||
showDetailOnlyMode();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -607,6 +621,7 @@
|
||||
syncSelectionForSource(getSelectedSource());
|
||||
renderSourceList();
|
||||
renderSelectors();
|
||||
showDetailOnlyMode();
|
||||
|
||||
if (state.searchQuery && state.activeSearchScope === "source") {
|
||||
void Promise.all([loadSelectedPassage(), runSearch("source")]);
|
||||
@@ -1468,6 +1483,7 @@
|
||||
syncSelectionForSource(getSelectedSource());
|
||||
renderSourceList();
|
||||
renderSelectors();
|
||||
showDetailOnlyMode();
|
||||
await loadSelectedPassage();
|
||||
clearActiveSearchUi({ preserveHighlight: true });
|
||||
renderDetail();
|
||||
@@ -1549,6 +1565,8 @@
|
||||
async function ensureAlphabetTextSection() {
|
||||
getElements();
|
||||
bindControls();
|
||||
window.TarotChromeUi?.initializeSidebarPopouts?.();
|
||||
window.TarotChromeUi?.initializeDetailPopouts?.();
|
||||
|
||||
if (!sourceListEl || !detailBodyEl) {
|
||||
return;
|
||||
|
||||
283
app/ui-chrome.js
283
app/ui-chrome.js
@@ -5,6 +5,40 @@
|
||||
const DETAIL_COLLAPSE_STORAGE_PREFIX = "tarot-detail-collapsed:";
|
||||
const DEFAULT_DATASET_ENTRY_COLLAPSED = true;
|
||||
const DEFAULT_DATASET_DETAIL_COLLAPSED = false;
|
||||
const MOBILE_TOPBAR_MEDIA_QUERY = "(max-width: 900px)";
|
||||
const sidebarControllers = new WeakMap();
|
||||
const detailControllers = new WeakMap();
|
||||
const AUTO_COLLAPSE_ENTRY_SELECTOR = [
|
||||
".planet-list-item",
|
||||
".tarot-list-item",
|
||||
"[role='option']",
|
||||
".kab-node[data-sephira]",
|
||||
".kab-path-hit[data-path]",
|
||||
".kab-path-tarot[data-path]",
|
||||
".kab-rose-petal[data-path]",
|
||||
".cube-face[role='button']",
|
||||
".cube-edge-line[role='button']",
|
||||
".cube-direction[role='button']",
|
||||
".cube-connector[role='button']",
|
||||
".cube-center[role='button']",
|
||||
".kab-chip[data-path]"
|
||||
].join(", ");
|
||||
const AUTO_COLLAPSE_IGNORE_SELECTOR = [
|
||||
".sidebar-toggle-inline",
|
||||
".sidebar-popout-open",
|
||||
".detail-toggle-inline",
|
||||
".detail-popout-open",
|
||||
"input",
|
||||
"select",
|
||||
"textarea",
|
||||
"label",
|
||||
"form",
|
||||
".dataset-search-wrap",
|
||||
".alpha-text-search-controls",
|
||||
".cube-rotation-controls",
|
||||
".cube-rotation-btn",
|
||||
".tarot-house-action-btn"
|
||||
].join(", ");
|
||||
|
||||
function loadSidebarCollapsedState(storageKey) {
|
||||
try {
|
||||
@@ -29,6 +63,128 @@
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLayoutTarget(target) {
|
||||
if (target instanceof HTMLElement) {
|
||||
if (target.matches(".planet-layout, .tarot-layout, .kab-layout")) {
|
||||
return target;
|
||||
}
|
||||
|
||||
return target.closest(".planet-layout, .tarot-layout, .kab-layout");
|
||||
}
|
||||
|
||||
if (typeof target === "string" && target) {
|
||||
const element = document.getElementById(target);
|
||||
if (element instanceof HTMLElement) {
|
||||
if (element.matches(".planet-layout, .tarot-layout, .kab-layout")) {
|
||||
return element;
|
||||
}
|
||||
|
||||
return element.querySelector(".planet-layout, .tarot-layout, .kab-layout")
|
||||
|| element.closest(".planet-layout, .tarot-layout, .kab-layout");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function setSidebarCollapsed(target, collapsed, persist = true) {
|
||||
const layout = resolveLayoutTarget(target);
|
||||
const controller = layout ? sidebarControllers.get(layout) : null;
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
|
||||
controller.applyCollapsedState(Boolean(collapsed), persist);
|
||||
return true;
|
||||
}
|
||||
|
||||
function setDetailCollapsed(target, collapsed, persist = true) {
|
||||
const layout = resolveLayoutTarget(target);
|
||||
const controller = layout ? detailControllers.get(layout) : null;
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
|
||||
controller.applyCollapsedState(Boolean(collapsed), persist);
|
||||
return true;
|
||||
}
|
||||
|
||||
function showDetailOnly(target, persist = true) {
|
||||
const layout = resolveLayoutTarget(target);
|
||||
if (!layout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const detailChanged = setDetailCollapsed(layout, false, persist);
|
||||
const sidebarChanged = setSidebarCollapsed(layout, true, persist);
|
||||
return detailChanged || sidebarChanged;
|
||||
}
|
||||
|
||||
function shouldAutoCollapseFromEvent(panel, target) {
|
||||
if (!(panel instanceof HTMLElement) || !(target instanceof Element) || !panel.contains(target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.closest(AUTO_COLLAPSE_IGNORE_SELECTOR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(target.closest(AUTO_COLLAPSE_ENTRY_SELECTOR));
|
||||
}
|
||||
|
||||
function scheduleAutoCollapse(layout) {
|
||||
if (!(layout instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
showDetailOnly(layout);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeSidebarAutoCollapse() {
|
||||
const layouts = document.querySelectorAll(".planet-layout, .tarot-layout, .kab-layout");
|
||||
|
||||
layouts.forEach((layout) => {
|
||||
if (!(layout instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = Array.from(layout.children).find((child) => (
|
||||
child instanceof HTMLElement
|
||||
&& child.matches("aside.planet-list-panel, aside.tarot-list-panel, aside.kab-tree-panel")
|
||||
));
|
||||
|
||||
if (!(panel instanceof HTMLElement) || panel.dataset.sidebarAutoCollapseReady === "1") {
|
||||
return;
|
||||
}
|
||||
|
||||
panel.dataset.sidebarAutoCollapseReady = "1";
|
||||
|
||||
panel.addEventListener("click", (event) => {
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (!shouldAutoCollapseFromEvent(panel, target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleAutoCollapse(layout);
|
||||
});
|
||||
|
||||
panel.addEventListener("keydown", (event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (!shouldAutoCollapseFromEvent(panel, target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleAutoCollapse(layout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initializeSidebarPopouts() {
|
||||
const layouts = document.querySelectorAll(".planet-layout, .tarot-layout, .kab-layout");
|
||||
|
||||
@@ -87,6 +243,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
sidebarControllers.set(layout, {
|
||||
applyCollapsedState,
|
||||
panel,
|
||||
collapseBtn,
|
||||
openBtn,
|
||||
storageKey
|
||||
});
|
||||
|
||||
collapseBtn.addEventListener("click", () => {
|
||||
applyCollapsedState(true);
|
||||
});
|
||||
@@ -173,6 +337,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
detailControllers.set(layout, {
|
||||
applyCollapsedState,
|
||||
detailPanel,
|
||||
collapseBtn,
|
||||
openBtn,
|
||||
detailStorageKey
|
||||
});
|
||||
|
||||
collapseBtn.addEventListener("click", () => {
|
||||
applyCollapsedState(true);
|
||||
});
|
||||
@@ -202,6 +374,103 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getTopbarElements() {
|
||||
const topbarEl = document.querySelector(".topbar");
|
||||
const actionsEl = document.getElementById("topbar-actions");
|
||||
const menuToggleEl = document.getElementById("topbar-menu-toggle");
|
||||
|
||||
return {
|
||||
topbarEl: topbarEl instanceof HTMLElement ? topbarEl : null,
|
||||
actionsEl: actionsEl instanceof HTMLElement ? actionsEl : null,
|
||||
menuToggleEl: menuToggleEl instanceof HTMLButtonElement ? menuToggleEl : null
|
||||
};
|
||||
}
|
||||
|
||||
function isMobileTopbarViewport() {
|
||||
return window.matchMedia(MOBILE_TOPBAR_MEDIA_QUERY).matches;
|
||||
}
|
||||
|
||||
function setTopbarMenuOpen(isOpen) {
|
||||
const { topbarEl, menuToggleEl } = getTopbarElements();
|
||||
if (!(topbarEl instanceof HTMLElement) || !(menuToggleEl instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextOpen = Boolean(isOpen);
|
||||
topbarEl.classList.toggle("is-menu-open", nextOpen);
|
||||
menuToggleEl.setAttribute("aria-expanded", nextOpen ? "true" : "false");
|
||||
menuToggleEl.textContent = nextOpen ? "Close" : "Menu";
|
||||
menuToggleEl.setAttribute("aria-label", nextOpen ? "Close navigation menu" : "Open navigation menu");
|
||||
|
||||
if (!nextOpen) {
|
||||
closeTopbarDropdowns();
|
||||
}
|
||||
}
|
||||
|
||||
function bindTopbarMobileMenu() {
|
||||
const { topbarEl, actionsEl, menuToggleEl } = getTopbarElements();
|
||||
if (!(topbarEl instanceof HTMLElement) || !(actionsEl instanceof HTMLElement) || !(menuToggleEl instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (menuToggleEl.dataset.mobileMenuReady === "1") {
|
||||
return;
|
||||
}
|
||||
|
||||
menuToggleEl.dataset.mobileMenuReady = "1";
|
||||
setTopbarMenuOpen(false);
|
||||
|
||||
menuToggleEl.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
const nextOpen = !topbarEl.classList.contains("is-menu-open");
|
||||
setTopbarMenuOpen(nextOpen);
|
||||
});
|
||||
|
||||
actionsEl.addEventListener("click", (event) => {
|
||||
const button = event.target instanceof Element
|
||||
? event.target.closest("button")
|
||||
: null;
|
||||
if (!(button instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDropdownTrigger = button.getAttribute("aria-haspopup") === "menu";
|
||||
const isMenuItem = button.getAttribute("role") === "menuitem";
|
||||
|
||||
if (!isDropdownTrigger || isMenuItem) {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (isMobileTopbarViewport()) {
|
||||
setTopbarMenuOpen(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
const clickTarget = event.target;
|
||||
if (!isMobileTopbarViewport()) {
|
||||
return;
|
||||
}
|
||||
if (clickTarget instanceof Node && topbarEl.contains(clickTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTopbarMenuOpen(false);
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
if (!isMobileTopbarViewport()) {
|
||||
setTopbarMenuOpen(false);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape") {
|
||||
setTopbarMenuOpen(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeTopbarDropdowns(exceptEl = null) {
|
||||
const topbarDropdownEls = Array.from(document.querySelectorAll(".topbar-dropdown"));
|
||||
topbarDropdownEls.forEach((dropdownEl) => {
|
||||
@@ -227,10 +496,16 @@
|
||||
setTopbarDropdownOpen(dropdownEl, false);
|
||||
|
||||
dropdownEl.addEventListener("mouseenter", () => {
|
||||
if (isMobileTopbarViewport()) {
|
||||
return;
|
||||
}
|
||||
setTopbarDropdownOpen(dropdownEl, true);
|
||||
});
|
||||
|
||||
dropdownEl.addEventListener("mouseleave", () => {
|
||||
if (isMobileTopbarViewport()) {
|
||||
return;
|
||||
}
|
||||
setTopbarDropdownOpen(dropdownEl, false);
|
||||
});
|
||||
|
||||
@@ -275,6 +550,8 @@
|
||||
function init() {
|
||||
initializeSidebarPopouts();
|
||||
initializeDetailPopouts();
|
||||
initializeSidebarAutoCollapse();
|
||||
bindTopbarMobileMenu();
|
||||
bindTopbarDropdownInteractions();
|
||||
}
|
||||
|
||||
@@ -283,6 +560,12 @@
|
||||
init,
|
||||
initializeSidebarPopouts,
|
||||
initializeDetailPopouts,
|
||||
initializeSidebarAutoCollapse,
|
||||
bindTopbarMobileMenu,
|
||||
setSidebarCollapsed,
|
||||
setDetailCollapsed,
|
||||
showDetailOnly,
|
||||
setTopbarMenuOpen,
|
||||
setTopbarDropdownOpen,
|
||||
closeTopbarDropdowns,
|
||||
bindTopbarDropdownInteractions
|
||||
|
||||
@@ -369,8 +369,8 @@
|
||||
|
||||
if (lightboxState.deckCompareMode) {
|
||||
compareGridSlots.forEach((slot) => {
|
||||
if (slot?.imageEl) {
|
||||
slot.imageEl.style.transformOrigin = nextOrigin;
|
||||
if (slot?.zoomLayerEl) {
|
||||
slot.zoomLayerEl.style.transformOrigin = nextOrigin;
|
||||
}
|
||||
});
|
||||
return;
|
||||
@@ -392,11 +392,12 @@
|
||||
|
||||
if (lightboxState.deckCompareMode) {
|
||||
compareGridSlots.forEach((slot) => {
|
||||
if (!slot?.imageEl) {
|
||||
if (!slot?.imageEl || !slot?.zoomLayerEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot.imageEl.style.transform = `scale(${activeZoomScale}) ${buildRotationTransform(lightboxState.primaryRotated)}`;
|
||||
slot.zoomLayerEl.style.transform = `scale(${activeZoomScale})`;
|
||||
slot.imageEl.style.transform = buildRotationTransform(lightboxState.primaryRotated);
|
||||
});
|
||||
|
||||
applyTransformOrigins();
|
||||
@@ -734,6 +735,9 @@
|
||||
slot.slotEl.style.display = "none";
|
||||
slot.imageEl.removeAttribute("src");
|
||||
slot.imageEl.alt = "Tarot compare image";
|
||||
if (slot.zoomLayerEl) {
|
||||
slot.zoomLayerEl.style.display = "none";
|
||||
}
|
||||
slot.imageEl.style.display = "none";
|
||||
slot.fallbackEl.style.display = "none";
|
||||
});
|
||||
@@ -749,6 +753,9 @@
|
||||
if (!cardRequest) {
|
||||
slot.slotEl.style.display = "none";
|
||||
slot.imageEl.removeAttribute("src");
|
||||
if (slot.zoomLayerEl) {
|
||||
slot.zoomLayerEl.style.display = "none";
|
||||
}
|
||||
slot.imageEl.style.display = "none";
|
||||
slot.fallbackEl.style.display = "none";
|
||||
return;
|
||||
@@ -761,11 +768,17 @@
|
||||
if (cardRequest.src) {
|
||||
slot.imageEl.src = cardRequest.src;
|
||||
slot.imageEl.alt = cardRequest.altText || cardRequest.label || "Tarot compare image";
|
||||
if (slot.zoomLayerEl) {
|
||||
slot.zoomLayerEl.style.display = "flex";
|
||||
}
|
||||
slot.imageEl.style.display = "block";
|
||||
slot.fallbackEl.style.display = "none";
|
||||
} else {
|
||||
slot.imageEl.removeAttribute("src");
|
||||
slot.imageEl.alt = "";
|
||||
if (slot.zoomLayerEl) {
|
||||
slot.zoomLayerEl.style.display = "none";
|
||||
}
|
||||
slot.imageEl.style.display = "none";
|
||||
slot.fallbackEl.textContent = cardRequest.missingReason || "Card image unavailable for this deck.";
|
||||
slot.fallbackEl.style.display = "block";
|
||||
@@ -983,12 +996,13 @@
|
||||
applyZoomTransform();
|
||||
}
|
||||
|
||||
function updateZoomOrigin(clientX, clientY, targetImage = imageEl) {
|
||||
if (!zoomed || !targetImage) {
|
||||
function updateZoomOrigin(clientX, clientY, targetImage = imageEl, targetFrame = null) {
|
||||
const referenceEl = targetFrame || targetImage;
|
||||
if (!zoomed || !referenceEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = targetImage.getBoundingClientRect();
|
||||
const rect = referenceEl.getBoundingClientRect();
|
||||
if (!rect.width || !rect.height) {
|
||||
return;
|
||||
}
|
||||
@@ -1000,12 +1014,13 @@
|
||||
applyTransformOrigins();
|
||||
}
|
||||
|
||||
function isPointOnCard(clientX, clientY, targetImage = imageEl) {
|
||||
if (!targetImage) {
|
||||
function isPointOnCard(clientX, clientY, targetImage = imageEl, targetFrame = null) {
|
||||
const frameElForHitTest = targetFrame || targetImage;
|
||||
if (!targetImage || !frameElForHitTest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rect = targetImage.getBoundingClientRect();
|
||||
const rect = frameElForHitTest.getBoundingClientRect();
|
||||
const naturalWidth = targetImage.naturalWidth;
|
||||
const naturalHeight = targetImage.naturalHeight;
|
||||
|
||||
@@ -1350,13 +1365,23 @@
|
||||
mediaEl.style.background = "rgba(2, 6, 23, 0.4)";
|
||||
mediaEl.style.overflow = "hidden";
|
||||
|
||||
const zoomLayerEl = document.createElement("div");
|
||||
zoomLayerEl.style.position = "absolute";
|
||||
zoomLayerEl.style.inset = "16px";
|
||||
zoomLayerEl.style.display = "flex";
|
||||
zoomLayerEl.style.alignItems = "center";
|
||||
zoomLayerEl.style.justifyContent = "center";
|
||||
zoomLayerEl.style.transform = "scale(1)";
|
||||
zoomLayerEl.style.transformOrigin = "50% 50%";
|
||||
zoomLayerEl.style.transition = "transform 120ms ease-out";
|
||||
|
||||
const compareImageEl = document.createElement("img");
|
||||
compareImageEl.alt = "Tarot compare image";
|
||||
compareImageEl.style.width = "100%";
|
||||
compareImageEl.style.height = "100%";
|
||||
compareImageEl.style.objectFit = "contain";
|
||||
compareImageEl.style.cursor = "zoom-in";
|
||||
compareImageEl.style.transform = "scale(1) rotate(0deg)";
|
||||
compareImageEl.style.transform = "rotate(0deg)";
|
||||
compareImageEl.style.transformOrigin = "center center";
|
||||
compareImageEl.style.transition = "transform 120ms ease-out";
|
||||
compareImageEl.style.userSelect = "none";
|
||||
@@ -1369,13 +1394,16 @@
|
||||
fallbackEl.style.font = "600 13px/1.45 sans-serif";
|
||||
fallbackEl.style.color = "rgba(226, 232, 240, 0.88)";
|
||||
|
||||
mediaEl.append(compareImageEl, fallbackEl);
|
||||
zoomLayerEl.appendChild(compareImageEl);
|
||||
mediaEl.append(zoomLayerEl, fallbackEl);
|
||||
slotEl.append(headerEl, mediaEl);
|
||||
|
||||
return {
|
||||
slotEl,
|
||||
badgeEl,
|
||||
cardLabelEl,
|
||||
mediaEl,
|
||||
zoomLayerEl,
|
||||
imageEl: compareImageEl,
|
||||
fallbackEl
|
||||
};
|
||||
@@ -1716,7 +1744,7 @@
|
||||
compareGridSlots.forEach((slot) => {
|
||||
slot.imageEl.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
if (!isPointOnCard(event.clientX, event.clientY, slot.imageEl)) {
|
||||
if (!isPointOnCard(event.clientX, event.clientY, slot.imageEl, slot.mediaEl)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
@@ -1724,7 +1752,7 @@
|
||||
if (!zoomed) {
|
||||
zoomed = true;
|
||||
applyZoomTransform();
|
||||
updateZoomOrigin(event.clientX, event.clientY, slot.imageEl);
|
||||
updateZoomOrigin(event.clientX, event.clientY, slot.imageEl, slot.mediaEl);
|
||||
applyComparePresentation();
|
||||
return;
|
||||
}
|
||||
@@ -1734,7 +1762,7 @@
|
||||
});
|
||||
|
||||
slot.imageEl.addEventListener("mousemove", (event) => {
|
||||
updateZoomOrigin(event.clientX, event.clientY, slot.imageEl);
|
||||
updateZoomOrigin(event.clientX, event.clientY, slot.imageEl, slot.mediaEl);
|
||||
});
|
||||
|
||||
slot.imageEl.addEventListener("mouseleave", () => {
|
||||
|
||||
Reference in New Issue
Block a user