@idealyst/datepicker 1.1.7 → 1.1.9
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 +19 -9
- package/src/DateInput.native.tsx +31 -27
- package/src/DateInput.web.tsx +28 -29
- package/src/DatePicker.native.tsx +124 -62
- package/src/DatePicker.styles.ts +218 -0
- package/src/DatePicker.tsx +2 -0
- package/src/DatePicker.web.tsx +133 -76
- package/src/DateTimePicker.native.tsx +12 -7
- package/src/DateTimePicker.styles.ts +43 -0
- package/src/DateTimePicker.web.tsx +13 -9
- package/src/IconSvg.web.tsx +34 -0
- package/src/InputStyles.ts +127 -0
- package/src/TimeInput.native.tsx +31 -27
- package/src/TimeInput.web.tsx +28 -29
- package/src/TimePicker.native.tsx +58 -30
- package/src/TimePicker.styles.ts +104 -0
- package/src/TimePicker.tsx +2 -0
- package/src/TimePicker.web.tsx +61 -32
- package/src/styles.ts +0 -254
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/datepicker",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "Cross-platform date and time picker components for React and React Native",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/datepicker#readme",
|
|
6
6
|
"readme": "README.md",
|
|
@@ -36,20 +36,25 @@
|
|
|
36
36
|
"publish:npm": "npm publish"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@idealyst/
|
|
40
|
-
"@
|
|
39
|
+
"@idealyst/theme": "^1.1.9",
|
|
40
|
+
"@mdi/js": ">=7.0.0",
|
|
41
|
+
"@mdi/react": ">=1.6.0",
|
|
41
42
|
"react": ">=16.8.0",
|
|
42
43
|
"react-native": ">=0.60.0",
|
|
43
44
|
"react-native-svg": ">=13.0.0",
|
|
44
|
-
"react-native-unistyles": "^3.0.4"
|
|
45
|
+
"react-native-unistyles": "^3.0.4",
|
|
46
|
+
"react-native-vector-icons": ">=10.0.0"
|
|
45
47
|
},
|
|
46
48
|
"peerDependenciesMeta": {
|
|
47
|
-
"@idealyst/components": {
|
|
48
|
-
"optional": false
|
|
49
|
-
},
|
|
50
49
|
"@idealyst/theme": {
|
|
51
50
|
"optional": false
|
|
52
51
|
},
|
|
52
|
+
"@mdi/js": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@mdi/react": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
53
58
|
"react-native": {
|
|
54
59
|
"optional": true
|
|
55
60
|
},
|
|
@@ -58,16 +63,21 @@
|
|
|
58
63
|
},
|
|
59
64
|
"react-native-unistyles": {
|
|
60
65
|
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"react-native-vector-icons": {
|
|
68
|
+
"optional": true
|
|
61
69
|
}
|
|
62
70
|
},
|
|
63
71
|
"devDependencies": {
|
|
64
|
-
"@idealyst/
|
|
65
|
-
"@
|
|
72
|
+
"@idealyst/theme": "^1.1.9",
|
|
73
|
+
"@mdi/js": "^7.4.47",
|
|
74
|
+
"@mdi/react": "^1.6.1",
|
|
66
75
|
"@types/react": "^19.1.0",
|
|
67
76
|
"react": "^19.1.0",
|
|
68
77
|
"react-native": "^0.80.1",
|
|
69
78
|
"react-native-svg": "^15.15.1",
|
|
70
79
|
"react-native-unistyles": "^3.0.10",
|
|
80
|
+
"react-native-vector-icons": "^10.2.0",
|
|
71
81
|
"typescript": "^5.0.0"
|
|
72
82
|
},
|
|
73
83
|
"files": [
|
package/src/DateInput.native.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { View, Text, TextInput, TouchableOpacity, Modal } from 'react-native';
|
|
3
|
+
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
4
4
|
import { DatePicker } from './DatePicker';
|
|
5
|
-
import {
|
|
5
|
+
import { dateTimeInputStyles } from './InputStyles';
|
|
6
6
|
import type { DateInputProps } from './types';
|
|
7
7
|
|
|
8
8
|
export const DateInput: React.FC<DateInputProps> = ({
|
|
@@ -19,6 +19,19 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
19
19
|
const [open, setOpen] = useState(false);
|
|
20
20
|
const [inputValue, setInputValue] = useState('');
|
|
21
21
|
|
|
22
|
+
// Get dynamic styles - call as functions for theme reactivity
|
|
23
|
+
const styles = dateTimeInputStyles;
|
|
24
|
+
const labelTextStyle = (styles.labelText as any)({});
|
|
25
|
+
const inputContainerStyle = (styles.inputContainer as any)({ disabled, error: !!error });
|
|
26
|
+
const textInputStyle = (styles.textInput as any)({ disabled });
|
|
27
|
+
const iconButtonStyle = (styles.iconButton as any)({ disabled });
|
|
28
|
+
const errorTextStyle = (styles.errorText as any)({});
|
|
29
|
+
const modalBackdropStyle = (styles.modalBackdrop as any)({});
|
|
30
|
+
const popoverContentStyle = (styles.popoverContent as any)({});
|
|
31
|
+
const closeButtonStyle = (styles.closeButton as any)({ disabled: false });
|
|
32
|
+
const closeButtonTextStyle = (styles.closeButtonText as any)({});
|
|
33
|
+
const iconStyle = (styles.iconColor as any)({ disabled });
|
|
34
|
+
|
|
22
35
|
// Format date to string
|
|
23
36
|
const formatDate = (date: Date | undefined): string => {
|
|
24
37
|
if (!date) return '';
|
|
@@ -69,40 +82,32 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
69
82
|
setOpen(false);
|
|
70
83
|
};
|
|
71
84
|
|
|
72
|
-
// Apply variants for input container
|
|
73
|
-
datePickerStyles.useVariants({
|
|
74
|
-
disabled,
|
|
75
|
-
error: !!error,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
85
|
return (
|
|
79
86
|
<View style={style}>
|
|
80
87
|
{label && (
|
|
81
|
-
<Text
|
|
88
|
+
<Text style={labelTextStyle}>
|
|
82
89
|
{label}
|
|
83
90
|
</Text>
|
|
84
91
|
)}
|
|
85
|
-
<View style={
|
|
86
|
-
<
|
|
92
|
+
<View style={inputContainerStyle}>
|
|
93
|
+
<TextInput
|
|
87
94
|
value={inputValue}
|
|
88
95
|
onChangeText={handleInputChange}
|
|
89
96
|
onBlur={handleInputBlur}
|
|
90
97
|
placeholder={placeholder}
|
|
91
98
|
editable={!disabled}
|
|
92
|
-
style={
|
|
99
|
+
style={textInputStyle}
|
|
93
100
|
/>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
size="sm"
|
|
101
|
+
<TouchableOpacity
|
|
102
|
+
style={iconButtonStyle}
|
|
97
103
|
onPress={() => !disabled && setOpen(true)}
|
|
98
104
|
disabled={disabled}
|
|
99
|
-
style={{ marginRight: 4 }}
|
|
100
105
|
>
|
|
101
|
-
<
|
|
102
|
-
</
|
|
106
|
+
<MaterialCommunityIcons name="calendar" size={18} style={iconStyle} />
|
|
107
|
+
</TouchableOpacity>
|
|
103
108
|
</View>
|
|
104
109
|
{error && (
|
|
105
|
-
<Text
|
|
110
|
+
<Text style={errorTextStyle}>
|
|
106
111
|
{error}
|
|
107
112
|
</Text>
|
|
108
113
|
)}
|
|
@@ -113,8 +118,8 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
113
118
|
animationType="fade"
|
|
114
119
|
onRequestClose={() => setOpen(false)}
|
|
115
120
|
>
|
|
116
|
-
<View style={
|
|
117
|
-
<View style={
|
|
121
|
+
<View style={modalBackdropStyle}>
|
|
122
|
+
<View style={popoverContentStyle}>
|
|
118
123
|
<DatePicker
|
|
119
124
|
value={value ?? undefined}
|
|
120
125
|
onChange={handleDateSelect}
|
|
@@ -122,13 +127,12 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
122
127
|
maxDate={maxDate}
|
|
123
128
|
disabled={disabled}
|
|
124
129
|
/>
|
|
125
|
-
<
|
|
126
|
-
|
|
130
|
+
<TouchableOpacity
|
|
131
|
+
style={closeButtonStyle}
|
|
127
132
|
onPress={() => setOpen(false)}
|
|
128
|
-
style={{ marginTop: 8 }}
|
|
129
133
|
>
|
|
130
|
-
Close
|
|
131
|
-
</
|
|
134
|
+
<Text style={closeButtonTextStyle}>Close</Text>
|
|
135
|
+
</TouchableOpacity>
|
|
132
136
|
</View>
|
|
133
137
|
</View>
|
|
134
138
|
</Modal>
|
package/src/DateInput.web.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
-
import {
|
|
3
|
+
import { mdiCalendar } from '@mdi/js';
|
|
4
4
|
import { PositionedPortal } from '@idealyst/components/internal';
|
|
5
|
+
import { IconSvg } from './IconSvg.web';
|
|
5
6
|
import { DatePicker } from './DatePicker';
|
|
6
|
-
import {
|
|
7
|
+
import { dateTimeInputStyles } from './InputStyles';
|
|
7
8
|
import type { DateInputProps } from './types';
|
|
8
9
|
|
|
9
10
|
export const DateInput: React.FC<DateInputProps> = ({
|
|
@@ -21,6 +22,17 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
21
22
|
const [inputValue, setInputValue] = useState('');
|
|
22
23
|
const triggerRef = useRef<HTMLDivElement>(null);
|
|
23
24
|
|
|
25
|
+
const styles = dateTimeInputStyles;
|
|
26
|
+
|
|
27
|
+
// Get dynamic styles
|
|
28
|
+
const labelTextStyle = (styles.labelText as any)({});
|
|
29
|
+
const inputContainerStyle = (styles.inputContainer as any)({ disabled, error: !!error });
|
|
30
|
+
const textInputStyle = (styles.textInput as any)({ disabled });
|
|
31
|
+
const iconButtonStyle = (styles.iconButton as any)({ disabled });
|
|
32
|
+
const errorTextStyle = (styles.errorText as any)({});
|
|
33
|
+
const popoverContentStyle = (styles.popoverContent as any)({});
|
|
34
|
+
const iconColorStyle = (styles.iconColor as any)({ disabled });
|
|
35
|
+
|
|
24
36
|
// Format date to string
|
|
25
37
|
const formatDate = (date: Date | undefined): string => {
|
|
26
38
|
if (!date) return '';
|
|
@@ -72,22 +84,13 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
72
84
|
setOpen(false);
|
|
73
85
|
};
|
|
74
86
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
disabled,
|
|
78
|
-
error: !!error,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Get web props for styled elements
|
|
82
|
-
const containerProps = getWebProps([datePickerStyles.inputContainer]);
|
|
83
|
-
const inputProps = getWebProps([datePickerStyles.textInput]);
|
|
87
|
+
// Get web props
|
|
88
|
+
const containerProps = getWebProps([inputContainerStyle]);
|
|
84
89
|
|
|
85
90
|
return (
|
|
86
|
-
<
|
|
91
|
+
<div style={style as React.CSSProperties}>
|
|
87
92
|
{label && (
|
|
88
|
-
<
|
|
89
|
-
{label}
|
|
90
|
-
</Text>
|
|
93
|
+
<span style={labelTextStyle}>{label}</span>
|
|
91
94
|
)}
|
|
92
95
|
<div ref={triggerRef} {...containerProps}>
|
|
93
96
|
<input
|
|
@@ -97,22 +100,18 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
97
100
|
onBlur={handleInputBlur}
|
|
98
101
|
placeholder={placeholder}
|
|
99
102
|
disabled={disabled}
|
|
100
|
-
{
|
|
103
|
+
style={textInputStyle}
|
|
101
104
|
/>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
onPress={() => !disabled && setOpen(!open)}
|
|
105
|
+
<button
|
|
106
|
+
style={iconButtonStyle}
|
|
107
|
+
onClick={() => !disabled && setOpen(!open)}
|
|
106
108
|
disabled={disabled}
|
|
107
|
-
style={{ marginRight: 4 }}
|
|
108
109
|
>
|
|
109
|
-
<
|
|
110
|
-
</
|
|
110
|
+
<IconSvg path={mdiCalendar} size={18} color={iconColorStyle.color} />
|
|
111
|
+
</button>
|
|
111
112
|
</div>
|
|
112
113
|
{error && (
|
|
113
|
-
<
|
|
114
|
-
{error}
|
|
115
|
-
</Text>
|
|
114
|
+
<span style={errorTextStyle}>{error}</span>
|
|
116
115
|
)}
|
|
117
116
|
|
|
118
117
|
<PositionedPortal
|
|
@@ -124,7 +123,7 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
124
123
|
onEscapeKey={() => setOpen(false)}
|
|
125
124
|
zIndex={9999}
|
|
126
125
|
>
|
|
127
|
-
<
|
|
126
|
+
<div style={popoverContentStyle}>
|
|
128
127
|
<DatePicker
|
|
129
128
|
value={value ?? undefined}
|
|
130
129
|
onChange={handleDateSelect}
|
|
@@ -132,8 +131,8 @@ export const DateInput: React.FC<DateInputProps> = ({
|
|
|
132
131
|
maxDate={maxDate}
|
|
133
132
|
disabled={disabled}
|
|
134
133
|
/>
|
|
135
|
-
</
|
|
134
|
+
</div>
|
|
136
135
|
</PositionedPortal>
|
|
137
|
-
</
|
|
136
|
+
</div>
|
|
138
137
|
);
|
|
139
138
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { View, Text, TouchableOpacity } from 'react-native';
|
|
3
|
+
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
4
|
+
import { datePickerCalendarStyles } from './DatePicker.styles';
|
|
5
5
|
import type { DatePickerProps } from './types';
|
|
6
6
|
|
|
7
7
|
const WEEKDAYS = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
|
|
@@ -20,7 +20,30 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
20
20
|
const [currentMonth, setCurrentMonth] = useState(() => value || new Date());
|
|
21
21
|
const [viewMode, setViewMode] = useState<ViewMode>('calendar');
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// Get dynamic styles - call as functions for theme reactivity
|
|
24
|
+
const styles = datePickerCalendarStyles;
|
|
25
|
+
const calendarStyle = (styles.calendar as any)({ disabled });
|
|
26
|
+
const calendarHeaderStyle = (styles.calendarHeader as any)({});
|
|
27
|
+
const weekdayRowStyle = (styles.weekdayRow as any)({});
|
|
28
|
+
const weekdayCellStyle = (styles.weekdayCell as any)({});
|
|
29
|
+
const calendarGridStyle = (styles.calendarGrid as any)({});
|
|
30
|
+
const monthGridStyle = (styles.monthGrid as any)({});
|
|
31
|
+
const yearGridStyle = (styles.yearGrid as any)({});
|
|
32
|
+
const dayCellStyle = (styles.dayCell as any)({});
|
|
33
|
+
const selectedDayStyle = (styles.selectedDay as any)({});
|
|
34
|
+
const selectedDayTextStyle = (styles.selectedDayText as any)({});
|
|
35
|
+
const todayDayStyle = (styles.todayDay as any)({});
|
|
36
|
+
const navButtonStyle = (styles.navButton as any)({ disabled });
|
|
37
|
+
const titleButtonStyle = (styles.titleButton as any)({ disabled });
|
|
38
|
+
const titleTextStyle = (styles.titleText as any)({});
|
|
39
|
+
const dayButtonStyle = (styles.dayButton as any)({ disabled: false });
|
|
40
|
+
const dayTextStyle = (styles.dayText as any)({});
|
|
41
|
+
const weekdayTextStyle = (styles.weekdayText as any)({});
|
|
42
|
+
const selectorItemStyle = (styles.selectorItem as any)({ disabled });
|
|
43
|
+
const selectorItemSelectedStyle = (styles.selectorItemSelected as any)({});
|
|
44
|
+
const selectorItemTextStyle = (styles.selectorItemText as any)({});
|
|
45
|
+
const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
|
|
46
|
+
const iconStyle = (styles.iconColor as any)({});
|
|
24
47
|
|
|
25
48
|
const { days, monthShort, year } = useMemo(() => {
|
|
26
49
|
const year = currentMonth.getFullYear();
|
|
@@ -124,32 +147,46 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
124
147
|
// Render month selector
|
|
125
148
|
if (viewMode === 'months') {
|
|
126
149
|
return (
|
|
127
|
-
<View style={[
|
|
128
|
-
<View style={
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
<View style={[calendarStyle, style]}>
|
|
151
|
+
<View style={calendarHeaderStyle}>
|
|
152
|
+
<TouchableOpacity
|
|
153
|
+
style={navButtonStyle}
|
|
154
|
+
onPress={() => setViewMode('calendar')}
|
|
155
|
+
disabled={disabled}
|
|
156
|
+
>
|
|
157
|
+
<MaterialCommunityIcons name="chevron-left" size={16} style={iconStyle} />
|
|
158
|
+
</TouchableOpacity>
|
|
159
|
+
<TouchableOpacity
|
|
160
|
+
style={titleButtonStyle}
|
|
161
|
+
onPress={() => setViewMode('years')}
|
|
162
|
+
disabled={disabled}
|
|
163
|
+
>
|
|
164
|
+
<Text style={titleTextStyle}>{year}</Text>
|
|
165
|
+
</TouchableOpacity>
|
|
135
166
|
<View style={{ width: 28 }} />
|
|
136
167
|
</View>
|
|
137
|
-
<View style={
|
|
168
|
+
<View style={monthGridStyle}>
|
|
138
169
|
{MONTHS.map((month, index) => {
|
|
139
170
|
const isCurrentMonth = index === currentMonth.getMonth();
|
|
140
171
|
return (
|
|
141
|
-
<
|
|
172
|
+
<TouchableOpacity
|
|
142
173
|
key={month}
|
|
143
|
-
|
|
144
|
-
|
|
174
|
+
style={[
|
|
175
|
+
selectorItemStyle,
|
|
176
|
+
isCurrentMonth && selectorItemSelectedStyle,
|
|
177
|
+
]}
|
|
145
178
|
onPress={() => handleMonthSelect(index)}
|
|
146
179
|
disabled={disabled}
|
|
147
|
-
style={{ minWidth: 48, margin: 2 }}
|
|
148
180
|
>
|
|
149
|
-
<Text
|
|
181
|
+
<Text
|
|
182
|
+
style={[
|
|
183
|
+
selectorItemTextStyle,
|
|
184
|
+
isCurrentMonth && selectorItemTextSelectedStyle,
|
|
185
|
+
]}
|
|
186
|
+
>
|
|
150
187
|
{month}
|
|
151
188
|
</Text>
|
|
152
|
-
</
|
|
189
|
+
</TouchableOpacity>
|
|
153
190
|
);
|
|
154
191
|
})}
|
|
155
192
|
</View>
|
|
@@ -160,34 +197,48 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
160
197
|
// Render year selector
|
|
161
198
|
if (viewMode === 'years') {
|
|
162
199
|
return (
|
|
163
|
-
<View style={[
|
|
164
|
-
<View style={
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
<View style={[calendarStyle, style]}>
|
|
201
|
+
<View style={calendarHeaderStyle}>
|
|
202
|
+
<TouchableOpacity
|
|
203
|
+
style={navButtonStyle}
|
|
204
|
+
onPress={goToPrevYearRange}
|
|
205
|
+
disabled={disabled}
|
|
206
|
+
>
|
|
207
|
+
<MaterialCommunityIcons name="chevron-left" size={16} style={iconStyle} />
|
|
208
|
+
</TouchableOpacity>
|
|
209
|
+
<Text style={titleTextStyle}>
|
|
169
210
|
{yearRange[0]} - {yearRange[yearRange.length - 1]}
|
|
170
211
|
</Text>
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
212
|
+
<TouchableOpacity
|
|
213
|
+
style={navButtonStyle}
|
|
214
|
+
onPress={goToNextYearRange}
|
|
215
|
+
disabled={disabled}
|
|
216
|
+
>
|
|
217
|
+
<MaterialCommunityIcons name="chevron-right" size={16} style={iconStyle} />
|
|
218
|
+
</TouchableOpacity>
|
|
174
219
|
</View>
|
|
175
|
-
<View style={
|
|
220
|
+
<View style={yearGridStyle}>
|
|
176
221
|
{yearRange.map((yr) => {
|
|
177
222
|
const isCurrentYear = yr === currentMonth.getFullYear();
|
|
178
223
|
return (
|
|
179
|
-
<
|
|
224
|
+
<TouchableOpacity
|
|
180
225
|
key={yr}
|
|
181
|
-
|
|
182
|
-
|
|
226
|
+
style={[
|
|
227
|
+
selectorItemStyle,
|
|
228
|
+
isCurrentYear && selectorItemSelectedStyle,
|
|
229
|
+
]}
|
|
183
230
|
onPress={() => handleYearSelect(yr)}
|
|
184
231
|
disabled={disabled}
|
|
185
|
-
style={{ minWidth: 48, margin: 2 }}
|
|
186
232
|
>
|
|
187
|
-
<Text
|
|
233
|
+
<Text
|
|
234
|
+
style={[
|
|
235
|
+
selectorItemTextStyle,
|
|
236
|
+
isCurrentYear && selectorItemTextSelectedStyle,
|
|
237
|
+
]}
|
|
238
|
+
>
|
|
188
239
|
{yr}
|
|
189
240
|
</Text>
|
|
190
|
-
</
|
|
241
|
+
</TouchableOpacity>
|
|
191
242
|
);
|
|
192
243
|
})}
|
|
193
244
|
</View>
|
|
@@ -197,65 +248,76 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
197
248
|
|
|
198
249
|
// Render calendar (default)
|
|
199
250
|
return (
|
|
200
|
-
<View style={[
|
|
251
|
+
<View style={[calendarStyle, style]}>
|
|
201
252
|
{/* Header */}
|
|
202
|
-
<View style={
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
253
|
+
<View style={calendarHeaderStyle}>
|
|
254
|
+
<TouchableOpacity
|
|
255
|
+
style={navButtonStyle}
|
|
256
|
+
onPress={goToPrevMonth}
|
|
257
|
+
disabled={disabled}
|
|
258
|
+
>
|
|
259
|
+
<MaterialCommunityIcons name="chevron-left" size={16} style={iconStyle} />
|
|
260
|
+
</TouchableOpacity>
|
|
261
|
+
<TouchableOpacity
|
|
262
|
+
style={titleButtonStyle}
|
|
263
|
+
onPress={() => setViewMode('months')}
|
|
264
|
+
disabled={disabled}
|
|
265
|
+
>
|
|
266
|
+
<Text style={titleTextStyle}>
|
|
208
267
|
{monthShort} {year}
|
|
209
268
|
</Text>
|
|
210
|
-
</
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
269
|
+
</TouchableOpacity>
|
|
270
|
+
<TouchableOpacity
|
|
271
|
+
style={navButtonStyle}
|
|
272
|
+
onPress={goToNextMonth}
|
|
273
|
+
disabled={disabled}
|
|
274
|
+
>
|
|
275
|
+
<MaterialCommunityIcons name="chevron-right" size={16} style={iconStyle} />
|
|
276
|
+
</TouchableOpacity>
|
|
214
277
|
</View>
|
|
215
278
|
|
|
216
279
|
{/* Weekday headers */}
|
|
217
|
-
<View style={
|
|
280
|
+
<View style={weekdayRowStyle}>
|
|
218
281
|
{WEEKDAYS.map((day, i) => (
|
|
219
|
-
<View key={i} style={
|
|
220
|
-
<Text
|
|
221
|
-
{day}
|
|
222
|
-
</Text>
|
|
282
|
+
<View key={i} style={weekdayCellStyle}>
|
|
283
|
+
<Text style={weekdayTextStyle}>{day}</Text>
|
|
223
284
|
</View>
|
|
224
285
|
))}
|
|
225
286
|
</View>
|
|
226
287
|
|
|
227
288
|
{/* Calendar grid */}
|
|
228
|
-
<View style={
|
|
289
|
+
<View style={calendarGridStyle}>
|
|
229
290
|
{days.map(({ date, isCurrentMonth }, index) => {
|
|
230
291
|
const selected = isSelected(date);
|
|
231
292
|
const today = isToday(date);
|
|
232
293
|
const dayDisabled = isDisabled(date);
|
|
294
|
+
const disabledDayButtonStyle = (styles.dayButton as any)({ disabled: dayDisabled });
|
|
233
295
|
|
|
234
296
|
return (
|
|
235
297
|
<View
|
|
236
298
|
key={index}
|
|
237
299
|
style={[
|
|
238
|
-
|
|
239
|
-
selected &&
|
|
300
|
+
dayCellStyle,
|
|
301
|
+
selected && selectedDayStyle,
|
|
240
302
|
!isCurrentMonth && { opacity: 0.3 },
|
|
241
|
-
today && !selected &&
|
|
303
|
+
today && !selected && todayDayStyle,
|
|
242
304
|
dayDisabled && { opacity: 0.3 },
|
|
243
305
|
]}
|
|
244
306
|
>
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
size="sm"
|
|
307
|
+
<TouchableOpacity
|
|
308
|
+
style={[dayButtonStyle, dayDisabled && disabledDayButtonStyle]}
|
|
248
309
|
onPress={() => handleDayPress(date)}
|
|
249
310
|
disabled={dayDisabled}
|
|
250
|
-
style={{ minWidth: 24, minHeight: 24, padding: 0 }}
|
|
251
311
|
>
|
|
252
312
|
<Text
|
|
253
|
-
|
|
254
|
-
|
|
313
|
+
style={[
|
|
314
|
+
dayTextStyle,
|
|
315
|
+
selected && selectedDayTextStyle,
|
|
316
|
+
]}
|
|
255
317
|
>
|
|
256
318
|
{date.getDate()}
|
|
257
319
|
</Text>
|
|
258
|
-
</
|
|
320
|
+
</TouchableOpacity>
|
|
259
321
|
</View>
|
|
260
322
|
);
|
|
261
323
|
})}
|