헤더: 코드로 참여 입력칸 빼고 테마 버튼(🌙/☀️) 추가 — 누르면 밝게↔어둡게
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,28 +1,12 @@
|
||||
/** PC top menu with an always-available room-code box (docs/07-ui-ux.md §3.2). */
|
||||
/** PC top menu (docs/07-ui-ux.md §3.2): 로고·메뉴, 오른쪽에 테마 버튼과 로그인/계정 메뉴. */
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Link, NavLink, useNavigate } from 'react-router';
|
||||
import { normalizeRoomCode } from '@bg/shared/lite';
|
||||
import { api } from '../net/api';
|
||||
import { useSession } from '../store/session';
|
||||
import { toast } from '../store/toast';
|
||||
import { Avatar, ConfirmSheet } from './ui';
|
||||
|
||||
export function SiteHeader() {
|
||||
const { me, siteName, role } = useSession();
|
||||
const [code, setCode] = useState('');
|
||||
const nav = useNavigate();
|
||||
const join = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const c = normalizeRoomCode(code);
|
||||
if (!c) return toast('방 코드 6자리를 넣어 주세요.', 'error');
|
||||
try {
|
||||
await api.room(c);
|
||||
setCode('');
|
||||
nav(`/r/${c}`);
|
||||
} catch (ex) {
|
||||
toast((ex as Error).message, 'error');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<header className="site-header">
|
||||
<div className="site-header-inner">
|
||||
@@ -37,10 +21,7 @@ export function SiteHeader() {
|
||||
<NavLink to="/games">게임 목록</NavLink>
|
||||
{role !== 'user' && <NavLink to="/admin">관리자</NavLink>}
|
||||
</nav>
|
||||
<form className="header-join" onSubmit={join} aria-label="코드로 참여">
|
||||
<input value={code} onChange={(e) => setCode(e.target.value)} placeholder="방 코드 6자리" inputMode="numeric" maxLength={40} aria-label="방 코드" />
|
||||
<button className="btn btn-primary btn-sm">참여</button>
|
||||
</form>
|
||||
<ThemeToggle />
|
||||
{me ? (
|
||||
<AccountMenu />
|
||||
) : (
|
||||
@@ -53,6 +34,28 @@ export function SiteHeader() {
|
||||
);
|
||||
}
|
||||
|
||||
const darkQuery = () => matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
/** 누를 때마다 밝게 ↔ 어둡게. '자동'이면 지금 보이는 색의 반대로 바꿔요. 설정 페이지의 '화면 색'과 같은 값이에요. */
|
||||
function ThemeToggle() {
|
||||
const theme = useSession((s) => s.settings.theme);
|
||||
const updateSettings = useSession((s) => s.updateSettings);
|
||||
const [systemDark, setSystemDark] = useState(() => darkQuery().matches);
|
||||
useEffect(() => {
|
||||
const q = darkQuery();
|
||||
const on = () => setSystemDark(q.matches);
|
||||
q.addEventListener('change', on);
|
||||
return () => q.removeEventListener('change', on);
|
||||
}, []);
|
||||
const dark = theme === 'dark' || (theme === 'auto' && systemDark);
|
||||
const label = dark ? '밝은 화면으로 바꾸기' : '어두운 화면으로 바꾸기';
|
||||
return (
|
||||
<button type="button" className="theme-toggle" aria-label={label} title={label} onClick={() => updateSettings({ theme: dark ? 'light' : 'dark' })}>
|
||||
<span aria-hidden>{dark ? '☀️' : '🌙'}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
/** 로그인한 뒤: 닉네임(디스코드 연결 시 디스코드 이름) → 누르면 [설정] [로그아웃] 메뉴. */
|
||||
function AccountMenu() {
|
||||
const me = useSession((s) => s.me)!;
|
||||
|
||||
@@ -589,29 +589,23 @@ label.field {
|
||||
color: var(--primary);
|
||||
background: var(--primary-soft);
|
||||
}
|
||||
.header-join {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
.theme-toggle {
|
||||
margin-left: auto;
|
||||
}
|
||||
.header-join input {
|
||||
width: 150px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 10px;
|
||||
flex: none;
|
||||
border: 2px solid var(--border);
|
||||
background: var(--bg);
|
||||
padding: 0 12px;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.header-join input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
.header-join .btn {
|
||||
min-height: 42px;
|
||||
.theme-toggle:hover {
|
||||
background: var(--surface-2);
|
||||
}
|
||||
.header-user {
|
||||
display: flex;
|
||||
@@ -693,9 +687,6 @@ button.header-user {
|
||||
padding: 0 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
.header-join {
|
||||
display: none;
|
||||
}
|
||||
.site-logo .full {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -53,11 +53,11 @@
|
||||
### 3.2 공통 상단 메뉴 (PC)
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ ● 같이 놀자 보드게임 홈 게임 목록 [코드 6자리 입력칸][참여] 👤 닉네임 ▾ │ 64px
|
||||
│ ● 같이 놀자 보드게임 홈 게임 목록 [🌙] 👤 닉네임 ▾ │ 64px
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
- 어느 화면에서든 코드를 바로 넣어 참여할 수 있게 상단에 코드 입력칸을 둔다.
|
||||
- 오른쪽 끝: 로그인 전에는 [로그인](→ `/login`: 큰 [디스코드로 로그인], 아래 [게스트로 시작하기] → `/login/guest`에서 계정 이름 정하기). 로그인 뒤에는 닉네임(디스코드로 가입했으면 디스코드 이름)을 보이고, 누르면 [⚙️ 설정](→ `/me`)·[🚪 로그아웃] 메뉴가 열린다. 화면·소리 설정은 `/me`에만 있다.
|
||||
- 오른쪽에 테마 버튼(🌙/☀️)을 둔다. 누를 때마다 밝은 화면 ↔ 어두운 화면으로 바뀌고, `/me`의 '화면 색' 설정과 같은 값을 쓴다('자동'이면 지금 보이는 색의 반대로). 코드로 참여는 홈의 [🔢 코드로 참여](→ `/j`)로 한다.
|
||||
- 오른쪽 끝: 로그인 전에는 [로그인](→ `/login`: 큰 [디스코드로 로그인], 아래 [게스트로 시작하기] → `/login/guest`에서 계정 이름 정하기). 로그인 뒤에는 닉네임(디스코드로 가입했으면 디스코드 이름)을 보이고, 누르면 [⚙️ 설정](→ `/me`)·[🚪 로그아웃] 메뉴가 열린다. 테마 버튼 말고 글자 크기·소리 같은 설정은 `/me`에 있다.
|
||||
- 내용 폭은 최대 1280px, 가운데 정렬. 게임 화면만 화면 전체 폭을 쓴다.
|
||||
|
||||
### 3.3 홈 (PC)
|
||||
|
||||
Reference in New Issue
Block a user