Animated Toggle View Component (components/Editor/animated-toggle-view.tsx)¶
Overview¶
A React component that provides an animated toggle switch for transitioning between split and full view modes. The component features smooth animations, icon indicators, and state synchronization with external controls.
Features¶
- Animated toggle transitions
- Split/Full view modes
- Icon indicators
- State synchronization
- External click handling
- Disabled state support
- Spring animations
- Text transitions
- Accessibility labels
- Responsive design
- Event propagation
- State persistence
- Visual feedback
- Error prevention
- Cleanup handling
Implementation¶
export default function AnimatedToggleViewComponent({
onToggle,
checked,
editedState,
clicked
}: {
onToggle: (checked: boolean) => void;
checked?: boolean;
editedState: boolean;
clicked: (newState: boolean) => void;
}) {
const [isChecked, setIsChecked] = useState(checked || false);
useEffect(() => {
setIsChecked(checked || false);
}, [checked]);
useEffect(() => {
const handleExternalClick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (target.closest('.toggle-label') && editedState) {
clicked(true);
}
};
document.addEventListener('click', handleExternalClick);
return () => document.removeEventListener('click', handleExternalClick);
}, [editedState]);
const handleToggle = () => {
if (editedState) return;
const newChecked = !isChecked;
setIsChecked(newChecked);
if (onToggle) onToggle(newChecked);
}
return (
<div className="inline-flex items-center mr-10">
<label className="relative inline-block w-24 h-12 rounded-full cursor-pointer bg-gray-200 flex-shrink-0 toggle-label">
<input
type="checkbox"
className="sr-only"
checked={isChecked}
onChange={handleToggle}
disabled={editedState}
/>
<Files className="absolute left-2 top-3 w-6 h-6 text-gray-600" />
<File className="absolute right-2 top-3 w-6 h-6 text-gray-600" />
<motion.div
className="absolute top-1 w-10 h-10 rounded-full shadow-md flex items-center justify-center"
animate={{
x: isChecked ? 52 : 2,
backgroundColor: isChecked ? "#072F75" : "#EE2A7B"
}}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
<span className="sr-only">{isChecked ? 'Full view' : 'Split view'}</span>
</label>
<div className="ml-4 flex items-center">
<div className="flex items-center text-lg font-semibold">
<AnimatePresence mode="wait">
<motion.span
key={isChecked ? 'full' : 'split'}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.2 }}
className="mr-1"
>
{isChecked ? 'Full' : 'Split'}
</motion.span>
</AnimatePresence>
<span>view</span>
</div>
</div>
</div>
);
}
Dependencies¶
import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Files, File } from 'lucide-react'
Components Used¶
Notes¶
- Uses Framer Motion for animations
- Handles external state synchronization
- Prevents toggle during edited state
- Manages click event listeners
- Provides visual feedback
- Supports keyboard accessibility
- Implements smooth transitions
- Maintains internal state
- Cleans up event listeners
- Uses spring animations