refactoring
This commit is contained in:
227
app/ui-tarot-house.js
Normal file
227
app/ui-tarot-house.js
Normal file
@@ -0,0 +1,227 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const HOUSE_MINOR_NUMBER_BANDS = [
|
||||
[2, 3, 4],
|
||||
[5, 6, 7],
|
||||
[8, 9, 10],
|
||||
[2, 3, 4],
|
||||
[5, 6, 7],
|
||||
[8, 9, 10]
|
||||
];
|
||||
const HOUSE_LEFT_SUITS = ["Wands", "Disks", "Swords", "Cups", "Wands", "Disks"];
|
||||
const HOUSE_RIGHT_SUITS = ["Swords", "Cups", "Wands", "Disks", "Swords", "Cups"];
|
||||
const HOUSE_MIDDLE_SUITS = ["Wands", "Cups", "Swords", "Disks"];
|
||||
const HOUSE_MIDDLE_RANKS = ["Ace", "Knight", "Queen", "Prince", "Princess"];
|
||||
const HOUSE_TRUMP_ROWS = [
|
||||
[0],
|
||||
[20, 21, 12],
|
||||
[19, 10, 2, 1, 3, 16],
|
||||
[18, 17, 15, 14, 13, 9, 8, 7, 6, 5, 4],
|
||||
[11]
|
||||
];
|
||||
|
||||
const config = {
|
||||
resolveTarotCardImage: null,
|
||||
getDisplayCardName: (card) => card?.name || "",
|
||||
clearChildren: () => {},
|
||||
normalizeTarotCardLookupName: (value) => String(value || "").trim().toLowerCase(),
|
||||
selectCardById: () => {},
|
||||
getCards: () => [],
|
||||
getSelectedCardId: () => ""
|
||||
};
|
||||
|
||||
function init(nextConfig = {}) {
|
||||
Object.assign(config, nextConfig || {});
|
||||
}
|
||||
|
||||
function getCardLookupMap(cards) {
|
||||
const lookup = new Map();
|
||||
(Array.isArray(cards) ? cards : []).forEach((card) => {
|
||||
const key = config.normalizeTarotCardLookupName(card?.name);
|
||||
if (key) {
|
||||
lookup.set(key, card);
|
||||
}
|
||||
});
|
||||
return lookup;
|
||||
}
|
||||
|
||||
function buildMinorCardName(rankNumber, suit) {
|
||||
const number = Number(rankNumber);
|
||||
const suitName = String(suit || "").trim();
|
||||
const rankName = ({ 1: "Ace", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten" })[number];
|
||||
if (!rankName || !suitName) {
|
||||
return "";
|
||||
}
|
||||
return `${rankName} of ${suitName}`;
|
||||
}
|
||||
|
||||
function buildCourtCardName(rank, suit) {
|
||||
const rankName = String(rank || "").trim();
|
||||
const suitName = String(suit || "").trim();
|
||||
if (!rankName || !suitName) {
|
||||
return "";
|
||||
}
|
||||
return `${rankName} of ${suitName}`;
|
||||
}
|
||||
|
||||
function findCardByLookupName(cardLookupMap, cardName) {
|
||||
const key = config.normalizeTarotCardLookupName(cardName);
|
||||
if (!key) {
|
||||
return null;
|
||||
}
|
||||
return cardLookupMap.get(key) || null;
|
||||
}
|
||||
|
||||
function findMajorCardByTrumpNumber(cards, trumpNumber) {
|
||||
const target = Number(trumpNumber);
|
||||
if (!Number.isFinite(target)) {
|
||||
return null;
|
||||
}
|
||||
return (Array.isArray(cards) ? cards : []).find((card) => card?.arcana === "Major" && Number(card?.number) === target) || null;
|
||||
}
|
||||
|
||||
function createHouseCardButton(card, elements) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "tarot-house-card-btn";
|
||||
|
||||
if (!card) {
|
||||
button.disabled = true;
|
||||
const fallback = document.createElement("span");
|
||||
fallback.className = "tarot-house-card-fallback";
|
||||
fallback.textContent = "Missing";
|
||||
button.appendChild(fallback);
|
||||
return button;
|
||||
}
|
||||
|
||||
const cardDisplayName = config.getDisplayCardName(card);
|
||||
button.title = cardDisplayName || card.name;
|
||||
button.setAttribute("aria-label", cardDisplayName || card.name);
|
||||
button.dataset.houseCardId = card.id;
|
||||
const imageUrl = typeof config.resolveTarotCardImage === "function"
|
||||
? config.resolveTarotCardImage(card.name)
|
||||
: null;
|
||||
|
||||
if (imageUrl) {
|
||||
const image = document.createElement("img");
|
||||
image.className = "tarot-house-card-image";
|
||||
image.src = imageUrl;
|
||||
image.alt = cardDisplayName || card.name;
|
||||
button.appendChild(image);
|
||||
} else {
|
||||
const fallback = document.createElement("span");
|
||||
fallback.className = "tarot-house-card-fallback";
|
||||
fallback.textContent = cardDisplayName || card.name;
|
||||
button.appendChild(fallback);
|
||||
}
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
config.selectCardById(card.id, elements);
|
||||
elements?.tarotCardListEl
|
||||
?.querySelector(`[data-card-id="${card.id}"]`)
|
||||
?.scrollIntoView({ block: "nearest" });
|
||||
});
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function updateSelection(elements) {
|
||||
if (!elements?.tarotHouseOfCardsEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedCardId = config.getSelectedCardId();
|
||||
const buttons = elements.tarotHouseOfCardsEl.querySelectorAll(".tarot-house-card-btn[data-house-card-id]");
|
||||
buttons.forEach((button) => {
|
||||
const isSelected = button.dataset.houseCardId === selectedCardId;
|
||||
button.classList.toggle("is-selected", isSelected);
|
||||
button.setAttribute("aria-current", isSelected ? "true" : "false");
|
||||
});
|
||||
}
|
||||
|
||||
function appendHouseMinorRow(columnEl, cardLookupMap, numbers, suit, elements) {
|
||||
const rowEl = document.createElement("div");
|
||||
rowEl.className = "tarot-house-row";
|
||||
|
||||
numbers.forEach((rankNumber) => {
|
||||
const cardName = buildMinorCardName(rankNumber, suit);
|
||||
const card = findCardByLookupName(cardLookupMap, cardName);
|
||||
rowEl.appendChild(createHouseCardButton(card, elements));
|
||||
});
|
||||
|
||||
columnEl.appendChild(rowEl);
|
||||
}
|
||||
|
||||
function appendHouseCourtRow(columnEl, cardLookupMap, rank, elements) {
|
||||
const rowEl = document.createElement("div");
|
||||
rowEl.className = "tarot-house-row";
|
||||
|
||||
HOUSE_MIDDLE_SUITS.forEach((suit) => {
|
||||
const cardName = buildCourtCardName(rank, suit);
|
||||
const card = findCardByLookupName(cardLookupMap, cardName);
|
||||
rowEl.appendChild(createHouseCardButton(card, elements));
|
||||
});
|
||||
|
||||
columnEl.appendChild(rowEl);
|
||||
}
|
||||
|
||||
function appendHouseTrumpRow(containerEl, trumpNumbers, elements, cards) {
|
||||
const rowEl = document.createElement("div");
|
||||
rowEl.className = "tarot-house-trump-row";
|
||||
|
||||
(trumpNumbers || []).forEach((trumpNumber) => {
|
||||
const card = findMajorCardByTrumpNumber(cards, trumpNumber);
|
||||
rowEl.appendChild(createHouseCardButton(card, elements));
|
||||
});
|
||||
|
||||
containerEl.appendChild(rowEl);
|
||||
}
|
||||
|
||||
function render(elements) {
|
||||
if (!elements?.tarotHouseOfCardsEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cards = config.getCards();
|
||||
config.clearChildren(elements.tarotHouseOfCardsEl);
|
||||
const cardLookupMap = getCardLookupMap(cards);
|
||||
|
||||
const trumpSectionEl = document.createElement("div");
|
||||
trumpSectionEl.className = "tarot-house-trumps";
|
||||
HOUSE_TRUMP_ROWS.forEach((trumpRow) => {
|
||||
appendHouseTrumpRow(trumpSectionEl, trumpRow, elements, cards);
|
||||
});
|
||||
|
||||
const bottomGridEl = document.createElement("div");
|
||||
bottomGridEl.className = "tarot-house-bottom-grid";
|
||||
|
||||
const leftColumnEl = document.createElement("div");
|
||||
leftColumnEl.className = "tarot-house-column";
|
||||
HOUSE_MINOR_NUMBER_BANDS.forEach((numbers, rowIndex) => {
|
||||
appendHouseMinorRow(leftColumnEl, cardLookupMap, numbers, HOUSE_LEFT_SUITS[rowIndex], elements);
|
||||
});
|
||||
|
||||
const middleColumnEl = document.createElement("div");
|
||||
middleColumnEl.className = "tarot-house-column";
|
||||
HOUSE_MIDDLE_RANKS.forEach((rank) => {
|
||||
appendHouseCourtRow(middleColumnEl, cardLookupMap, rank, elements);
|
||||
});
|
||||
|
||||
const rightColumnEl = document.createElement("div");
|
||||
rightColumnEl.className = "tarot-house-column";
|
||||
HOUSE_MINOR_NUMBER_BANDS.forEach((numbers, rowIndex) => {
|
||||
appendHouseMinorRow(rightColumnEl, cardLookupMap, numbers, HOUSE_RIGHT_SUITS[rowIndex], elements);
|
||||
});
|
||||
|
||||
bottomGridEl.append(leftColumnEl, middleColumnEl, rightColumnEl);
|
||||
elements.tarotHouseOfCardsEl.append(trumpSectionEl, bottomGridEl);
|
||||
updateSelection(elements);
|
||||
}
|
||||
|
||||
window.TarotHouseUi = {
|
||||
init,
|
||||
render,
|
||||
updateSelection
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user