Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 20x 20x 10x 40x 10x | import type { LucideIcon } from 'lucide-react';
import { History, Layers } from 'lucide-react';
import { motion } from 'framer-motion';
import { NAMES, UI_LABELS } from '@/shared/constants';
type PanelKey = 'layers' | 'history' | null;
interface SidebarItem {
key: Exclude<PanelKey, null>;
label: string;
icon: LucideIcon;
color: string;
}
interface RightSidebarProps {
active: PanelKey;
onSelect: (key: PanelKey) => void;
}
/**
* Static right sidebar — controls opening of side panels (Layers, History, Settings).
* Always visible and visually separated from the canvas.
*/
export function RightSidebar({ active, onSelect }: RightSidebarProps) {
const items: SidebarItem[] = [
{
key: NAMES.LAYERS,
label: UI_LABELS.LAYERS,
icon: Layers,
color: 'bg-indigo-600',
},
{
key: NAMES.HISTORY,
label: UI_LABELS.HISTORY,
icon: History,
color: 'bg-amber-600',
},
];
const handleToggle = (key: Exclude<PanelKey, null>) =>
onSelect(active === key ? null : key);
return (
<aside className="fixed top-12 right-0 bottom-16 w-16 flex flex-col items-center justify-start gap-4 py-6 bg-gray-900 text-white shadow-2xl border-l border-gray-800 z-50">
{items.map(({ key, label, icon: Icon, color }) => {
const isActive = active === key;
return (
<motion.button
key={key}
onClick={() => handleToggle(key)}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
className={`relative w-10 h-10 flex items-center justify-center rounded-md transition-colors duration-200 ${
isActive
? `${color} shadow-lg`
: 'bg-gray-700 hover:bg-gray-600'
}`}
title={label}
aria-pressed={isActive}
>
<Icon className="w-5 h-5" />
{isActive && (
<motion.span
layoutId="sidebar-active-indicator"
className="absolute inset-0 rounded-md ring-2 ring-white/30"
transition={{
type: 'spring',
stiffness: 300,
damping: 25,
}}
/>
)}
</motion.button>
);
})}
</aside>
);
}
|