설정과 모바일 동선 정리
This commit is contained in:
@@ -166,7 +166,7 @@ const isGuideNextDisabled = computed(() => guideStepIndex.value >= guideSteps.le
|
||||
const isLightTheme = computed(() => themeMode.value === 'light')
|
||||
const themeToggleLabel = computed(() => (isLightTheme.value ? '다크 모드' : '라이트 모드'))
|
||||
const showSettingsThemePanel = computed(() => route.name === 'profile')
|
||||
const showTopicViewToggle = computed(() => ['home', 'templates', 'topicHub', 'me', 'favorites', 'followingFeed'].includes(String(route.name || '')))
|
||||
const showTopicViewToggle = computed(() => !isMobileLayout.value && ['home', 'templates', 'topicHub', 'me', 'favorites', 'followingFeed'].includes(String(route.name || '')))
|
||||
const topicViewMode = computed(() => (route.query.view === 'list' ? 'list' : 'grid'))
|
||||
const showBackendFallback = computed(() => !isPreviewMode.value && ['maintenance', 'offline'].includes(backendState.value))
|
||||
const shouldLockRightRailBodyScroll = computed(() => isRightRailOverlay.value && rightRailOpen.value && !showBackendFallback.value)
|
||||
@@ -692,9 +692,11 @@ function reloadApp() {
|
||||
</main>
|
||||
</template>
|
||||
<template v-else>
|
||||
<aside class="leftRail">
|
||||
<button v-if="isMobileLayout && mobileLeftNavOpen" class="leftRailBackdrop" type="button" aria-label="왼쪽 패널 닫기" @click="toggleLeftRail"></button>
|
||||
|
||||
<aside class="leftRail" :class="{ 'leftRail--overlay': isMobileLayout }" :aria-hidden="isMobileLayout && !mobileLeftNavOpen">
|
||||
<div class="leftRail__top railHeader">
|
||||
<button v-if="!isMobileLayout" class="ghostIcon ghostIcon--iconOnly" type="button" aria-label="왼쪽 패널 토글" @click="toggleLeftRail">
|
||||
<button class="ghostIcon ghostIcon--iconOnly" type="button" :aria-label="isMobileLayout ? '왼쪽 패널 닫기' : '왼쪽 패널 토글'" @click="toggleLeftRail">
|
||||
<SvgIcon :src="iconDockToRight" :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -709,16 +711,6 @@ function reloadApp() {
|
||||
<div class="appUserCard__name">{{ accountName }}</div>
|
||||
<div class="appUserCard__email" :class="{ 'appUserCard__email--hint': isAccountEmailHint }">{{ accountEmail }}</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="isMobileLayout"
|
||||
class="appUserCard__navToggle"
|
||||
type="button"
|
||||
:aria-label="mobileLeftNavOpen ? '네비게이션 메뉴 닫기' : '네비게이션 메뉴 열기'"
|
||||
:aria-expanded="mobileLeftNavOpen"
|
||||
@click="toggleLeftRail"
|
||||
>
|
||||
<SvgIcon :src="mobileLeftNavOpen ? iconDockToLeft : iconDockToRight" :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -794,9 +786,15 @@ function reloadApp() {
|
||||
<SvgIcon :src="iconLists" :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
<button v-if="isMobileLayout" class="ghostIcon ghostIcon--iconOnly" type="button" aria-label="왼쪽 패널 열기" @click="toggleLeftRail">
|
||||
<SvgIcon :src="iconDockToRight" :size="24" />
|
||||
</button>
|
||||
<button v-if="!rightRailOpen" class="ghostIcon ghostIcon--iconOnly" type="button" aria-label="패널 열기" @click="toggleRightRail">
|
||||
<SvgIcon :src="iconDockToLeft" :size="24" />
|
||||
</button>
|
||||
<button v-else-if="isMobileLayout" class="ghostIcon ghostIcon--iconOnly" type="button" aria-label="오른쪽 패널 닫기" @click="toggleRightRail">
|
||||
<SvgIcon :src="iconDockToLeft" :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="workspaceBody" :class="{ 'workspaceBody--localRail': usesLocalRightRail }">
|
||||
@@ -2151,10 +2149,10 @@ function reloadApp() {
|
||||
grid-template-columns: var(--left-rail-width, 248px) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.rightRailBackdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: block;
|
||||
.rightRailBackdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: block;
|
||||
border: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 29;
|
||||
@@ -2263,19 +2261,49 @@ function reloadApp() {
|
||||
}
|
||||
|
||||
.leftRail {
|
||||
min-height: auto;
|
||||
height: auto;
|
||||
min-height: 100dvh;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--theme-border);
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.leftRailBackdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: block;
|
||||
border: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 29;
|
||||
}
|
||||
|
||||
.leftRail--overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: min(340px, calc(100vw - 20px));
|
||||
height: 100dvh;
|
||||
z-index: 30;
|
||||
background: var(--theme-shell-bg);
|
||||
border-right: 1px solid var(--theme-border);
|
||||
box-shadow: 18px 0 36px rgba(0, 0, 0, 0.34);
|
||||
transition:
|
||||
transform 220ms ease,
|
||||
opacity 220ms ease;
|
||||
}
|
||||
|
||||
.appShell--mobileNavClosed .leftRail--overlay {
|
||||
transform: translateX(calc(-100% - 24px));
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leftRail__top {
|
||||
display: none;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.leftRail__body {
|
||||
max-height: none;
|
||||
padding: 12px 14px;
|
||||
padding: 12px 14px calc(18px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.appUserCard {
|
||||
@@ -2290,10 +2318,6 @@ function reloadApp() {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.appUserCard__navToggle {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.workspaceHead .ghostIcon--iconOnly,
|
||||
.rightRail__top .ghostIcon--iconOnly {
|
||||
width: 42px;
|
||||
@@ -2392,18 +2416,6 @@ function reloadApp() {
|
||||
margin: 14px 14px 0;
|
||||
}
|
||||
|
||||
.appShell--mobileNavClosed .leftRail__mobileMenu {
|
||||
max-height: 0;
|
||||
margin-top: -8px;
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.appShell--mobileNavClosed .leftRail__bottom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rightRail--overlay .rightRail__body {
|
||||
padding: 14px 20px calc(32px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ watch(() => route.query.q, loadHomeFeed)
|
||||
<div class="pageHead__main">
|
||||
<div class="pageHead__eyebrow">Feed</div>
|
||||
<h1 class="pageHead__title">홈</h1>
|
||||
<div class="pageHead__desc">사용자가 공개한 티어표를 최신순으로 살펴보고, 추천 티어표는 상단에서 바로 볼 수 있어요.</div>
|
||||
<div class="pageHead__desc">다른 사용자들이 공개한 티어표를 살펴볼 수 있습니다.</div>
|
||||
<div v-if="query" class="pageHead__searchState">"{{ query }}"에 맞는 공개 티어표만 보고 있어요.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -80,7 +80,7 @@ function openList(t) {
|
||||
<div class="pageHead__main">
|
||||
<div class="pageHead__eyebrow">Tier Lists</div>
|
||||
<h2 class="pageHead__title">나의 티어표</h2>
|
||||
<div class="pageHead__desc">직접 저장한 티어표를 같은 카드 레이아웃으로 다시 열고 정리할 수 있어요.</div>
|
||||
<div class="pageHead__desc">직접 저장한 티어표를 관리할 수 있는 페이지입니다.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ async function logout() {
|
||||
<div class="pageHead__main">
|
||||
<div class="pageHead__eyebrow">Account</div>
|
||||
<h2 class="pageHead__title">설정</h2>
|
||||
<div class="pageHead__desc">수시로 바꾸지 않는 정보는 요약해서 보여주고, 필요할 때만 모달로 열어 바꾸는 흐름으로 정리했어요.</div>
|
||||
<div class="pageHead__desc">닉네임은 변경은 쿨타임이 있으니 신중하게 설정해주세요.</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -368,12 +368,10 @@ async function logout() {
|
||||
</div>
|
||||
|
||||
<div class="settingsSummaryItem">
|
||||
<div class="settingsSummaryItem__label">이메일</div>
|
||||
<div class="settingsSummaryItem__label">ID (이메일)</div>
|
||||
<div class="settingsSummaryItem__valueRow">
|
||||
<div class="settingsSummaryItem__value">{{ authEmail }}</div>
|
||||
<span class="settingsSummaryItem__status">읽기 전용</span>
|
||||
</div>
|
||||
<div class="settingsSummaryItem__meta">현재 로그인에 사용하는 계정 이메일이며, 설정 화면에서는 변경할 수 없습니다.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -381,7 +379,7 @@ async function logout() {
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="settingsThemeCard">
|
||||
<article class="settingsThemeCard settingsThemeCard--compact">
|
||||
<div class="settingsThemeCard__eyebrow">Security</div>
|
||||
<div class="settingsThemeCard__title">보안 설정</div>
|
||||
<div class="settingsCompactRow">
|
||||
@@ -395,7 +393,7 @@ async function logout() {
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="settingsThemeCard">
|
||||
<article class="settingsThemeCard settingsThemeCard--compact">
|
||||
<div class="settingsThemeCard__eyebrow">Session</div>
|
||||
<div class="settingsThemeCard__title">계정 상태</div>
|
||||
<div class="settingsCompactList">
|
||||
@@ -537,6 +535,10 @@ async function logout() {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.settingsThemeCard--compact {
|
||||
grid-template-rows: auto auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.settingsThemeCard__eyebrow {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.12em;
|
||||
@@ -691,17 +693,6 @@ async function logout() {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.settingsSummaryItem__status {
|
||||
flex-shrink: 0;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--theme-border);
|
||||
background: var(--theme-pill-bg);
|
||||
color: var(--theme-text-soft);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.settingsActionRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -712,6 +703,7 @@ async function logout() {
|
||||
.settingsCompactList {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.settingsCompactRow {
|
||||
@@ -719,6 +711,7 @@ async function logout() {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
min-height: 100%;
|
||||
padding: 16px 18px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid var(--theme-border);
|
||||
|
||||
@@ -83,7 +83,7 @@ function templateThumbUrl(template) {
|
||||
<div class="pageHead__main">
|
||||
<div class="pageHead__eyebrow">Topic</div>
|
||||
<h1 class="pageHead__title">템플릿</h1>
|
||||
<p class="pageHead__desc">자주 쓰는 주제 템플릿을 빠르게 고르고, 필요하면 바로 커스텀 티어표를 시작할 수 있어요.</p>
|
||||
<p class="pageHead__desc">미리 설정된 템플릿을 이용하여 쉽고 빠르게 만들 수 있습니다.</p>
|
||||
<p v-if="query" class="pageHead__searchState">"{{ query }}"에 맞는 주제 템플릿만 보고 있어요.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -367,11 +367,6 @@ function closeItemContextMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
function scrollWorkspaceBodyToTop() {
|
||||
const workspaceBody = document.querySelector('.workspaceBody')
|
||||
workspaceBody?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
|
||||
function updateEditorSidebarMaxHeight() {
|
||||
if (typeof window === 'undefined' || !sidebarEl.value) return
|
||||
const bottomGap = 14
|
||||
@@ -1104,6 +1099,11 @@ function confirmNavigationDiscard() {
|
||||
router.push(nextPath)
|
||||
}
|
||||
|
||||
function openTemplateTopic() {
|
||||
if (!templateId.value) return
|
||||
requestEditorNavigation(topicPath(templateId.value))
|
||||
}
|
||||
|
||||
function openSourceTierList() {
|
||||
if (!sourceTierListId.value) return
|
||||
requestEditorNavigation(editorPath(templateId.value, sourceTierListId.value))
|
||||
@@ -1535,7 +1535,7 @@ onUnmounted(() => {
|
||||
|
||||
<div v-if="isNavigationConfirmModalOpen" class="modalOverlay" @click.self="closeNavigationConfirmModal">
|
||||
<div class="modalCard" role="dialog" aria-modal="true" aria-labelledby="navigationConfirmTitle">
|
||||
<div id="navigationConfirmTitle" class="modalCard__title">원본 티어표로 이동</div>
|
||||
<div id="navigationConfirmTitle" class="modalCard__title">다른 화면으로 이동</div>
|
||||
<div class="modalCard__desc">
|
||||
아직 저장하지 않은 수정 내용이 있어요. 이대로 이동하면 현재 변경 내용은 사라집니다.
|
||||
</div>
|
||||
@@ -1656,9 +1656,9 @@ onUnmounted(() => {
|
||||
<button
|
||||
class="editorMain__title editorMain__titleButton"
|
||||
type="button"
|
||||
title="본문을 화면 위로 이동"
|
||||
@click="scrollWorkspaceBodyToTop"
|
||||
@keydown.space.prevent="scrollWorkspaceBodyToTop"
|
||||
title="이 템플릿 화면으로 이동"
|
||||
@click="openTemplateTopic"
|
||||
@keydown.space.prevent="openTemplateTopic"
|
||||
>
|
||||
{{ templateName || templateId }}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user