@idealyst/datepicker 1.1.3 → 1.1.5

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 (80) hide show
  1. package/package.json +6 -5
  2. package/src/DateInput.native.tsx +155 -0
  3. package/src/DateInput.tsx +2 -0
  4. package/src/DateInput.web.tsx +146 -0
  5. package/src/DatePicker.tsx +276 -0
  6. package/src/DateTimePicker.tsx +89 -0
  7. package/src/TimeInput.native.tsx +175 -0
  8. package/src/TimeInput.tsx +2 -0
  9. package/src/TimeInput.web.tsx +171 -0
  10. package/src/TimePicker.tsx +106 -0
  11. package/src/examples/DatePickerExamples.tsx +113 -149
  12. package/src/examples/index.ts +1 -1
  13. package/src/index.native.ts +15 -20
  14. package/src/index.ts +14 -19
  15. package/src/styles.ts +127 -0
  16. package/src/types.ts +56 -0
  17. package/src/DateInput/DateInput.native.tsx +0 -61
  18. package/src/DateInput/DateInput.styles.tsx +0 -26
  19. package/src/DateInput/DateInput.web.tsx +0 -61
  20. package/src/DateInput/DateInputBase.tsx +0 -228
  21. package/src/DateInput/index.native.ts +0 -2
  22. package/src/DateInput/index.ts +0 -2
  23. package/src/DateInput/types.ts +0 -60
  24. package/src/DatePicker/Calendar.native.tsx +0 -261
  25. package/src/DatePicker/Calendar.styles.tsx +0 -230
  26. package/src/DatePicker/Calendar.web.tsx +0 -159
  27. package/src/DatePicker/DatePicker.native.tsx +0 -51
  28. package/src/DatePicker/DatePicker.styles.tsx +0 -76
  29. package/src/DatePicker/DatePicker.web.tsx +0 -31
  30. package/src/DatePicker/index.native.ts +0 -3
  31. package/src/DatePicker/index.ts +0 -3
  32. package/src/DatePicker/types.ts +0 -78
  33. package/src/DateRangePicker/DateRangePicker.native.tsx +0 -39
  34. package/src/DateRangePicker/DateRangePicker.styles.tsx +0 -83
  35. package/src/DateRangePicker/DateRangePicker.web.tsx +0 -40
  36. package/src/DateRangePicker/RangeCalendar.native.tsx +0 -355
  37. package/src/DateRangePicker/RangeCalendar.styles.tsx +0 -200
  38. package/src/DateRangePicker/RangeCalendar.web.tsx +0 -384
  39. package/src/DateRangePicker/index.native.ts +0 -3
  40. package/src/DateRangePicker/index.ts +0 -3
  41. package/src/DateRangePicker/types.ts +0 -107
  42. package/src/DateTimePicker/DateTimePicker.native.tsx +0 -24
  43. package/src/DateTimePicker/DateTimePicker.styles.tsx +0 -104
  44. package/src/DateTimePicker/DateTimePicker.tsx +0 -21
  45. package/src/DateTimePicker/DateTimePickerBase.tsx +0 -185
  46. package/src/DateTimePicker/TimePicker.native.tsx +0 -17
  47. package/src/DateTimePicker/TimePicker.styles.tsx +0 -115
  48. package/src/DateTimePicker/TimePicker.tsx +0 -14
  49. package/src/DateTimePicker/TimePickerBase.tsx +0 -232
  50. package/src/DateTimePicker/index.native.ts +0 -3
  51. package/src/DateTimePicker/index.ts +0 -3
  52. package/src/DateTimePicker/primitives/ClockFace.native.tsx +0 -195
  53. package/src/DateTimePicker/primitives/ClockFace.web.tsx +0 -168
  54. package/src/DateTimePicker/primitives/TimeInput.native.tsx +0 -53
  55. package/src/DateTimePicker/primitives/TimeInput.web.tsx +0 -66
  56. package/src/DateTimePicker/primitives/index.native.ts +0 -2
  57. package/src/DateTimePicker/primitives/index.ts +0 -2
  58. package/src/DateTimePicker/primitives/index.web.ts +0 -2
  59. package/src/DateTimePicker/types.ts +0 -80
  60. package/src/DateTimePicker/utils/dimensions.native.ts +0 -9
  61. package/src/DateTimePicker/utils/dimensions.ts +0 -9
  62. package/src/DateTimePicker/utils/dimensions.web.ts +0 -33
  63. package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +0 -24
  64. package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +0 -118
  65. package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +0 -21
  66. package/src/DateTimeRangePicker/DateTimeRangePickerBase.tsx +0 -327
  67. package/src/DateTimeRangePicker/index.native.ts +0 -2
  68. package/src/DateTimeRangePicker/index.ts +0 -2
  69. package/src/DateTimeRangePicker/types.ts +0 -70
  70. package/src/primitives/CalendarGrid/CalendarGrid.styles.tsx +0 -51
  71. package/src/primitives/CalendarGrid/CalendarGrid.tsx +0 -146
  72. package/src/primitives/CalendarGrid/index.ts +0 -1
  73. package/src/primitives/CalendarHeader/CalendarHeader.styles.tsx +0 -25
  74. package/src/primitives/CalendarHeader/CalendarHeader.tsx +0 -69
  75. package/src/primitives/CalendarHeader/index.ts +0 -1
  76. package/src/primitives/CalendarOverlay/CalendarOverlay.styles.tsx +0 -86
  77. package/src/primitives/CalendarOverlay/CalendarOverlay.tsx +0 -136
  78. package/src/primitives/CalendarOverlay/index.ts +0 -1
  79. package/src/primitives/Wrapper/Wrapper.web.tsx +0 -33
  80. package/src/primitives/Wrapper/index.ts +0 -1
@@ -1,61 +0,0 @@
1
- import React from 'react';
2
- import { View, Input, Text } from '@idealyst/components';
3
- import { DateInputBase } from './DateInputBase';
4
- import { DateInputProps } from './types';
5
- import { dateInputStyles } from './DateInput.styles';
6
-
7
- export const DateInput: React.FC<DateInputProps> = (props) => {
8
- const {
9
- label,
10
- error,
11
- helperText,
12
- size = 'md',
13
- variant = 'outlined',
14
- disabled = false,
15
- style,
16
- inputStyle,
17
- testID,
18
- ...baseProps
19
- } = props;
20
-
21
-
22
- return (
23
- <View style={style} testID={testID}>
24
- {label && (
25
- <Text style={dateInputStyles.label} testID={testID ? `${testID}-label` : undefined}>
26
- {label}
27
- </Text>
28
- )}
29
-
30
- <DateInputBase {...baseProps} disabled={disabled} testID={testID}>
31
- {({ value, onChangeText, onFocus, onBlur, placeholder, disabled: inputDisabled, testID: inputTestID }) => (
32
- <Input
33
- value={value}
34
- onChangeText={onChangeText}
35
- onFocus={onFocus}
36
- onBlur={onBlur}
37
- placeholder={placeholder}
38
- disabled={inputDisabled}
39
- size={size}
40
- variant={variant}
41
- hasError={error ? true : false}
42
- style={inputStyle}
43
- testID={inputTestID}
44
- />
45
- )}
46
- </DateInputBase>
47
-
48
- {error && (
49
- <Text style={dateInputStyles.errorText} testID={testID ? `${testID}-error` : undefined}>
50
- {error}
51
- </Text>
52
- )}
53
-
54
- {!error && helperText && (
55
- <Text style={dateInputStyles.helperText} testID={testID ? `${testID}-helper` : undefined}>
56
- {helperText}
57
- </Text>
58
- )}
59
- </View>
60
- );
61
- };
@@ -1,228 +0,0 @@
1
- import React, { useState, useCallback, useRef, useEffect } from 'react';
2
- import { View } from '@idealyst/components';
3
- import { DateInputProps } from './types';
4
- import { dateInputStyles } from './DateInput.styles';
5
-
6
- interface DateInputBaseProps extends DateInputProps {
7
- children: (props: {
8
- value: string;
9
- onChangeText: (text: string) => void;
10
- onFocus: () => void;
11
- onBlur: () => void;
12
- placeholder?: string;
13
- disabled: boolean;
14
- testID?: string;
15
- }) => React.ReactNode;
16
- }
17
-
18
- // Common date formats for parsing
19
- const DEFAULT_INPUT_FORMATS = [
20
- 'MM/dd/yyyy',
21
- 'M/d/yyyy',
22
- 'MM/dd/yy',
23
- 'M/d/yy',
24
- 'yyyy-MM-dd',
25
- 'MM-dd-yyyy',
26
- 'M-d-yyyy',
27
- 'dd/MM/yyyy',
28
- 'd/M/yyyy',
29
- 'dd-MM-yyyy',
30
- 'd-M-yyyy',
31
- 'yyyy/MM/dd',
32
- 'MMM dd, yyyy',
33
- 'MMM d, yyyy',
34
- 'MMMM dd, yyyy',
35
- 'MMMM d, yyyy',
36
- ];
37
-
38
- const DEFAULT_DISPLAY_FORMAT = 'MMMM d, yyyy';
39
-
40
- export const DateInputBase: React.FC<DateInputBaseProps> = ({
41
- value,
42
- onChange,
43
- minDate,
44
- maxDate,
45
- disabled = false,
46
- placeholder = 'Enter date...',
47
- displayFormat = DEFAULT_DISPLAY_FORMAT,
48
- inputFormats = DEFAULT_INPUT_FORMATS,
49
- locale = 'en-US',
50
- style,
51
- testID,
52
- onFocus,
53
- onBlur,
54
- children,
55
- }) => {
56
- const [inputValue, setInputValue] = useState('');
57
- const [isFocused, setIsFocused] = useState(false);
58
- const [hasError, setHasError] = useState(false);
59
- const inputRef = useRef<any>(null);
60
-
61
- // Format date for display when not focused
62
- const formatDateForDisplay = useCallback((date: Date) => {
63
- try {
64
- return date.toLocaleDateString(locale, {
65
- year: 'numeric',
66
- month: 'long',
67
- day: 'numeric',
68
- });
69
- } catch {
70
- return date.toLocaleDateString('en-US', {
71
- year: 'numeric',
72
- month: 'long',
73
- day: 'numeric',
74
- });
75
- }
76
- }, [locale]);
77
-
78
- // Parse date from various input formats
79
- const parseDate = useCallback((dateString: string): Date | null => {
80
- if (!dateString.trim()) return null;
81
-
82
- // Try direct Date parsing first
83
- const directParse = new Date(dateString);
84
- if (!isNaN(directParse.getTime())) {
85
- return directParse;
86
- }
87
-
88
- // Try common formats
89
- const trimmed = dateString.trim();
90
-
91
- // Handle MM/dd/yyyy and variations
92
- const slashFormats = [
93
- /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
94
- /^(\d{1,2})\/(\d{1,2})\/(\d{2})$/,
95
- ];
96
-
97
- for (const format of slashFormats) {
98
- const match = trimmed.match(format);
99
- if (match) {
100
- const [, month, day, year] = match;
101
- const fullYear = year.length === 2 ? 2000 + parseInt(year) : parseInt(year);
102
- const date = new Date(fullYear, parseInt(month) - 1, parseInt(day));
103
- if (!isNaN(date.getTime())) return date;
104
- }
105
- }
106
-
107
- // Handle dd/MM/yyyy variations (European format)
108
- const europeanMatch = trimmed.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
109
- if (europeanMatch) {
110
- const [, day, month, year] = europeanMatch;
111
- const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
112
- if (!isNaN(date.getTime())) return date;
113
- }
114
-
115
- // Handle yyyy-MM-dd (ISO format)
116
- const isoMatch = trimmed.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
117
- if (isoMatch) {
118
- const [, year, month, day] = isoMatch;
119
- const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
120
- if (!isNaN(date.getTime())) return date;
121
- }
122
-
123
- // Handle dash formats
124
- const dashFormats = [
125
- /^(\d{1,2})-(\d{1,2})-(\d{4})$/,
126
- /^(\d{4})-(\d{1,2})-(\d{1,2})$/,
127
- ];
128
-
129
- for (const format of dashFormats) {
130
- const match = trimmed.match(format);
131
- if (match) {
132
- const [, first, second, third] = match;
133
- let date: Date;
134
-
135
- if (third.length === 4) {
136
- // MM-dd-yyyy or dd-MM-yyyy
137
- date = new Date(parseInt(third), parseInt(first) - 1, parseInt(second));
138
- } else {
139
- // yyyy-MM-dd
140
- date = new Date(parseInt(first), parseInt(second) - 1, parseInt(third));
141
- }
142
-
143
- if (!isNaN(date.getTime())) return date;
144
- }
145
- }
146
-
147
- return null;
148
- }, []);
149
-
150
- // Validate date against constraints
151
- const validateDate = useCallback((date: Date): boolean => {
152
- if (minDate && date < minDate) return false;
153
- if (maxDate && date > maxDate) return false;
154
- return true;
155
- }, [minDate, maxDate]);
156
-
157
- // Update input value when value prop changes
158
- useEffect(() => {
159
- if (isFocused) {
160
- // When focused, keep the raw input value
161
- return;
162
- }
163
-
164
- if (value) {
165
- setInputValue(formatDateForDisplay(value));
166
- setHasError(false);
167
- } else {
168
- setInputValue('');
169
- setHasError(false);
170
- }
171
- }, [value, isFocused, formatDateForDisplay]);
172
-
173
- const handleFocus = useCallback(() => {
174
- setIsFocused(true);
175
- // Switch to raw input format when focused
176
- if (value) {
177
- // Show in a common input format for editing
178
- const editFormat = value.toLocaleDateString('en-US');
179
- setInputValue(editFormat);
180
- }
181
- onFocus?.();
182
- }, [value, onFocus]);
183
-
184
- const handleBlur = useCallback(() => {
185
- setIsFocused(false);
186
-
187
- if (inputValue.trim()) {
188
- const parsedDate = parseDate(inputValue);
189
-
190
- if (parsedDate && validateDate(parsedDate)) {
191
- onChange(parsedDate);
192
- setHasError(false);
193
- } else {
194
- setHasError(true);
195
- // Revert to previous valid value
196
- if (value) {
197
- setInputValue(formatDateForDisplay(value));
198
- } else {
199
- setInputValue('');
200
- }
201
- }
202
- } else {
203
- onChange(null);
204
- setHasError(false);
205
- }
206
-
207
- onBlur?.();
208
- }, [inputValue, parseDate, validateDate, onChange, value, formatDateForDisplay, onBlur]);
209
-
210
- const handleChangeText = useCallback((text: string) => {
211
- setInputValue(text);
212
- setHasError(false);
213
- }, []);
214
-
215
- return (
216
- <View style={[dateInputStyles.container, style]} testID={testID}>
217
- {children({
218
- value: inputValue,
219
- onChangeText: handleChangeText,
220
- onFocus: handleFocus,
221
- onBlur: handleBlur,
222
- placeholder,
223
- disabled,
224
- testID: testID ? `${testID}-input` : undefined,
225
- })}
226
- </View>
227
- );
228
- };
@@ -1,2 +0,0 @@
1
- export { DateInput } from './DateInput.native';
2
- export type { DateInputProps } from './types';
@@ -1,2 +0,0 @@
1
- export { DateInput } from './DateInput.web';
2
- export type { DateInputProps } from './types';
@@ -1,60 +0,0 @@
1
- import { ViewStyle, TextStyle } from 'react-native';
2
-
3
- export interface DateInputProps {
4
- /** Current selected date */
5
- value?: Date;
6
-
7
- /** Called when date changes */
8
- onChange: (date: Date | null) => void;
9
-
10
- /** Minimum selectable date */
11
- minDate?: Date;
12
-
13
- /** Maximum selectable date */
14
- maxDate?: Date;
15
-
16
- /** Disabled state */
17
- disabled?: boolean;
18
-
19
- /** Placeholder text when no date is selected */
20
- placeholder?: string;
21
-
22
- /** Label for the input */
23
- label?: string;
24
-
25
- /** Error message to display */
26
- error?: string;
27
-
28
- /** Helper text */
29
- helperText?: string;
30
-
31
- /** Date format for display when not focused (default: 'MMMM d, yyyy') */
32
- displayFormat?: string;
33
-
34
- /** Accepted input formats for parsing (default includes common formats) */
35
- inputFormats?: string[];
36
-
37
- /** Locale for date formatting */
38
- locale?: string;
39
-
40
- /** Size variant */
41
- size?: 'sm' | 'md' | 'lg';
42
-
43
- /** Visual variant */
44
- variant?: 'outlined' | 'filled';
45
-
46
- /** Custom styles */
47
- style?: ViewStyle;
48
-
49
- /** Custom text input styles */
50
- inputStyle?: TextStyle;
51
-
52
- /** Test ID for testing */
53
- testID?: string;
54
-
55
- /** Called when input is focused */
56
- onFocus?: () => void;
57
-
58
- /** Called when input is blurred */
59
- onBlur?: () => void;
60
- }
@@ -1,261 +0,0 @@
1
- import React, { useState, useMemo, useCallback, memo, useRef } from 'react';
2
- import { View, Text, Button } from '@idealyst/components';
3
- import { TouchableOpacity, StyleSheet } from 'react-native';
4
- import { CalendarProps } from './types';
5
- import { calendarStyles } from './Calendar.styles';
6
-
7
- // Memoized day cell component for better performance
8
- const DayCell = memo(({
9
- date,
10
- isSelected,
11
- isDisabled,
12
- onPress,
13
- dayButtonStyle,
14
- textColorSelected,
15
- textColorDefault
16
- }: {
17
- date: Date | null;
18
- isSelected: boolean;
19
- isDisabled: boolean;
20
- onPress: (date: Date) => void;
21
- dayButtonStyle: any;
22
- textColorSelected: string;
23
- textColorDefault: string;
24
- }) => {
25
- const handlePress = useCallback(() => {
26
- if (date && !isDisabled) {
27
- onPress(date);
28
- }
29
- }, [date, isDisabled, onPress]);
30
-
31
- if (!date) {
32
- return <View style={calendarStyles.dayCell} />;
33
- }
34
-
35
- return (
36
- <View style={calendarStyles.dayCell}>
37
- <TouchableOpacity
38
- onPressIn={handlePress}
39
- disabled={isDisabled}
40
- style={[
41
- dayButtonStyle,
42
- isSelected && styles.selectedButton,
43
- isDisabled && styles.disabledButton
44
- ]}
45
- >
46
- <Text
47
- style={[
48
- styles.dayText,
49
- { color: isSelected ? textColorSelected : textColorDefault }
50
- ]}
51
- >
52
- {date.getDate()}
53
- </Text>
54
- </TouchableOpacity>
55
- </View>
56
- );
57
- }, (prevProps, nextProps) => {
58
- // Custom comparison function to prevent unnecessary re-renders
59
- // Only re-render if the relevant props actually changed
60
- return (
61
- prevProps.date === nextProps.date &&
62
- prevProps.isSelected === nextProps.isSelected &&
63
- prevProps.isDisabled === nextProps.isDisabled &&
64
- prevProps.onPress === nextProps.onPress
65
- );
66
- });
67
-
68
- DayCell.displayName = 'DayCell';
69
-
70
- export const Calendar: React.FC<CalendarProps> = memo(({
71
- value,
72
- onChange,
73
- minDate,
74
- maxDate,
75
- disabled = false,
76
- currentMonth: controlledCurrentMonth,
77
- onMonthChange,
78
- style,
79
- testID,
80
- }) => {
81
- const [internalCurrentMonth, setInternalCurrentMonth] = useState(
82
- controlledCurrentMonth || value || new Date()
83
- );
84
-
85
- const currentMonth = controlledCurrentMonth || internalCurrentMonth;
86
-
87
- // Store latest callbacks in refs to avoid recreating functions
88
- const onChangeRef = useRef(onChange);
89
- const onMonthChangeRef = useRef(onMonthChange);
90
- onChangeRef.current = onChange;
91
- onMonthChangeRef.current = onMonthChange;
92
-
93
- const handleMonthChange = useCallback((newMonth: Date) => {
94
- if (onMonthChangeRef.current) {
95
- onMonthChangeRef.current(newMonth);
96
- } else {
97
- setInternalCurrentMonth(newMonth);
98
- }
99
- }, []);
100
-
101
- // Memoize calendar data calculation with selection state
102
- const { calendarDays, monthName } = useMemo(() => {
103
- const monthStart = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
104
- const monthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0);
105
- const daysInMonth = monthEnd.getDate();
106
- const startingDayOfWeek = monthStart.getDay();
107
-
108
- // Pre-calculate all calendar days with their states
109
- const days: Array<{
110
- date: Date | null;
111
- isSelected: boolean;
112
- isDisabled: boolean;
113
- key: string;
114
- }> = [];
115
-
116
- // Add empty cells for days before month starts
117
- for (let i = 0; i < startingDayOfWeek; i++) {
118
- days.push({
119
- date: null,
120
- isSelected: false,
121
- isDisabled: false,
122
- key: `empty-${i}`
123
- });
124
- }
125
-
126
- // Add days of the month with pre-calculated states
127
- for (let day = 1; day <= daysInMonth; day++) {
128
- const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
129
- const dateTime = date.getTime();
130
-
131
- // Pre-calculate selection state
132
- const isSelected = value ? (
133
- date.getDate() === value.getDate() &&
134
- date.getMonth() === value.getMonth() &&
135
- date.getFullYear() === value.getFullYear()
136
- ) : false;
137
-
138
- // Pre-calculate disabled state
139
- const isDisabled = disabled ||
140
- (minDate && dateTime < minDate.getTime()) ||
141
- (maxDate && dateTime > maxDate.getTime());
142
-
143
- days.push({
144
- date,
145
- isSelected,
146
- isDisabled: !!isDisabled,
147
- key: `${date.getFullYear()}-${date.getMonth()}-${day}`
148
- });
149
- }
150
-
151
- const name = currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
152
-
153
- return { calendarDays: days, monthName: name };
154
- }, [currentMonth, value, disabled, minDate, maxDate]);
155
-
156
-
157
- const handleDateClick = useCallback((date: Date) => {
158
- onChangeRef.current(date);
159
- }, []);
160
-
161
- const goToPreviousMonth = useCallback(() => {
162
- const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
163
- handleMonthChange(newMonth);
164
- }, [currentMonth, handleMonthChange]);
165
-
166
- const goToNextMonth = useCallback(() => {
167
- const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
168
- handleMonthChange(newMonth);
169
- }, [currentMonth, handleMonthChange]);
170
-
171
- // Use Unistyles
172
- calendarStyles.useVariants({});
173
-
174
- // Pre-calculate styles to avoid inline objects
175
- const dayButtonStyle = calendarStyles.dayButton;
176
-
177
- return (
178
- <View style={[calendarStyles.container, style]} testID={testID}>
179
- {/* Header */}
180
- <View style={calendarStyles.header}>
181
- <Button
182
- variant="text"
183
- size="sm"
184
- onPress={goToPreviousMonth}
185
- disabled={disabled}
186
- style={calendarStyles.headerButton}
187
- >
188
-
189
- </Button>
190
- <Text weight="semibold">{monthName}</Text>
191
- <Button
192
- variant="text"
193
- size="sm"
194
- onPress={goToNextMonth}
195
- disabled={disabled}
196
- style={calendarStyles.headerButton}
197
- >
198
-
199
- </Button>
200
- </View>
201
-
202
- {/* Weekday headers */}
203
- <View style={calendarStyles.weekdayHeader}>
204
- {weekdays.map((day) => (
205
- <View key={day} style={calendarStyles.weekdayCell}>
206
- <Text style={calendarStyles.weekdayText}>
207
- {day}
208
- </Text>
209
- </View>
210
- ))}
211
- </View>
212
-
213
- {/* Calendar grid */}
214
- <View style={calendarStyles.calendarGrid}>
215
- {calendarDays.map((dayInfo) => (
216
- <DayCell
217
- key={dayInfo.key}
218
- date={dayInfo.date}
219
- isSelected={dayInfo.isSelected}
220
- isDisabled={dayInfo.isDisabled}
221
- onPress={handleDateClick}
222
- dayButtonStyle={dayButtonStyle}
223
- textColorSelected="#ffffff"
224
- textColorDefault="#000000"
225
- />
226
- ))}
227
- </View>
228
- </View>
229
- );
230
- }, (prevProps, nextProps) => {
231
- // Custom comparison to prevent unnecessary re-renders
232
- // Skip checking callbacks since we handle them with refs
233
- return (
234
- prevProps.value?.getTime() === nextProps.value?.getTime() &&
235
- prevProps.minDate?.getTime() === nextProps.minDate?.getTime() &&
236
- prevProps.maxDate?.getTime() === nextProps.maxDate?.getTime() &&
237
- prevProps.disabled === nextProps.disabled &&
238
- prevProps.currentMonth?.getTime() === nextProps.currentMonth?.getTime() &&
239
- prevProps.style === nextProps.style &&
240
- prevProps.testID === nextProps.testID
241
- );
242
- });
243
-
244
- Calendar.displayName = 'Calendar';
245
-
246
- // Static weekday labels
247
- const weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
248
-
249
- // Pre-defined styles to avoid inline style objects
250
- const styles = StyleSheet.create({
251
- selectedButton: {
252
- backgroundColor: '#3b82f6'
253
- },
254
- disabledButton: {
255
- opacity: 0.5
256
- },
257
- dayText: {
258
- fontSize: 13,
259
- textAlign: 'center'
260
- }
261
- });