@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,33 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text } from '@idealyst/components';
3
+ import { DatePickerProps } from './types';
4
+ import { Calendar } from './Calendar.native';
5
+ import { datePickerStyles } from './DatePicker.styles';
6
+
7
+ const DatePicker: React.FC<DatePickerProps> = ({
8
+ value,
9
+ onChange,
10
+ minDate,
11
+ maxDate,
12
+ disabled = false,
13
+ style,
14
+ testID,
15
+ }) => {
16
+ const handleDateSelect = (date: Date) => {
17
+ onChange(date);
18
+ };
19
+
20
+ return (
21
+ <Calendar
22
+ value={value}
23
+ onChange={handleDateSelect}
24
+ minDate={minDate}
25
+ maxDate={maxDate}
26
+ disabled={disabled}
27
+ style={style}
28
+ testID={testID}
29
+ />
30
+ );
31
+ };
32
+
33
+ export default DatePicker;
@@ -0,0 +1,69 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const datePickerStyles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ // Base container styles
6
+ },
7
+
8
+ label: {
9
+ marginBottom: theme.spacing?.sm || 8,
10
+ fontSize: theme.typography?.sizes?.small || 14,
11
+ fontWeight: '500',
12
+ color: theme.colors?.text?.primary || '#1f2937',
13
+ },
14
+
15
+ picker: {
16
+ borderRadius: theme.borderRadius?.md || 8,
17
+ border: `1px solid ${theme.colors?.border?.primary || '#e5e7eb'}`,
18
+ backgroundColor: theme.colors?.surface?.primary || '#ffffff',
19
+ padding: theme.spacing?.md || 16,
20
+ boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',
21
+
22
+ // Native specific styles
23
+ _native: {
24
+ borderWidth: 1,
25
+ borderColor: theme.colors?.border?.primary || '#e5e7eb',
26
+ shadowColor: '#000',
27
+ shadowOffset: { width: 0, height: 1 },
28
+ shadowOpacity: 0.1,
29
+ shadowRadius: 3,
30
+ elevation: 2,
31
+ },
32
+ },
33
+
34
+ selectedDateHeader: {
35
+ marginBottom: theme.spacing?.sm || 12,
36
+ paddingBottom: theme.spacing?.sm || 12,
37
+ borderBottomWidth: 1,
38
+ borderBottomColor: theme.colors?.border?.secondary || '#f3f4f6',
39
+
40
+ // Web specific border
41
+ _web: {
42
+ borderBottom: `1px solid ${theme.colors?.border?.secondary || '#f3f4f6'}`,
43
+ },
44
+ },
45
+
46
+ selectedDateLabel: {
47
+ fontSize: theme.typography?.sizes?.small || 12,
48
+ color: theme.colors?.text?.secondary || '#6b7280',
49
+ marginBottom: 4,
50
+ },
51
+
52
+ selectedDateValue: {
53
+ fontSize: theme.typography?.sizes?.medium || 16,
54
+ fontWeight: '600',
55
+ color: theme.colors?.text?.primary || '#1f2937',
56
+ },
57
+
58
+ errorText: {
59
+ marginTop: 4,
60
+ fontSize: theme.typography?.sizes?.small || 12,
61
+ color: theme.colors?.text?.error || '#dc2626',
62
+ },
63
+
64
+ helperText: {
65
+ marginTop: 4,
66
+ fontSize: theme.typography?.sizes?.small || 12,
67
+ color: theme.colors?.text?.secondary || '#6b7280',
68
+ },
69
+ }));
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { DatePickerProps } from './types';
3
+ import { Calendar } from './Calendar';
4
+
5
+ const DatePicker: React.FC<DatePickerProps> = ({
6
+ value,
7
+ onChange,
8
+ minDate,
9
+ maxDate,
10
+ disabled = false,
11
+ style,
12
+ testID,
13
+ }) => {
14
+ const handleDateSelect = (date: Date) => {
15
+ onChange(date);
16
+ };
17
+
18
+ return (
19
+ <Calendar
20
+ value={value}
21
+ onChange={handleDateSelect}
22
+ minDate={minDate}
23
+ maxDate={maxDate}
24
+ disabled={disabled}
25
+ style={style}
26
+ testID={testID}
27
+ />
28
+ );
29
+ };
30
+
31
+ export default DatePicker;
@@ -0,0 +1,3 @@
1
+ export { default as DatePicker } from './DatePicker.native';
2
+ export { Calendar } from './Calendar.native';
3
+ export type { DatePickerProps, CalendarProps } from './types';
@@ -0,0 +1,3 @@
1
+ export { default as DatePicker } from './DatePicker.web';
2
+ export { Calendar } from './Calendar';
3
+ export type { DatePickerProps, CalendarProps } from './types';
@@ -0,0 +1,78 @@
1
+ import { ReactNode } from 'react';
2
+ import { ViewStyle } from 'react-native';
3
+
4
+ export interface DatePickerProps {
5
+ /** Current selected date */
6
+ value?: Date;
7
+
8
+ /** Called when date changes */
9
+ onChange: (date: Date | null) => void;
10
+
11
+ /** Minimum selectable date */
12
+ minDate?: Date;
13
+
14
+ /** Maximum selectable date */
15
+ maxDate?: Date;
16
+
17
+ /** Disabled state */
18
+ disabled?: boolean;
19
+
20
+ /** Placeholder text when no date is selected */
21
+ placeholder?: string;
22
+
23
+ /** Label for the picker */
24
+ label?: string;
25
+
26
+ /** Error message to display */
27
+ error?: string;
28
+
29
+ /** Helper text */
30
+ helperText?: string;
31
+
32
+ /** Date format for display (default: 'MM/dd/yyyy') */
33
+ format?: string;
34
+
35
+ /** Locale for date formatting */
36
+ locale?: string;
37
+
38
+ /** Size variant */
39
+ size?: 'small' | 'medium' | 'large';
40
+
41
+ /** Visual variant */
42
+ variant?: 'outlined' | 'filled';
43
+
44
+ /** Custom styles */
45
+ style?: ViewStyle;
46
+
47
+ /** Test ID for testing */
48
+ testID?: string;
49
+ }
50
+
51
+ export interface CalendarProps {
52
+ /** Current selected date */
53
+ value?: Date;
54
+
55
+ /** Called when date is selected */
56
+ onChange: (date: Date) => void;
57
+
58
+ /** Minimum selectable date */
59
+ minDate?: Date;
60
+
61
+ /** Maximum selectable date */
62
+ maxDate?: Date;
63
+
64
+ /** Disabled state */
65
+ disabled?: boolean;
66
+
67
+ /** Current month being viewed */
68
+ currentMonth?: Date;
69
+
70
+ /** Called when month changes */
71
+ onMonthChange?: (month: Date) => void;
72
+
73
+ /** Custom styles */
74
+ style?: ViewStyle;
75
+
76
+ /** Test ID for testing */
77
+ testID?: string;
78
+ }
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { View, Text } from '@idealyst/components';
3
+ import { DateRangePickerProps, DateRange } from './types';
4
+ import { RangeCalendar } from './RangeCalendar.native';
5
+ import { dateRangePickerStyles } from './DateRangePicker.styles';
6
+
7
+ const DateRangePicker: React.FC<DateRangePickerProps> = ({
8
+ value,
9
+ onChange,
10
+ minDate,
11
+ maxDate,
12
+ disabled = false,
13
+ allowSameDay = true,
14
+ maxDays,
15
+ style,
16
+ testID,
17
+ }) => {
18
+
19
+ const handleRangeChange = (newRange: DateRange) => {
20
+ onChange(newRange.startDate || newRange.endDate ? newRange : null);
21
+ };
22
+
23
+
24
+ return (
25
+ <RangeCalendar
26
+ value={value || {}}
27
+ onChange={handleRangeChange}
28
+ minDate={minDate}
29
+ maxDate={maxDate}
30
+ disabled={disabled}
31
+ allowSameDay={allowSameDay}
32
+ maxDays={maxDays}
33
+ style={style}
34
+ testID={testID}
35
+ />
36
+ );
37
+ };
38
+
39
+ export default DateRangePicker;
@@ -0,0 +1,83 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const dateRangePickerStyles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ gap: theme.spacing?.md || 16,
6
+ },
7
+
8
+ label: {
9
+ fontSize: theme.typography?.sizes?.small || 14,
10
+ fontWeight: '600',
11
+ color: theme.colors?.text?.primary || '#111827',
12
+ marginBottom: theme.spacing?.xs || 4,
13
+ },
14
+
15
+ picker: {
16
+ gap: theme.spacing?.md || 16,
17
+ },
18
+
19
+ selectedRangeHeader: {
20
+ padding: theme.spacing?.sm || 12,
21
+ backgroundColor: theme.colors?.surface?.secondary || '#f9fafb',
22
+ borderRadius: theme.borderRadius?.md || 8,
23
+ borderWidth: 1,
24
+ borderColor: theme.colors?.border?.primary || '#e5e7eb',
25
+ gap: theme.spacing?.xs || 4,
26
+
27
+ _web: {
28
+ border: `1px solid ${theme.colors?.border?.primary || '#e5e7eb'}`,
29
+ },
30
+ },
31
+
32
+ selectedRangeLabel: {
33
+ fontSize: theme.typography?.sizes?.small || 12,
34
+ fontWeight: '500',
35
+ color: theme.colors?.text?.secondary || '#6b7280',
36
+ },
37
+
38
+ selectedRangeValue: {
39
+ fontSize: theme.typography?.sizes?.medium || 16,
40
+ fontWeight: '600',
41
+ color: theme.colors?.text?.primary || '#111827',
42
+ },
43
+
44
+ rangeInputs: {
45
+ flexDirection: 'row',
46
+ alignItems: 'center',
47
+ gap: theme.spacing?.sm || 12,
48
+ },
49
+
50
+ rangeInput: {
51
+ flex: 1,
52
+ padding: theme.spacing?.sm || 12,
53
+ borderWidth: 1,
54
+ borderColor: theme.colors?.border?.primary || '#e5e7eb',
55
+ borderRadius: theme.borderRadius?.md || 8,
56
+ backgroundColor: theme.colors?.surface?.primary || '#ffffff',
57
+ fontSize: theme.typography?.sizes?.medium || 16,
58
+ textAlign: 'center',
59
+
60
+ _web: {
61
+ border: `1px solid ${theme.colors?.border?.primary || '#e5e7eb'}`,
62
+ outline: 'none',
63
+ },
64
+ },
65
+
66
+ rangeSeparator: {
67
+ fontSize: theme.typography?.sizes?.medium || 16,
68
+ fontWeight: '500',
69
+ color: theme.colors?.text?.secondary || '#6b7280',
70
+ },
71
+
72
+ errorText: {
73
+ fontSize: theme.typography?.sizes?.small || 12,
74
+ color: theme.colors?.semantic?.error || '#dc2626',
75
+ marginTop: theme.spacing?.xs || 4,
76
+ },
77
+
78
+ helperText: {
79
+ fontSize: theme.typography?.sizes?.small || 12,
80
+ color: theme.colors?.text?.secondary || '#6b7280',
81
+ marginTop: theme.spacing?.xs || 4,
82
+ },
83
+ }));
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { View, Text } from '@idealyst/components';
3
+ import { getWebProps } from 'react-native-unistyles/web';
4
+ import { DateRangePickerProps, DateRange } from './types';
5
+ import { RangeCalendar } from './RangeCalendar.web';
6
+ import { dateRangePickerStyles } from './DateRangePicker.styles';
7
+
8
+ const DateRangePicker: React.FC<DateRangePickerProps> = ({
9
+ value,
10
+ onChange,
11
+ minDate,
12
+ maxDate,
13
+ disabled = false,
14
+ allowSameDay = true,
15
+ maxDays,
16
+ style,
17
+ testID,
18
+ }) => {
19
+
20
+ const handleRangeChange = (newRange: DateRange) => {
21
+ onChange(newRange.startDate || newRange.endDate ? newRange : null);
22
+ };
23
+
24
+
25
+ return (
26
+ <RangeCalendar
27
+ value={value || {}}
28
+ onChange={handleRangeChange}
29
+ minDate={minDate}
30
+ maxDate={maxDate}
31
+ disabled={disabled}
32
+ allowSameDay={allowSameDay}
33
+ maxDays={maxDays}
34
+ style={style}
35
+ testID={testID}
36
+ />
37
+ );
38
+ };
39
+
40
+ export default DateRangePicker;
@@ -0,0 +1,267 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import { View, Text, Button } from '@idealyst/components';
3
+ import { TouchableOpacity } from 'react-native';
4
+ import { RangeCalendarProps, DateRange } from './types';
5
+ import { rangeCalendarStyles } from './RangeCalendar.styles';
6
+
7
+ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
8
+ value = {},
9
+ onChange,
10
+ minDate,
11
+ maxDate,
12
+ disabled = false,
13
+ currentMonth: controlledCurrentMonth,
14
+ onMonthChange,
15
+ allowSameDay = true,
16
+ maxDays,
17
+ style,
18
+ testID,
19
+ }) => {
20
+ const [internalCurrentMonth, setInternalCurrentMonth] = useState(
21
+ controlledCurrentMonth || value?.startDate || new Date()
22
+ );
23
+ const [selectingEnd, setSelectingEnd] = useState(false);
24
+
25
+ const currentMonth = controlledCurrentMonth || internalCurrentMonth;
26
+
27
+ const handleMonthChange = (newMonth: Date) => {
28
+ if (onMonthChange) {
29
+ onMonthChange(newMonth);
30
+ } else {
31
+ setInternalCurrentMonth(newMonth);
32
+ }
33
+ };
34
+
35
+ const { monthStart, monthEnd, daysInMonth, startingDayOfWeek } = useMemo(() => {
36
+ const monthStart = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
37
+ const monthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0);
38
+ const daysInMonth = monthEnd.getDate();
39
+ const startingDayOfWeek = monthStart.getDay();
40
+
41
+ return { monthStart, monthEnd, daysInMonth, startingDayOfWeek };
42
+ }, [currentMonth]);
43
+
44
+ const isDateDisabled = (date: Date): boolean => {
45
+ if (disabled) return true;
46
+ if (minDate && date < minDate) return true;
47
+ if (maxDate && date > maxDate) return true;
48
+ return false;
49
+ };
50
+
51
+ const isDateInRange = (date: Date): boolean => {
52
+ const { startDate, endDate } = value || {};
53
+ if (!startDate || !endDate) return false;
54
+ return date > startDate && date < endDate;
55
+ };
56
+
57
+ const isDateRangeStart = (date: Date): boolean => {
58
+ const { startDate } = value || {};
59
+ if (!startDate) return false;
60
+ return (
61
+ date.getDate() === startDate.getDate() &&
62
+ date.getMonth() === startDate.getMonth() &&
63
+ date.getFullYear() === startDate.getFullYear()
64
+ );
65
+ };
66
+
67
+ const isDateRangeEnd = (date: Date): boolean => {
68
+ const { endDate } = value || {};
69
+ if (!endDate) return false;
70
+ return (
71
+ date.getDate() === endDate.getDate() &&
72
+ date.getMonth() === endDate.getMonth() &&
73
+ date.getFullYear() === endDate.getFullYear()
74
+ );
75
+ };
76
+
77
+ const isDateSelected = (date: Date): boolean => {
78
+ return isDateRangeStart(date) || isDateRangeEnd(date);
79
+ };
80
+
81
+ const handleDateClick = (date: Date) => {
82
+ if (isDateDisabled(date)) return;
83
+
84
+ const { startDate, endDate } = value || {};
85
+
86
+ // If no range is selected or we're starting fresh
87
+ if (!startDate || (startDate && endDate)) {
88
+ onChange({ startDate: date, endDate: undefined });
89
+ setSelectingEnd(true);
90
+ return;
91
+ }
92
+
93
+ // If we have a start date but no end date
94
+ if (startDate && !endDate) {
95
+ let newStartDate = startDate;
96
+ let newEndDate = date;
97
+
98
+ // Swap if end date is before start date
99
+ if (date < startDate) {
100
+ newStartDate = date;
101
+ newEndDate = startDate;
102
+ }
103
+
104
+ // Check if same day selection is allowed
105
+ if (!allowSameDay && newStartDate.getTime() === newEndDate.getTime()) {
106
+ return;
107
+ }
108
+
109
+ // Check max days constraint
110
+ if (maxDays) {
111
+ const daysDiff = Math.ceil((newEndDate.getTime() - newStartDate.getTime()) / (1000 * 60 * 60 * 24));
112
+ if (daysDiff > maxDays) {
113
+ return;
114
+ }
115
+ }
116
+
117
+ onChange({ startDate: newStartDate, endDate: newEndDate });
118
+ setSelectingEnd(false);
119
+ }
120
+ };
121
+
122
+ const goToPreviousMonth = () => {
123
+ const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
124
+ handleMonthChange(newMonth);
125
+ };
126
+
127
+ const goToNextMonth = () => {
128
+ const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
129
+ handleMonthChange(newMonth);
130
+ };
131
+
132
+ const handlePresetRange = (days: number) => {
133
+ const startDate = new Date();
134
+ const endDate = new Date();
135
+ endDate.setDate(endDate.getDate() + days - 1);
136
+
137
+ onChange({ startDate, endDate });
138
+ setSelectingEnd(false);
139
+ };
140
+
141
+ const clearRange = () => {
142
+ onChange({});
143
+ setSelectingEnd(false);
144
+ };
145
+
146
+ const monthName = currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
147
+
148
+ // Create calendar grid
149
+ const calendarDays = [];
150
+
151
+ // Add empty cells for days before month starts
152
+ for (let i = 0; i < startingDayOfWeek; i++) {
153
+ calendarDays.push(null);
154
+ }
155
+
156
+ // Add days of the month
157
+ for (let day = 1; day <= daysInMonth; day++) {
158
+ const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
159
+ calendarDays.push(date);
160
+ }
161
+
162
+ rangeCalendarStyles.useVariants({});
163
+
164
+ return (
165
+ <View style={[rangeCalendarStyles.container, style]} testID={testID}>
166
+ {/* Header */}
167
+ <View style={rangeCalendarStyles.header}>
168
+ <Button
169
+ variant="text"
170
+ size="small"
171
+ onPress={goToPreviousMonth}
172
+ disabled={disabled}
173
+ style={rangeCalendarStyles.headerButton}
174
+ >
175
+
176
+ </Button>
177
+ <Text weight="semibold">{monthName}</Text>
178
+ <Button
179
+ variant="text"
180
+ size="small"
181
+ onPress={goToNextMonth}
182
+ disabled={disabled}
183
+ style={rangeCalendarStyles.headerButton}
184
+ >
185
+
186
+ </Button>
187
+ </View>
188
+
189
+ {/* Weekday headers */}
190
+ <View style={rangeCalendarStyles.weekdayHeader}>
191
+ {['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((day) => (
192
+ <View key={day} style={rangeCalendarStyles.weekdayCell}>
193
+ <Text style={rangeCalendarStyles.weekdayText}>
194
+ {day}
195
+ </Text>
196
+ </View>
197
+ ))}
198
+ </View>
199
+
200
+ {/* Calendar grid */}
201
+ <View style={rangeCalendarStyles.calendarGrid}>
202
+ {calendarDays.map((date, index) => (
203
+ <View key={index} style={rangeCalendarStyles.dayCell}>
204
+ {date && (
205
+ <TouchableOpacity
206
+ onPress={() => handleDateClick(date)}
207
+ disabled={isDateDisabled(date)}
208
+ style={[
209
+ rangeCalendarStyles.dayButton,
210
+ {
211
+ backgroundColor: isDateSelected(date)
212
+ ? '#3b82f6'
213
+ : isDateInRange(date)
214
+ ? '#3b82f620'
215
+ : 'transparent',
216
+ opacity: isDateDisabled(date) ? 0.5 : 1,
217
+ }
218
+ ]}
219
+ >
220
+ <Text
221
+ style={{
222
+ color: isDateSelected(date) ? 'white' : 'black',
223
+ fontSize: 13,
224
+ fontWeight: isDateSelected(date) ? '600' : '400',
225
+ }}
226
+ >
227
+ {date.getDate()}
228
+ </Text>
229
+ </TouchableOpacity>
230
+ )}
231
+ </View>
232
+ ))}
233
+ </View>
234
+
235
+ {/* Range presets */}
236
+ <View style={rangeCalendarStyles.rangePresets}>
237
+ <Button
238
+ variant="text"
239
+ size="small"
240
+ onPress={() => handlePresetRange(7)}
241
+ disabled={disabled}
242
+ style={rangeCalendarStyles.presetButton}
243
+ >
244
+ Next 7 days
245
+ </Button>
246
+ <Button
247
+ variant="text"
248
+ size="small"
249
+ onPress={() => handlePresetRange(30)}
250
+ disabled={disabled}
251
+ style={rangeCalendarStyles.presetButton}
252
+ >
253
+ Next 30 days
254
+ </Button>
255
+ <Button
256
+ variant="outlined"
257
+ size="small"
258
+ onPress={clearRange}
259
+ disabled={disabled}
260
+ style={rangeCalendarStyles.clearButton}
261
+ >
262
+ Clear Range
263
+ </Button>
264
+ </View>
265
+ </View>
266
+ );
267
+ };