@idealyst/datepicker 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +88 -0
  2. package/package.json +77 -0
  3. package/src/DatePicker/Calendar.native.tsx +159 -0
  4. package/src/DatePicker/Calendar.styles.tsx +224 -0
  5. package/src/DatePicker/Calendar.tsx +154 -0
  6. package/src/DatePicker/DatePicker.native.tsx +33 -0
  7. package/src/DatePicker/DatePicker.styles.tsx +69 -0
  8. package/src/DatePicker/DatePicker.web.tsx +31 -0
  9. package/src/DatePicker/index.native.ts +3 -0
  10. package/src/DatePicker/index.ts +3 -0
  11. package/src/DatePicker/types.ts +78 -0
  12. package/src/DateRangePicker/DateRangePicker.native.tsx +39 -0
  13. package/src/DateRangePicker/DateRangePicker.styles.tsx +83 -0
  14. package/src/DateRangePicker/DateRangePicker.web.tsx +40 -0
  15. package/src/DateRangePicker/RangeCalendar.native.tsx +267 -0
  16. package/src/DateRangePicker/RangeCalendar.styles.tsx +170 -0
  17. package/src/DateRangePicker/RangeCalendar.web.tsx +275 -0
  18. package/src/DateRangePicker/index.native.ts +3 -0
  19. package/src/DateRangePicker/index.ts +3 -0
  20. package/src/DateRangePicker/types.ts +98 -0
  21. package/src/DateTimePicker/DateTimePicker.native.tsx +82 -0
  22. package/src/DateTimePicker/DateTimePicker.styles.tsx +77 -0
  23. package/src/DateTimePicker/DateTimePicker.tsx +79 -0
  24. package/src/DateTimePicker/TimePicker.native.tsx +204 -0
  25. package/src/DateTimePicker/TimePicker.styles.tsx +116 -0
  26. package/src/DateTimePicker/TimePicker.tsx +406 -0
  27. package/src/DateTimePicker/index.native.ts +3 -0
  28. package/src/DateTimePicker/index.ts +3 -0
  29. package/src/DateTimePicker/types.ts +84 -0
  30. package/src/DateTimeRangePicker/DateTimeRangePicker.native.tsx +213 -0
  31. package/src/DateTimeRangePicker/DateTimeRangePicker.styles.tsx +95 -0
  32. package/src/DateTimeRangePicker/DateTimeRangePicker.web.tsx +141 -0
  33. package/src/DateTimeRangePicker/index.native.ts +2 -0
  34. package/src/DateTimeRangePicker/index.ts +2 -0
  35. package/src/DateTimeRangePicker/types.ts +72 -0
  36. package/src/examples/DatePickerExamples.tsx +274 -0
  37. package/src/examples/index.ts +1 -0
  38. package/src/index.native.ts +16 -0
  39. package/src/index.ts +16 -0
  40. package/src/primitives/CalendarGrid/CalendarGrid.styles.tsx +62 -0
  41. package/src/primitives/CalendarGrid/CalendarGrid.tsx +138 -0
  42. package/src/primitives/CalendarGrid/index.ts +1 -0
  43. package/src/primitives/CalendarHeader/CalendarHeader.styles.tsx +25 -0
  44. package/src/primitives/CalendarHeader/CalendarHeader.tsx +69 -0
  45. package/src/primitives/CalendarHeader/index.ts +1 -0
  46. package/src/primitives/CalendarOverlay/CalendarOverlay.styles.tsx +81 -0
  47. package/src/primitives/CalendarOverlay/CalendarOverlay.tsx +130 -0
  48. package/src/primitives/CalendarOverlay/index.ts +1 -0
  49. package/src/primitives/Wrapper/Wrapper.web.tsx +33 -0
  50. package/src/primitives/Wrapper/index.ts +1 -0
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { View } from '@idealyst/components';
3
+ import { DateTimePickerProps } from './types';
4
+ import { Calendar } from '../DatePicker/Calendar';
5
+ import { TimePicker } from './TimePicker';
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
+
57
+ return (
58
+ <View style={[containerStyle, style]} data-testid={testID}>
59
+ <Calendar
60
+ value={value}
61
+ onChange={handleDateChange}
62
+ minDate={minDate}
63
+ maxDate={maxDate}
64
+ disabled={disabled}
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>
76
+ );
77
+ };
78
+
79
+ export default DateTimePicker;
@@ -0,0 +1,204 @@
1
+ import React from 'react';
2
+ import { View, Text, Button } from '@idealyst/components';
3
+ import { TimePickerProps } from './types';
4
+ import { timePickerStyles } from './TimePicker.styles';
5
+
6
+ export const TimePicker: React.FC<TimePickerProps> = ({
7
+ value = new Date(),
8
+ onChange,
9
+ disabled = false,
10
+ mode = '12h',
11
+ showSeconds = false,
12
+ step = 1,
13
+ style,
14
+ testID,
15
+ }) => {
16
+ const hours = value.getHours();
17
+ const minutes = value.getMinutes();
18
+ const seconds = value.getSeconds();
19
+
20
+ const displayHours = mode === '12h' ? (hours === 0 ? 12 : hours > 12 ? hours - 12 : hours) : hours;
21
+ const ampm = mode === '12h' ? (hours >= 12 ? 'PM' : 'AM') : null;
22
+
23
+ const updateTime = (newHours: number, newMinutes: number, newSeconds?: number) => {
24
+ const newDate = new Date(value);
25
+ newDate.setHours(newHours, newMinutes, newSeconds || 0);
26
+ onChange(newDate);
27
+ };
28
+
29
+ const handleHourChange = (delta: number) => {
30
+ let newHours = hours + delta;
31
+ if (mode === '12h') {
32
+ if (newHours < 0) newHours = 23;
33
+ if (newHours > 23) newHours = 0;
34
+ } else {
35
+ if (newHours < 0) newHours = 23;
36
+ if (newHours > 23) newHours = 0;
37
+ }
38
+ updateTime(newHours, minutes, seconds);
39
+ };
40
+
41
+ const handleMinuteChange = (delta: number) => {
42
+ let newMinutes = minutes + (delta * step);
43
+ let newHours = hours;
44
+
45
+ if (newMinutes < 0) {
46
+ newMinutes = 60 + newMinutes;
47
+ newHours = hours - 1;
48
+ if (newHours < 0) newHours = 23;
49
+ } else if (newMinutes >= 60) {
50
+ newMinutes = newMinutes - 60;
51
+ newHours = hours + 1;
52
+ if (newHours > 23) newHours = 0;
53
+ }
54
+
55
+ updateTime(newHours, newMinutes, seconds);
56
+ };
57
+
58
+ const handleSecondChange = (delta: number) => {
59
+ let newSeconds = seconds + delta;
60
+ let newMinutes = minutes;
61
+ let newHours = hours;
62
+
63
+ if (newSeconds < 0) {
64
+ newSeconds = 59;
65
+ newMinutes = minutes - 1;
66
+ if (newMinutes < 0) {
67
+ newMinutes = 59;
68
+ newHours = hours - 1;
69
+ if (newHours < 0) newHours = 23;
70
+ }
71
+ } else if (newSeconds >= 60) {
72
+ newSeconds = 0;
73
+ newMinutes = minutes + 1;
74
+ if (newMinutes >= 60) {
75
+ newMinutes = 0;
76
+ newHours = hours + 1;
77
+ if (newHours > 23) newHours = 0;
78
+ }
79
+ }
80
+
81
+ updateTime(newHours, newMinutes, newSeconds);
82
+ };
83
+
84
+ const toggleAmPm = () => {
85
+ if (mode === '12h') {
86
+ const newHours = hours >= 12 ? hours - 12 : hours + 12;
87
+ updateTime(newHours, minutes, seconds);
88
+ }
89
+ };
90
+
91
+ timePickerStyles.useVariants({});
92
+
93
+ return (
94
+ <View style={[timePickerStyles.container, style]} testID={testID}>
95
+ {/* Hours */}
96
+ <View style={timePickerStyles.timeSection}>
97
+ <View style={timePickerStyles.stepperContainer}>
98
+ <Button
99
+ variant="text"
100
+ size="small"
101
+ onPress={() => handleHourChange(1)}
102
+ disabled={disabled}
103
+ style={timePickerStyles.stepperButton}
104
+ >
105
+ <Text style={timePickerStyles.stepperText}>▲</Text>
106
+ </Button>
107
+ <View style={timePickerStyles.timeInput}>
108
+ <Text style={{ textAlign: 'center', fontSize: 16, fontWeight: '600' }}>
109
+ {String(displayHours).padStart(2, '0')}
110
+ </Text>
111
+ </View>
112
+ <Button
113
+ variant="text"
114
+ size="small"
115
+ onPress={() => handleHourChange(-1)}
116
+ disabled={disabled}
117
+ style={timePickerStyles.stepperButton}
118
+ >
119
+ <Text style={timePickerStyles.stepperText}>▼</Text>
120
+ </Button>
121
+ </View>
122
+ </View>
123
+
124
+ {/* Separator */}
125
+ <Text style={timePickerStyles.timeSeparator}>:</Text>
126
+
127
+ {/* Minutes */}
128
+ <View style={timePickerStyles.timeSection}>
129
+ <View style={timePickerStyles.stepperContainer}>
130
+ <Button
131
+ variant="text"
132
+ size="small"
133
+ onPress={() => handleMinuteChange(1)}
134
+ disabled={disabled}
135
+ style={timePickerStyles.stepperButton}
136
+ >
137
+ <Text style={timePickerStyles.stepperText}>▲</Text>
138
+ </Button>
139
+ <View style={timePickerStyles.timeInput}>
140
+ <Text style={{ textAlign: 'center', fontSize: 16, fontWeight: '600' }}>
141
+ {String(minutes).padStart(2, '0')}
142
+ </Text>
143
+ </View>
144
+ <Button
145
+ variant="text"
146
+ size="small"
147
+ onPress={() => handleMinuteChange(-1)}
148
+ disabled={disabled}
149
+ style={timePickerStyles.stepperButton}
150
+ >
151
+ <Text style={timePickerStyles.stepperText}>▼</Text>
152
+ </Button>
153
+ </View>
154
+ </View>
155
+
156
+ {/* Seconds */}
157
+ {showSeconds && (
158
+ <>
159
+ <Text style={timePickerStyles.timeSeparator}>:</Text>
160
+ <View style={timePickerStyles.timeSection}>
161
+ <View style={timePickerStyles.stepperContainer}>
162
+ <Button
163
+ variant="text"
164
+ size="small"
165
+ onPress={() => handleSecondChange(1)}
166
+ disabled={disabled}
167
+ style={timePickerStyles.stepperButton}
168
+ >
169
+ <Text style={timePickerStyles.stepperText}>▲</Text>
170
+ </Button>
171
+ <View style={timePickerStyles.timeInput}>
172
+ <Text style={{ textAlign: 'center', fontSize: 16, fontWeight: '600' }}>
173
+ {String(seconds).padStart(2, '0')}
174
+ </Text>
175
+ </View>
176
+ <Button
177
+ variant="text"
178
+ size="small"
179
+ onPress={() => handleSecondChange(-1)}
180
+ disabled={disabled}
181
+ style={timePickerStyles.stepperButton}
182
+ >
183
+ <Text style={timePickerStyles.stepperText}>▼</Text>
184
+ </Button>
185
+ </View>
186
+ </View>
187
+ </>
188
+ )}
189
+
190
+ {/* AM/PM */}
191
+ {mode === '12h' && ampm && (
192
+ <Button
193
+ variant="outlined"
194
+ size="small"
195
+ onPress={toggleAmPm}
196
+ disabled={disabled}
197
+ style={timePickerStyles.ampmButton}
198
+ >
199
+ {ampm}
200
+ </Button>
201
+ )}
202
+ </View>
203
+ );
204
+ };
@@ -0,0 +1,116 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const timePickerStyles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ flexDirection: 'column',
6
+ alignItems: 'center',
7
+ gap: 4,
8
+ width: 170,
9
+ },
10
+ clockContainer: {
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ marginBottom: 4,
14
+
15
+ _web: {
16
+ display: 'flex',
17
+ alignItems: 'center',
18
+ justifyContent: 'center',
19
+ }
20
+ },
21
+ clockSvg: {
22
+ cursor: 'pointer',
23
+
24
+ _web: {
25
+ cursor: 'pointer',
26
+ }
27
+ },
28
+ timeInputRow: {
29
+ flexDirection: 'row',
30
+ alignItems: 'center',
31
+ gap: 8,
32
+
33
+ _web: {
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ }
37
+ },
38
+ timeInput: {
39
+ width: 32,
40
+ height: 32,
41
+ textAlign: 'center',
42
+ fontSize: 16,
43
+ fontWeight: '600',
44
+ color: theme.colors?.text?.primary || '#111827',
45
+ borderWidth: 0,
46
+ borderBottomWidth: 0,
47
+ borderRadius: 0,
48
+ backgroundColor: 'transparent',
49
+
50
+ _web: {
51
+ border: 'none',
52
+ borderBottom: 'none',
53
+ outline: 'none',
54
+ cursor: 'pointer',
55
+ }
56
+ },
57
+ activeInput: {
58
+ color: theme.colors?.accent?.primary || '#3b82f6',
59
+ borderBottomWidth: 2,
60
+ borderBottomColor: theme.colors?.accent?.primary || '#3b82f6',
61
+ borderRadius: 0,
62
+ },
63
+ timeSeparator: {
64
+ fontSize: 16,
65
+ fontWeight: '300',
66
+ color: theme.colors?.text?.secondary || '#6b7280',
67
+ },
68
+ ampmButton: {
69
+ paddingHorizontal: 8,
70
+ paddingVertical: 4,
71
+ borderRadius: 4,
72
+ minWidth: 36,
73
+ fontSize: 12,
74
+ fontWeight: '600',
75
+ },
76
+ tabBar: {
77
+ flexDirection: 'row',
78
+ gap: 4,
79
+ marginBottom: 4,
80
+ padding: 2,
81
+ backgroundColor: theme.colors?.surface?.secondary || '#f3f4f6',
82
+ borderRadius: 6,
83
+
84
+ _web: {
85
+ display: 'flex',
86
+ }
87
+ },
88
+ tabButton: {
89
+ flex: 1,
90
+ paddingHorizontal: 12,
91
+ paddingVertical: 6,
92
+ fontSize: 13,
93
+ fontWeight: '500',
94
+ borderRadius: 4,
95
+ borderWidth: 0,
96
+ cursor: 'pointer',
97
+
98
+ _web: {
99
+ border: 'none',
100
+ cursor: 'pointer',
101
+ transition: 'all 0.2s',
102
+ }
103
+ },
104
+ activeTab: {
105
+ backgroundColor: theme.colors?.surface?.primary || '#ffffff',
106
+ color: theme.colors?.accent?.primary || '#3b82f6',
107
+
108
+ _web: {
109
+ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
110
+ }
111
+ },
112
+ inactiveTab: {
113
+ backgroundColor: 'transparent',
114
+ color: theme.colors?.text?.secondary || '#6b7280',
115
+ },
116
+ }));