3.8 KiB
3.8 KiB
name, description
| name | description |
|---|---|
| react-component | Panduan membuat React component untuk Tour Planner App. Gunakan skill ini ketika membuat komponen baru, baik UI umum maupun komponen spesifik fitur (trip, schedule, packing, budget). |
Skill: React Component — Tour Planner App
Aturan Dasar
Semua komponen HARUS mengikuti pola berikut:
- Functional component dengan arrow function
- Props didestruktur di parameter
- Menggunakan CSS variables dari design system (lihat
src/index.css) - Menggunakan Framer Motion untuk animasi
- Menggunakan Lucide React untuk icon
Template Dasar Komponen
import { motion } from 'framer-motion';
import { IconName } from 'lucide-react';
import styles from './ComponentName.module.css'; // opsional
const ComponentName = ({ prop1, prop2, onAction }) => {
return (
<motion.div
className="component-name"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
{/* content */}
</motion.div>
);
};
export default ComponentName;
Komponen UI yang Tersedia
Button
<Button variant="primary|secondary|ghost|danger" size="sm|md|lg" icon={IconComponent} onClick={fn}>
Label
</Button>
Card
<Card glassmorphism hoverable>
{/* content */}
</Card>
Variants: glassmorphism (default), elevated, outlined
Modal
<Modal isOpen={bool} onClose={fn} title="Judul Modal">
{/* content */}
</Modal>
Badge
<Badge variant="primary|success|warning|danger">{text}</Badge>
ProgressBar
<ProgressBar value={0-100} label="Teks" showPercent />
Animasi Standar
// Fade in dari bawah (card, item list)
const fadeInUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.3 }
};
// Stagger anak-anak (list items)
const containerVariants = {
animate: { transition: { staggerChildren: 0.08 } }
};
// Scale on hover (interactive card)
const hoverScale = {
whileHover: { scale: 1.02 },
whileTap: { scale: 0.98 }
};
CSS Classes Tersedia (dari index.css)
.glass — glassmorphism card
.glass-dark — glassmorphism gelap
.btn-primary — tombol primary
.btn-secondary — tombol secondary
.btn-ghost — tombol ghost
.text-gradient — teks dengan gradient
.badge — badge dasar
.input-field — input form
.section-title — heading section
File Baru Selalu di Folder yang Tepat
| Tipe | Lokasi |
|---|---|
| Layout component | src/components/layout/ |
| UI generic | src/components/ui/ |
| Trip feature | src/components/trip/ |
| Schedule feature | src/components/schedule/ |
| Packing feature | src/components/packing/ |
| Budget feature | src/components/budget/ |
| Full page | src/pages/ |
Contoh: Membuat Activity Item
// src/components/schedule/ActivityItem.jsx
import { motion } from 'framer-motion';
import { Clock, MapPin, GripVertical, Trash2 } from 'lucide-react';
const ActivityItem = ({ activity, onDelete, dragHandleProps }) => {
return (
<motion.div
className="activity-item"
layout
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
>
<div className="activity-drag-handle" {...dragHandleProps}>
<GripVertical size={16} />
</div>
<div className="activity-time">
<Clock size={14} />
<span>{activity.time}</span>
</div>
<div className="activity-content">
<h4>{activity.name}</h4>
<div className="activity-location">
<MapPin size={12} />
<span>{activity.location}</span>
</div>
</div>
<button onClick={() => onDelete(activity.id)}>
<Trash2 size={16} />
</button>
</motion.div>
);
};
export default ActivityItem;