v4.0.0: 덱 UX·i18n·썸네일 저장 개선 및 413 수정
즐겨찾기·홀드 미리보기·페이지 도움말·3개국어 번역을 추가하고, 덱 저장 시 썸네일을 1200px로 통일하며 Nginx/API 업로드 제한을 상향했다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
118
cardList.html
118
cardList.html
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sticky top-0 z-50 h-16 bg-white/80 backdrop-blur-md border-b px-4 py-3 flex justify-between items-center">
|
||||
<button type="button" onclick="goToHome()" class="font-bold text-slate-600 hover:text-blue-600 cursor-pointer">← 홈</button>
|
||||
<button type="button" id="i18n-home" onclick="goToHome()" class="font-bold text-slate-600 hover:text-blue-600 cursor-pointer">← 홈</button>
|
||||
<div class="font-bold text-slate-800">
|
||||
<span id="i18n-searchResults">검색결과</span>
|
||||
:
|
||||
@@ -478,6 +478,7 @@
|
||||
};
|
||||
|
||||
safeSet('i18n-back', 'back');
|
||||
safeSet('i18n-home', 'homeNav');
|
||||
safeSet('i18n-searchResults', 'searchResults');
|
||||
safeSet('i18n-resetBtn', 'resetSelection');
|
||||
safeSet('i18n-labelSearch', 'search');
|
||||
@@ -505,58 +506,89 @@
|
||||
window.location.href = lang && lang !== 'ko' ? `/?lang=${lang}` : '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* 동일 카드 번호 그룹 합계
|
||||
* @param {string} cardNumber
|
||||
* @returns {number}
|
||||
*/
|
||||
function getNumberGroupTotal(cardNumber) {
|
||||
return cardList
|
||||
.filter((c) => c.Number === cardNumber)
|
||||
.reduce((sum, c) => sum + c.count, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동일 카드 번호 그룹 최대 수량
|
||||
* @param {string} cardNumber
|
||||
* @returns {number}
|
||||
*/
|
||||
function getGroupMaxCount(cardNumber) {
|
||||
const cards = cardList.filter((c) => c.Number === cardNumber);
|
||||
if (!cards.length) return 4;
|
||||
return Math.max(...cards.map((c) => c.maxCount || 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 컨테이너 조회
|
||||
* @param {string} imgSrc
|
||||
* @returns {HTMLElement|null}
|
||||
*/
|
||||
function getCardContainer(imgSrc) {
|
||||
return document.querySelector(`.cardContainer[data-imgsrc="${CSS.escape(imgSrc)}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동일 번호 카드들의 +/- UI 동기화
|
||||
* @param {string} cardNumber
|
||||
*/
|
||||
function updateCountControlsForNumber(cardNumber) {
|
||||
const groupTotal = getNumberGroupTotal(cardNumber);
|
||||
const groupAtMax = groupTotal >= getGroupMaxCount(cardNumber);
|
||||
|
||||
cardList
|
||||
.filter((c) => c.Number === cardNumber)
|
||||
.forEach((sibling) => {
|
||||
const container = getCardContainer(sibling.imgSrc);
|
||||
if (!container) return;
|
||||
|
||||
const minusBtn = container.querySelector('.minusBtn');
|
||||
const plusBtn = container.querySelector('.plusBtn');
|
||||
const countSpan = container.querySelector('.countText');
|
||||
const keyBtn = container.querySelector('.keyBtn');
|
||||
|
||||
if (countSpan) countSpan.textContent = sibling.count;
|
||||
|
||||
if (minusBtn) {
|
||||
minusBtn.classList.toggle('invisible', sibling.count === 0);
|
||||
minusBtn.classList.toggle('visible', sibling.count > 0);
|
||||
}
|
||||
|
||||
if (plusBtn) {
|
||||
plusBtn.classList.toggle('invisible', groupAtMax);
|
||||
plusBtn.classList.toggle('visible', !groupAtMax);
|
||||
}
|
||||
|
||||
if (keyBtn) {
|
||||
keyBtn.className = `keyBtn w-full py-1 rounded text-[9px] lg:text-[10px] font-bold border transition-all ${sibling.key && sibling.count > 0 ? 'bg-indigo-600 border-indigo-600 text-white shadow-md' : 'bg-white border-slate-200 text-slate-400'}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.handleCount = function (imgSrc, delta, event) {
|
||||
event.stopPropagation();
|
||||
const card = cardList.find((c) => c.imgSrc === imgSrc);
|
||||
if (!card) return;
|
||||
|
||||
// 1. 데이터 업데이트
|
||||
const currentTotal = cardList.filter((c) => c.Number === card.Number).reduce((s, c) => s + c.count, 0);
|
||||
if (delta > 0 && currentTotal < (card.maxCount || 4)) {
|
||||
const maxCount = getGroupMaxCount(card.Number);
|
||||
const currentTotal = getNumberGroupTotal(card.Number);
|
||||
if (delta > 0 && currentTotal < maxCount) {
|
||||
card.count++;
|
||||
} else if (delta < 0 && card.count > 0) {
|
||||
card.count--;
|
||||
if (card.count === 0) card.key = false;
|
||||
}
|
||||
|
||||
// 2. UI 업데이트
|
||||
const container = document.querySelector(`.cardContainer[data-imgsrc="${imgSrc}"]`);
|
||||
if (container) {
|
||||
const minusBtn = container.querySelector('.minusBtn');
|
||||
const plusBtn = container.querySelector('.plusBtn');
|
||||
const countSpan = container.querySelector('.countText');
|
||||
const keyBtn = container.querySelector('.keyBtn'); // 클래스로 직접 참조
|
||||
|
||||
if (countSpan) countSpan.textContent = card.count;
|
||||
|
||||
// 마이너스 버튼 제어
|
||||
if (minusBtn) {
|
||||
if (card.count > 0) {
|
||||
minusBtn.classList.remove('invisible');
|
||||
minusBtn.classList.add('visible');
|
||||
} else {
|
||||
minusBtn.classList.remove('visible');
|
||||
minusBtn.classList.add('invisible');
|
||||
}
|
||||
}
|
||||
|
||||
// 플러스 버튼 제어 (isMax 결과에 따라 즉시 반영)
|
||||
if (plusBtn) {
|
||||
if (isMax(card)) {
|
||||
plusBtn.classList.remove('visible');
|
||||
plusBtn.classList.add('invisible');
|
||||
} else {
|
||||
plusBtn.classList.remove('invisible');
|
||||
plusBtn.classList.add('visible');
|
||||
}
|
||||
}
|
||||
|
||||
// 키 버튼 상태 업데이트
|
||||
if (keyBtn) {
|
||||
keyBtn.className = `keyBtn w-full py-1 rounded text-[9px] lg:text-[10px] font-bold border transition-all ${card.key && card.count > 0 ? 'bg-indigo-600 border-indigo-600 text-white shadow-md' : 'bg-white border-slate-200 text-slate-400'}`;
|
||||
}
|
||||
}
|
||||
|
||||
updateCountControlsForNumber(card.Number);
|
||||
syncToStorageAndCounters();
|
||||
};
|
||||
|
||||
@@ -585,7 +617,7 @@
|
||||
};
|
||||
|
||||
function isMax(card) {
|
||||
return cardList.filter((c) => c.Number === card.Number).reduce((s, c) => s + c.count, 0) >= (card.maxCount || 4);
|
||||
return getNumberGroupTotal(card.Number) >= getGroupMaxCount(card.Number);
|
||||
}
|
||||
|
||||
function updateCardList() {
|
||||
|
||||
Reference in New Issue
Block a user