feat(web): add button size toggle (S/M/L) in header
Adds a size selector in the header bar that allows users to choose between Small, Medium, and Large sound buttons. Choice persists via localStorage. Responsive breakpoints also respect the setting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ba8c07f347
commit
e0bbe03851
2 changed files with 142 additions and 1 deletions
|
|
@ -25,6 +25,12 @@ const THEMES = [
|
|||
];
|
||||
|
||||
type Tab = 'all' | 'favorites' | 'recent';
|
||||
type BtnSize = 'S' | 'M' | 'L';
|
||||
const BTN_SIZES: { id: BtnSize; label: string }[] = [
|
||||
{ id: 'S', label: 'S' },
|
||||
{ id: 'M', label: 'M' },
|
||||
{ id: 'L', label: 'L' },
|
||||
];
|
||||
|
||||
export default function App() {
|
||||
/* ── State ── */
|
||||
|
|
@ -44,6 +50,7 @@ export default function App() {
|
|||
const [volume, setVolume] = useState(1);
|
||||
const [favs, setFavs] = useState<Record<string, boolean>>({});
|
||||
const [theme, setTheme] = useState(() => localStorage.getItem('jb-theme') || 'midnight');
|
||||
const [btnSize, setBtnSize] = useState<BtnSize>(() => (localStorage.getItem('jb-btn-size') as BtnSize) || 'M');
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [showAdmin, setShowAdmin] = useState(false);
|
||||
|
||||
|
|
@ -91,6 +98,11 @@ export default function App() {
|
|||
localStorage.setItem('jb-theme', theme);
|
||||
}, [theme]);
|
||||
|
||||
/* ── Button Size ── */
|
||||
useEffect(() => {
|
||||
localStorage.setItem('jb-btn-size', btnSize);
|
||||
}, [btnSize]);
|
||||
|
||||
/* ── SSE ── */
|
||||
useEffect(() => {
|
||||
const unsub = subscribeEvents((msg) => {
|
||||
|
|
@ -230,7 +242,7 @@ export default function App() {
|
|||
|
||||
/* ── Render ── */
|
||||
return (
|
||||
<div className={`app-shell ${chaosMode ? 'party-active' : ''}`}>
|
||||
<div className={`app-shell ${chaosMode ? 'party-active' : ''}`} data-btn-size={btnSize}>
|
||||
|
||||
{/* ════════ Header ════════ */}
|
||||
<header className="header">
|
||||
|
|
@ -256,6 +268,18 @@ export default function App() {
|
|||
<strong>{total}</strong> Sounds
|
||||
</div>
|
||||
|
||||
<div className="size-toggle" title="Button-Größe">
|
||||
{BTN_SIZES.map(s => (
|
||||
<button
|
||||
key={s.id}
|
||||
className={`size-opt ${btnSize === s.id ? 'active' : ''}`}
|
||||
onClick={() => setBtnSize(s.id)}
|
||||
>
|
||||
{s.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<select
|
||||
className="select-clean"
|
||||
value={theme}
|
||||
|
|
|
|||
|
|
@ -378,6 +378,40 @@ input, select {
|
|||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ── Size Toggle (S / M / L) ── */
|
||||
.size-toggle {
|
||||
display: inline-flex;
|
||||
background: var(--bg-surface-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.size-opt {
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
transition: all 0.15s;
|
||||
border-right: 1px solid var(--border);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.size-opt:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.size-opt:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-surface-3);
|
||||
}
|
||||
|
||||
.size-opt.active {
|
||||
color: var(--accent);
|
||||
background: var(--accent-subtle);
|
||||
}
|
||||
|
||||
/* Theme & Channel selects */
|
||||
.select-clean {
|
||||
appearance: none;
|
||||
|
|
@ -677,6 +711,71 @@ input, select {
|
|||
background: var(--warning);
|
||||
}
|
||||
|
||||
/* ── Button Size Variants (S / M / L) ── */
|
||||
/* S = default (36px) */
|
||||
|
||||
[data-btn-size="M"] .sound-btn {
|
||||
height: 46px;
|
||||
font-size: 13.5px;
|
||||
max-width: 260px;
|
||||
padding: 0 16px 0 0;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn .sound-label {
|
||||
line-height: 46px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn .cat-bar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn .fav-star {
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn .fav-star .material-icons {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn .badge-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn {
|
||||
height: 58px;
|
||||
font-size: 14.5px;
|
||||
max-width: 300px;
|
||||
padding: 0 20px 0 0;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn .sound-label {
|
||||
line-height: 58px;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn .cat-bar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn .fav-star {
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn .fav-star .material-icons {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn .badge-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
Control Bar (Bottom)
|
||||
──────────────────────────────────────────── */
|
||||
|
|
@ -1212,6 +1311,24 @@ input, select {
|
|||
max-width: 180px;
|
||||
}
|
||||
|
||||
[data-btn-size="M"] .sound-btn {
|
||||
height: 40px;
|
||||
font-size: 12.5px;
|
||||
max-width: 220px;
|
||||
}
|
||||
[data-btn-size="M"] .sound-btn .sound-label {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
[data-btn-size="L"] .sound-btn {
|
||||
height: 50px;
|
||||
font-size: 13.5px;
|
||||
max-width: 260px;
|
||||
}
|
||||
[data-btn-size="L"] .sound-btn .sound-label {
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.control-bar {
|
||||
padding: 0 12px;
|
||||
height: auto;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue