fix: improve mobile navigation and calculator usability
Publish GitHub Pages output / publish (push) Successful in 32s
Publish GitHub Pages output / publish (push) Successful in 32s
This commit is contained in:
1 parent
3b1c1aeb6f
commit
0bdade44e3
11 files changed
+205
-22
No files matched your search
@@ -317,6 +317,43 @@
|
||||
boardViewport.scrollTop = contentY * boardZoom - pointerY;
|
||||
}
|
||||
|
||||
async function changeBoardZoom(factor: number) {
|
||||
if (!boardViewport) return;
|
||||
const centerX = boardViewport.clientWidth / 2;
|
||||
const centerY = boardViewport.clientHeight / 2;
|
||||
const contentX = (boardViewport.scrollLeft + centerX) / boardZoom;
|
||||
const contentY = (boardViewport.scrollTop + centerY) / boardZoom;
|
||||
boardZoom = Math.max(0.4, Math.min(1.8, boardZoom * factor));
|
||||
await tick();
|
||||
boardViewport.scrollLeft = contentX * boardZoom - centerX;
|
||||
boardViewport.scrollTop = contentY * boardZoom - centerY;
|
||||
}
|
||||
|
||||
function manageCardDialog(node: HTMLElement) {
|
||||
const previousFocus = document.activeElement;
|
||||
const previousOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = 'hidden';
|
||||
node.focus();
|
||||
function keydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') { event.preventDefault(); selectedSocketGroupId = ''; }
|
||||
if (event.key !== 'Tab') return;
|
||||
const controls = Array.from(node.querySelectorAll<HTMLElement>('button:not(:disabled), input:not(:disabled), [tabindex="0"]'));
|
||||
const first = controls[0];
|
||||
const last = controls.at(-1);
|
||||
if (event.shiftKey && (document.activeElement === first || document.activeElement === node)) {
|
||||
event.preventDefault(); last?.focus();
|
||||
} else if (!event.shiftKey && document.activeElement === last) {
|
||||
event.preventDefault(); first?.focus();
|
||||
}
|
||||
}
|
||||
node.addEventListener('keydown', keydown);
|
||||
return { destroy() {
|
||||
node.removeEventListener('keydown', keydown);
|
||||
document.body.style.overflow = previousOverflow;
|
||||
if (previousFocus instanceof HTMLElement) previousFocus.focus({ preventScroll: true });
|
||||
} };
|
||||
}
|
||||
|
||||
function startBoardDrag(event: PointerEvent) {
|
||||
if (!boardViewport || event.button !== 0 || (event.target as HTMLElement).closest('.board-node')) return;
|
||||
boardDragging = true;
|
||||
@@ -1378,6 +1415,13 @@
|
||||
<p><strong>{selectedActiveBoardNodeCount}</strong> / {selectedBoardNodeCount}개 활성화</p>
|
||||
<div><button type="button" on:click={activateAllBlueNodes}>모든 파란 노드 활성화</button><button type="button" on:click={() => setAllBoardNodes(true)}>모두 활성화</button><button type="button" on:click={() => setAllBoardNodes(false)}>모두 해제</button></div>
|
||||
</div>
|
||||
<div class="board-zoom" aria-label="보드 확대 및 축소">
|
||||
<button type="button" aria-label="보드 축소" disabled={boardZoom <= .4} on:click={() => changeBoardZoom(1 / 1.25)}>−</button>
|
||||
<span>{Math.round(boardZoom * 100)}%</span>
|
||||
<button type="button" aria-label="보드 확대" disabled={boardZoom >= 1.8} on:click={() => changeBoardZoom(1.25)}>+</button>
|
||||
<button type="button" on:click={fitBoardViewport}>화면 맞춤</button>
|
||||
</div>
|
||||
<p class="board-note">보드의 빈 곳을 드래그하면 이동합니다. +·− 버튼으로 확대·축소하고, 보드 바깥을 쓸어 페이지를 이동하세요.</p>
|
||||
<div
|
||||
class="board-scroll"
|
||||
class:dragging={boardDragging}
|
||||
@@ -1451,7 +1495,7 @@
|
||||
|
||||
{#if selectedSocketGroupId}
|
||||
<div class="card-picker-backdrop" role="presentation" on:click={(event) => { if (event.target === event.currentTarget) selectedSocketGroupId = ''; }}>
|
||||
<div class="card-picker" role="dialog" aria-modal="true" aria-labelledby="card-picker-title" tabindex="-1">
|
||||
<div class="card-picker" use:manageCardDialog role="dialog" aria-modal="true" aria-labelledby="card-picker-title" tabindex="-1">
|
||||
<div class="card-picker-heading">
|
||||
<div><p class="section-label">연결 노드</p><h3 id="card-picker-title">배치할 카드 선택</h3></div>
|
||||
<button type="button" aria-label="닫기" on:click={() => selectedSocketGroupId = ''}>×</button>
|
||||
@@ -1753,7 +1797,7 @@
|
||||
.board-legend i { width: 9px; height: 9px; border-radius: 50%; }
|
||||
.board-note { margin: 10px 0 0; color: #7f8999; font-size: 13px; line-height: 1.5; }
|
||||
.card-picker-backdrop { position: fixed; inset: 0; z-index: 30; display: grid; place-items: center; padding: 20px; background: rgb(5 8 12 / 72%); }
|
||||
.card-picker { width: min(620px, 100%); max-height: min(760px, calc(100vh - 40px)); overflow: hidden; border: 1px solid #3b4453; border-radius: 16px; background: #171c24; box-shadow: 0 20px 70px rgb(0 0 0 / 45%); }
|
||||
.card-picker { display: flex; flex-direction: column; width: min(620px, 100%); max-height: min(760px, calc(100dvh - 40px)); overflow: hidden; border: 1px solid #3b4453; border-radius: 16px; background: #171c24; box-shadow: 0 20px 70px rgb(0 0 0 / 45%); }
|
||||
.card-picker-heading { display: flex; align-items: center; justify-content: space-between; padding: 20px 20px 10px; }
|
||||
.card-picker-heading h3 { margin: 2px 0 0; color: #edf0f5; font-size: 20px; }
|
||||
.card-picker-heading button { width: 34px; height: 34px; border: 1px solid #3b4453; border-radius: 9px; color: #c9d0da; background: #242b36; font-size: 22px; cursor: pointer; }
|
||||
@@ -1763,7 +1807,7 @@
|
||||
.current-placement span { color: #d990af; font-size: 12px; font-weight: 850; }
|
||||
.current-placement strong { overflow: hidden; color: #dce1e9; font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.current-placement button { padding: 5px 8px; border: 1px solid #65404f; border-radius: 6px; color: #f0a8c5; background: transparent; font-size: 12px; cursor: pointer; }
|
||||
.connect-card-list { display: grid; gap: 6px; max-height: 520px; overflow-y: auto; padding: 0 20px 20px; scrollbar-width: thin; }
|
||||
.connect-card-list { min-height: 0; display: grid; gap: 6px; max-height: 520px; overflow-y: auto; padding: 0 20px 20px; scrollbar-width: thin; }
|
||||
.connect-card-list > button { display: grid; grid-template-columns: 38px minmax(0, 1fr) 54px auto; align-items: center; gap: 11px; min-height: 66px; padding: 8px 10px; border: 1px solid #303845; border-radius: 10px; color: inherit; background: #12171e; text-align: left; cursor: pointer; }
|
||||
.connect-card-list > button:hover, .connect-card-list > button.selected { border-color: #a95e7f; background: #211a23; }
|
||||
.connect-card-rarity { color: #f1a5c5; font-size: 13px; font-weight: 900; }
|
||||
@@ -1791,7 +1835,7 @@
|
||||
aside { margin-top: 16px; padding: 18px 20px; border: 1px solid #292f3b; border-radius: 14px; color: #929cad; background: #151920; font-size: 13px; line-height: 1.6; }
|
||||
aside strong { color: #cbd1db; }
|
||||
aside p { margin: 5px 0 0; }
|
||||
@media (max-width: 700px) {
|
||||
@media (max-width: 900px) {
|
||||
main { width: min(100% - 24px, 900px); padding-top: var(--page-content-top); }
|
||||
.summary { grid-template-columns: repeat(2, 1fr); }
|
||||
.settings-grid { grid-template-columns: 1fr; }
|
||||
@@ -1822,11 +1866,31 @@
|
||||
.efficiency-table-head, .efficiency-list > button { grid-template-columns: 28px 210px 100px 90px 110px 115px; min-width: 710px; }
|
||||
.board-scroll { height: min(58vh, 560px); }
|
||||
.card-picker-backdrop { align-items: end; padding: 0; }
|
||||
.card-picker { max-height: 88vh; border-radius: 16px 16px 0 0; }
|
||||
.connect-card-list { max-height: 56vh; }
|
||||
.card-picker { max-height: 88dvh; padding-bottom: env(safe-area-inset-bottom); border-radius: 16px 16px 0 0; }
|
||||
.connect-card-list { max-height: none; overflow-y: auto; }
|
||||
.connect-card-list > button { grid-template-columns: 28px minmax(0, 1fr) 48px auto; gap: 7px; padding-inline: 8px; }
|
||||
.connect-range-preview { width: 44px; height: 44px; }
|
||||
.actions { display: grid; }
|
||||
.actions .danger { margin: 10px 0 0; }
|
||||
}
|
||||
|
||||
.card-picker-heading, .card-picker-note, .card-picker-search, .current-placement { flex-shrink: 0; }
|
||||
.connect-card-list { grid-auto-rows: max-content; align-content: start; }
|
||||
.board-zoom { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
||||
.board-zoom button { min-width: 44px; min-height: 44px; padding: 8px 12px; border: 1px solid #465064; border-radius: 8px; color: #d5dbe4; background: #202733; cursor: pointer; }
|
||||
.board-zoom button:disabled { opacity: .45; cursor: default; }
|
||||
.board-zoom span { min-width: 42px; text-align: center; }
|
||||
.board-zoom + .board-note { margin-bottom: 10px; }
|
||||
@media (max-width: 900px) {
|
||||
.board-toolbar, .board-toolbar div { flex-wrap: wrap; }
|
||||
.board-toolbar button, .card-picker-heading button { min-height: 44px; }
|
||||
.card-picker-heading button { min-width: 44px; }
|
||||
.card-table .table-row { grid-template-columns: 34px minmax(0, 1fr); }
|
||||
.awakening-field { grid-column: 2; }
|
||||
.awakening-stepper { height: 46px; grid-template-columns: 44px minmax(0, 1fr) 44px; }
|
||||
.identity { flex-wrap: wrap; }
|
||||
.identity strong, .max-awakening-check > strong, .connect-card-name strong, .current-placement strong { white-space: normal; overflow-wrap: anywhere; }
|
||||
.connect-card-list > button { grid-template-columns: 28px minmax(0, 1fr) 48px; }
|
||||
.connect-card-effect { grid-column: 2 / -1; justify-items: start; }
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user