@idealyst/datepicker 1.0.0

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.
Files changed (50) hide show
  1. package/README.md +88 -0
  2. package/package.json +77 -0
  3. package/src/DatePicker/Calendar.native.tsx +159 -0
  4. package/src/DatePicker/Calendar.styles.tsx +224 -0
  5. package/src/DatePicker/Calendar.tsx +154 -0
  6. package/src/DatePicker/DatePicker.native.tsx +33 -0
  7. package/src/DatePicker/DatePicker.styles.tsx +69 -0
  8. package/src/DatePicker/DatePicker.web.tsx +31 -0
  9. package/src/DatePicker/index.native.ts +3 -0
  10. package/src/DatePicker/index.ts +3 -0
  11. package/src/DatePicker/types.ts +78 -0
  12. package/src/DateRangePicker/DateRangePicker.native.tsx +39 -0
  13. package/src/DateRangePicker/DateRangePicker.styles.tsx +83 -0
  14. package/src/DateRangePicker/DateRangePicker.web.tsx +40 -0
  15. package/src/DateRangePicker/RangeCalendar.native.tsx +267 -0
  16. package/src/DateRangePicker/RangeCalendar.styles.tsx +170 -0
  17. package/src/DateRangePicker/RangeCalendar.web.tsx +275 -0
  18. package/src/DateRangePicker/index.native.ts +3 -0
  19. package/src/DateRangePicker/index.ts +3 -0
  20. package/src/DateRangePicker/types.ts +98 -0
  21. package/src/DateTimePicker/DateTimePicker.native.tsx +82 -0
  22. package/src/DateTimePicker/DateTimePicker.styles.tsx +77 -0
  23. package/src/DateTimePicker/DateTimePicker.tsx +79 -0
  24. package/src/DateTimePicker/TimePicker.native.tsx +204 -0
  25. package/src/DateTimePicker/TimePicker.styles.tsx +116 -0
  26. package/src/DateTimePicker/TimePicker.tsx +406 -0
  27. package/src/DateTimePicker/index.native.ts +3 -0
  28. package/src/DateTimePicker/index.ts +3 -0
  29. package/src/DateTimePicker/types.ts +84 -0
  30. package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +213 -0
  31. package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +95 -0
  32. package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +141 -0
  33. package/src/DateTimeRangePicker/index.native.ts +2 -0
  34. package/src/DateTimeRangePicker/index.ts +2 -0
  35. package/src/DateTimeRangePicker/types.ts +72 -0
  36. package/src/examples/DatePickerExamples.tsx +274 -0
  37. package/src/examples/index.ts +1 -0
  38. package/src/index.native.ts +16 -0
  39. package/src/index.ts +16 -0
  40. package/src/primitives/CalendarGrid/CalendarGrid.styles.tsx +62 -0
  41. package/src/primitives/CalendarGrid/CalendarGrid.tsx +138 -0
  42. package/src/primitives/CalendarGrid/index.ts +1 -0
  43. package/src/primitives/CalendarHeader/CalendarHeader.styles.tsx +25 -0
  44. package/src/primitives/CalendarHeader/CalendarHeader.tsx +69 -0
  45. package/src/primitives/CalendarHeader/index.ts +1 -0
  46. package/src/primitives/CalendarOverlay/CalendarOverlay.styles.tsx +81 -0
  47. package/src/primitives/CalendarOverlay/CalendarOverlay.tsx +130 -0
  48. package/src/primitives/CalendarOverlay/index.ts +1 -0
  49. package/src/primitives/Wrapper/Wrapper.web.tsx +33 -0
  50. package/src/primitives/Wrapper/index.ts +1 -0
@@ -0,0 +1,274 @@
1
+ import { useState } from 'react';
2
+ import { Screen, View, Text, Button } from '@idealyst/components';
3
+ import { DatePicker } from '../DatePicker';
4
+ import { DateTimePicker } from '../DateTimePicker';
5
+ import { DateRangePicker, DateRange } from '../DateRangePicker';
6
+ import { DateTimeRangePicker, DateTimeRange } from '../DateTimeRangePicker';
7
+
8
+ export const DatePickerExamples = () => {
9
+ const [basicDate, setBasicDate] = useState<Date | null>(null);
10
+ const [rangeDate, setRangeDate] = useState<Date | null>(null);
11
+ const [disabledDate, setDisabledDate] = useState<Date | null>(new Date());
12
+ const [dateTime, setDateTime] = useState<Date | null>(null);
13
+ const [dateTime24h, setDateTime24h] = useState<Date | null>(null);
14
+ const [dateRange, setDateRange] = useState<DateRange | null>(null);
15
+ const [restrictedRange, setRestrictedRange] = useState<DateRange | null>(null);
16
+ const [dateTimeRange, setDateTimeRange] = useState<DateTimeRange | null>(null);
17
+
18
+ const tomorrow = new Date();
19
+ tomorrow.setDate(tomorrow.getDate() + 1);
20
+
21
+ const nextMonth = new Date();
22
+ nextMonth.setMonth(nextMonth.getMonth() + 1);
23
+
24
+ return (
25
+ <Screen background="primary" padding="lg">
26
+ <View spacing="none">
27
+ <Text size="large" weight="bold" align="center">
28
+ DatePicker Examples
29
+ </Text>
30
+
31
+ {/* Basic DatePicker */}
32
+ <View spacing="md">
33
+ <Text size="medium" weight="semibold">Basic DatePicker</Text>
34
+ <DatePicker
35
+ value={basicDate}
36
+ onChange={setBasicDate}
37
+ label="Select Date"
38
+ placeholder="Choose a date"
39
+ helperText="Pick any date"
40
+ />
41
+ {basicDate && (
42
+ <Text size="small" color="secondary">
43
+ Selected: {basicDate.toDateString()}
44
+ </Text>
45
+ )}
46
+ </View>
47
+
48
+ {/* Date Range Restricted */}
49
+ <View spacing="md">
50
+ <Text size="medium" weight="semibold">Date Range Restricted</Text>
51
+ <DatePicker
52
+ value={rangeDate}
53
+ onChange={setRangeDate}
54
+ label="Future Dates Only"
55
+ placeholder="Select future date"
56
+ minDate={tomorrow}
57
+ maxDate={nextMonth}
58
+ helperText="Only dates between tomorrow and next month"
59
+ />
60
+ {rangeDate && (
61
+ <Text size="small" color="secondary">
62
+ Selected: {rangeDate.toDateString()}
63
+ </Text>
64
+ )}
65
+ </View>
66
+
67
+ {/* Disabled DatePicker */}
68
+ <View spacing="md">
69
+ <Text size="medium" weight="semibold">Disabled DatePicker</Text>
70
+ <DatePicker
71
+ value={disabledDate}
72
+ onChange={setDisabledDate}
73
+ label="Disabled"
74
+ placeholder="Cannot select"
75
+ disabled
76
+ helperText="This picker is disabled"
77
+ />
78
+ </View>
79
+
80
+ {/* Size Variants */}
81
+ <View spacing="md">
82
+ <Text size="medium" weight="semibold">Size Variants</Text>
83
+ <View spacing="sm">
84
+ <DatePicker
85
+ value={null}
86
+ onChange={() => {}}
87
+ label="Small"
88
+ placeholder="Small size"
89
+ size="small"
90
+ />
91
+ <DatePicker
92
+ value={null}
93
+ onChange={() => {}}
94
+ label="Medium"
95
+ placeholder="Medium size"
96
+ size="medium"
97
+ />
98
+ <DatePicker
99
+ value={null}
100
+ onChange={() => {}}
101
+ label="Large"
102
+ placeholder="Large size"
103
+ size="large"
104
+ />
105
+ </View>
106
+ </View>
107
+
108
+ {/* Actions */}
109
+ <View spacing="md">
110
+ <Text size="medium" weight="semibold">Actions</Text>
111
+ <View style={{ flexDirection: 'row', gap: 8 }}>
112
+ <Button
113
+ variant="outlined"
114
+ onPress={() => setBasicDate(new Date())}
115
+ >
116
+ Set Today
117
+ </Button>
118
+ <Button
119
+ variant="outlined"
120
+ onPress={() => {
121
+ setBasicDate(null);
122
+ setRangeDate(null);
123
+ }}
124
+ >
125
+ Clear All
126
+ </Button>
127
+ </View>
128
+ </View>
129
+
130
+ {/* DateTime Picker Examples */}
131
+ <View spacing="md">
132
+ <Text size="medium" weight="semibold">DateTimePicker Examples</Text>
133
+
134
+ {/* Basic DateTime */}
135
+ <View spacing="sm">
136
+ <Text size="small" weight="medium">12-hour format</Text>
137
+ <DateTimePicker
138
+ value={dateTime}
139
+ onChange={setDateTime}
140
+ label="Select Date & Time"
141
+ placeholder="Choose date and time"
142
+ helperText="12-hour format with AM/PM"
143
+ />
144
+ {dateTime && (
145
+ <Text size="small" color="secondary">
146
+ Selected: {dateTime.toLocaleString()}
147
+ </Text>
148
+ )}
149
+ </View>
150
+
151
+ {/* 24-hour format */}
152
+ <View spacing="sm">
153
+ <Text size="small" weight="medium">24-hour format with seconds</Text>
154
+ <DateTimePicker
155
+ value={dateTime24h}
156
+ onChange={setDateTime24h}
157
+ label="24-hour with seconds"
158
+ placeholder="Choose date and time"
159
+ timeMode="24h"
160
+ showSeconds={true}
161
+ timeStep={5}
162
+ helperText="24-hour format with seconds, 5-minute steps"
163
+ />
164
+ {dateTime24h && (
165
+ <Text size="small" color="secondary">
166
+ Selected: {dateTime24h.toLocaleString()}
167
+ </Text>
168
+ )}
169
+ </View>
170
+ </View>
171
+
172
+ {/* Date Range Picker Examples */}
173
+ <View spacing="md">
174
+ <Text size="medium" weight="semibold">DateRangePicker Examples</Text>
175
+
176
+ {/* Basic Range */}
177
+ <View spacing="sm">
178
+ <Text size="small" weight="medium">Basic range selection</Text>
179
+ <DateRangePicker
180
+ value={dateRange}
181
+ onChange={setDateRange}
182
+ label="Select Date Range"
183
+ placeholder="Choose start and end dates"
184
+ helperText="Click dates to select a range"
185
+ />
186
+ {dateRange?.startDate && dateRange?.endDate && (
187
+ <Text size="small" color="secondary">
188
+ Range: {dateRange.startDate.toDateString()} to {dateRange.endDate.toDateString()}
189
+ </Text>
190
+ )}
191
+ </View>
192
+
193
+ {/* Restricted Range */}
194
+ <View spacing="sm">
195
+ <Text size="small" weight="medium">Max 14 days, future dates only</Text>
196
+ <DateRangePicker
197
+ value={restrictedRange}
198
+ onChange={setRestrictedRange}
199
+ label="Restricted Range"
200
+ placeholder="Maximum 14 days"
201
+ minDate={tomorrow}
202
+ maxDays={14}
203
+ helperText="Future dates only, max 14 days"
204
+ />
205
+ {restrictedRange?.startDate && restrictedRange?.endDate && (
206
+ <Text size="small" color="secondary">
207
+ Range: {restrictedRange.startDate.toDateString()} to {restrictedRange.endDate.toDateString()}
208
+ </Text>
209
+ )}
210
+ </View>
211
+ </View>
212
+
213
+ {/* Date Time Range Picker Examples */}
214
+ <View spacing="md">
215
+ <Text size="medium" weight="semibold">DateTimeRangePicker Examples</Text>
216
+
217
+ {/* Basic DateTime Range */}
218
+ <View spacing="sm">
219
+ <Text size="small" weight="medium">Date and time range selection</Text>
220
+ <DateTimeRangePicker
221
+ value={dateTimeRange}
222
+ onChange={setDateTimeRange}
223
+ label="Select Date & Time Range"
224
+ placeholder="Choose date/time range"
225
+ helperText="Select date range first, then adjust times"
226
+ />
227
+ {dateTimeRange?.startDate && dateTimeRange?.endDate && (
228
+ <Text size="small" color="secondary">
229
+ Range: {dateTimeRange.startDate.toLocaleString()} to {dateTimeRange.endDate.toLocaleString()}
230
+ </Text>
231
+ )}
232
+ </View>
233
+ </View>
234
+
235
+ {/* Features Description */}
236
+ <View spacing="md">
237
+ <Text size="medium" weight="semibold">Features</Text>
238
+ <View spacing="sm">
239
+ <Text size="small" color="secondary">
240
+ • Cross-platform calendar picker
241
+ </Text>
242
+ <Text size="small" color="secondary">
243
+ • Date and time selection
244
+ </Text>
245
+ <Text size="small" color="secondary">
246
+ • Date range selection
247
+ </Text>
248
+ <Text size="small" color="secondary">
249
+ • Date/time range selection
250
+ </Text>
251
+ <Text size="small" color="secondary">
252
+ • 12/24 hour time formats
253
+ </Text>
254
+ <Text size="small" color="secondary">
255
+ • Min/max date restrictions
256
+ </Text>
257
+ <Text size="small" color="secondary">
258
+ • Multiple size variants
259
+ </Text>
260
+ <Text size="small" color="secondary">
261
+ • Accessible and keyboard navigable
262
+ </Text>
263
+ <Text size="small" color="secondary">
264
+ • Theme-aware styling
265
+ </Text>
266
+ <Text size="small" color="secondary">
267
+ • Customizable date/time formats
268
+ </Text>
269
+ </View>
270
+ </View>
271
+ </View>
272
+ </Screen>
273
+ );
274
+ };
@@ -0,0 +1 @@
1
+ export { DatePickerExamples } from './DatePickerExamples';
@@ -0,0 +1,16 @@
1
+ // Date Picker Components
2
+ export { DatePicker, Calendar } from './DatePicker/index.native';
3
+ export type { DatePickerProps, CalendarProps } from './DatePicker';
4
+
5
+ // Date Time Picker Components
6
+ export { DateTimePicker, TimePicker } from './DateTimePicker/index.native';
7
+ export type { DateTimePickerProps, TimePickerProps } from './DateTimePicker';
8
+
9
+ // Date Range Picker Components
10
+ export { DateRangePicker, RangeCalendar } from './DateRangePicker/index.native';
11
+ export type { DateRangePickerProps, RangeCalendarProps, DateRange } from './DateRangePicker';
12
+
13
+ // Date Time Range Picker Components
14
+ export { DateTimeRangePicker } from './DateTimeRangePicker/index.native';
15
+ export type { DateTimeRangePickerProps, DateTimeRange } from './DateTimeRangePicker';
16
+
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Date Picker Components
2
+ export { DatePicker, Calendar } from './DatePicker';
3
+ export type { DatePickerProps, CalendarProps } from './DatePicker';
4
+
5
+ // Date Time Picker Components
6
+ export { DateTimePicker, TimePicker } from './DateTimePicker';
7
+ export type { DateTimePickerProps, TimePickerProps } from './DateTimePicker';
8
+
9
+ // Date Range Picker Components
10
+ export { DateRangePicker, RangeCalendar } from './DateRangePicker';
11
+ export type { DateRangePickerProps, RangeCalendarProps, DateRange } from './DateRangePicker';
12
+
13
+ // Date Time Range Picker Components
14
+ export { DateTimeRangePicker } from './DateTimeRangePicker';
15
+ export type { DateTimeRangePickerProps, DateTimeRange } from './DateTimeRangePicker';
16
+
@@ -0,0 +1,62 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const calendarGridStyles = StyleSheet.create((theme) => ({
4
+ weekdayHeader: {
5
+ display: 'grid',
6
+ gridTemplateColumns: 'repeat(7, 1fr)',
7
+ gap: 2,
8
+ marginBottom: 8,
9
+
10
+ _web: {
11
+ display: 'grid',
12
+ gridTemplateColumns: 'repeat(7, 1fr)',
13
+ }
14
+ },
15
+ weekdayCell: {
16
+ alignItems: 'center',
17
+ justifyContent: 'center',
18
+ paddingVertical: 4,
19
+
20
+ _web: {
21
+ display: 'flex',
22
+ alignItems: 'center',
23
+ justifyContent: 'center',
24
+ }
25
+ },
26
+ weekdayText: {
27
+ fontSize: 12,
28
+ fontWeight: '500',
29
+ color: theme.colors?.text?.secondary || '#6b7280',
30
+ },
31
+ calendarGrid: {
32
+ gap: 2,
33
+ marginBottom: 8,
34
+ height: 192,
35
+
36
+ _web: {
37
+ display: 'grid',
38
+ gridTemplateColumns: 'repeat(7, 1fr)',
39
+ }
40
+ },
41
+ dayCell: {
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ minHeight: 0,
45
+
46
+ _web: {
47
+ display: 'flex',
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ }
51
+ },
52
+ dayButton: {
53
+ width: '100%',
54
+ height: '100%',
55
+ maxWidth: 36,
56
+ minWidth: 24,
57
+ minHeight: 24,
58
+ padding: 0,
59
+ borderRadius: 4,
60
+ fontSize: 13,
61
+ },
62
+ }));
@@ -0,0 +1,138 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Text, Button, View } from '@idealyst/components';
3
+ import { calendarGridStyles } from './CalendarGrid.styles';
4
+
5
+ interface CalendarGridProps {
6
+ currentMonth: Date;
7
+ selectedDate?: Date;
8
+ onDateSelect: (date: Date) => void;
9
+ onMonthChange?: (month: Date) => void;
10
+ minDate?: Date;
11
+ maxDate?: Date;
12
+ disabled?: boolean;
13
+ }
14
+
15
+
16
+ export const CalendarGrid: React.FC<CalendarGridProps> = ({
17
+ currentMonth,
18
+ selectedDate,
19
+ onDateSelect,
20
+ onMonthChange,
21
+ minDate,
22
+ maxDate,
23
+ disabled = false,
24
+ }) => {
25
+ const { calendarDays, startingDayOfWeek, daysInMonth, weekCount } = useMemo(() => {
26
+ const monthStart = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
27
+ const monthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0);
28
+ const daysInMonth = monthEnd.getDate();
29
+ const startingDayOfWeek = monthStart.getDay();
30
+
31
+ const calendarDays = [];
32
+
33
+ // Add previous month days to fill start of week
34
+ const prevMonthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 0);
35
+ const prevMonthDaysToShow = startingDayOfWeek;
36
+ for (let i = prevMonthDaysToShow - 1; i >= 0; i--) {
37
+ const day = prevMonthEnd.getDate() - i;
38
+ const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, day);
39
+ calendarDays.push({ date, isCurrentMonth: false });
40
+ }
41
+
42
+ // Add days of the current month
43
+ for (let day = 1; day <= daysInMonth; day++) {
44
+ const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
45
+ calendarDays.push({ date, isCurrentMonth: true });
46
+ }
47
+
48
+ // Add next month days only to complete partial weeks, never add full weeks of next month
49
+ const currentLength = calendarDays.length;
50
+ const weeksNeeded = Math.ceil(currentLength / 7);
51
+ const maxDays = weeksNeeded * 7;
52
+
53
+ // Only add next month days to fill partial week
54
+ const remainingDays = maxDays - currentLength;
55
+ for (let day = 1; day <= remainingDays; day++) {
56
+ const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, day);
57
+ calendarDays.push({ date, isCurrentMonth: false });
58
+ }
59
+
60
+ const weekCount = Math.ceil(calendarDays.length / 7);
61
+ return { calendarDays, startingDayOfWeek, daysInMonth, weekCount };
62
+ }, [currentMonth]);
63
+
64
+ const isDateDisabled = (date: Date): boolean => {
65
+ if (disabled) return true;
66
+ if (minDate && date < minDate) return true;
67
+ if (maxDate && date > maxDate) return true;
68
+ return false;
69
+ };
70
+
71
+ const isDateSelected = (date: Date): boolean => {
72
+ if (!selectedDate) return false;
73
+ return (
74
+ date.getDate() === selectedDate.getDate() &&
75
+ date.getMonth() === selectedDate.getMonth() &&
76
+ date.getFullYear() === selectedDate.getFullYear()
77
+ );
78
+ };
79
+
80
+ const isCurrentMonth = (date: Date): boolean => {
81
+ return (
82
+ date.getMonth() === currentMonth.getMonth() &&
83
+ date.getFullYear() === currentMonth.getFullYear()
84
+ );
85
+ };
86
+
87
+ const handleDateSelect = (date: Date) => {
88
+ // If selecting a date from previous/next month, change the current month first
89
+ if (!isCurrentMonth(date) && onMonthChange) {
90
+ const newMonth = new Date(date.getFullYear(), date.getMonth(), 1);
91
+ onMonthChange(newMonth);
92
+ }
93
+ onDateSelect(date);
94
+ };
95
+
96
+ return (
97
+ <>
98
+ {/* Weekday headers */}
99
+ <View style={calendarGridStyles.weekdayHeader}>
100
+ {['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((day) => (
101
+ <View key={day} style={calendarGridStyles.weekdayCell}>
102
+ <Text style={calendarGridStyles.weekdayText}>
103
+ {day}
104
+ </Text>
105
+ </View>
106
+ ))}
107
+ </View>
108
+
109
+ {/* Calendar grid */}
110
+ <View
111
+ style={[calendarGridStyles.calendarGrid, { gridTemplateRows: `repeat(${weekCount}, 1fr)` }]}
112
+ >
113
+ {calendarDays.map((dayData, index) => {
114
+ const { date, isCurrentMonth } = dayData;
115
+ const buttonStyle = [
116
+ calendarGridStyles.dayButton,
117
+ { opacity: isCurrentMonth ? 1 : 0.4 }
118
+ ];
119
+
120
+ return (
121
+ <View key={index} style={calendarGridStyles.dayCell}>
122
+ <Button
123
+ variant={isDateSelected(date) ? 'contained' : 'text'}
124
+ intent={isDateSelected(date) ? 'primary' : 'neutral'}
125
+ disabled={isDateDisabled(date)}
126
+ onPress={() => handleDateSelect(date)}
127
+ size="small"
128
+ style={buttonStyle}
129
+ >
130
+ {date.getDate()}
131
+ </Button>
132
+ </View>
133
+ );
134
+ })}
135
+ </View>
136
+ </>
137
+ );
138
+ };
@@ -0,0 +1 @@
1
+ export { CalendarGrid } from './CalendarGrid';
@@ -0,0 +1,25 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const calendarHeaderStyles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ flexDirection: 'row',
6
+ justifyContent: 'space-between',
7
+ alignItems: 'center',
8
+ marginBottom: 16,
9
+ },
10
+ centerSection: {
11
+ flexDirection: 'row',
12
+ alignItems: 'center',
13
+ gap: 8,
14
+ },
15
+ navButton: {
16
+ minWidth: 32,
17
+ minHeight: 32,
18
+ paddingHorizontal: 8,
19
+ paddingVertical: 4,
20
+ },
21
+ titleButton: {
22
+ paddingHorizontal: 8,
23
+ paddingVertical: 4,
24
+ },
25
+ }));
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import { Text, Button, View } from '@idealyst/components';
3
+ import { calendarHeaderStyles } from './CalendarHeader.styles';
4
+
5
+ interface CalendarHeaderProps {
6
+ currentMonth: Date;
7
+ onPreviousMonth: () => void;
8
+ onNextMonth: () => void;
9
+ onMonthClick: () => void;
10
+ onYearClick: () => void;
11
+ disabled?: boolean;
12
+ }
13
+
14
+
15
+ export const CalendarHeader: React.FC<CalendarHeaderProps> = ({
16
+ currentMonth,
17
+ onPreviousMonth,
18
+ onNextMonth,
19
+ onMonthClick,
20
+ onYearClick,
21
+ disabled = false,
22
+ }) => {
23
+ const monthName = currentMonth.toLocaleDateString('en-US', { month: 'long' });
24
+ const year = currentMonth.getFullYear();
25
+
26
+ return (
27
+ <View style={calendarHeaderStyles.container}>
28
+ <Button
29
+ variant="text"
30
+ size="small"
31
+ onPress={onPreviousMonth}
32
+ disabled={disabled}
33
+ style={calendarHeaderStyles.navButton}
34
+ >
35
+
36
+ </Button>
37
+
38
+ <View style={calendarHeaderStyles.centerSection}>
39
+ <Button
40
+ variant="text"
41
+ onPress={onMonthClick}
42
+ disabled={disabled}
43
+ style={calendarHeaderStyles.titleButton}
44
+ >
45
+ <Text weight="semibold">{monthName}</Text>
46
+ </Button>
47
+
48
+ <Button
49
+ variant="text"
50
+ onPress={onYearClick}
51
+ disabled={disabled}
52
+ style={calendarHeaderStyles.titleButton}
53
+ >
54
+ <Text weight="semibold">{year}</Text>
55
+ </Button>
56
+ </View>
57
+
58
+ <Button
59
+ variant="text"
60
+ size="small"
61
+ onPress={onNextMonth}
62
+ disabled={disabled}
63
+ style={calendarHeaderStyles.navButton}
64
+ >
65
+
66
+ </Button>
67
+ </View>
68
+ );
69
+ };
@@ -0,0 +1 @@
1
+ export { CalendarHeader } from './CalendarHeader';
@@ -0,0 +1,81 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const calendarOverlayStyles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ position: 'absolute',
6
+ top: 40,
7
+ left: 0,
8
+ right: 0,
9
+ backgroundColor: theme.colors?.surface?.primary || 'white',
10
+ borderRadius: 8,
11
+ padding: 12,
12
+ borderWidth: 1,
13
+ borderColor: theme.colors?.border?.primary || '#e5e7eb',
14
+ zIndex: 10,
15
+
16
+ _web: {
17
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
18
+ }
19
+ },
20
+ header: {
21
+ flexDirection: 'row',
22
+ justifyContent: 'space-between',
23
+ alignItems: 'center',
24
+ marginBottom: 12,
25
+ },
26
+ title: {
27
+ fontSize: 14,
28
+ fontWeight: '600',
29
+ margin: 0,
30
+ color: theme.colors?.text?.secondary || '#6b7280',
31
+ },
32
+ closeButton: {
33
+ minWidth: 24,
34
+ minHeight: 24,
35
+ padding: 2,
36
+ fontSize: 12,
37
+ },
38
+ monthGrid: {
39
+ gap: 6,
40
+
41
+ _web: {
42
+ display: 'grid',
43
+ gridTemplateColumns: 'repeat(3, 1fr)',
44
+ }
45
+ },
46
+ yearContainer: {
47
+ flexDirection: 'column',
48
+ gap: 8,
49
+ },
50
+ yearNavigation: {
51
+ flexDirection: 'row',
52
+ justifyContent: 'space-between',
53
+ alignItems: 'center',
54
+ },
55
+ yearGrid: {
56
+ gap: 6,
57
+
58
+ _web: {
59
+ display: 'grid',
60
+ gridTemplateColumns: 'repeat(4, 1fr)',
61
+ }
62
+ },
63
+ yearNavButton: {
64
+ minWidth: 32,
65
+ minHeight: 24,
66
+ paddingHorizontal: 8,
67
+ paddingVertical: 4,
68
+ fontSize: 12,
69
+ },
70
+ yearRangeText: {
71
+ fontSize: 13,
72
+ fontWeight: '500',
73
+ color: theme.colors?.text?.primary || '#374151',
74
+ },
75
+ gridButton: {
76
+ minHeight: 28,
77
+ paddingHorizontal: 8,
78
+ paddingVertical: 4,
79
+ fontSize: 12,
80
+ },
81
+ }));