Files
pi-multifx-pedal-ui/src/MainMenu.jsx
T
shawn db644cec6f add prefers-reduced-motion support (#audit-P2)
The transform: scale(.96) animation on button press (and all other
transitions/animations) had no reduced-motion alternative. Added:
@media (prefers-reduced-motion: reduce) { *, *::before, *::after {
  animation-duration: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0s !important;
}}

Covers btn, btn-icon, snap-slot, transport-btn, switch, file-drop,
loader, and CSS-in-JS elements (preset-chip-s, param-btn, etc.).
2026-06-14 17:25:06 -04:00

143 lines
5.0 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useRef, useEffect } from "react";
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8", blueDim: "#1E4060",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6", textSec: "#A0A0BC", textDim: "#7878A0",
};
const MENU_ITEMS = [
{ id: "save", label: "Save Preset", icon: "💾", desc: "Save current preset to bank" },
{ id: "bypass", label: "Bypass/Control", icon: "🔌", desc: "Footswitch & pedal assignment" },
{ id: "settings", label: "Global Settings",icon: "⚙", desc: "Master volume, input, MIDI, display" },
{ id: "eq", label: "Global EQ", icon: "📊", desc: "Low, Mid, High shelving EQ" },
{ id: "tuner", label: "Tuner", icon: "🎵", desc: "Instrument tuner" },
{ id: "wifi", label: "Wi-Fi / Bluetooth",icon: "📶", desc: "Wireless & network settings" },
];
export default function MainMenu({ onClose, onItemSelect }) {
const sidebarRef = useRef(null);
const backdropRef = useRef(null);
useEffect(() => {
const el = sidebarRef.current;
if (!el) return;
// Trigger enter animation — slide sidebar into view
requestAnimationFrame(() => { el.style.transform = "translateX(0)"; });
}, []);
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) onClose?.();
};
return (
<div
ref={backdropRef}
onClick={handleBackdropClick}
style={{
position: "fixed", inset: 0, zIndex: 999,
display: "flex",
background: "rgba(0,0,0,0.55)",
transition: "background .2s ease",
}}
>
{/* Sidebar */}
<div
ref={sidebarRef}
style={{
width: 260, maxWidth: "75vw", height: "100%",
background: T.panel,
borderRight: `1px solid ${T.border}`,
display: "flex", flexDirection: "column",
boxShadow: "4px 0 24px rgba(0,0,0,0.5)",
transform: "translateX(-100%)",
transition: "transform .22s cubic-bezier(.22,.61,.36,1)",
}}
>
{/* Header */}
<div style={{
padding: "14px 16px",
borderBottom: `1px solid ${T.border}`,
display: "flex", alignItems: "center", justifyContent: "space-between",
flexShrink: 0,
}}>
<div style={{ fontSize: 14, fontWeight: 600, color: T.textPrimary }}>
Menu
</div>
<button
onClick={onClose}
style={{
width: 28, height: 28, borderRadius: 6,
display: "flex", alignItems: "center", justifyContent: "center",
background: T.surface, border: `1px solid ${T.border}`,
color: T.textSec, fontSize: 13, cursor: "pointer",
}}
>
</button>
</div>
{/* Menu items */}
<div style={{
flex: 1, overflowY: "auto", padding: "8px 0",
}}>
{MENU_ITEMS.map((item) => (
<button
key={item.id}
onClick={() => onItemSelect?.(item.id)}
style={{
width: "100%", display: "flex", alignItems: "center", gap: 12,
padding: "12px 16px",
background: "none", border: "none", cursor: "pointer",
textAlign: "left", color: T.textPrimary,
transition: "background .1s",
borderBottom: `1px solid ${T.border}33`,
}}
onMouseEnter={(e) => { e.currentTarget.style.background = T.surface; }}
onMouseLeave={(e) => { e.currentTarget.style.background = "none"; }}
>
<div style={{
width: 34, height: 34, borderRadius: 8,
display: "flex", alignItems: "center", justifyContent: "center",
background: T.surface, border: `1px solid ${T.border}`,
fontSize: 16, flexShrink: 0,
}}>
{item.icon}
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{
fontSize: 13, fontWeight: 600,
color: T.textPrimary,
}}>
{item.label}
</div>
<div style={{
fontSize: 10, color: T.textDim, marginTop: 1,
whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis",
}}>
{item.desc}
</div>
</div>
<span style={{ color: T.textDim, fontSize: 11 }}></span>
</button>
))}
</div>
{/* Footer */}
<div style={{
padding: "12px 16px",
borderTop: `1px solid ${T.border}`,
flexShrink: 0,
}}>
<div style={{
fontSize: 9, color: T.textDim, textAlign: "center",
letterSpacing: ".06em",
}}>
Helix Stadium v0.1
</div>
</div>
</div>
</div>
);
}