@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.
- package/README.md +88 -0
- package/package.json +77 -0
- package/src/DatePicker/Calendar.native.tsx +159 -0
- package/src/DatePicker/Calendar.styles.tsx +224 -0
- package/src/DatePicker/Calendar.tsx +154 -0
- package/src/DatePicker/DatePicker.native.tsx +33 -0
- package/src/DatePicker/DatePicker.styles.tsx +69 -0
- package/src/DatePicker/DatePicker.web.tsx +31 -0
- package/src/DatePicker/index.native.ts +3 -0
- package/src/DatePicker/index.ts +3 -0
- package/src/DatePicker/types.ts +78 -0
- package/src/DateRangePicker/DateRangePicker.native.tsx +39 -0
- package/src/DateRangePicker/DateRangePicker.styles.tsx +83 -0
- package/src/DateRangePicker/DateRangePicker.web.tsx +40 -0
- package/src/DateRangePicker/RangeCalendar.native.tsx +267 -0
- package/src/DateRangePicker/RangeCalendar.styles.tsx +170 -0
- package/src/DateRangePicker/RangeCalendar.web.tsx +275 -0
- package/src/DateRangePicker/index.native.ts +3 -0
- package/src/DateRangePicker/index.ts +3 -0
- package/src/DateRangePicker/types.ts +98 -0
- package/src/DateTimePicker/DateTimePicker.native.tsx +82 -0
- package/src/DateTimePicker/DateTimePicker.styles.tsx +77 -0
- package/src/DateTimePicker/DateTimePicker.tsx +79 -0
- package/src/DateTimePicker/TimePicker.native.tsx +204 -0
- package/src/DateTimePicker/TimePicker.styles.tsx +116 -0
- package/src/DateTimePicker/TimePicker.tsx +406 -0
- package/src/DateTimePicker/index.native.ts +3 -0
- package/src/DateTimePicker/index.ts +3 -0
- package/src/DateTimePicker/types.ts +84 -0
- package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +213 -0
- package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +95 -0
- package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +141 -0
- package/src/DateTimeRangePicker/index.native.ts +2 -0
- package/src/DateTimeRangePicker/index.ts +2 -0
- package/src/DateTimeRangePicker/types.ts +72 -0
- package/src/examples/DatePickerExamples.tsx +274 -0
- package/src/examples/index.ts +1 -0
- package/src/index.native.ts +16 -0
- package/src/index.ts +16 -0
- package/src/primitives/CalendarGrid/CalendarGrid.styles.tsx +62 -0
- package/src/primitives/CalendarGrid/CalendarGrid.tsx +138 -0
- package/src/primitives/CalendarGrid/index.ts +1 -0
- package/src/primitives/CalendarHeader/CalendarHeader.styles.tsx +25 -0
- package/src/primitives/CalendarHeader/CalendarHeader.tsx +69 -0
- package/src/primitives/CalendarHeader/index.ts +1 -0
- package/src/primitives/CalendarOverlay/CalendarOverlay.styles.tsx +81 -0
- package/src/primitives/CalendarOverlay/CalendarOverlay.tsx +130 -0
- package/src/primitives/CalendarOverlay/index.ts +1 -0
- package/src/primitives/Wrapper/Wrapper.web.tsx +33 -0
- package/src/primitives/Wrapper/index.ts +1 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text } from '@idealyst/components';
|
|
3
|
+
import { DateTimeRangePickerProps, DateTimeRange } from './types';
|
|
4
|
+
import { RangeCalendar } from '../DateRangePicker/RangeCalendar.native';
|
|
5
|
+
import { TimePicker } from '../DateTimePicker/TimePicker.native';
|
|
6
|
+
import { dateTimeRangePickerStyles } from './DateTimeRangePicker.styles';
|
|
7
|
+
|
|
8
|
+
const DateTimeRangePicker: React.FC<DateTimeRangePickerProps> = ({
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
11
|
+
minDate,
|
|
12
|
+
maxDate,
|
|
13
|
+
disabled = false,
|
|
14
|
+
placeholder = 'Select date/time range',
|
|
15
|
+
label,
|
|
16
|
+
error,
|
|
17
|
+
helperText,
|
|
18
|
+
format = 'MM/dd/yyyy HH:mm',
|
|
19
|
+
locale,
|
|
20
|
+
size = 'medium',
|
|
21
|
+
variant = 'outlined',
|
|
22
|
+
timeMode = '12h',
|
|
23
|
+
showSeconds = false,
|
|
24
|
+
timeStep = 1,
|
|
25
|
+
allowSameDay = true,
|
|
26
|
+
maxDays,
|
|
27
|
+
style,
|
|
28
|
+
testID,
|
|
29
|
+
}) => {
|
|
30
|
+
const formatDateTime = (date: Date): string => {
|
|
31
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
32
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
33
|
+
const year = date.getFullYear();
|
|
34
|
+
|
|
35
|
+
let hours = date.getHours();
|
|
36
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
37
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
38
|
+
|
|
39
|
+
let timeString = '';
|
|
40
|
+
if (timeMode === '12h') {
|
|
41
|
+
const ampm = hours >= 12 ? 'PM' : 'AM';
|
|
42
|
+
hours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
|
43
|
+
timeString = showSeconds
|
|
44
|
+
? `${String(hours).padStart(2, '0')}:${minutes}:${seconds} ${ampm}`
|
|
45
|
+
: `${String(hours).padStart(2, '0')}:${minutes} ${ampm}`;
|
|
46
|
+
} else {
|
|
47
|
+
timeString = showSeconds
|
|
48
|
+
? `${String(hours).padStart(2, '0')}:${minutes}:${seconds}`
|
|
49
|
+
: `${String(hours).padStart(2, '0')}:${minutes}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return `${month}/${day}/${year} ${timeString}`;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const formatRange = (range: DateTimeRange): string => {
|
|
56
|
+
if (!range.startDate) return '';
|
|
57
|
+
if (!range.endDate) return `${formatDateTime(range.startDate)} - ...`;
|
|
58
|
+
return `${formatDateTime(range.startDate)} - ${formatDateTime(range.endDate)}`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleDateRangeChange = (newRange: { startDate?: Date; endDate?: Date }) => {
|
|
62
|
+
// Preserve existing times when date changes
|
|
63
|
+
const updatedRange: DateTimeRange = {};
|
|
64
|
+
|
|
65
|
+
if (newRange.startDate) {
|
|
66
|
+
updatedRange.startDate = new Date(newRange.startDate);
|
|
67
|
+
if (value?.startDate) {
|
|
68
|
+
updatedRange.startDate.setHours(
|
|
69
|
+
value.startDate.getHours(),
|
|
70
|
+
value.startDate.getMinutes(),
|
|
71
|
+
value.startDate.getSeconds()
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (newRange.endDate) {
|
|
77
|
+
updatedRange.endDate = new Date(newRange.endDate);
|
|
78
|
+
if (value?.endDate) {
|
|
79
|
+
updatedRange.endDate.setHours(
|
|
80
|
+
value.endDate.getHours(),
|
|
81
|
+
value.endDate.getMinutes(),
|
|
82
|
+
value.endDate.getSeconds()
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onChange(updatedRange.startDate || updatedRange.endDate ? updatedRange : null);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleStartTimeChange = (newTime: Date) => {
|
|
91
|
+
if (value?.startDate) {
|
|
92
|
+
const updatedDate = new Date(value.startDate);
|
|
93
|
+
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
94
|
+
onChange({ ...value, startDate: updatedDate });
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleEndTimeChange = (newTime: Date) => {
|
|
99
|
+
if (value?.endDate) {
|
|
100
|
+
const updatedDate = new Date(value.endDate);
|
|
101
|
+
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
102
|
+
onChange({ ...value, endDate: updatedDate });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const daysDifference = (range: DateTimeRange): number => {
|
|
107
|
+
if (!range.startDate || !range.endDate) return 0;
|
|
108
|
+
const diffTime = range.endDate.getTime() - range.startDate.getTime();
|
|
109
|
+
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const hoursDifference = (range: DateTimeRange): number => {
|
|
113
|
+
if (!range.startDate || !range.endDate) return 0;
|
|
114
|
+
const diffTime = range.endDate.getTime() - range.startDate.getTime();
|
|
115
|
+
return Math.ceil(diffTime / (1000 * 60 * 60));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
dateTimeRangePickerStyles.useVariants({});
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<View style={[dateTimeRangePickerStyles.container, style]} testID={testID}>
|
|
122
|
+
{label && (
|
|
123
|
+
<Text style={dateTimeRangePickerStyles.label}>
|
|
124
|
+
{label}
|
|
125
|
+
</Text>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
<View style={dateTimeRangePickerStyles.picker}>
|
|
129
|
+
{/* Selected Range Summary */}
|
|
130
|
+
{value?.startDate && value?.endDate && (
|
|
131
|
+
<View style={dateTimeRangePickerStyles.selectedRangeHeader}>
|
|
132
|
+
<Text style={dateTimeRangePickerStyles.selectedRangeLabel}>
|
|
133
|
+
Selected Range ({daysDifference(value)} days, {hoursDifference(value)} hours):
|
|
134
|
+
</Text>
|
|
135
|
+
<Text style={dateTimeRangePickerStyles.selectedRangeValue}>
|
|
136
|
+
{formatRange(value)}
|
|
137
|
+
</Text>
|
|
138
|
+
</View>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{/* Date Section */}
|
|
142
|
+
<View style={dateTimeRangePickerStyles.section}>
|
|
143
|
+
<Text style={dateTimeRangePickerStyles.sectionLabel}>Date Range</Text>
|
|
144
|
+
<View style={dateTimeRangePickerStyles.sectionContent}>
|
|
145
|
+
<RangeCalendar
|
|
146
|
+
value={value || {}}
|
|
147
|
+
onChange={handleDateRangeChange}
|
|
148
|
+
minDate={minDate}
|
|
149
|
+
maxDate={maxDate}
|
|
150
|
+
disabled={disabled}
|
|
151
|
+
allowSameDay={allowSameDay}
|
|
152
|
+
maxDays={maxDays}
|
|
153
|
+
/>
|
|
154
|
+
</View>
|
|
155
|
+
</View>
|
|
156
|
+
|
|
157
|
+
{/* Time Section */}
|
|
158
|
+
{(value?.startDate || value?.endDate) && (
|
|
159
|
+
<View style={dateTimeRangePickerStyles.section}>
|
|
160
|
+
<Text style={dateTimeRangePickerStyles.sectionLabel}>Time Selection</Text>
|
|
161
|
+
<View style={dateTimeRangePickerStyles.sectionContent}>
|
|
162
|
+
<View style={dateTimeRangePickerStyles.timeSection}>
|
|
163
|
+
{/* Start Time */}
|
|
164
|
+
{value?.startDate && (
|
|
165
|
+
<View style={dateTimeRangePickerStyles.timeGroup}>
|
|
166
|
+
<Text style={dateTimeRangePickerStyles.timeGroupLabel}>Start Time</Text>
|
|
167
|
+
<TimePicker
|
|
168
|
+
value={value.startDate}
|
|
169
|
+
onChange={handleStartTimeChange}
|
|
170
|
+
disabled={disabled}
|
|
171
|
+
mode={timeMode}
|
|
172
|
+
showSeconds={showSeconds}
|
|
173
|
+
step={timeStep}
|
|
174
|
+
/>
|
|
175
|
+
</View>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
{/* End Time */}
|
|
179
|
+
{value?.endDate && (
|
|
180
|
+
<View style={dateTimeRangePickerStyles.timeGroup}>
|
|
181
|
+
<Text style={dateTimeRangePickerStyles.timeGroupLabel}>End Time</Text>
|
|
182
|
+
<TimePicker
|
|
183
|
+
value={value.endDate}
|
|
184
|
+
onChange={handleEndTimeChange}
|
|
185
|
+
disabled={disabled}
|
|
186
|
+
mode={timeMode}
|
|
187
|
+
showSeconds={showSeconds}
|
|
188
|
+
step={timeStep}
|
|
189
|
+
/>
|
|
190
|
+
</View>
|
|
191
|
+
)}
|
|
192
|
+
</View>
|
|
193
|
+
</View>
|
|
194
|
+
</View>
|
|
195
|
+
)}
|
|
196
|
+
</View>
|
|
197
|
+
|
|
198
|
+
{error && (
|
|
199
|
+
<Text style={dateTimeRangePickerStyles.errorText}>
|
|
200
|
+
{error}
|
|
201
|
+
</Text>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
{helperText && !error && (
|
|
205
|
+
<Text style={dateTimeRangePickerStyles.helperText}>
|
|
206
|
+
{helperText}
|
|
207
|
+
</Text>
|
|
208
|
+
)}
|
|
209
|
+
</View>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export default DateTimeRangePicker;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
|
|
3
|
+
export const dateTimeRangePickerStyles = 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
|
+
section: {
|
|
45
|
+
gap: theme.spacing?.xs || 8,
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
sectionLabel: {
|
|
49
|
+
fontSize: theme.typography?.sizes?.small || 14,
|
|
50
|
+
fontWeight: '600',
|
|
51
|
+
color: theme.colors?.text?.primary || '#111827',
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
sectionContent: {
|
|
55
|
+
padding: theme.spacing?.sm || 12,
|
|
56
|
+
borderWidth: 1,
|
|
57
|
+
borderColor: theme.colors?.border?.primary || '#e5e7eb',
|
|
58
|
+
borderRadius: theme.borderRadius?.md || 8,
|
|
59
|
+
backgroundColor: theme.colors?.surface?.primary || '#ffffff',
|
|
60
|
+
|
|
61
|
+
_web: {
|
|
62
|
+
border: `1px solid ${theme.colors?.border?.primary || '#e5e7eb'}`,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
timeSection: {
|
|
67
|
+
flexDirection: 'row',
|
|
68
|
+
alignItems: 'flex-start',
|
|
69
|
+
gap: theme.spacing?.lg || 24,
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
timeGroup: {
|
|
73
|
+
flex: 1,
|
|
74
|
+
gap: theme.spacing?.xs || 8,
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
timeGroupLabel: {
|
|
79
|
+
fontSize: theme.typography?.sizes?.small || 12,
|
|
80
|
+
fontWeight: '500',
|
|
81
|
+
color: theme.colors?.text?.secondary || '#6b7280',
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
errorText: {
|
|
85
|
+
fontSize: theme.typography?.sizes?.small || 12,
|
|
86
|
+
color: theme.colors?.semantic?.error || '#dc2626',
|
|
87
|
+
marginTop: theme.spacing?.xs || 4,
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
helperText: {
|
|
91
|
+
fontSize: theme.typography?.sizes?.small || 12,
|
|
92
|
+
color: theme.colors?.text?.secondary || '#6b7280',
|
|
93
|
+
marginTop: theme.spacing?.xs || 4,
|
|
94
|
+
},
|
|
95
|
+
}));
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text } from '@idealyst/components';
|
|
3
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
4
|
+
import { DateTimeRangePickerProps, DateTimeRange } from './types';
|
|
5
|
+
import { RangeCalendar } from '../DateRangePicker/RangeCalendar.web';
|
|
6
|
+
import { TimePicker } from '../DateTimePicker/TimePicker';
|
|
7
|
+
import { dateTimeRangePickerStyles } from './DateTimeRangePicker.styles';
|
|
8
|
+
|
|
9
|
+
const DateTimeRangePicker: React.FC<DateTimeRangePickerProps> = ({
|
|
10
|
+
value,
|
|
11
|
+
onChange,
|
|
12
|
+
minDate,
|
|
13
|
+
maxDate,
|
|
14
|
+
disabled = false,
|
|
15
|
+
timeMode = '12h',
|
|
16
|
+
showSeconds = false,
|
|
17
|
+
timeStep = 1,
|
|
18
|
+
allowSameDay = true,
|
|
19
|
+
maxDays,
|
|
20
|
+
style,
|
|
21
|
+
testID,
|
|
22
|
+
}) => {
|
|
23
|
+
|
|
24
|
+
const handleDateRangeChange = (newRange: { startDate?: Date; endDate?: Date }) => {
|
|
25
|
+
// Preserve existing times when date changes
|
|
26
|
+
const updatedRange: DateTimeRange = {};
|
|
27
|
+
|
|
28
|
+
if (newRange.startDate) {
|
|
29
|
+
updatedRange.startDate = new Date(newRange.startDate);
|
|
30
|
+
if (value?.startDate) {
|
|
31
|
+
updatedRange.startDate.setHours(
|
|
32
|
+
value.startDate.getHours(),
|
|
33
|
+
value.startDate.getMinutes(),
|
|
34
|
+
value.startDate.getSeconds()
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (newRange.endDate) {
|
|
40
|
+
updatedRange.endDate = new Date(newRange.endDate);
|
|
41
|
+
if (value?.endDate) {
|
|
42
|
+
updatedRange.endDate.setHours(
|
|
43
|
+
value.endDate.getHours(),
|
|
44
|
+
value.endDate.getMinutes(),
|
|
45
|
+
value.endDate.getSeconds()
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onChange(updatedRange.startDate || updatedRange.endDate ? updatedRange : null);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleStartTimeChange = (newTime: Date) => {
|
|
54
|
+
if (value?.startDate) {
|
|
55
|
+
const updatedDate = new Date(value.startDate);
|
|
56
|
+
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
57
|
+
onChange({ ...value, startDate: updatedDate });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleEndTimeChange = (newTime: Date) => {
|
|
62
|
+
if (value?.endDate) {
|
|
63
|
+
const updatedDate = new Date(value.endDate);
|
|
64
|
+
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
65
|
+
onChange({ ...value, endDate: updatedDate });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
dateTimeRangePickerStyles.useVariants({});
|
|
71
|
+
|
|
72
|
+
const containerProps = getWebProps([dateTimeRangePickerStyles.container, style]);
|
|
73
|
+
const sectionProps = getWebProps([dateTimeRangePickerStyles.section]);
|
|
74
|
+
const sectionLabelProps = getWebProps([dateTimeRangePickerStyles.sectionLabel]);
|
|
75
|
+
const sectionContentProps = getWebProps([dateTimeRangePickerStyles.sectionContent]);
|
|
76
|
+
const timeSectionProps = getWebProps([dateTimeRangePickerStyles.timeSection]);
|
|
77
|
+
const timeGroupProps = getWebProps([dateTimeRangePickerStyles.timeGroup]);
|
|
78
|
+
const timeGroupLabelProps = getWebProps([dateTimeRangePickerStyles.timeGroupLabel]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div {...containerProps} data-testid={testID}>
|
|
82
|
+
{/* Date Section */}
|
|
83
|
+
<div {...sectionProps}>
|
|
84
|
+
<div {...sectionLabelProps}>Date Range</div>
|
|
85
|
+
<div {...sectionContentProps}>
|
|
86
|
+
<RangeCalendar
|
|
87
|
+
value={value || {}}
|
|
88
|
+
onChange={handleDateRangeChange}
|
|
89
|
+
minDate={minDate}
|
|
90
|
+
maxDate={maxDate}
|
|
91
|
+
disabled={disabled}
|
|
92
|
+
allowSameDay={allowSameDay}
|
|
93
|
+
maxDays={maxDays}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{/* Time Section */}
|
|
99
|
+
{(value?.startDate || value?.endDate) && (
|
|
100
|
+
<div {...sectionProps}>
|
|
101
|
+
<div {...sectionLabelProps}>Time Selection</div>
|
|
102
|
+
<div {...sectionContentProps}>
|
|
103
|
+
<div {...timeSectionProps}>
|
|
104
|
+
{/* Start Time */}
|
|
105
|
+
{value?.startDate && (
|
|
106
|
+
<div {...timeGroupProps}>
|
|
107
|
+
<div {...timeGroupLabelProps}>Start Time</div>
|
|
108
|
+
<TimePicker
|
|
109
|
+
value={value.startDate}
|
|
110
|
+
onChange={handleStartTimeChange}
|
|
111
|
+
disabled={disabled}
|
|
112
|
+
mode={timeMode}
|
|
113
|
+
showSeconds={showSeconds}
|
|
114
|
+
step={timeStep}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{/* End Time */}
|
|
120
|
+
{value?.endDate && (
|
|
121
|
+
<div {...timeGroupProps}>
|
|
122
|
+
<div {...timeGroupLabelProps}>End Time</div>
|
|
123
|
+
<TimePicker
|
|
124
|
+
value={value.endDate}
|
|
125
|
+
onChange={handleEndTimeChange}
|
|
126
|
+
disabled={disabled}
|
|
127
|
+
mode={timeMode}
|
|
128
|
+
showSeconds={showSeconds}
|
|
129
|
+
step={timeStep}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default DateTimeRangePicker;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface DateTimeRange {
|
|
5
|
+
/** Start date and time of the range */
|
|
6
|
+
startDate?: Date;
|
|
7
|
+
|
|
8
|
+
/** End date and time of the range */
|
|
9
|
+
endDate?: Date;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DateTimeRangePickerProps {
|
|
13
|
+
/** Current selected date/time range */
|
|
14
|
+
value?: DateTimeRange;
|
|
15
|
+
|
|
16
|
+
/** Called when date/time range changes */
|
|
17
|
+
onChange: (range: DateTimeRange | null) => void;
|
|
18
|
+
|
|
19
|
+
/** Minimum selectable date */
|
|
20
|
+
minDate?: Date;
|
|
21
|
+
|
|
22
|
+
/** Maximum selectable date */
|
|
23
|
+
maxDate?: Date;
|
|
24
|
+
|
|
25
|
+
/** Disabled state */
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
|
|
28
|
+
/** Placeholder text when no range is selected */
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
|
|
31
|
+
/** Label for the picker */
|
|
32
|
+
label?: string;
|
|
33
|
+
|
|
34
|
+
/** Error message to display */
|
|
35
|
+
error?: string;
|
|
36
|
+
|
|
37
|
+
/** Helper text */
|
|
38
|
+
helperText?: string;
|
|
39
|
+
|
|
40
|
+
/** Date format for display (default: 'MM/dd/yyyy HH:mm') */
|
|
41
|
+
format?: string;
|
|
42
|
+
|
|
43
|
+
/** Locale for date formatting */
|
|
44
|
+
locale?: string;
|
|
45
|
+
|
|
46
|
+
/** Size variant */
|
|
47
|
+
size?: 'small' | 'medium' | 'large';
|
|
48
|
+
|
|
49
|
+
/** Visual variant */
|
|
50
|
+
variant?: 'outlined' | 'filled';
|
|
51
|
+
|
|
52
|
+
/** Time picker mode */
|
|
53
|
+
timeMode?: '12h' | '24h';
|
|
54
|
+
|
|
55
|
+
/** Show seconds in time picker */
|
|
56
|
+
showSeconds?: boolean;
|
|
57
|
+
|
|
58
|
+
/** Time step in minutes */
|
|
59
|
+
timeStep?: number;
|
|
60
|
+
|
|
61
|
+
/** Allow same day selection for start and end */
|
|
62
|
+
allowSameDay?: boolean;
|
|
63
|
+
|
|
64
|
+
/** Maximum number of days in range */
|
|
65
|
+
maxDays?: number;
|
|
66
|
+
|
|
67
|
+
/** Custom styles */
|
|
68
|
+
style?: ViewStyle;
|
|
69
|
+
|
|
70
|
+
/** Test ID for testing */
|
|
71
|
+
testID?: string;
|
|
72
|
+
}
|