@idealyst/datepicker 1.1.4 → 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
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Modal, TextInput as RNTextInput } from 'react-native';
|
|
3
|
+
import { View, Text, Button, Icon } from '@idealyst/components';
|
|
4
|
+
import { TimePicker } from './TimePicker';
|
|
5
|
+
import { datePickerStyles } from './styles';
|
|
6
|
+
import type { TimeInputProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const TimeInput: React.FC<TimeInputProps> = ({
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
11
|
+
label,
|
|
12
|
+
placeholder = '12:00 PM',
|
|
13
|
+
mode = '12h',
|
|
14
|
+
minuteStep = 1,
|
|
15
|
+
disabled = false,
|
|
16
|
+
error,
|
|
17
|
+
style,
|
|
18
|
+
}) => {
|
|
19
|
+
const [open, setOpen] = useState(false);
|
|
20
|
+
const [inputValue, setInputValue] = useState('');
|
|
21
|
+
|
|
22
|
+
// Format time to string
|
|
23
|
+
const formatTime = (date: Date | undefined): string => {
|
|
24
|
+
if (!date) return '';
|
|
25
|
+
const hours = date.getHours();
|
|
26
|
+
const minutes = date.getMinutes();
|
|
27
|
+
|
|
28
|
+
if (mode === '24h') {
|
|
29
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const period = hours >= 12 ? 'PM' : 'AM';
|
|
33
|
+
const displayHours = hours % 12 || 12;
|
|
34
|
+
return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Parse string to date (time only)
|
|
38
|
+
const parseTime = (str: string): Date | null => {
|
|
39
|
+
const match12h = str.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
|
|
40
|
+
if (match12h) {
|
|
41
|
+
let [, hours, minutes, period] = match12h;
|
|
42
|
+
let h = parseInt(hours);
|
|
43
|
+
const m = parseInt(minutes);
|
|
44
|
+
if (h < 1 || h > 12 || m > 59) return null;
|
|
45
|
+
if (period.toUpperCase() === 'PM' && h !== 12) h += 12;
|
|
46
|
+
if (period.toUpperCase() === 'AM' && h === 12) h = 0;
|
|
47
|
+
const date = new Date();
|
|
48
|
+
date.setHours(h, m, 0, 0);
|
|
49
|
+
return date;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const match24h = str.match(/^(\d{1,2}):(\d{2})$/);
|
|
53
|
+
if (match24h) {
|
|
54
|
+
const [, hours, minutes] = match24h;
|
|
55
|
+
const h = parseInt(hours);
|
|
56
|
+
const m = parseInt(minutes);
|
|
57
|
+
if (h > 23 || m > 59) return null;
|
|
58
|
+
const date = new Date();
|
|
59
|
+
date.setHours(h, m, 0, 0);
|
|
60
|
+
return date;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
setInputValue(formatTime(value ?? undefined));
|
|
68
|
+
}, [value, mode]);
|
|
69
|
+
|
|
70
|
+
const handleInputChange = (newValue: string) => {
|
|
71
|
+
setInputValue(newValue);
|
|
72
|
+
const parsed = parseTime(newValue);
|
|
73
|
+
if (parsed) {
|
|
74
|
+
onChange(parsed);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleInputBlur = () => {
|
|
79
|
+
const parsed = parseTime(inputValue);
|
|
80
|
+
if (!parsed && value) {
|
|
81
|
+
setInputValue(formatTime(value));
|
|
82
|
+
} else if (!parsed) {
|
|
83
|
+
setInputValue('');
|
|
84
|
+
onChange(null);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const handleTimeChange = (date: Date) => {
|
|
89
|
+
onChange(date);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<View style={style}>
|
|
94
|
+
{label && (
|
|
95
|
+
<Text typography="body2" weight="medium" style={{ marginBottom: 4 }}>
|
|
96
|
+
{label}
|
|
97
|
+
</Text>
|
|
98
|
+
)}
|
|
99
|
+
<View
|
|
100
|
+
style={{
|
|
101
|
+
flexDirection: 'row',
|
|
102
|
+
alignItems: 'center',
|
|
103
|
+
borderWidth: 1,
|
|
104
|
+
borderColor: error ? '#ef4444' : '#d1d5db',
|
|
105
|
+
borderRadius: 6,
|
|
106
|
+
backgroundColor: disabled ? '#f3f4f6' : 'white',
|
|
107
|
+
overflow: 'hidden',
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
<RNTextInput
|
|
111
|
+
value={inputValue}
|
|
112
|
+
onChangeText={handleInputChange}
|
|
113
|
+
onBlur={handleInputBlur}
|
|
114
|
+
placeholder={placeholder}
|
|
115
|
+
editable={!disabled}
|
|
116
|
+
style={{
|
|
117
|
+
flex: 1,
|
|
118
|
+
padding: 8,
|
|
119
|
+
paddingHorizontal: 12,
|
|
120
|
+
fontSize: 14,
|
|
121
|
+
backgroundColor: 'transparent',
|
|
122
|
+
color: disabled ? '#9ca3af' : '#111827',
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
<Button
|
|
126
|
+
type="text"
|
|
127
|
+
size="sm"
|
|
128
|
+
onPress={() => !disabled && setOpen(true)}
|
|
129
|
+
disabled={disabled}
|
|
130
|
+
style={{ marginRight: 4 }}
|
|
131
|
+
>
|
|
132
|
+
<Icon name="clock-outline" size={18} />
|
|
133
|
+
</Button>
|
|
134
|
+
</View>
|
|
135
|
+
{error && (
|
|
136
|
+
<Text typography="caption" style={{ marginTop: 4, color: '#ef4444' }}>
|
|
137
|
+
{error}
|
|
138
|
+
</Text>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
<Modal
|
|
142
|
+
visible={open}
|
|
143
|
+
transparent
|
|
144
|
+
animationType="fade"
|
|
145
|
+
onRequestClose={() => setOpen(false)}
|
|
146
|
+
>
|
|
147
|
+
<View
|
|
148
|
+
style={{
|
|
149
|
+
flex: 1,
|
|
150
|
+
justifyContent: 'center',
|
|
151
|
+
alignItems: 'center',
|
|
152
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
<View style={datePickerStyles.popoverContent}>
|
|
156
|
+
<TimePicker
|
|
157
|
+
value={value ?? undefined}
|
|
158
|
+
onChange={handleTimeChange}
|
|
159
|
+
mode={mode}
|
|
160
|
+
minuteStep={minuteStep}
|
|
161
|
+
disabled={disabled}
|
|
162
|
+
/>
|
|
163
|
+
<Button
|
|
164
|
+
type="text"
|
|
165
|
+
onPress={() => setOpen(false)}
|
|
166
|
+
style={{ marginTop: 8 }}
|
|
167
|
+
>
|
|
168
|
+
Close
|
|
169
|
+
</Button>
|
|
170
|
+
</View>
|
|
171
|
+
</View>
|
|
172
|
+
</Modal>
|
|
173
|
+
</View>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { View, Text, Button, Icon } from '@idealyst/components';
|
|
3
|
+
import { PositionedPortal } from '@idealyst/components/internal';
|
|
4
|
+
import { TimePicker } from './TimePicker';
|
|
5
|
+
import { datePickerStyles } from './styles';
|
|
6
|
+
import type { TimeInputProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const TimeInput: React.FC<TimeInputProps> = ({
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
11
|
+
label,
|
|
12
|
+
placeholder = '12:00 PM',
|
|
13
|
+
mode = '12h',
|
|
14
|
+
minuteStep = 1,
|
|
15
|
+
disabled = false,
|
|
16
|
+
error,
|
|
17
|
+
style,
|
|
18
|
+
}) => {
|
|
19
|
+
const [open, setOpen] = useState(false);
|
|
20
|
+
const [inputValue, setInputValue] = useState('');
|
|
21
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
22
|
+
|
|
23
|
+
// Format time to string
|
|
24
|
+
const formatTime = (date: Date | undefined): string => {
|
|
25
|
+
if (!date) return '';
|
|
26
|
+
const hours = date.getHours();
|
|
27
|
+
const minutes = date.getMinutes();
|
|
28
|
+
|
|
29
|
+
if (mode === '24h') {
|
|
30
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const period = hours >= 12 ? 'PM' : 'AM';
|
|
34
|
+
const displayHours = hours % 12 || 12;
|
|
35
|
+
return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Parse string to date (time only)
|
|
39
|
+
const parseTime = (str: string): Date | null => {
|
|
40
|
+
// Try 12h format: "1:30 PM" or "12:00 AM"
|
|
41
|
+
const match12h = str.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
|
|
42
|
+
if (match12h) {
|
|
43
|
+
let [, hours, minutes, period] = match12h;
|
|
44
|
+
let h = parseInt(hours);
|
|
45
|
+
const m = parseInt(minutes);
|
|
46
|
+
if (h < 1 || h > 12 || m > 59) return null;
|
|
47
|
+
if (period.toUpperCase() === 'PM' && h !== 12) h += 12;
|
|
48
|
+
if (period.toUpperCase() === 'AM' && h === 12) h = 0;
|
|
49
|
+
const date = new Date();
|
|
50
|
+
date.setHours(h, m, 0, 0);
|
|
51
|
+
return date;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Try 24h format: "13:30"
|
|
55
|
+
const match24h = str.match(/^(\d{1,2}):(\d{2})$/);
|
|
56
|
+
if (match24h) {
|
|
57
|
+
const [, hours, minutes] = match24h;
|
|
58
|
+
const h = parseInt(hours);
|
|
59
|
+
const m = parseInt(minutes);
|
|
60
|
+
if (h > 23 || m > 59) return null;
|
|
61
|
+
const date = new Date();
|
|
62
|
+
date.setHours(h, m, 0, 0);
|
|
63
|
+
return date;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Sync input value with prop value
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
setInputValue(formatTime(value ?? undefined));
|
|
72
|
+
}, [value, mode]);
|
|
73
|
+
|
|
74
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
75
|
+
const newValue = e.target.value;
|
|
76
|
+
setInputValue(newValue);
|
|
77
|
+
|
|
78
|
+
// Try to parse and update if valid
|
|
79
|
+
const parsed = parseTime(newValue);
|
|
80
|
+
if (parsed) {
|
|
81
|
+
onChange(parsed);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleInputBlur = () => {
|
|
86
|
+
const parsed = parseTime(inputValue);
|
|
87
|
+
if (!parsed && value) {
|
|
88
|
+
setInputValue(formatTime(value));
|
|
89
|
+
} else if (!parsed) {
|
|
90
|
+
setInputValue('');
|
|
91
|
+
onChange(null);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const handleTimeChange = (date: Date) => {
|
|
96
|
+
onChange(date);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<View style={style}>
|
|
101
|
+
{label && (
|
|
102
|
+
<Text typography="body2" weight="medium" style={{ marginBottom: 4 }}>
|
|
103
|
+
{label}
|
|
104
|
+
</Text>
|
|
105
|
+
)}
|
|
106
|
+
<div
|
|
107
|
+
ref={triggerRef}
|
|
108
|
+
style={{
|
|
109
|
+
display: 'flex',
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
border: `1px solid ${error ? '#ef4444' : '#d1d5db'}`,
|
|
112
|
+
borderRadius: 6,
|
|
113
|
+
backgroundColor: disabled ? '#f3f4f6' : 'white',
|
|
114
|
+
overflow: 'hidden',
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<input
|
|
118
|
+
type="text"
|
|
119
|
+
value={inputValue}
|
|
120
|
+
onChange={handleInputChange}
|
|
121
|
+
onBlur={handleInputBlur}
|
|
122
|
+
placeholder={placeholder}
|
|
123
|
+
disabled={disabled}
|
|
124
|
+
style={{
|
|
125
|
+
flex: 1,
|
|
126
|
+
padding: '8px 12px',
|
|
127
|
+
fontSize: 14,
|
|
128
|
+
border: 'none',
|
|
129
|
+
outline: 'none',
|
|
130
|
+
backgroundColor: 'transparent',
|
|
131
|
+
color: disabled ? '#9ca3af' : '#111827',
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
<Button
|
|
135
|
+
type="text"
|
|
136
|
+
size="sm"
|
|
137
|
+
onPress={() => !disabled && setOpen(!open)}
|
|
138
|
+
disabled={disabled}
|
|
139
|
+
style={{ marginRight: 4 }}
|
|
140
|
+
>
|
|
141
|
+
<Icon name="clock-outline" size={18} />
|
|
142
|
+
</Button>
|
|
143
|
+
</div>
|
|
144
|
+
{error && (
|
|
145
|
+
<Text typography="caption" style={{ marginTop: 4, color: '#ef4444' }}>
|
|
146
|
+
{error}
|
|
147
|
+
</Text>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
<PositionedPortal
|
|
151
|
+
open={open}
|
|
152
|
+
anchor={triggerRef}
|
|
153
|
+
placement="bottom-start"
|
|
154
|
+
offset={4}
|
|
155
|
+
onClickOutside={() => setOpen(false)}
|
|
156
|
+
onEscapeKey={() => setOpen(false)}
|
|
157
|
+
zIndex={9999}
|
|
158
|
+
>
|
|
159
|
+
<View style={datePickerStyles.popoverContent}>
|
|
160
|
+
<TimePicker
|
|
161
|
+
value={value ?? undefined}
|
|
162
|
+
onChange={handleTimeChange}
|
|
163
|
+
mode={mode}
|
|
164
|
+
minuteStep={minuteStep}
|
|
165
|
+
disabled={disabled}
|
|
166
|
+
/>
|
|
167
|
+
</View>
|
|
168
|
+
</PositionedPortal>
|
|
169
|
+
</View>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, Button, Icon } from '@idealyst/components';
|
|
3
|
+
import { datePickerStyles } from './styles';
|
|
4
|
+
import type { TimePickerProps } from './types';
|
|
5
|
+
|
|
6
|
+
export const TimePicker: React.FC<TimePickerProps> = ({
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
mode = '12h',
|
|
10
|
+
minuteStep = 1,
|
|
11
|
+
disabled = false,
|
|
12
|
+
style,
|
|
13
|
+
}) => {
|
|
14
|
+
datePickerStyles.useVariants({ disabled });
|
|
15
|
+
|
|
16
|
+
const currentDate = value || new Date();
|
|
17
|
+
const hours = currentDate.getHours();
|
|
18
|
+
const minutes = currentDate.getMinutes();
|
|
19
|
+
const is12Hour = mode === '12h';
|
|
20
|
+
const isPM = hours >= 12;
|
|
21
|
+
const displayHours = is12Hour ? (hours % 12 || 12) : hours;
|
|
22
|
+
|
|
23
|
+
const updateTime = (newHours: number, newMinutes: number) => {
|
|
24
|
+
const updated = new Date(currentDate);
|
|
25
|
+
updated.setHours(newHours, newMinutes, 0, 0);
|
|
26
|
+
onChange(updated);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const incrementHours = () => {
|
|
30
|
+
const newHours = (hours + 1) % 24;
|
|
31
|
+
updateTime(newHours, minutes);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const decrementHours = () => {
|
|
35
|
+
const newHours = (hours - 1 + 24) % 24;
|
|
36
|
+
updateTime(newHours, minutes);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const incrementMinutes = () => {
|
|
40
|
+
const newMinutes = (minutes + minuteStep) % 60;
|
|
41
|
+
const hourChange = minutes + minuteStep >= 60 ? 1 : 0;
|
|
42
|
+
updateTime((hours + hourChange) % 24, newMinutes);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const decrementMinutes = () => {
|
|
46
|
+
const newMinutes = (minutes - minuteStep + 60) % 60;
|
|
47
|
+
const hourChange = minutes - minuteStep < 0 ? -1 : 0;
|
|
48
|
+
updateTime((hours + hourChange + 24) % 24, newMinutes);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const togglePeriod = () => {
|
|
52
|
+
const newHours = isPM ? hours - 12 : hours + 12;
|
|
53
|
+
updateTime(newHours, minutes);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<View style={[datePickerStyles.timePicker, style]}>
|
|
58
|
+
<View style={datePickerStyles.timeColumns}>
|
|
59
|
+
{/* Hours column */}
|
|
60
|
+
<View style={datePickerStyles.timeColumn}>
|
|
61
|
+
<Button type="text" size="sm" onPress={incrementHours} disabled={disabled}>
|
|
62
|
+
<Icon name="chevron-up" size={20} />
|
|
63
|
+
</Button>
|
|
64
|
+
<Text typography="h3" weight="semibold">
|
|
65
|
+
{String(displayHours).padStart(2, '0')}
|
|
66
|
+
</Text>
|
|
67
|
+
<Button type="text" size="sm" onPress={decrementHours} disabled={disabled}>
|
|
68
|
+
<Icon name="chevron-down" size={20} />
|
|
69
|
+
</Button>
|
|
70
|
+
</View>
|
|
71
|
+
|
|
72
|
+
{/* Separator */}
|
|
73
|
+
<View style={datePickerStyles.timeSeparator}>
|
|
74
|
+
<Text typography="h3" weight="semibold">:</Text>
|
|
75
|
+
</View>
|
|
76
|
+
|
|
77
|
+
{/* Minutes column */}
|
|
78
|
+
<View style={datePickerStyles.timeColumn}>
|
|
79
|
+
<Button type="text" size="sm" onPress={incrementMinutes} disabled={disabled}>
|
|
80
|
+
<Icon name="chevron-up" size={20} />
|
|
81
|
+
</Button>
|
|
82
|
+
<Text typography="h3" weight="semibold">
|
|
83
|
+
{String(minutes).padStart(2, '0')}
|
|
84
|
+
</Text>
|
|
85
|
+
<Button type="text" size="sm" onPress={decrementMinutes} disabled={disabled}>
|
|
86
|
+
<Icon name="chevron-down" size={20} />
|
|
87
|
+
</Button>
|
|
88
|
+
</View>
|
|
89
|
+
|
|
90
|
+
{/* AM/PM toggle for 12-hour mode */}
|
|
91
|
+
{is12Hour && (
|
|
92
|
+
<View style={datePickerStyles.timeColumn}>
|
|
93
|
+
<Button
|
|
94
|
+
type="outlined"
|
|
95
|
+
size="sm"
|
|
96
|
+
onPress={togglePeriod}
|
|
97
|
+
disabled={disabled}
|
|
98
|
+
>
|
|
99
|
+
{isPM ? 'PM' : 'AM'}
|
|
100
|
+
</Button>
|
|
101
|
+
</View>
|
|
102
|
+
)}
|
|
103
|
+
</View>
|
|
104
|
+
</View>
|
|
105
|
+
);
|
|
106
|
+
};
|