fix(viewer): remove topbar settings menu, keep locale switcher only (user request)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+1
-124
@@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue';
|
||||
import { computed, watchEffect } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import LocaleSwitcher from './components/LocaleSwitcher.vue';
|
||||
import { reducedMotion, toggleReducedMotion } from './ui/prefs';
|
||||
// UI-02 V01 视觉 token:变量全部作用域在 .viewer-shell 类上(非 :root 全局),
|
||||
// embed 分支不挂该类 → 嵌入 iframe 不消费任何 token(隔离红线)。
|
||||
import './styles/viewer-tokens.css';
|
||||
@@ -20,34 +19,6 @@ watchEffect(() => {
|
||||
document.title = t('app.title');
|
||||
document.documentElement.setAttribute('lang', locale.value);
|
||||
});
|
||||
|
||||
// —— 顶栏轻量菜单(UI-02 §3.2):减少动画入口收进语言旁「⋯」,可发现但不占整行 ——
|
||||
const prefsOpen = ref(false);
|
||||
const prefsRootEl = ref<HTMLElement | null>(null);
|
||||
|
||||
function closePrefs(): void {
|
||||
prefsOpen.value = false;
|
||||
}
|
||||
|
||||
function onPrefsKeydown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape') closePrefs();
|
||||
}
|
||||
|
||||
function onOuterPointerDown(event: PointerEvent): void {
|
||||
if (prefsOpen.value && prefsRootEl.value !== null && !prefsRootEl.value.contains(event.target as Node)) {
|
||||
closePrefs();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('pointerdown', onOuterPointerDown, true);
|
||||
document.addEventListener('keydown', onPrefsKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('pointerdown', onOuterPointerDown, true);
|
||||
document.removeEventListener('keydown', onPrefsKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,34 +34,6 @@ onBeforeUnmount(() => {
|
||||
</span>
|
||||
<div class="topbar-actions">
|
||||
<LocaleSwitcher />
|
||||
<div ref="prefsRootEl" class="prefs-menu">
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn"
|
||||
:aria-label="t('viewer.settings')"
|
||||
aria-haspopup="menu"
|
||||
:aria-expanded="prefsOpen"
|
||||
@click="prefsOpen = !prefsOpen"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
<circle cx="5" cy="12" r="2" fill="currentColor" />
|
||||
<circle cx="12" cy="12" r="2" fill="currentColor" />
|
||||
<circle cx="19" cy="12" r="2" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
<div v-if="prefsOpen" class="prefs-popover" role="menu" :aria-label="t('viewer.settings')">
|
||||
<button
|
||||
type="button"
|
||||
class="prefs-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="reducedMotion"
|
||||
@click="toggleReducedMotion"
|
||||
>
|
||||
<span class="prefs-check" aria-hidden="true">{{ reducedMotion ? '✓' : '' }}</span>
|
||||
{{ t('viewer.reduceMotion') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main class="viewer-main">
|
||||
@@ -166,72 +109,6 @@ body {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.prefs-menu {
|
||||
position: relative;
|
||||
}
|
||||
.icon-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--viewer-touch-target);
|
||||
height: var(--viewer-touch-target);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--viewer-text-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-btn:hover {
|
||||
background: var(--viewer-page-bg);
|
||||
}
|
||||
.icon-btn:focus-visible {
|
||||
outline: 2px solid var(--viewer-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.icon-btn svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.prefs-popover {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
right: 0;
|
||||
z-index: 130;
|
||||
min-width: 176px;
|
||||
border: 1px solid var(--viewer-divider);
|
||||
border-radius: 12px;
|
||||
background: var(--viewer-surface);
|
||||
box-shadow: 0 8px 24px rgba(23, 32, 51, 0.12);
|
||||
padding: 4px;
|
||||
display: grid;
|
||||
}
|
||||
.prefs-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: var(--viewer-touch-target);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--viewer-text);
|
||||
padding: 0 12px;
|
||||
font-size: 0.875rem;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.prefs-item:hover {
|
||||
background: var(--viewer-page-bg);
|
||||
}
|
||||
.prefs-item:focus-visible {
|
||||
outline: 2px solid var(--viewer-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.prefs-check {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
color: var(--viewer-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
/* V01 §3.2:内容页不再有全局 16px 包围——宣传图/照片边到边,内边距由
|
||||
* 信息区(AlbumHeader)与照片区(PhotoFeed,Task 3)各自负责。 */
|
||||
.viewer-main {
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('viewer shell tokens and embed isolation (UI-02 V01)', () => {
|
||||
expect(css).toContain('.viewer-shell');
|
||||
});
|
||||
|
||||
it('normal routes render .viewer-shell with PhotoLive brand top bar and prefs menu', async () => {
|
||||
it('normal routes render .viewer-shell with PhotoLive brand top bar (locale only, no prefs menu)', async () => {
|
||||
await router.push('/');
|
||||
await router.isReady();
|
||||
const app = mountApp();
|
||||
@@ -86,17 +86,9 @@ describe('viewer shell tokens and embed isolation (UI-02 V01)', () => {
|
||||
expect(topbar.find('.brand-name').text()).toBe('PhotoLive');
|
||||
expect(topbar.find('.locale-trigger').exists()).toBe(true);
|
||||
|
||||
// 「⋯」轻量菜单(§3.2):减少动画入口可发现、可切换。
|
||||
const more = topbar.find('.icon-btn');
|
||||
expect(more.attributes('aria-haspopup')).toBe('menu');
|
||||
await more.trigger('click');
|
||||
const item = app.find('.prefs-item');
|
||||
expect(item.text()).toBe('减少动画');
|
||||
const before = item.attributes('aria-checked');
|
||||
await item.trigger('click');
|
||||
expect(app.find('.prefs-item').attributes('aria-checked')).toBe(before === 'true' ? 'false' : 'true');
|
||||
// 还原模块级偏好单例,避免影响后续用例。
|
||||
await app.find('.prefs-item').trigger('click');
|
||||
// 设置菜单已按用户要求移除(2026-09-23):顶栏仅保留语言切换。
|
||||
expect(topbar.find('.prefs-menu').exists()).toBe(false);
|
||||
expect(topbar.find('.icon-btn').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('embed routes keep the bare shell: no .viewer-shell, no top bar, no outer padding wrapper', async () => {
|
||||
|
||||
Reference in New Issue
Block a user