auto winn
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
---
|
||||
name: react-component
|
||||
description: 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
|
||||
|
||||
```jsx
|
||||
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
|
||||
```jsx
|
||||
<Button variant="primary|secondary|ghost|danger" size="sm|md|lg" icon={IconComponent} onClick={fn}>
|
||||
Label
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Card
|
||||
```jsx
|
||||
<Card glassmorphism hoverable>
|
||||
{/* content */}
|
||||
</Card>
|
||||
```
|
||||
Variants: `glassmorphism` (default), `elevated`, `outlined`
|
||||
|
||||
### Modal
|
||||
```jsx
|
||||
<Modal isOpen={bool} onClose={fn} title="Judul Modal">
|
||||
{/* content */}
|
||||
</Modal>
|
||||
```
|
||||
|
||||
### Badge
|
||||
```jsx
|
||||
<Badge variant="primary|success|warning|danger">{text}</Badge>
|
||||
```
|
||||
|
||||
### ProgressBar
|
||||
```jsx
|
||||
<ProgressBar value={0-100} label="Teks" showPercent />
|
||||
```
|
||||
|
||||
## Animasi Standar
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```jsx
|
||||
// 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;
|
||||
```
|
||||
Reference in New Issue
Block a user