@idealyst/datepicker 1.0.0 → 1.0.58
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 +10 -5
- package/src/DateInput/DateInput.native.tsx +80 -0
- package/src/DateInput/DateInput.styles.tsx +118 -0
- package/src/DateInput/DateInput.web.tsx +79 -0
- package/src/DateInput/DateInputBase.tsx +233 -0
- package/src/DateInput/index.native.ts +2 -0
- package/src/DateInput/index.ts +2 -0
- package/src/DateInput/types.ts +60 -0
- package/src/DatePicker/Calendar.native.tsx +180 -78
- package/src/DatePicker/Calendar.styles.tsx +73 -70
- package/src/DatePicker/DatePicker.native.tsx +24 -6
- package/src/DatePicker/DatePicker.styles.tsx +18 -11
- package/src/DatePicker/DatePicker.web.tsx +1 -1
- package/src/DatePicker/index.ts +1 -1
- package/src/DateRangePicker/RangeCalendar.native.tsx +143 -55
- package/src/DateRangePicker/RangeCalendar.styles.tsx +65 -39
- package/src/DateRangePicker/RangeCalendar.web.tsx +169 -60
- package/src/DateRangePicker/types.ts +9 -0
- package/src/DateTimePicker/DateTimePicker.native.tsx +11 -69
- package/src/DateTimePicker/DateTimePicker.tsx +12 -70
- package/src/DateTimePicker/DateTimePickerBase.tsx +204 -0
- package/src/DateTimePicker/TimePicker.native.tsx +9 -196
- package/src/DateTimePicker/TimePicker.styles.tsx +4 -2
- package/src/DateTimePicker/TimePicker.tsx +9 -401
- package/src/DateTimePicker/TimePickerBase.tsx +232 -0
- package/src/DateTimePicker/primitives/ClockFace.native.tsx +195 -0
- package/src/DateTimePicker/primitives/ClockFace.web.tsx +168 -0
- package/src/DateTimePicker/primitives/TimeInput.native.tsx +53 -0
- package/src/DateTimePicker/primitives/TimeInput.web.tsx +66 -0
- package/src/DateTimePicker/primitives/index.native.ts +2 -0
- package/src/DateTimePicker/primitives/index.ts +2 -0
- package/src/DateTimePicker/primitives/index.web.ts +2 -0
- package/src/DateTimePicker/types.ts +0 -4
- package/src/DateTimePicker/utils/dimensions.native.ts +9 -0
- package/src/DateTimePicker/utils/dimensions.ts +9 -0
- package/src/DateTimePicker/utils/dimensions.web.ts +33 -0
- package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +10 -199
- package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +3 -0
- package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +11 -131
- package/src/DateTimeRangePicker/DateTimeRangePickerBase.tsx +337 -0
- package/src/DateTimeRangePicker/types.ts +0 -2
- package/src/examples/DatePickerExamples.tsx +42 -118
- package/src/index.native.ts +4 -0
- package/src/index.ts +4 -0
- /package/src/DatePicker/{Calendar.tsx → Calendar.web.tsx} +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import React, { useState, useMemo } from 'react';
|
|
1
|
+
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
|
2
2
|
import { View, Text, Button } from '@idealyst/components';
|
|
3
3
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
4
4
|
import { RangeCalendarProps, DateRange } from './types';
|
|
5
5
|
import { rangeCalendarStyles } from './RangeCalendar.styles';
|
|
6
|
+
import { CalendarOverlay } from '../primitives/CalendarOverlay';
|
|
6
7
|
|
|
7
8
|
export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
8
9
|
value = {},
|
|
9
10
|
onChange,
|
|
11
|
+
onDateSelected,
|
|
12
|
+
showTimes = false,
|
|
13
|
+
timeMode = '12h',
|
|
10
14
|
minDate,
|
|
11
15
|
maxDate,
|
|
12
16
|
disabled = false,
|
|
@@ -21,9 +25,28 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
21
25
|
controlledCurrentMonth || value?.startDate || new Date()
|
|
22
26
|
);
|
|
23
27
|
const [selectingEnd, setSelectingEnd] = useState(false);
|
|
28
|
+
const [overlayMode, setOverlayMode] = useState<'month' | 'year' | null>(null);
|
|
29
|
+
const containerRef = useRef<View>(null);
|
|
24
30
|
|
|
25
31
|
const currentMonth = controlledCurrentMonth || internalCurrentMonth;
|
|
26
32
|
|
|
33
|
+
// Close overlay when clicking outside
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
36
|
+
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
|
37
|
+
setOverlayMode(null);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (overlayMode) {
|
|
42
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return () => {
|
|
46
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
47
|
+
};
|
|
48
|
+
}, [overlayMode]);
|
|
49
|
+
|
|
27
50
|
const handleMonthChange = (newMonth: Date) => {
|
|
28
51
|
if (onMonthChange) {
|
|
29
52
|
onMonthChange(newMonth);
|
|
@@ -78,6 +101,34 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
78
101
|
return isDateRangeStart(date) || isDateRangeEnd(date);
|
|
79
102
|
};
|
|
80
103
|
|
|
104
|
+
const formatTime = (date: Date): string => {
|
|
105
|
+
let hours = date.getHours();
|
|
106
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
107
|
+
|
|
108
|
+
if (timeMode === '12h') {
|
|
109
|
+
const ampm = hours >= 12 ? 'PM' : 'AM';
|
|
110
|
+
hours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
|
111
|
+
return `${hours}:${minutes}${ampm}`;
|
|
112
|
+
} else {
|
|
113
|
+
return `${String(hours).padStart(2, '0')}:${minutes}`;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const getDateTimeInfo = (date: Date) => {
|
|
118
|
+
if (!showTimes) return null;
|
|
119
|
+
|
|
120
|
+
const isStart = isDateRangeStart(date);
|
|
121
|
+
const isEnd = isDateRangeEnd(date);
|
|
122
|
+
|
|
123
|
+
if (isStart && value?.startDate) {
|
|
124
|
+
return { type: 'start', time: formatTime(value.startDate) };
|
|
125
|
+
}
|
|
126
|
+
if (isEnd && value?.endDate) {
|
|
127
|
+
return { type: 'end', time: formatTime(value.endDate) };
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
};
|
|
131
|
+
|
|
81
132
|
const handleDateClick = (date: Date) => {
|
|
82
133
|
if (isDateDisabled(date)) return;
|
|
83
134
|
|
|
@@ -87,6 +138,7 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
87
138
|
if (!startDate || (startDate && endDate)) {
|
|
88
139
|
onChange({ startDate: date, endDate: undefined });
|
|
89
140
|
setSelectingEnd(true);
|
|
141
|
+
onDateSelected?.('start');
|
|
90
142
|
return;
|
|
91
143
|
}
|
|
92
144
|
|
|
@@ -116,6 +168,7 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
116
168
|
|
|
117
169
|
onChange({ startDate: newStartDate, endDate: newEndDate });
|
|
118
170
|
setSelectingEnd(false);
|
|
171
|
+
onDateSelected?.('end');
|
|
119
172
|
}
|
|
120
173
|
};
|
|
121
174
|
|
|
@@ -129,34 +182,56 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
129
182
|
handleMonthChange(newMonth);
|
|
130
183
|
};
|
|
131
184
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
setSelectingEnd(false);
|
|
185
|
+
const createDateWithDayAdjustment = (year: number, month: number, day: number): Date => {
|
|
186
|
+
// Get the last day of the target month
|
|
187
|
+
const lastDayOfMonth = new Date(year, month + 1, 0).getDate();
|
|
188
|
+
// Use the smaller of the requested day or the last day of the month
|
|
189
|
+
const adjustedDay = Math.min(day, lastDayOfMonth);
|
|
190
|
+
return new Date(year, month, adjustedDay);
|
|
139
191
|
};
|
|
140
192
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
193
|
+
const handleMonthSelect = (monthIndex: number) => {
|
|
194
|
+
const newMonth = new Date(currentMonth.getFullYear(), monthIndex, 1);
|
|
195
|
+
handleMonthChange(newMonth);
|
|
196
|
+
setOverlayMode(null);
|
|
144
197
|
};
|
|
145
198
|
|
|
199
|
+
const handleYearSelect = (year: number) => {
|
|
200
|
+
const newMonth = new Date(year, currentMonth.getMonth(), 1);
|
|
201
|
+
handleMonthChange(newMonth);
|
|
202
|
+
setOverlayMode(null);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
|
|
146
206
|
const monthName = currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
147
207
|
|
|
148
208
|
// Create calendar grid
|
|
149
209
|
const calendarDays = [];
|
|
150
210
|
|
|
151
|
-
// Add
|
|
152
|
-
|
|
153
|
-
|
|
211
|
+
// Add previous month days to fill start of week
|
|
212
|
+
const prevMonthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 0);
|
|
213
|
+
const prevMonthDaysToShow = startingDayOfWeek;
|
|
214
|
+
for (let i = prevMonthDaysToShow - 1; i >= 0; i--) {
|
|
215
|
+
const day = prevMonthEnd.getDate() - i;
|
|
216
|
+
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, day);
|
|
217
|
+
calendarDays.push({ date, isCurrentMonth: false });
|
|
154
218
|
}
|
|
155
219
|
|
|
156
|
-
// Add days of the month
|
|
220
|
+
// Add days of the current month
|
|
157
221
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
158
222
|
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
|
|
159
|
-
calendarDays.push(date);
|
|
223
|
+
calendarDays.push({ date, isCurrentMonth: true });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Add next month days to complete partial weeks
|
|
227
|
+
const currentLength = calendarDays.length;
|
|
228
|
+
const weeksNeeded = Math.ceil(currentLength / 7);
|
|
229
|
+
const totalCalendarDays = weeksNeeded * 7;
|
|
230
|
+
|
|
231
|
+
const remainingDays = totalCalendarDays - currentLength;
|
|
232
|
+
for (let day = 1; day <= remainingDays; day++) {
|
|
233
|
+
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, day);
|
|
234
|
+
calendarDays.push({ date, isCurrentMonth: false });
|
|
160
235
|
}
|
|
161
236
|
|
|
162
237
|
rangeCalendarStyles.useVariants({});
|
|
@@ -166,7 +241,7 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
166
241
|
const headerTitleProps = getWebProps([rangeCalendarStyles.headerTitle]);
|
|
167
242
|
|
|
168
243
|
return (
|
|
169
|
-
<div {...containerProps} data-testid={testID}>
|
|
244
|
+
<div {...containerProps} data-testid={testID} ref={containerRef}>
|
|
170
245
|
{/* Header */}
|
|
171
246
|
<div {...headerProps}>
|
|
172
247
|
<Button
|
|
@@ -179,8 +254,25 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
179
254
|
←
|
|
180
255
|
</Button>
|
|
181
256
|
|
|
182
|
-
<div {...headerTitleProps}>
|
|
183
|
-
<
|
|
257
|
+
<div {...headerTitleProps} style={{ display: 'flex', gap: '8px' }}>
|
|
258
|
+
<Button
|
|
259
|
+
variant="text"
|
|
260
|
+
size="small"
|
|
261
|
+
onPress={() => setOverlayMode('month')}
|
|
262
|
+
disabled={disabled}
|
|
263
|
+
style={{ padding: '4px 8px' }}
|
|
264
|
+
>
|
|
265
|
+
<Text weight="semibold">{currentMonth.toLocaleDateString('en-US', { month: 'long' })}</Text>
|
|
266
|
+
</Button>
|
|
267
|
+
<Button
|
|
268
|
+
variant="text"
|
|
269
|
+
size="small"
|
|
270
|
+
onPress={() => setOverlayMode('year')}
|
|
271
|
+
disabled={disabled}
|
|
272
|
+
style={{ padding: '4px 8px' }}
|
|
273
|
+
>
|
|
274
|
+
<Text weight="semibold">{currentMonth.getFullYear()}</Text>
|
|
275
|
+
</Button>
|
|
184
276
|
</div>
|
|
185
277
|
|
|
186
278
|
<Button
|
|
@@ -207,69 +299,86 @@ export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
|
207
299
|
|
|
208
300
|
{/* Calendar grid */}
|
|
209
301
|
<div {...getWebProps([rangeCalendarStyles.calendarGrid])}>
|
|
210
|
-
{calendarDays.map((
|
|
302
|
+
{calendarDays.map((dayInfo, index) => (
|
|
211
303
|
<div key={index} {...getWebProps([rangeCalendarStyles.dayCell])}>
|
|
212
|
-
{
|
|
304
|
+
{dayInfo && (
|
|
213
305
|
<Button
|
|
214
306
|
variant="text"
|
|
215
|
-
disabled={isDateDisabled(date)}
|
|
216
|
-
onPress={() => handleDateClick(date)}
|
|
307
|
+
disabled={isDateDisabled(dayInfo.date)}
|
|
308
|
+
onPress={() => handleDateClick(dayInfo.date)}
|
|
217
309
|
size="small"
|
|
218
310
|
style={{
|
|
219
311
|
...getWebProps([rangeCalendarStyles.dayButton]).style,
|
|
220
|
-
backgroundColor: isDateSelected(date)
|
|
312
|
+
backgroundColor: isDateSelected(dayInfo.date)
|
|
221
313
|
? '#3b82f6'
|
|
222
|
-
: isDateInRange(date)
|
|
314
|
+
: isDateInRange(dayInfo.date)
|
|
223
315
|
? '#3b82f620'
|
|
224
316
|
: 'transparent',
|
|
225
|
-
color: isDateSelected(date) ? 'white' : 'black',
|
|
226
|
-
fontWeight: isDateSelected(date) ? '600' : '400',
|
|
227
|
-
borderRadius: isDateRangeStart(date)
|
|
317
|
+
color: isDateSelected(dayInfo.date) ? 'white' : dayInfo.isCurrentMonth ? 'black' : '#9ca3af',
|
|
318
|
+
fontWeight: isDateSelected(dayInfo.date) ? '600' : '400',
|
|
319
|
+
borderRadius: isDateRangeStart(dayInfo.date)
|
|
228
320
|
? '6px 0 0 6px'
|
|
229
|
-
: isDateRangeEnd(date)
|
|
321
|
+
: isDateRangeEnd(dayInfo.date)
|
|
230
322
|
? '0 6px 6px 0'
|
|
231
|
-
: isDateInRange(date)
|
|
323
|
+
: isDateInRange(dayInfo.date)
|
|
232
324
|
? '0'
|
|
233
325
|
: '6px',
|
|
326
|
+
flexDirection: 'column',
|
|
327
|
+
justifyContent: 'center',
|
|
328
|
+
alignItems: 'center',
|
|
329
|
+
position: 'relative',
|
|
330
|
+
width: '100%',
|
|
331
|
+
height: '100%',
|
|
332
|
+
minWidth: '36px',
|
|
333
|
+
minHeight: '36px',
|
|
334
|
+
opacity: dayInfo.isCurrentMonth ? 1 : 0.5,
|
|
234
335
|
}}
|
|
235
336
|
>
|
|
236
|
-
{
|
|
337
|
+
<div style={{ textAlign: 'center', fontSize: '13px' }}>
|
|
338
|
+
{dayInfo.date.getDate()}
|
|
339
|
+
</div>
|
|
340
|
+
{(() => {
|
|
341
|
+
const timeInfo = getDateTimeInfo(dayInfo.date);
|
|
342
|
+
if (timeInfo) {
|
|
343
|
+
return (
|
|
344
|
+
<div
|
|
345
|
+
style={{
|
|
346
|
+
color: isDateSelected(dayInfo.date) ? 'white' : '#666',
|
|
347
|
+
fontSize: '7px',
|
|
348
|
+
fontWeight: '500',
|
|
349
|
+
textAlign: 'center',
|
|
350
|
+
lineHeight: '1',
|
|
351
|
+
marginTop: '1px',
|
|
352
|
+
whiteSpace: 'nowrap',
|
|
353
|
+
overflow: 'hidden',
|
|
354
|
+
textOverflow: 'ellipsis',
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
{timeInfo.time}
|
|
358
|
+
</div>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
362
|
+
})()}
|
|
237
363
|
</Button>
|
|
238
364
|
)}
|
|
239
365
|
</div>
|
|
240
366
|
))}
|
|
241
367
|
</div>
|
|
242
368
|
|
|
243
|
-
{/*
|
|
244
|
-
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
369
|
+
{/* Overlay for month/year selection */}
|
|
370
|
+
{overlayMode && (
|
|
371
|
+
<CalendarOverlay
|
|
372
|
+
mode={overlayMode}
|
|
373
|
+
currentMonth={currentMonth.getMonth()}
|
|
374
|
+
currentYear={currentMonth.getFullYear()}
|
|
375
|
+
onMonthSelect={handleMonthSelect}
|
|
376
|
+
onYearSelect={handleYearSelect}
|
|
377
|
+
onClose={() => setOverlayMode(null)}
|
|
249
378
|
disabled={disabled}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
</Button>
|
|
254
|
-
<Button
|
|
255
|
-
variant="text"
|
|
256
|
-
size="small"
|
|
257
|
-
onPress={() => handlePresetRange(30)}
|
|
258
|
-
disabled={disabled}
|
|
259
|
-
style={getWebProps([rangeCalendarStyles.presetButton]).style}
|
|
260
|
-
>
|
|
261
|
-
Next 30 days
|
|
262
|
-
</Button>
|
|
263
|
-
<Button
|
|
264
|
-
variant="outlined"
|
|
265
|
-
size="small"
|
|
266
|
-
onPress={clearRange}
|
|
267
|
-
disabled={disabled}
|
|
268
|
-
style={getWebProps([rangeCalendarStyles.clearButton]).style}
|
|
269
|
-
>
|
|
270
|
-
Clear Range
|
|
271
|
-
</Button>
|
|
272
|
-
</div>
|
|
379
|
+
/>
|
|
380
|
+
)}
|
|
381
|
+
|
|
273
382
|
</div>
|
|
274
383
|
);
|
|
275
384
|
};
|
|
@@ -69,6 +69,15 @@ export interface RangeCalendarProps {
|
|
|
69
69
|
/** Called when range is selected */
|
|
70
70
|
onChange: (range: DateRange) => void;
|
|
71
71
|
|
|
72
|
+
/** Called when a date is selected to indicate which end (start/end) */
|
|
73
|
+
onDateSelected?: (type: 'start' | 'end') => void;
|
|
74
|
+
|
|
75
|
+
/** Show times in calendar cells (for DateTimeRangePicker) */
|
|
76
|
+
showTimes?: boolean;
|
|
77
|
+
|
|
78
|
+
/** Time mode for display */
|
|
79
|
+
timeMode?: '12h' | '24h';
|
|
80
|
+
|
|
72
81
|
/** Minimum selectable date */
|
|
73
82
|
minDate?: Date;
|
|
74
83
|
|
|
@@ -1,81 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View, Text } from '@idealyst/components';
|
|
3
2
|
import { DateTimePickerProps } from './types';
|
|
3
|
+
import { DateTimePickerBase } from './DateTimePickerBase';
|
|
4
4
|
import { Calendar } from '../DatePicker/Calendar.native';
|
|
5
5
|
import { TimePicker } from './TimePicker.native';
|
|
6
6
|
import { dateTimePickerStyles } from './DateTimePicker.styles';
|
|
7
7
|
|
|
8
|
-
const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
9
|
-
value,
|
|
10
|
-
onChange,
|
|
11
|
-
minDate,
|
|
12
|
-
maxDate,
|
|
13
|
-
disabled = false,
|
|
14
|
-
timeMode = '12h',
|
|
15
|
-
showSeconds = false,
|
|
16
|
-
timeStep = 1,
|
|
17
|
-
style,
|
|
18
|
-
testID,
|
|
19
|
-
}) => {
|
|
20
|
-
|
|
21
|
-
const handleDateChange = (newDate: Date) => {
|
|
22
|
-
if (value) {
|
|
23
|
-
// Preserve the time from current value
|
|
24
|
-
const updatedDate = new Date(newDate);
|
|
25
|
-
updatedDate.setHours(value.getHours(), value.getMinutes(), value.getSeconds());
|
|
26
|
-
onChange(updatedDate);
|
|
27
|
-
} else {
|
|
28
|
-
onChange(newDate);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const handleTimeChange = (newTime: Date) => {
|
|
33
|
-
if (value) {
|
|
34
|
-
// Update time while preserving the date
|
|
35
|
-
const updatedDate = new Date(value);
|
|
36
|
-
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
37
|
-
onChange(updatedDate);
|
|
38
|
-
} else {
|
|
39
|
-
// If no date is selected, use today's date with the new time
|
|
40
|
-
const today = new Date();
|
|
41
|
-
today.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
42
|
-
onChange(today);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
8
|
+
const DateTimePicker: React.FC<DateTimePickerProps> = (props) => {
|
|
46
9
|
dateTimePickerStyles.useVariants({});
|
|
47
10
|
|
|
48
11
|
return (
|
|
49
|
-
<
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
maxDate={maxDate}
|
|
59
|
-
disabled={disabled}
|
|
60
|
-
/>
|
|
61
|
-
</View>
|
|
62
|
-
</View>
|
|
63
|
-
|
|
64
|
-
{/* Time Section */}
|
|
65
|
-
<View style={dateTimePickerStyles.section}>
|
|
66
|
-
<Text style={dateTimePickerStyles.sectionLabel}>Time</Text>
|
|
67
|
-
<View style={dateTimePickerStyles.sectionContent}>
|
|
68
|
-
<TimePicker
|
|
69
|
-
value={value || new Date()}
|
|
70
|
-
onChange={handleTimeChange}
|
|
71
|
-
disabled={disabled}
|
|
72
|
-
mode={timeMode}
|
|
73
|
-
showSeconds={showSeconds}
|
|
74
|
-
step={timeStep}
|
|
75
|
-
/>
|
|
76
|
-
</View>
|
|
77
|
-
</View>
|
|
78
|
-
</View>
|
|
12
|
+
<DateTimePickerBase
|
|
13
|
+
{...props}
|
|
14
|
+
renderCalendar={(calendarProps) => (
|
|
15
|
+
<Calendar {...calendarProps} />
|
|
16
|
+
)}
|
|
17
|
+
renderTimePicker={(timePickerProps) => (
|
|
18
|
+
<TimePicker {...timePickerProps} />
|
|
19
|
+
)}
|
|
20
|
+
/>
|
|
79
21
|
);
|
|
80
22
|
};
|
|
81
23
|
|
|
@@ -1,78 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View } from '@idealyst/components';
|
|
3
2
|
import { DateTimePickerProps } from './types';
|
|
4
|
-
import {
|
|
3
|
+
import { DateTimePickerBase } from './DateTimePickerBase';
|
|
4
|
+
import { Calendar } from '../DatePicker/Calendar.web';
|
|
5
5
|
import { TimePicker } from './TimePicker';
|
|
6
6
|
|
|
7
|
-
const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
8
|
-
value,
|
|
9
|
-
onChange,
|
|
10
|
-
minDate,
|
|
11
|
-
maxDate,
|
|
12
|
-
disabled = false,
|
|
13
|
-
timeMode = '12h',
|
|
14
|
-
showSeconds = false,
|
|
15
|
-
timeStep = 1,
|
|
16
|
-
style,
|
|
17
|
-
testID,
|
|
18
|
-
}) => {
|
|
19
|
-
|
|
20
|
-
const handleDateChange = (newDate: Date) => {
|
|
21
|
-
if (value) {
|
|
22
|
-
// Preserve the time from current value
|
|
23
|
-
const updatedDate = new Date(newDate);
|
|
24
|
-
updatedDate.setHours(value.getHours(), value.getMinutes(), value.getSeconds());
|
|
25
|
-
onChange(updatedDate);
|
|
26
|
-
} else {
|
|
27
|
-
onChange(newDate);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const handleTimeChange = (newTime: Date) => {
|
|
32
|
-
if (value) {
|
|
33
|
-
// Update time while preserving the date
|
|
34
|
-
const updatedDate = new Date(value);
|
|
35
|
-
updatedDate.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
36
|
-
onChange(updatedDate);
|
|
37
|
-
} else {
|
|
38
|
-
// If no date is selected, use today's date with the new time
|
|
39
|
-
const today = new Date();
|
|
40
|
-
today.setHours(newTime.getHours(), newTime.getMinutes(), newTime.getSeconds());
|
|
41
|
-
onChange(today);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const containerStyle = {
|
|
46
|
-
flexDirection: 'row' as const,
|
|
47
|
-
gap: 16,
|
|
48
|
-
alignItems: 'flex-start',
|
|
49
|
-
|
|
50
|
-
_web: {
|
|
51
|
-
display: 'flex',
|
|
52
|
-
flexDirection: 'row',
|
|
53
|
-
alignItems: 'flex-start',
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
7
|
+
const DateTimePicker: React.FC<DateTimePickerProps> = (props) => {
|
|
57
8
|
return (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<TimePicker
|
|
68
|
-
value={value || new Date()}
|
|
69
|
-
onChange={handleTimeChange}
|
|
70
|
-
disabled={disabled}
|
|
71
|
-
mode={timeMode}
|
|
72
|
-
showSeconds={showSeconds}
|
|
73
|
-
step={timeStep}
|
|
74
|
-
/>
|
|
75
|
-
</View>
|
|
9
|
+
<DateTimePickerBase
|
|
10
|
+
{...props}
|
|
11
|
+
renderCalendar={(calendarProps) => (
|
|
12
|
+
<Calendar {...calendarProps} />
|
|
13
|
+
)}
|
|
14
|
+
renderTimePicker={(timePickerProps) => (
|
|
15
|
+
<TimePicker {...timePickerProps} />
|
|
16
|
+
)}
|
|
17
|
+
/>
|
|
76
18
|
);
|
|
77
19
|
};
|
|
78
20
|
|