@umituz/react-native-design-system 2.6.94 → 2.6.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/atoms/AtomicAvatar.README.md +284 -397
- package/src/atoms/AtomicBadge.README.md +123 -358
- package/src/atoms/AtomicCard.README.md +358 -247
- package/src/atoms/AtomicDatePicker.README.md +127 -332
- package/src/atoms/AtomicFab.README.md +194 -352
- package/src/atoms/AtomicIcon.README.md +241 -274
- package/src/atoms/AtomicProgress.README.md +100 -338
- package/src/atoms/AtomicSpinner.README.md +304 -337
- package/src/atoms/AtomicText.README.md +153 -389
- package/src/atoms/AtomicTextArea.README.md +267 -268
- package/src/atoms/EmptyState.README.md +247 -292
- package/src/atoms/GlassView/README.md +313 -444
- package/src/atoms/button/README.md +186 -297
- package/src/atoms/button/STRATEGY.md +252 -0
- package/src/atoms/chip/README.md +242 -290
- package/src/atoms/input/README.md +296 -290
- package/src/atoms/picker/README.md +278 -309
- package/src/atoms/skeleton/AtomicSkeleton.README.md +394 -252
- package/src/molecules/BaseModal/README.md +356 -0
- package/src/molecules/BaseModal.README.md +324 -200
- package/src/molecules/ConfirmationModal.README.md +349 -302
- package/src/molecules/Divider/README.md +293 -376
- package/src/molecules/FormField.README.md +321 -534
- package/src/molecules/GlowingCard/GlowingCard.tsx +1 -1
- package/src/molecules/GlowingCard/README.md +230 -372
- package/src/molecules/List/README.md +281 -488
- package/src/molecules/ListItem.README.md +320 -315
- package/src/molecules/SearchBar/README.md +332 -430
- package/src/molecules/StepHeader/README.md +311 -411
- package/src/molecules/StepProgress/README.md +281 -448
- package/src/molecules/alerts/README.md +272 -355
- package/src/molecules/avatar/README.md +295 -356
- package/src/molecules/bottom-sheet/README.md +303 -340
- package/src/molecules/calendar/README.md +301 -265
- package/src/molecules/countdown/README.md +347 -456
- package/src/molecules/emoji/README.md +281 -514
- package/src/molecules/listitem/README.md +307 -399
- package/src/molecules/media-card/MediaCard.tsx +31 -34
- package/src/molecules/media-card/README.md +217 -319
- package/src/molecules/navigation/README.md +263 -284
- package/src/molecules/navigation/components/NavigationHeader.tsx +77 -0
- package/src/molecules/navigation/index.ts +1 -0
- package/src/molecules/splash/README.md +76 -80
- package/src/molecules/swipe-actions/README.md +376 -588
|
@@ -1,339 +1,375 @@
|
|
|
1
1
|
# AtomicCalendar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A full-featured calendar component for React Native with monthly view, event display, date selection, and timezone support.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Import & Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- 🔵 **Event Indicators**: Etkinlik noktaları gösterimi
|
|
10
|
-
- 🌍 **Timezone Bilinci**: Timezone uyumlu
|
|
11
|
-
- 🎨 **Özelleştirilebilir**: Stil ve tema desteği
|
|
12
|
-
- ♿ **Erişilebilir**: Tam erişilebilirlik desteği
|
|
13
|
-
- 📱 **Responsive**: Cihaz boyutuna uyum
|
|
14
|
-
|
|
15
|
-
## Kurulum
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { AtomicCalendar } from 'react-native-design-system';
|
|
7
|
+
```typescript
|
|
8
|
+
import { AtomicCalendar } from 'react-native-design-system/src/molecules/calendar';
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```tsx
|
|
24
|
-
import React, { useState } from 'react';
|
|
25
|
-
import { View } from 'react-native';
|
|
26
|
-
import { AtomicCalendar } from 'react-native-design-system';
|
|
27
|
-
|
|
28
|
-
export const BasicExample = () => {
|
|
29
|
-
const [selectedDate, setSelectedDate] = useState(new Date());
|
|
30
|
-
|
|
31
|
-
// Calendar days'i hesaplayın (custom hook veya service kullanabilirsiniz)
|
|
32
|
-
const days = calculateCalendarDays(selectedDate);
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<View style={{ padding: 16 }}>
|
|
36
|
-
<AtomicCalendar
|
|
37
|
-
days={days}
|
|
38
|
-
selectedDate={selectedDate}
|
|
39
|
-
onDateSelect={setSelectedDate}
|
|
40
|
-
/>
|
|
41
|
-
</View>
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
```
|
|
11
|
+
**Location:** `src/molecules/calendar/AtomicCalendar.tsx`
|
|
45
12
|
|
|
46
|
-
## Basic
|
|
13
|
+
## Basic Usage
|
|
47
14
|
|
|
48
15
|
```tsx
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
selectedDate={selectedDate}
|
|
52
|
-
onDateSelect={(date) => setSelectedDate(date)}
|
|
53
|
-
/>
|
|
54
|
-
```
|
|
16
|
+
const [selectedDate, setSelectedDate] = useState(new Date());
|
|
17
|
+
const days = calculateCalendarDays(selectedDate);
|
|
55
18
|
|
|
56
|
-
## Weekday Headers Gizle
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
19
|
<AtomicCalendar
|
|
60
|
-
days={
|
|
20
|
+
days={days}
|
|
61
21
|
selectedDate={selectedDate}
|
|
62
22
|
onDateSelect={setSelectedDate}
|
|
63
|
-
showWeekdayHeaders={false}
|
|
64
23
|
/>
|
|
65
24
|
```
|
|
66
25
|
|
|
67
|
-
##
|
|
26
|
+
## Strategy
|
|
68
27
|
|
|
69
|
-
|
|
70
|
-
<AtomicCalendar
|
|
71
|
-
days={calendarDays}
|
|
72
|
-
selectedDate={selectedDate}
|
|
73
|
-
onDateSelect={setSelectedDate}
|
|
74
|
-
maxEventIndicators={3}
|
|
75
|
-
/>
|
|
76
|
-
```
|
|
28
|
+
**Purpose**: Provide a standardized, accessible calendar interface for date selection and event visualization.
|
|
77
29
|
|
|
78
|
-
|
|
30
|
+
**When to Use**:
|
|
31
|
+
- Date pickers for forms
|
|
32
|
+
- Event scheduling and management
|
|
33
|
+
- Appointment booking
|
|
34
|
+
- Task management with due dates
|
|
35
|
+
- Attendance tracking
|
|
36
|
+
- Holiday/leave planning
|
|
37
|
+
- Availability displays
|
|
79
38
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
dayStyle={{ borderRadius: 8 }}
|
|
86
|
-
/>
|
|
87
|
-
```
|
|
39
|
+
**When NOT to Use**:
|
|
40
|
+
- For simple date selection (use DatePicker instead)
|
|
41
|
+
- For date range selection only (use DateRangePicker instead)
|
|
42
|
+
- For time-only selection (use TimePicker instead)
|
|
43
|
+
- For recurring event configuration (use specialized scheduler UI)
|
|
88
44
|
|
|
89
|
-
##
|
|
45
|
+
## Rules
|
|
90
46
|
|
|
91
|
-
###
|
|
47
|
+
### Required
|
|
92
48
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
]);
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<View style={{ padding: 16 }}>
|
|
103
|
-
<AtomicText type="headlineMedium" style={{ marginBottom: 16 }}>
|
|
104
|
-
Takvim
|
|
105
|
-
</AtomicText>
|
|
106
|
-
|
|
107
|
-
<AtomicCalendar
|
|
108
|
-
days={getCalendarDaysWithEvents(events)}
|
|
109
|
-
selectedDate={selectedDate}
|
|
110
|
-
onDateSelect={setSelectedDate}
|
|
111
|
-
/>
|
|
112
|
-
</View>
|
|
113
|
-
);
|
|
114
|
-
};
|
|
115
|
-
```
|
|
49
|
+
1. **MUST** provide `days` array (42 days, 6-week grid)
|
|
50
|
+
2. **MUST** have `selectedDate` and `onDateSelect` handlers
|
|
51
|
+
3. **ALWAYS** calculate days correctly with timezone handling
|
|
52
|
+
4. **MUST** handle month boundaries (padding days from prev/next months)
|
|
53
|
+
5. **SHOULD** show event indicators when events exist
|
|
54
|
+
6. **MUST** respect isDisabled flag for unavailable dates
|
|
116
55
|
|
|
117
|
-
###
|
|
56
|
+
### Day Calculation
|
|
118
57
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
58
|
+
1. **42-day grid**: Always return 42 days (6 weeks)
|
|
59
|
+
2. **Month padding**: Include days from prev/next months to fill grid
|
|
60
|
+
3. **Timezone handling**: Use consistent timezone throughout
|
|
61
|
+
4. **Date validation**: Ensure all dates are valid Date objects
|
|
123
62
|
|
|
124
|
-
|
|
125
|
-
setSelectedDate(date);
|
|
126
|
-
loadAppointmentsForDate(date);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<View style={{ padding: 16 }}>
|
|
131
|
-
<AtomicCalendar
|
|
132
|
-
days={getCalendarDays()}
|
|
133
|
-
selectedDate={selectedDate}
|
|
134
|
-
onDateSelect={handleDateSelect}
|
|
135
|
-
/>
|
|
136
|
-
|
|
137
|
-
{selectedDate && (
|
|
138
|
-
<View style={{ marginTop: 16 }}>
|
|
139
|
-
<AtomicText type="titleMedium">
|
|
140
|
-
{selectedDate.toLocaleDateString('tr-TR')}
|
|
141
|
-
</AtomicText>
|
|
142
|
-
|
|
143
|
-
{appointments.length > 0 ? (
|
|
144
|
-
appointments.map((apt) => (
|
|
145
|
-
<AppointmentCard key={apt.id} appointment={apt} />
|
|
146
|
-
))
|
|
147
|
-
) : (
|
|
148
|
-
<AtomicText type="bodyMedium" color="textSecondary">
|
|
149
|
-
Randevu yok
|
|
150
|
-
</AtomicText>
|
|
151
|
-
)}
|
|
152
|
-
</View>
|
|
153
|
-
)}
|
|
154
|
-
</View>
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
```
|
|
63
|
+
### Event Indicators
|
|
158
64
|
|
|
159
|
-
|
|
65
|
+
1. **Max indicators**: Default to 3, adjust based on design
|
|
66
|
+
2. **Event mapping**: Map events to correct dates
|
|
67
|
+
3. **Visual distinction**: Different colors for different event types
|
|
68
|
+
4. **Performance**: Limit events per day for performance
|
|
160
69
|
|
|
161
|
-
|
|
162
|
-
export const TaskCalendar = () => {
|
|
163
|
-
const [selectedDate, setSelectedDate] = useState(new Date());
|
|
164
|
-
|
|
165
|
-
const getDaysWithTasks = () => {
|
|
166
|
-
const days = calculateCalendarDays(selectedDate);
|
|
167
|
-
return days.map(day => ({
|
|
168
|
-
...day,
|
|
169
|
-
events: getTasksForDate(day.date).slice(0, 3),
|
|
170
|
-
}));
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
return (
|
|
174
|
-
<View style={{ padding: 16 }}>
|
|
175
|
-
<AtomicCalendar
|
|
176
|
-
days={getDaysWithTasks()}
|
|
177
|
-
selectedDate={selectedDate}
|
|
178
|
-
onDateSelect={setSelectedDate}
|
|
179
|
-
maxEventIndicators={5}
|
|
180
|
-
/>
|
|
181
|
-
</View>
|
|
182
|
-
);
|
|
183
|
-
};
|
|
184
|
-
```
|
|
70
|
+
## Forbidden
|
|
185
71
|
|
|
186
|
-
|
|
72
|
+
❌ **NEVER** do these:
|
|
187
73
|
|
|
188
74
|
```tsx
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const getDaysWithBirthdays = () => {
|
|
196
|
-
const days = calculateCalendarDays(selectedDate);
|
|
197
|
-
return days.map(day => ({
|
|
198
|
-
...day,
|
|
199
|
-
events: birthdays.filter(b =>
|
|
200
|
-
b.month === day.date.getMonth() && b.day === day.date.getDate()
|
|
201
|
-
),
|
|
202
|
-
}));
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<View style={{ padding: 16 }}>
|
|
207
|
-
<AtomicCalendar
|
|
208
|
-
days={getDaysWithBirthdays()}
|
|
209
|
-
selectedDate={selectedDate}
|
|
210
|
-
onDateSelect={setSelectedDate}
|
|
211
|
-
/>
|
|
212
|
-
</View>
|
|
213
|
-
);
|
|
214
|
-
};
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Check-in Takvimi
|
|
75
|
+
// ❌ No days array
|
|
76
|
+
<AtomicCalendar
|
|
77
|
+
selectedDate={selectedDate}
|
|
78
|
+
onDateSelect={setSelectedDate}
|
|
79
|
+
/>
|
|
218
80
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
hasCheckin: checkins.some(c =>
|
|
228
|
-
isSameDay(c.date, day.date)
|
|
229
|
-
),
|
|
230
|
-
}));
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
return (
|
|
234
|
-
<View style={{ padding: 16 }}>
|
|
235
|
-
<AtomicCalendar
|
|
236
|
-
days={getDaysWithCheckins()}
|
|
237
|
-
selectedDate={selectedDate}
|
|
238
|
-
onDateSelect={setSelectedDate}
|
|
239
|
-
/>
|
|
240
|
-
</View>
|
|
241
|
-
);
|
|
81
|
+
// ❌ Wrong day count (not 42 days)
|
|
82
|
+
const getDays = () => {
|
|
83
|
+
const days = [];
|
|
84
|
+
// ❌ Only returns 30 days for current month
|
|
85
|
+
for (let i = 1; i <= 30; i++) {
|
|
86
|
+
days.push({ date: new Date(year, month, i) });
|
|
87
|
+
}
|
|
88
|
+
return days;
|
|
242
89
|
};
|
|
243
|
-
```
|
|
244
90
|
|
|
245
|
-
|
|
91
|
+
// ❌ No timezone handling
|
|
92
|
+
const days = calculateCalendarDays(selectedDate); // ❌ Uses local time
|
|
246
93
|
|
|
247
|
-
|
|
94
|
+
// ❌ Missing month padding
|
|
95
|
+
const days = currentMonthDays; // ❌ No prev/next month days
|
|
248
96
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
| `maxEventIndicators` | `number` | `3` | Maksimum etkinlik sayısı |
|
|
256
|
-
| `dayStyle` | `StyleProp<ViewStyle>` | - | Gün hücresi stili |
|
|
257
|
-
| `showEventCount` | `boolean` | `true` | Event sayısını göster |
|
|
258
|
-
| `style` | `StyleProp<ViewStyle>` | - | Container stili |
|
|
259
|
-
| `testID` | `string` | - | Test ID'si |
|
|
97
|
+
// ❌ Not handling disabled dates
|
|
98
|
+
<AtomicCalendar
|
|
99
|
+
days={days}
|
|
100
|
+
selectedDate={new Date('2024-01-01')} // ❌ Past date
|
|
101
|
+
onDateSelect={setSelectedDate}
|
|
102
|
+
/>
|
|
260
103
|
|
|
261
|
-
|
|
104
|
+
// ❌ Too many event indicators
|
|
105
|
+
<AtomicCalendar
|
|
106
|
+
days={days}
|
|
107
|
+
selectedDate={selectedDate}
|
|
108
|
+
maxEventIndicators={20} // ❌ Too many, performance issue
|
|
109
|
+
/>
|
|
262
110
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
date:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
isToday: boolean; // Bugün mü
|
|
269
|
-
events?: Event[]; // Etkinlikler
|
|
270
|
-
isDisabled?: boolean; // Devre dışı mı
|
|
271
|
-
}
|
|
111
|
+
// ❌ No date validation
|
|
112
|
+
const days = [
|
|
113
|
+
{ date: null }, // ❌ Invalid date
|
|
114
|
+
{ date: '2024-01-01' }, // ❌ String, not Date object
|
|
115
|
+
];
|
|
272
116
|
```
|
|
273
117
|
|
|
274
118
|
## Best Practices
|
|
275
119
|
|
|
276
|
-
###
|
|
120
|
+
### Day Calculation
|
|
277
121
|
|
|
122
|
+
✅ **DO**:
|
|
278
123
|
```tsx
|
|
279
|
-
//
|
|
280
|
-
const calculateCalendarDays = (date: Date) => {
|
|
124
|
+
// ✅ Good - 42-day grid with padding
|
|
125
|
+
const calculateCalendarDays = (date: Date): CalendarDay[] => {
|
|
281
126
|
const year = date.getFullYear();
|
|
282
127
|
const month = date.getMonth();
|
|
283
128
|
const firstDay = new Date(year, month, 1);
|
|
284
129
|
const lastDay = new Date(year, month + 1, 0);
|
|
285
130
|
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
131
|
+
const firstDayOfWeek = firstDay.getDay(); // 0 = Sunday
|
|
132
|
+
const daysInMonth = lastDay.getDate();
|
|
133
|
+
|
|
134
|
+
const days: CalendarDay[] = [];
|
|
135
|
+
|
|
136
|
+
// Add padding days from previous month
|
|
137
|
+
for (let i = firstDayOfWeek - 1; i >= 0; i--) {
|
|
138
|
+
const prevDate = new Date(year, month, -i);
|
|
139
|
+
days.push({
|
|
140
|
+
date: prevDate,
|
|
141
|
+
isCurrentMonth: false,
|
|
142
|
+
isSelected: isSameDay(prevDate, selectedDate),
|
|
143
|
+
isToday: isSameDay(prevDate, new Date()),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add current month days
|
|
148
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
149
|
+
const currentDate = new Date(year, month, i);
|
|
150
|
+
days.push({
|
|
151
|
+
date: currentDate,
|
|
152
|
+
isCurrentMonth: true,
|
|
153
|
+
isSelected: isSameDay(currentDate, selectedDate),
|
|
154
|
+
isToday: isSameDay(currentDate, new Date()),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Add padding days from next month to reach 42
|
|
159
|
+
const remainingDays = 42 - days.length;
|
|
160
|
+
for (let i = 1; i <= remainingDays; i++) {
|
|
161
|
+
const nextDate = new Date(year, month + 1, i);
|
|
162
|
+
days.push({
|
|
163
|
+
date: nextDate,
|
|
164
|
+
isCurrentMonth: false,
|
|
165
|
+
isSelected: isSameDay(nextDate, selectedDate),
|
|
166
|
+
isToday: isSameDay(nextDate, new Date()),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
289
170
|
return days;
|
|
290
171
|
};
|
|
291
172
|
```
|
|
292
173
|
|
|
293
|
-
|
|
174
|
+
❌ **DON'T**:
|
|
175
|
+
```tsx
|
|
176
|
+
// ❌ Bad - only current month, no padding
|
|
177
|
+
const getDays = (date: Date) => {
|
|
178
|
+
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
179
|
+
const days = [];
|
|
180
|
+
for (let i = 1; i <= lastDay.getDate(); i++) {
|
|
181
|
+
days.push({
|
|
182
|
+
date: new Date(date.getFullYear(), date.getMonth(), i),
|
|
183
|
+
isCurrentMonth: true,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return days; // ❌ Only 28-31 days, breaks grid layout
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Event Mapping
|
|
294
191
|
|
|
192
|
+
✅ **DO**:
|
|
295
193
|
```tsx
|
|
296
|
-
//
|
|
297
|
-
const
|
|
194
|
+
// ✅ Good - map events to days
|
|
195
|
+
const getDaysWithEvents = (date: Date, events: Event[]): CalendarDay[] => {
|
|
196
|
+
const days = calculateCalendarDays(date);
|
|
298
197
|
return days.map(day => ({
|
|
299
198
|
...day,
|
|
300
|
-
events: events.filter(
|
|
199
|
+
events: events.filter(event =>
|
|
200
|
+
isSameDay(event.date, day.date)
|
|
201
|
+
).slice(0, 3), // Limit to 3 for performance
|
|
301
202
|
}));
|
|
302
203
|
};
|
|
303
204
|
```
|
|
304
205
|
|
|
305
|
-
|
|
206
|
+
## AI Coding Guidelines
|
|
207
|
+
|
|
208
|
+
### For AI Agents
|
|
209
|
+
|
|
210
|
+
When generating AtomicCalendar components, follow these rules:
|
|
211
|
+
|
|
212
|
+
1. **Always import from correct path**:
|
|
213
|
+
```typescript
|
|
214
|
+
import { AtomicCalendar } from 'react-native-design-system/src/molecules/calendar';
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
2. **Always calculate 42-day grid**:
|
|
218
|
+
```tsx
|
|
219
|
+
// ✅ Good - always 42 days
|
|
220
|
+
const calculateCalendarDays = (date: Date): CalendarDay[] => {
|
|
221
|
+
// Calculate with padding from prev/next months
|
|
222
|
+
// Must return exactly 42 days (6 weeks)
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// ❌ Bad - variable day count
|
|
226
|
+
const getDays = (date: Date) => {
|
|
227
|
+
// Returns 28-31 days
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
3. **Always handle month boundaries**:
|
|
232
|
+
```tsx
|
|
233
|
+
// ✅ Good - include padding days
|
|
234
|
+
const days: CalendarDay[] = [];
|
|
235
|
+
|
|
236
|
+
// Previous month padding
|
|
237
|
+
for (let i = firstDayOfWeek - 1; i >= 0; i--) {
|
|
238
|
+
days.push(createDay(new Date(year, month, -i), false));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Current month
|
|
242
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
243
|
+
days.push(createDay(new Date(year, month, i), true));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Next month padding (to reach 42)
|
|
247
|
+
for (let i = 1; days.length < 42; i++) {
|
|
248
|
+
days.push(createDay(new Date(year, month + 1, i), false));
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
4. **Always validate date objects**:
|
|
253
|
+
```tsx
|
|
254
|
+
// ✅ Good - validate dates
|
|
255
|
+
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
|
256
|
+
throw new Error('Invalid date');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ❌ Bad - no validation
|
|
260
|
+
const days = [{ date: someInput }]; // Could be invalid
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
5. **Always limit event indicators**:
|
|
264
|
+
```tsx
|
|
265
|
+
// ✅ Good - limit events
|
|
266
|
+
const daysWithEvents = days.map(day => ({
|
|
267
|
+
...day,
|
|
268
|
+
events: events
|
|
269
|
+
.filter(e => isSameDay(e.date, day.date))
|
|
270
|
+
.slice(0, 3), // Max 3 events
|
|
271
|
+
}));
|
|
272
|
+
|
|
273
|
+
// ❌ Bad - unlimited events
|
|
274
|
+
events.filter(e => isSameDay(e.date, day.date)) // Could be 100+
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Common Patterns
|
|
278
|
+
|
|
279
|
+
#### Basic Calendar
|
|
280
|
+
```tsx
|
|
281
|
+
const [selectedDate, setSelectedDate] = useState(new Date());
|
|
282
|
+
const days = useMemo(() => calculateCalendarDays(selectedDate), [selectedDate]);
|
|
306
283
|
|
|
284
|
+
<AtomicCalendar
|
|
285
|
+
days={days}
|
|
286
|
+
selectedDate={selectedDate}
|
|
287
|
+
onDateSelect={setSelectedDate}
|
|
288
|
+
/>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### Calendar with Events
|
|
307
292
|
```tsx
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
293
|
+
const [selectedDate, setSelectedDate] = useState(new Date());
|
|
294
|
+
const [events, setEvents] = useState<Event[]>([]);
|
|
295
|
+
|
|
296
|
+
const days = useMemo(() => {
|
|
297
|
+
const calendarDays = calculateCalendarDays(selectedDate);
|
|
298
|
+
return calendarDays.map(day => ({
|
|
299
|
+
...day,
|
|
300
|
+
events: events
|
|
301
|
+
.filter(e => isSameDay(e.date, day.date))
|
|
302
|
+
.slice(0, 3),
|
|
303
|
+
}));
|
|
304
|
+
}, [selectedDate, events]);
|
|
305
|
+
|
|
306
|
+
<AtomicCalendar
|
|
307
|
+
days={days}
|
|
308
|
+
selectedDate={selectedDate}
|
|
309
|
+
onDateSelect={(date) => {
|
|
310
|
+
setSelectedDate(date);
|
|
311
|
+
loadEventsForDate(date);
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### Calendar with Disabled Dates
|
|
317
|
+
```tsx
|
|
318
|
+
const days = useMemo(() => {
|
|
319
|
+
const calendarDays = calculateCalendarDays(selectedDate);
|
|
320
|
+
return calendarDays.map(day => ({
|
|
321
|
+
...day,
|
|
322
|
+
isDisabled: day.date < new Date(), // Disable past dates
|
|
323
|
+
}));
|
|
324
|
+
}, [selectedDate]);
|
|
313
325
|
```
|
|
314
326
|
|
|
315
|
-
##
|
|
327
|
+
## Props Reference
|
|
328
|
+
|
|
329
|
+
| Prop | Type | Required | Default | Description |
|
|
330
|
+
|------|------|----------|---------|-------------|
|
|
331
|
+
| `days` | `CalendarDay[]` | Yes | - | Calendar days (42 days) |
|
|
332
|
+
| `selectedDate` | `Date` | Yes | - | Selected date |
|
|
333
|
+
| `onDateSelect` | `(date: Date) => void` | Yes | - | Date selection callback |
|
|
334
|
+
| `showWeekdayHeaders` | `boolean` | No | `true` | Show weekday headers |
|
|
335
|
+
| `maxEventIndicators` | `number` | No | `3` | Maximum event indicators |
|
|
336
|
+
| `dayStyle` | `ViewStyle` | No | - | Day cell style |
|
|
337
|
+
| `showEventCount` | `boolean` | No | `true` | Show event count |
|
|
338
|
+
|
|
339
|
+
### CalendarDay Interface
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
interface CalendarDay {
|
|
343
|
+
date: Date; // Date object
|
|
344
|
+
isCurrentMonth: boolean; // Is in current month
|
|
345
|
+
isSelected: boolean; // Is selected
|
|
346
|
+
isToday: boolean; // Is today
|
|
347
|
+
events?: Event[]; // Events for this day
|
|
348
|
+
isDisabled?: boolean; // Is disabled
|
|
349
|
+
}
|
|
350
|
+
```
|
|
316
351
|
|
|
317
|
-
|
|
352
|
+
## Accessibility
|
|
318
353
|
|
|
319
|
-
- ✅ Screen reader
|
|
320
|
-
- ✅
|
|
321
|
-
- ✅ Event bilgileri
|
|
322
|
-
- ✅ Touch uygun boyut
|
|
354
|
+
- ✅ Screen reader announces date and events
|
|
355
|
+
- ✅ Touch target size maintained (min 44x44pt)
|
|
323
356
|
- ✅ Keyboard navigation (web)
|
|
357
|
+
- ✅ Semantic date information
|
|
358
|
+
- ✅ Visual indicators for today and selected date
|
|
324
359
|
|
|
325
|
-
##
|
|
360
|
+
## Performance Tips
|
|
326
361
|
|
|
327
|
-
1. **Memoization**: `days` array
|
|
328
|
-
2. **
|
|
329
|
-
3. **
|
|
362
|
+
1. **Memoization**: Always memo `days` array with useMemo
|
|
363
|
+
2. **Limit events**: Max 3-5 event indicators per day
|
|
364
|
+
3. **Lazy load**: Load events only when needed
|
|
365
|
+
4. **Debounce selection**: Debounce rapid date changes
|
|
330
366
|
|
|
331
|
-
##
|
|
367
|
+
## Related Components
|
|
332
368
|
|
|
333
|
-
- [`
|
|
334
|
-
- [`FormField`](../FormField/README.md) - Form
|
|
335
|
-
- [`
|
|
369
|
+
- [`BaseModal`](../BaseModal/README.md) - Modal component
|
|
370
|
+
- [`FormField`](../FormField/README.md) - Form field component
|
|
371
|
+
- [`Button`](../../atoms/button/README.md) - Button component
|
|
336
372
|
|
|
337
|
-
##
|
|
373
|
+
## License
|
|
338
374
|
|
|
339
375
|
MIT
|