@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.
- package/package.json +6 -5
- package/src/DateInput.native.tsx +155 -0
- package/src/DateInput.tsx +2 -0
- package/src/DateInput.web.tsx +146 -0
- package/src/DatePicker.tsx +276 -0
- package/src/DateTimePicker.tsx +89 -0
- package/src/TimeInput.native.tsx +175 -0
- package/src/TimeInput.tsx +2 -0
- package/src/TimeInput.web.tsx +171 -0
- package/src/TimePicker.tsx +106 -0
- package/src/examples/DatePickerExamples.tsx +113 -149
- package/src/examples/index.ts +1 -1
- package/src/index.native.ts +15 -20
- package/src/index.ts +14 -19
- package/src/styles.ts +127 -0
- package/src/types.ts +56 -0
- package/src/DateInput/DateInput.native.tsx +0 -61
- package/src/DateInput/DateInput.styles.tsx +0 -26
- package/src/DateInput/DateInput.web.tsx +0 -61
- package/src/DateInput/DateInputBase.tsx +0 -228
- package/src/DateInput/index.native.ts +0 -2
- package/src/DateInput/index.ts +0 -2
- package/src/DateInput/types.ts +0 -60
- package/src/DatePicker/Calendar.native.tsx +0 -261
- package/src/DatePicker/Calendar.styles.tsx +0 -230
- package/src/DatePicker/Calendar.web.tsx +0 -159
- package/src/DatePicker/DatePicker.native.tsx +0 -51
- package/src/DatePicker/DatePicker.styles.tsx +0 -76
- package/src/DatePicker/DatePicker.web.tsx +0 -31
- package/src/DatePicker/index.native.ts +0 -3
- package/src/DatePicker/index.ts +0 -3
- package/src/DatePicker/types.ts +0 -78
- package/src/DateRangePicker/DateRangePicker.native.tsx +0 -39
- package/src/DateRangePicker/DateRangePicker.styles.tsx +0 -83
- package/src/DateRangePicker/DateRangePicker.web.tsx +0 -40
- package/src/DateRangePicker/RangeCalendar.native.tsx +0 -355
- package/src/DateRangePicker/RangeCalendar.styles.tsx +0 -200
- package/src/DateRangePicker/RangeCalendar.web.tsx +0 -384
- package/src/DateRangePicker/index.native.ts +0 -3
- package/src/DateRangePicker/index.ts +0 -3
- package/src/DateRangePicker/types.ts +0 -107
- package/src/DateTimePicker/DateTimePicker.native.tsx +0 -24
- package/src/DateTimePicker/DateTimePicker.styles.tsx +0 -104
- package/src/DateTimePicker/DateTimePicker.tsx +0 -21
- package/src/DateTimePicker/DateTimePickerBase.tsx +0 -185
- package/src/DateTimePicker/TimePicker.native.tsx +0 -17
- package/src/DateTimePicker/TimePicker.styles.tsx +0 -115
- package/src/DateTimePicker/TimePicker.tsx +0 -14
- package/src/DateTimePicker/TimePickerBase.tsx +0 -232
- package/src/DateTimePicker/index.native.ts +0 -3
- package/src/DateTimePicker/index.ts +0 -3
- package/src/DateTimePicker/primitives/ClockFace.native.tsx +0 -195
- package/src/DateTimePicker/primitives/ClockFace.web.tsx +0 -168
- package/src/DateTimePicker/primitives/TimeInput.native.tsx +0 -53
- package/src/DateTimePicker/primitives/TimeInput.web.tsx +0 -66
- package/src/DateTimePicker/primitives/index.native.ts +0 -2
- package/src/DateTimePicker/primitives/index.ts +0 -2
- package/src/DateTimePicker/primitives/index.web.ts +0 -2
- package/src/DateTimePicker/types.ts +0 -80
- package/src/DateTimePicker/utils/dimensions.native.ts +0 -9
- package/src/DateTimePicker/utils/dimensions.ts +0 -9
- package/src/DateTimePicker/utils/dimensions.web.ts +0 -33
- package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +0 -24
- package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +0 -118
- package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +0 -21
- package/src/DateTimeRangePicker/DateTimeRangePickerBase.tsx +0 -327
- package/src/DateTimeRangePicker/index.native.ts +0 -2
- package/src/DateTimeRangePicker/index.ts +0 -2
- package/src/DateTimeRangePicker/types.ts +0 -70
- package/src/primitives/CalendarGrid/CalendarGrid.styles.tsx +0 -51
- package/src/primitives/CalendarGrid/CalendarGrid.tsx +0 -146
- package/src/primitives/CalendarGrid/index.ts +0 -1
- package/src/primitives/CalendarHeader/CalendarHeader.styles.tsx +0 -25
- package/src/primitives/CalendarHeader/CalendarHeader.tsx +0 -69
- package/src/primitives/CalendarHeader/index.ts +0 -1
- package/src/primitives/CalendarOverlay/CalendarOverlay.styles.tsx +0 -86
- package/src/primitives/CalendarOverlay/CalendarOverlay.tsx +0 -136
- package/src/primitives/CalendarOverlay/index.ts +0 -1
- package/src/primitives/Wrapper/Wrapper.web.tsx +0 -33
- package/src/primitives/Wrapper/index.ts +0 -1
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
|
2
|
-
import { View, Text, Button } from '@idealyst/components';
|
|
3
|
-
import { getWebProps } from 'react-native-unistyles/web';
|
|
4
|
-
import { RangeCalendarProps, DateRange } from './types';
|
|
5
|
-
import { rangeCalendarStyles } from './RangeCalendar.styles';
|
|
6
|
-
import { CalendarOverlay } from '../primitives/CalendarOverlay';
|
|
7
|
-
|
|
8
|
-
export const RangeCalendar: React.FC<RangeCalendarProps> = ({
|
|
9
|
-
value = {},
|
|
10
|
-
onChange,
|
|
11
|
-
onDateSelected,
|
|
12
|
-
showTimes = false,
|
|
13
|
-
timeMode = '12h',
|
|
14
|
-
minDate,
|
|
15
|
-
maxDate,
|
|
16
|
-
disabled = false,
|
|
17
|
-
currentMonth: controlledCurrentMonth,
|
|
18
|
-
onMonthChange,
|
|
19
|
-
allowSameDay = true,
|
|
20
|
-
maxDays,
|
|
21
|
-
style,
|
|
22
|
-
testID,
|
|
23
|
-
}) => {
|
|
24
|
-
const [internalCurrentMonth, setInternalCurrentMonth] = useState(
|
|
25
|
-
controlledCurrentMonth || value?.startDate || new Date()
|
|
26
|
-
);
|
|
27
|
-
const [selectingEnd, setSelectingEnd] = useState(false);
|
|
28
|
-
const [overlayMode, setOverlayMode] = useState<'month' | 'year' | null>(null);
|
|
29
|
-
const containerRef = useRef<View>(null);
|
|
30
|
-
|
|
31
|
-
const currentMonth = controlledCurrentMonth || internalCurrentMonth;
|
|
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
|
-
|
|
50
|
-
const handleMonthChange = (newMonth: Date) => {
|
|
51
|
-
if (onMonthChange) {
|
|
52
|
-
onMonthChange(newMonth);
|
|
53
|
-
} else {
|
|
54
|
-
setInternalCurrentMonth(newMonth);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const { monthStart, monthEnd, daysInMonth, startingDayOfWeek } = useMemo(() => {
|
|
59
|
-
const monthStart = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
|
|
60
|
-
const monthEnd = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0);
|
|
61
|
-
const daysInMonth = monthEnd.getDate();
|
|
62
|
-
const startingDayOfWeek = monthStart.getDay();
|
|
63
|
-
|
|
64
|
-
return { monthStart, monthEnd, daysInMonth, startingDayOfWeek };
|
|
65
|
-
}, [currentMonth]);
|
|
66
|
-
|
|
67
|
-
const isDateDisabled = (date: Date): boolean => {
|
|
68
|
-
if (disabled) return true;
|
|
69
|
-
if (minDate && date < minDate) return true;
|
|
70
|
-
if (maxDate && date > maxDate) return true;
|
|
71
|
-
return false;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const isDateInRange = (date: Date): boolean => {
|
|
75
|
-
const { startDate, endDate } = value || {};
|
|
76
|
-
if (!startDate || !endDate) return false;
|
|
77
|
-
return date > startDate && date < endDate;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const isDateRangeStart = (date: Date): boolean => {
|
|
81
|
-
const { startDate } = value || {};
|
|
82
|
-
if (!startDate) return false;
|
|
83
|
-
return (
|
|
84
|
-
date.getDate() === startDate.getDate() &&
|
|
85
|
-
date.getMonth() === startDate.getMonth() &&
|
|
86
|
-
date.getFullYear() === startDate.getFullYear()
|
|
87
|
-
);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const isDateRangeEnd = (date: Date): boolean => {
|
|
91
|
-
const { endDate } = value || {};
|
|
92
|
-
if (!endDate) return false;
|
|
93
|
-
return (
|
|
94
|
-
date.getDate() === endDate.getDate() &&
|
|
95
|
-
date.getMonth() === endDate.getMonth() &&
|
|
96
|
-
date.getFullYear() === endDate.getFullYear()
|
|
97
|
-
);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const isDateSelected = (date: Date): boolean => {
|
|
101
|
-
return isDateRangeStart(date) || isDateRangeEnd(date);
|
|
102
|
-
};
|
|
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
|
-
|
|
132
|
-
const handleDateClick = (date: Date) => {
|
|
133
|
-
if (isDateDisabled(date)) return;
|
|
134
|
-
|
|
135
|
-
const { startDate, endDate } = value || {};
|
|
136
|
-
|
|
137
|
-
// If no range is selected or we're starting fresh
|
|
138
|
-
if (!startDate || (startDate && endDate)) {
|
|
139
|
-
onChange({ startDate: date, endDate: undefined });
|
|
140
|
-
setSelectingEnd(true);
|
|
141
|
-
onDateSelected?.('start');
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// If we have a start date but no end date
|
|
146
|
-
if (startDate && !endDate) {
|
|
147
|
-
let newStartDate = startDate;
|
|
148
|
-
let newEndDate = date;
|
|
149
|
-
|
|
150
|
-
// Swap if end date is before start date
|
|
151
|
-
if (date < startDate) {
|
|
152
|
-
newStartDate = date;
|
|
153
|
-
newEndDate = startDate;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Check if same day selection is allowed
|
|
157
|
-
if (!allowSameDay && newStartDate.getTime() === newEndDate.getTime()) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Check max days constraint
|
|
162
|
-
if (maxDays) {
|
|
163
|
-
const daysDiff = Math.ceil((newEndDate.getTime() - newStartDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
164
|
-
if (daysDiff > maxDays) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
onChange({ startDate: newStartDate, endDate: newEndDate });
|
|
170
|
-
setSelectingEnd(false);
|
|
171
|
-
onDateSelected?.('end');
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const goToPreviousMonth = () => {
|
|
176
|
-
const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
|
|
177
|
-
handleMonthChange(newMonth);
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const goToNextMonth = () => {
|
|
181
|
-
const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
|
|
182
|
-
handleMonthChange(newMonth);
|
|
183
|
-
};
|
|
184
|
-
|
|
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);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const handleMonthSelect = (monthIndex: number) => {
|
|
194
|
-
const newMonth = new Date(currentMonth.getFullYear(), monthIndex, 1);
|
|
195
|
-
handleMonthChange(newMonth);
|
|
196
|
-
setOverlayMode(null);
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const handleYearSelect = (year: number) => {
|
|
200
|
-
const newMonth = new Date(year, currentMonth.getMonth(), 1);
|
|
201
|
-
handleMonthChange(newMonth);
|
|
202
|
-
setOverlayMode(null);
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const monthName = currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
207
|
-
|
|
208
|
-
// Create calendar grid
|
|
209
|
-
const calendarDays = [];
|
|
210
|
-
|
|
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 });
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Add days of the current month
|
|
221
|
-
for (let day = 1; day <= daysInMonth; day++) {
|
|
222
|
-
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
|
|
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 });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
rangeCalendarStyles.useVariants({});
|
|
238
|
-
|
|
239
|
-
const containerProps = getWebProps([rangeCalendarStyles.container, style]);
|
|
240
|
-
const headerProps = getWebProps([rangeCalendarStyles.header]);
|
|
241
|
-
const headerTitleProps = getWebProps([rangeCalendarStyles.headerTitle]);
|
|
242
|
-
|
|
243
|
-
return (
|
|
244
|
-
<div {...containerProps} data-testid={testID} ref={containerRef}>
|
|
245
|
-
{/* Header */}
|
|
246
|
-
<div {...headerProps}>
|
|
247
|
-
<Button
|
|
248
|
-
type="text"
|
|
249
|
-
size="sm"
|
|
250
|
-
onPress={goToPreviousMonth}
|
|
251
|
-
disabled={disabled}
|
|
252
|
-
style={getWebProps([rangeCalendarStyles.headerButton]).style}
|
|
253
|
-
>
|
|
254
|
-
←
|
|
255
|
-
</Button>
|
|
256
|
-
|
|
257
|
-
<div {...headerTitleProps} style={{ display: 'flex', gap: '8px' }}>
|
|
258
|
-
<Button
|
|
259
|
-
type="text"
|
|
260
|
-
size="sm"
|
|
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
|
-
type="text"
|
|
269
|
-
size="sm"
|
|
270
|
-
onPress={() => setOverlayMode('year')}
|
|
271
|
-
disabled={disabled}
|
|
272
|
-
style={{ padding: '4px 8px' }}
|
|
273
|
-
>
|
|
274
|
-
<Text weight="semibold">{currentMonth.getFullYear()}</Text>
|
|
275
|
-
</Button>
|
|
276
|
-
</div>
|
|
277
|
-
|
|
278
|
-
<Button
|
|
279
|
-
type="text"
|
|
280
|
-
size="sm"
|
|
281
|
-
onPress={goToNextMonth}
|
|
282
|
-
disabled={disabled}
|
|
283
|
-
style={getWebProps([rangeCalendarStyles.headerButton]).style}
|
|
284
|
-
>
|
|
285
|
-
→
|
|
286
|
-
</Button>
|
|
287
|
-
</div>
|
|
288
|
-
|
|
289
|
-
{/* Weekday headers */}
|
|
290
|
-
<div {...getWebProps([rangeCalendarStyles.weekdayHeader])}>
|
|
291
|
-
{['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((day) => (
|
|
292
|
-
<div key={day} {...getWebProps([rangeCalendarStyles.weekdayCell])}>
|
|
293
|
-
<div {...getWebProps([rangeCalendarStyles.weekdayText])}>
|
|
294
|
-
{day}
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
|
-
))}
|
|
298
|
-
</div>
|
|
299
|
-
|
|
300
|
-
{/* Calendar grid */}
|
|
301
|
-
<div {...getWebProps([rangeCalendarStyles.calendarGrid])}>
|
|
302
|
-
{calendarDays.map((dayInfo, index) => (
|
|
303
|
-
<div key={index} {...getWebProps([rangeCalendarStyles.dayCell])}>
|
|
304
|
-
{dayInfo && (
|
|
305
|
-
<Button
|
|
306
|
-
type="text"
|
|
307
|
-
disabled={isDateDisabled(dayInfo.date)}
|
|
308
|
-
onPress={() => handleDateClick(dayInfo.date)}
|
|
309
|
-
size="sm"
|
|
310
|
-
style={{
|
|
311
|
-
...getWebProps([rangeCalendarStyles.dayButton]).style,
|
|
312
|
-
backgroundColor: isDateSelected(dayInfo.date)
|
|
313
|
-
? '#3b82f6'
|
|
314
|
-
: isDateInRange(dayInfo.date)
|
|
315
|
-
? '#3b82f620'
|
|
316
|
-
: 'transparent',
|
|
317
|
-
color: isDateSelected(dayInfo.date) ? 'white' : dayInfo.isCurrentMonth ? 'black' : '#9ca3af',
|
|
318
|
-
fontWeight: isDateSelected(dayInfo.date) ? '600' : '400',
|
|
319
|
-
borderRadius: isDateRangeStart(dayInfo.date)
|
|
320
|
-
? '6px 0 0 6px'
|
|
321
|
-
: isDateRangeEnd(dayInfo.date)
|
|
322
|
-
? '0 6px 6px 0'
|
|
323
|
-
: isDateInRange(dayInfo.date)
|
|
324
|
-
? '0'
|
|
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,
|
|
335
|
-
}}
|
|
336
|
-
>
|
|
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
|
-
})()}
|
|
363
|
-
</Button>
|
|
364
|
-
)}
|
|
365
|
-
</div>
|
|
366
|
-
))}
|
|
367
|
-
</div>
|
|
368
|
-
|
|
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)}
|
|
378
|
-
disabled={disabled}
|
|
379
|
-
/>
|
|
380
|
-
)}
|
|
381
|
-
|
|
382
|
-
</div>
|
|
383
|
-
);
|
|
384
|
-
};
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { ViewStyle } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export interface DateRange {
|
|
5
|
-
/** Start date of the range */
|
|
6
|
-
startDate?: Date;
|
|
7
|
-
|
|
8
|
-
/** End date of the range */
|
|
9
|
-
endDate?: Date;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface DateRangePickerProps {
|
|
13
|
-
/** Current selected date range */
|
|
14
|
-
value?: DateRange;
|
|
15
|
-
|
|
16
|
-
/** Called when date range changes */
|
|
17
|
-
onChange: (range: DateRange | 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') */
|
|
41
|
-
format?: string;
|
|
42
|
-
|
|
43
|
-
/** Locale for date formatting */
|
|
44
|
-
locale?: string;
|
|
45
|
-
|
|
46
|
-
/** Size variant */
|
|
47
|
-
size?: 'sm' | 'md' | 'lg';
|
|
48
|
-
|
|
49
|
-
/** Visual variant */
|
|
50
|
-
variant?: 'outlined' | 'filled';
|
|
51
|
-
|
|
52
|
-
/** Allow same day selection for start and end */
|
|
53
|
-
allowSameDay?: boolean;
|
|
54
|
-
|
|
55
|
-
/** Maximum number of days in range */
|
|
56
|
-
maxDays?: number;
|
|
57
|
-
|
|
58
|
-
/** Custom styles */
|
|
59
|
-
style?: ViewStyle;
|
|
60
|
-
|
|
61
|
-
/** Test ID for testing */
|
|
62
|
-
testID?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface RangeCalendarProps {
|
|
66
|
-
/** Current selected date range */
|
|
67
|
-
value?: DateRange;
|
|
68
|
-
|
|
69
|
-
/** Called when range is selected */
|
|
70
|
-
onChange: (range: DateRange) => void;
|
|
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
|
-
|
|
81
|
-
/** Minimum selectable date */
|
|
82
|
-
minDate?: Date;
|
|
83
|
-
|
|
84
|
-
/** Maximum selectable date */
|
|
85
|
-
maxDate?: Date;
|
|
86
|
-
|
|
87
|
-
/** Disabled state */
|
|
88
|
-
disabled?: boolean;
|
|
89
|
-
|
|
90
|
-
/** Current month being viewed */
|
|
91
|
-
currentMonth?: Date;
|
|
92
|
-
|
|
93
|
-
/** Called when month changes */
|
|
94
|
-
onMonthChange?: (month: Date) => void;
|
|
95
|
-
|
|
96
|
-
/** Allow same day selection */
|
|
97
|
-
allowSameDay?: boolean;
|
|
98
|
-
|
|
99
|
-
/** Maximum number of days in range */
|
|
100
|
-
maxDays?: number;
|
|
101
|
-
|
|
102
|
-
/** Custom styles */
|
|
103
|
-
style?: ViewStyle;
|
|
104
|
-
|
|
105
|
-
/** Test ID for testing */
|
|
106
|
-
testID?: string;
|
|
107
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { DateTimePickerProps } from './types';
|
|
3
|
-
import { DateTimePickerBase } from './DateTimePickerBase';
|
|
4
|
-
import { Calendar } from '../DatePicker/Calendar.native';
|
|
5
|
-
import { TimePicker } from './TimePicker.native';
|
|
6
|
-
import { dateTimePickerStyles } from './DateTimePicker.styles';
|
|
7
|
-
|
|
8
|
-
const DateTimePicker: React.FC<DateTimePickerProps> = (props) => {
|
|
9
|
-
dateTimePickerStyles.useVariants({});
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<DateTimePickerBase
|
|
13
|
-
{...props}
|
|
14
|
-
renderCalendar={(calendarProps) => (
|
|
15
|
-
<Calendar {...calendarProps} />
|
|
16
|
-
)}
|
|
17
|
-
renderTimePicker={(timePickerProps) => (
|
|
18
|
-
<TimePicker {...timePickerProps} />
|
|
19
|
-
)}
|
|
20
|
-
/>
|
|
21
|
-
);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default DateTimePicker;
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
-
|
|
3
|
-
export const dateTimePickerStyles = StyleSheet.create((theme) => ({
|
|
4
|
-
container: {
|
|
5
|
-
gap: theme.spacing?.md || 16,
|
|
6
|
-
},
|
|
7
|
-
|
|
8
|
-
label: {
|
|
9
|
-
fontSize: theme.typography?.fontSize?.sm || 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
|
-
selectedDateTimeHeader: {
|
|
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
|
-
selectedDateTimeLabel: {
|
|
33
|
-
fontSize: theme.typography?.fontSize?.sm || 12,
|
|
34
|
-
fontWeight: '500',
|
|
35
|
-
color: theme.colors?.text?.secondary || '#6b7280',
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
selectedDateTimeValue: {
|
|
39
|
-
fontSize: theme.typography?.fontSize?.md || 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?.fontSize?.sm || 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
|
-
errorText: {
|
|
67
|
-
fontSize: theme.typography?.fontSize?.sm || 12,
|
|
68
|
-
color: theme.colors?.semantic?.error || '#dc2626',
|
|
69
|
-
marginTop: theme.spacing?.xs || 4,
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
helperText: {
|
|
73
|
-
fontSize: theme.typography?.fontSize?.sm || 12,
|
|
74
|
-
color: theme.colors?.text?.secondary || '#6b7280',
|
|
75
|
-
marginTop: theme.spacing?.xs || 4,
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
sideBySideLayout: {
|
|
79
|
-
flexDirection: 'row',
|
|
80
|
-
gap: theme.spacing?.md || 16,
|
|
81
|
-
alignItems: 'flex-start',
|
|
82
|
-
_web: {
|
|
83
|
-
display: 'flex',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
stepNavigation: {
|
|
88
|
-
flexDirection: 'row',
|
|
89
|
-
gap: theme.spacing?.xs || 8,
|
|
90
|
-
marginBottom: theme.spacing?.xs || 8,
|
|
91
|
-
_web: {
|
|
92
|
-
display: 'flex',
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
stepButton: {
|
|
97
|
-
flex: 1,
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
backButtonContainer: {
|
|
101
|
-
marginTop: theme.spacing?.sm || 12,
|
|
102
|
-
alignItems: 'flex-start',
|
|
103
|
-
},
|
|
104
|
-
}));
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { DateTimePickerProps } from './types';
|
|
3
|
-
import { DateTimePickerBase } from './DateTimePickerBase';
|
|
4
|
-
import { Calendar } from '../DatePicker/Calendar.web';
|
|
5
|
-
import { TimePicker } from './TimePicker';
|
|
6
|
-
|
|
7
|
-
const DateTimePicker: React.FC<DateTimePickerProps> = (props) => {
|
|
8
|
-
return (
|
|
9
|
-
<DateTimePickerBase
|
|
10
|
-
{...props}
|
|
11
|
-
renderCalendar={(calendarProps) => (
|
|
12
|
-
<Calendar {...calendarProps} />
|
|
13
|
-
)}
|
|
14
|
-
renderTimePicker={(timePickerProps) => (
|
|
15
|
-
<TimePicker {...timePickerProps} />
|
|
16
|
-
)}
|
|
17
|
-
/>
|
|
18
|
-
);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default DateTimePicker;
|