@idealyst/datepicker 1.1.8 → 1.2.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.
- 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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatePicker (calendar) styles using defineStyle with dynamic props.
|
|
3
|
+
*/
|
|
4
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
5
|
+
import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
|
|
6
|
+
import type { Theme as BaseTheme } from '@idealyst/theme';
|
|
7
|
+
|
|
8
|
+
// Required: Unistyles must see StyleSheet usage in original source to process this file
|
|
9
|
+
void StyleSheet;
|
|
10
|
+
|
|
11
|
+
// Wrap theme for $iterator support
|
|
12
|
+
type Theme = ThemeStyleWrapper<BaseTheme>;
|
|
13
|
+
|
|
14
|
+
export type DatePickerDynamicProps = {
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* DatePicker calendar styles with theme reactivity.
|
|
20
|
+
*/
|
|
21
|
+
export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme: Theme) => ({
|
|
22
|
+
// Calendar container - compact
|
|
23
|
+
calendar: ({ disabled = false }: DatePickerDynamicProps) => ({
|
|
24
|
+
padding: 8,
|
|
25
|
+
backgroundColor: theme.colors.surface.primary,
|
|
26
|
+
borderRadius: 6,
|
|
27
|
+
width: 220,
|
|
28
|
+
opacity: disabled ? 0.6 : 1,
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
// Calendar header with month/year and navigation
|
|
32
|
+
calendarHeader: (_props: DatePickerDynamicProps) => ({
|
|
33
|
+
flexDirection: 'row' as const,
|
|
34
|
+
alignItems: 'center' as const,
|
|
35
|
+
justifyContent: 'space-between' as const,
|
|
36
|
+
marginBottom: 4,
|
|
37
|
+
paddingHorizontal: 2,
|
|
38
|
+
_web: {
|
|
39
|
+
display: 'flex',
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
|
|
43
|
+
calendarTitle: (_props: DatePickerDynamicProps) => ({
|
|
44
|
+
flexDirection: 'row' as const,
|
|
45
|
+
alignItems: 'center' as const,
|
|
46
|
+
gap: 2,
|
|
47
|
+
_web: {
|
|
48
|
+
display: 'flex',
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
|
|
52
|
+
// Weekday header row
|
|
53
|
+
weekdayRow: (_props: DatePickerDynamicProps) => ({
|
|
54
|
+
flexDirection: 'row' as const,
|
|
55
|
+
marginBottom: 2,
|
|
56
|
+
_web: {
|
|
57
|
+
display: 'flex',
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
weekdayCell: (_props: DatePickerDynamicProps) => ({
|
|
62
|
+
width: 28,
|
|
63
|
+
height: 20,
|
|
64
|
+
alignItems: 'center' as const,
|
|
65
|
+
justifyContent: 'center' as const,
|
|
66
|
+
_web: {
|
|
67
|
+
display: 'flex',
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
|
|
71
|
+
// Calendar grid
|
|
72
|
+
calendarGrid: (_props: DatePickerDynamicProps) => ({
|
|
73
|
+
flexDirection: 'row' as const,
|
|
74
|
+
flexWrap: 'wrap' as const,
|
|
75
|
+
_web: {
|
|
76
|
+
display: 'flex',
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
|
|
80
|
+
// Month selector grid (3x4)
|
|
81
|
+
monthGrid: (_props: DatePickerDynamicProps) => ({
|
|
82
|
+
flexDirection: 'row' as const,
|
|
83
|
+
flexWrap: 'wrap' as const,
|
|
84
|
+
justifyContent: 'center' as const,
|
|
85
|
+
paddingVertical: 8,
|
|
86
|
+
_web: {
|
|
87
|
+
display: 'flex',
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
|
|
91
|
+
// Year selector grid
|
|
92
|
+
yearGrid: (_props: DatePickerDynamicProps) => ({
|
|
93
|
+
flexDirection: 'row' as const,
|
|
94
|
+
flexWrap: 'wrap' as const,
|
|
95
|
+
justifyContent: 'center' as const,
|
|
96
|
+
paddingVertical: 8,
|
|
97
|
+
_web: {
|
|
98
|
+
display: 'flex',
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
|
|
102
|
+
// Individual day cell - compact
|
|
103
|
+
dayCell: (_props: DatePickerDynamicProps) => ({
|
|
104
|
+
width: 28,
|
|
105
|
+
height: 28,
|
|
106
|
+
alignItems: 'center' as const,
|
|
107
|
+
justifyContent: 'center' as const,
|
|
108
|
+
_web: {
|
|
109
|
+
display: 'flex',
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
|
|
113
|
+
// Navigation button
|
|
114
|
+
navButton: ({ disabled = false }: DatePickerDynamicProps) => ({
|
|
115
|
+
width: 28,
|
|
116
|
+
height: 28,
|
|
117
|
+
alignItems: 'center' as const,
|
|
118
|
+
justifyContent: 'center' as const,
|
|
119
|
+
borderRadius: 4,
|
|
120
|
+
opacity: disabled ? 0.4 : 1,
|
|
121
|
+
_web: {
|
|
122
|
+
display: 'flex',
|
|
123
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
|
|
127
|
+
// Title button (month/year selector)
|
|
128
|
+
titleButton: ({ disabled = false }: DatePickerDynamicProps) => ({
|
|
129
|
+
paddingHorizontal: 8,
|
|
130
|
+
paddingVertical: 4,
|
|
131
|
+
borderRadius: 4,
|
|
132
|
+
opacity: disabled ? 0.6 : 1,
|
|
133
|
+
_web: {
|
|
134
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
|
|
138
|
+
titleText: (_props: DatePickerDynamicProps) => ({
|
|
139
|
+
fontSize: 14,
|
|
140
|
+
fontWeight: '600' as const,
|
|
141
|
+
color: theme.colors.text.primary,
|
|
142
|
+
}),
|
|
143
|
+
|
|
144
|
+
// Day button
|
|
145
|
+
dayButton: ({ disabled = false }: DatePickerDynamicProps) => ({
|
|
146
|
+
width: 24,
|
|
147
|
+
height: 24,
|
|
148
|
+
alignItems: 'center' as const,
|
|
149
|
+
justifyContent: 'center' as const,
|
|
150
|
+
borderRadius: 12,
|
|
151
|
+
opacity: disabled ? 0.4 : 1,
|
|
152
|
+
_web: {
|
|
153
|
+
display: 'flex',
|
|
154
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
|
|
158
|
+
dayText: (_props: DatePickerDynamicProps) => ({
|
|
159
|
+
fontSize: 12,
|
|
160
|
+
color: theme.colors.text.primary,
|
|
161
|
+
}),
|
|
162
|
+
|
|
163
|
+
weekdayText: (_props: DatePickerDynamicProps) => ({
|
|
164
|
+
fontSize: 11,
|
|
165
|
+
color: theme.colors.text.secondary,
|
|
166
|
+
}),
|
|
167
|
+
|
|
168
|
+
// Month/Year selector item
|
|
169
|
+
selectorItem: ({ disabled = false }: DatePickerDynamicProps) => ({
|
|
170
|
+
minWidth: 48,
|
|
171
|
+
paddingVertical: 6,
|
|
172
|
+
paddingHorizontal: 8,
|
|
173
|
+
margin: 2,
|
|
174
|
+
alignItems: 'center' as const,
|
|
175
|
+
justifyContent: 'center' as const,
|
|
176
|
+
borderRadius: 4,
|
|
177
|
+
opacity: disabled ? 0.4 : 1,
|
|
178
|
+
_web: {
|
|
179
|
+
display: 'flex',
|
|
180
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
181
|
+
},
|
|
182
|
+
}),
|
|
183
|
+
|
|
184
|
+
selectorItemSelected: (_props: DatePickerDynamicProps) => ({
|
|
185
|
+
backgroundColor: theme.intents.primary.primary,
|
|
186
|
+
}),
|
|
187
|
+
|
|
188
|
+
selectorItemText: (_props: DatePickerDynamicProps) => ({
|
|
189
|
+
fontSize: 12,
|
|
190
|
+
color: theme.colors.text.primary,
|
|
191
|
+
}),
|
|
192
|
+
|
|
193
|
+
selectorItemTextSelected: (_props: DatePickerDynamicProps) => ({
|
|
194
|
+
color: theme.intents.primary.contrast,
|
|
195
|
+
}),
|
|
196
|
+
|
|
197
|
+
// Selected day styling
|
|
198
|
+
selectedDay: (_props: DatePickerDynamicProps) => ({
|
|
199
|
+
backgroundColor: theme.intents.primary.primary,
|
|
200
|
+
borderRadius: 14,
|
|
201
|
+
}),
|
|
202
|
+
|
|
203
|
+
selectedDayText: (_props: DatePickerDynamicProps) => ({
|
|
204
|
+
color: theme.intents.primary.contrast,
|
|
205
|
+
}),
|
|
206
|
+
|
|
207
|
+
// Today styling
|
|
208
|
+
todayDay: (_props: DatePickerDynamicProps) => ({
|
|
209
|
+
borderWidth: 1,
|
|
210
|
+
borderColor: theme.intents.primary.primary,
|
|
211
|
+
borderRadius: 14,
|
|
212
|
+
}),
|
|
213
|
+
|
|
214
|
+
// Icon color helper
|
|
215
|
+
iconColor: (_props: DatePickerDynamicProps) => ({
|
|
216
|
+
color: theme.colors.text.primary,
|
|
217
|
+
}),
|
|
218
|
+
}));
|
package/src/DatePicker.web.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
|
4
|
+
import { IconSvg } from './IconSvg.web';
|
|
5
|
+
import { datePickerCalendarStyles } from './DatePicker.styles';
|
|
5
6
|
import type { DatePickerProps } from './types';
|
|
6
7
|
|
|
7
8
|
const WEEKDAYS = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
|
|
@@ -20,7 +21,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
20
21
|
const [currentMonth, setCurrentMonth] = useState(() => value || new Date());
|
|
21
22
|
const [viewMode, setViewMode] = useState<ViewMode>('calendar');
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
const styles = datePickerCalendarStyles;
|
|
24
25
|
|
|
25
26
|
const { days, monthShort, year } = useMemo(() => {
|
|
26
27
|
const year = currentMonth.getFullYear();
|
|
@@ -121,44 +122,75 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
121
122
|
setCurrentMonth(new Date(currentMonth.getFullYear() + 10, currentMonth.getMonth(), 1));
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
// Get
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
const
|
|
125
|
+
// Get dynamic styles
|
|
126
|
+
const calendarStyle = (styles.calendar as any)({ disabled });
|
|
127
|
+
const headerStyle = (styles.calendarHeader as any)({});
|
|
128
|
+
const weekdayRowStyle = (styles.weekdayRow as any)({});
|
|
129
|
+
const weekdayCellStyle = (styles.weekdayCell as any)({});
|
|
130
|
+
const gridStyle = (styles.calendarGrid as any)({});
|
|
131
|
+
const monthGridStyle = (styles.monthGrid as any)({});
|
|
132
|
+
const yearGridStyle = (styles.yearGrid as any)({});
|
|
133
|
+
const navButtonStyle = (styles.navButton as any)({ disabled });
|
|
134
|
+
const titleButtonStyle = (styles.titleButton as any)({ disabled });
|
|
135
|
+
const titleTextStyle = (styles.titleText as any)({});
|
|
136
|
+
const weekdayTextStyle = (styles.weekdayText as any)({});
|
|
137
|
+
const selectorItemStyle = (styles.selectorItem as any)({ disabled });
|
|
138
|
+
const selectorItemSelectedStyle = (styles.selectorItemSelected as any)({});
|
|
139
|
+
const selectorItemTextStyle = (styles.selectorItemText as any)({});
|
|
140
|
+
const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
|
|
141
|
+
const iconColorStyle = (styles.iconColor as any)({});
|
|
142
|
+
|
|
143
|
+
// Get web props
|
|
144
|
+
const calendarProps = getWebProps([calendarStyle, style as any]);
|
|
145
|
+
const headerProps = getWebProps([headerStyle]);
|
|
146
|
+
const weekdayRowProps = getWebProps([weekdayRowStyle]);
|
|
147
|
+
const gridProps = getWebProps([gridStyle]);
|
|
148
|
+
const monthGridProps = getWebProps([monthGridStyle]);
|
|
149
|
+
const yearGridProps = getWebProps([yearGridStyle]);
|
|
132
150
|
|
|
133
151
|
// Render month selector
|
|
134
152
|
if (viewMode === 'months') {
|
|
135
153
|
return (
|
|
136
154
|
<div {...calendarProps}>
|
|
137
155
|
<div {...headerProps}>
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
<button
|
|
157
|
+
style={navButtonStyle}
|
|
158
|
+
onClick={() => setViewMode('calendar')}
|
|
159
|
+
disabled={disabled}
|
|
160
|
+
>
|
|
161
|
+
<IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
|
|
162
|
+
</button>
|
|
163
|
+
<button
|
|
164
|
+
style={titleButtonStyle}
|
|
165
|
+
onClick={() => setViewMode('years')}
|
|
166
|
+
disabled={disabled}
|
|
167
|
+
>
|
|
168
|
+
<span style={titleTextStyle}>{year}</span>
|
|
169
|
+
</button>
|
|
144
170
|
<div style={{ width: 28 }} />
|
|
145
171
|
</div>
|
|
146
172
|
<div {...monthGridProps}>
|
|
147
173
|
{MONTHS.map((month, index) => {
|
|
148
174
|
const isCurrentMonth = index === currentMonth.getMonth();
|
|
149
175
|
return (
|
|
150
|
-
<
|
|
176
|
+
<button
|
|
151
177
|
key={month}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
style={{
|
|
179
|
+
...selectorItemStyle,
|
|
180
|
+
...(isCurrentMonth ? selectorItemSelectedStyle : {}),
|
|
181
|
+
}}
|
|
182
|
+
onClick={() => handleMonthSelect(index)}
|
|
155
183
|
disabled={disabled}
|
|
156
|
-
style={{ minWidth: 48, margin: 2 }}
|
|
157
184
|
>
|
|
158
|
-
<
|
|
185
|
+
<span
|
|
186
|
+
style={{
|
|
187
|
+
...selectorItemTextStyle,
|
|
188
|
+
...(isCurrentMonth ? selectorItemTextSelectedStyle : {}),
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
159
191
|
{month}
|
|
160
|
-
</
|
|
161
|
-
</
|
|
192
|
+
</span>
|
|
193
|
+
</button>
|
|
162
194
|
);
|
|
163
195
|
})}
|
|
164
196
|
</div>
|
|
@@ -171,32 +203,46 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
171
203
|
return (
|
|
172
204
|
<div {...calendarProps}>
|
|
173
205
|
<div {...headerProps}>
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
<button
|
|
207
|
+
style={navButtonStyle}
|
|
208
|
+
onClick={goToPrevYearRange}
|
|
209
|
+
disabled={disabled}
|
|
210
|
+
>
|
|
211
|
+
<IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
|
|
212
|
+
</button>
|
|
213
|
+
<span style={titleTextStyle}>
|
|
178
214
|
{yearRange[0]} - {yearRange[yearRange.length - 1]}
|
|
179
|
-
</
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
215
|
+
</span>
|
|
216
|
+
<button
|
|
217
|
+
style={navButtonStyle}
|
|
218
|
+
onClick={goToNextYearRange}
|
|
219
|
+
disabled={disabled}
|
|
220
|
+
>
|
|
221
|
+
<IconSvg path={mdiChevronRight} size={16} color={iconColorStyle.color} />
|
|
222
|
+
</button>
|
|
183
223
|
</div>
|
|
184
224
|
<div {...yearGridProps}>
|
|
185
225
|
{yearRange.map((yr) => {
|
|
186
226
|
const isCurrentYear = yr === currentMonth.getFullYear();
|
|
187
227
|
return (
|
|
188
|
-
<
|
|
228
|
+
<button
|
|
189
229
|
key={yr}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
style={{
|
|
231
|
+
...selectorItemStyle,
|
|
232
|
+
...(isCurrentYear ? selectorItemSelectedStyle : {}),
|
|
233
|
+
}}
|
|
234
|
+
onClick={() => handleYearSelect(yr)}
|
|
193
235
|
disabled={disabled}
|
|
194
|
-
style={{ minWidth: 48, margin: 2 }}
|
|
195
236
|
>
|
|
196
|
-
<
|
|
237
|
+
<span
|
|
238
|
+
style={{
|
|
239
|
+
...selectorItemTextStyle,
|
|
240
|
+
...(isCurrentYear ? selectorItemTextSelectedStyle : {}),
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
197
243
|
{yr}
|
|
198
|
-
</
|
|
199
|
-
</
|
|
244
|
+
</span>
|
|
245
|
+
</button>
|
|
200
246
|
);
|
|
201
247
|
})}
|
|
202
248
|
</div>
|
|
@@ -209,26 +255,36 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
209
255
|
<div {...calendarProps}>
|
|
210
256
|
{/* Header */}
|
|
211
257
|
<div {...headerProps}>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
258
|
+
<button
|
|
259
|
+
style={navButtonStyle}
|
|
260
|
+
onClick={goToPrevMonth}
|
|
261
|
+
disabled={disabled}
|
|
262
|
+
>
|
|
263
|
+
<IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
|
|
264
|
+
</button>
|
|
265
|
+
<button
|
|
266
|
+
style={titleButtonStyle}
|
|
267
|
+
onClick={() => setViewMode('months')}
|
|
268
|
+
disabled={disabled}
|
|
269
|
+
>
|
|
270
|
+
<span style={titleTextStyle}>
|
|
217
271
|
{monthShort} {year}
|
|
218
|
-
</
|
|
219
|
-
</
|
|
220
|
-
<
|
|
221
|
-
|
|
222
|
-
|
|
272
|
+
</span>
|
|
273
|
+
</button>
|
|
274
|
+
<button
|
|
275
|
+
style={navButtonStyle}
|
|
276
|
+
onClick={goToNextMonth}
|
|
277
|
+
disabled={disabled}
|
|
278
|
+
>
|
|
279
|
+
<IconSvg path={mdiChevronRight} size={16} color={iconColorStyle.color} />
|
|
280
|
+
</button>
|
|
223
281
|
</div>
|
|
224
282
|
|
|
225
283
|
{/* Weekday headers */}
|
|
226
284
|
<div {...weekdayRowProps}>
|
|
227
285
|
{WEEKDAYS.map((day, i) => (
|
|
228
|
-
<div key={i} {
|
|
229
|
-
<
|
|
230
|
-
{day}
|
|
231
|
-
</Text>
|
|
286
|
+
<div key={i} style={weekdayCellStyle}>
|
|
287
|
+
<span style={weekdayTextStyle}>{day}</span>
|
|
232
288
|
</div>
|
|
233
289
|
))}
|
|
234
290
|
</div>
|
|
@@ -240,36 +296,37 @@ export const DatePicker: React.FC<DatePickerProps> = ({
|
|
|
240
296
|
const today = isToday(date);
|
|
241
297
|
const dayDisabled = isDisabled(date);
|
|
242
298
|
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const cellStyle: React.CSSProperties = {
|
|
250
|
-
opacity: (!isCurrentMonth || dayDisabled) ? 0.3 : 1,
|
|
251
|
-
};
|
|
299
|
+
const dayCellStyle = (styles.dayCell as any)({});
|
|
300
|
+
const selectedDayStyle = (styles.selectedDay as any)({});
|
|
301
|
+
const todayDayStyle = (styles.todayDay as any)({});
|
|
302
|
+
const dayButtonStyle = (styles.dayButton as any)({ disabled: dayDisabled });
|
|
303
|
+
const dayTextStyle = (styles.dayText as any)({});
|
|
304
|
+
const selectedDayTextStyle = (styles.selectedDayText as any)({});
|
|
252
305
|
|
|
253
306
|
return (
|
|
254
307
|
<div
|
|
255
308
|
key={index}
|
|
256
|
-
{
|
|
257
|
-
|
|
309
|
+
style={{
|
|
310
|
+
...dayCellStyle,
|
|
311
|
+
...(selected ? selectedDayStyle : {}),
|
|
312
|
+
...(today && !selected ? todayDayStyle : {}),
|
|
313
|
+
opacity: (!isCurrentMonth || dayDisabled) ? 0.3 : 1,
|
|
314
|
+
}}
|
|
258
315
|
>
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
onPress={() => handleDayPress(date)}
|
|
316
|
+
<button
|
|
317
|
+
style={dayButtonStyle}
|
|
318
|
+
onClick={() => handleDayPress(date)}
|
|
263
319
|
disabled={dayDisabled}
|
|
264
|
-
style={{ minWidth: 24, minHeight: 24, padding: 0 }}
|
|
265
320
|
>
|
|
266
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
321
|
+
<span
|
|
322
|
+
style={{
|
|
323
|
+
...dayTextStyle,
|
|
324
|
+
...(selected ? selectedDayTextStyle : {}),
|
|
325
|
+
}}
|
|
269
326
|
>
|
|
270
327
|
{date.getDate()}
|
|
271
|
-
</
|
|
272
|
-
</
|
|
328
|
+
</span>
|
|
329
|
+
</button>
|
|
273
330
|
</div>
|
|
274
331
|
);
|
|
275
332
|
})}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
3
|
-
import { Text } from '@idealyst/components';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
4
3
|
import { DateInput } from './DateInput';
|
|
5
4
|
import { TimeInput } from './TimeInput';
|
|
6
|
-
import {
|
|
5
|
+
import { dateTimePickerStyles } from './DateTimePicker.styles';
|
|
7
6
|
import type { DateTimePickerProps } from './types';
|
|
8
7
|
|
|
9
8
|
export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
@@ -18,6 +17,12 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
18
17
|
error,
|
|
19
18
|
style,
|
|
20
19
|
}) => {
|
|
20
|
+
// Get dynamic styles - call as functions for theme reactivity
|
|
21
|
+
const styles = dateTimePickerStyles;
|
|
22
|
+
const inputRowStyle = (styles.inputRow as any)({});
|
|
23
|
+
const labelTextStyle = (styles.labelText as any)({});
|
|
24
|
+
const inputColumnStyle = (styles.inputColumn as any)({});
|
|
25
|
+
|
|
21
26
|
const handleDateChange = (date: Date | null) => {
|
|
22
27
|
if (!date) {
|
|
23
28
|
onChange(null);
|
|
@@ -58,12 +63,12 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
58
63
|
return (
|
|
59
64
|
<View style={style}>
|
|
60
65
|
{label && (
|
|
61
|
-
<Text
|
|
66
|
+
<Text style={labelTextStyle}>
|
|
62
67
|
{label}
|
|
63
68
|
</Text>
|
|
64
69
|
)}
|
|
65
|
-
<View style={
|
|
66
|
-
<View style={
|
|
70
|
+
<View style={inputRowStyle}>
|
|
71
|
+
<View style={inputColumnStyle}>
|
|
67
72
|
<DateInput
|
|
68
73
|
value={value ?? undefined}
|
|
69
74
|
onChange={handleDateChange}
|
|
@@ -74,7 +79,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
74
79
|
error={error}
|
|
75
80
|
/>
|
|
76
81
|
</View>
|
|
77
|
-
<View style={
|
|
82
|
+
<View style={inputColumnStyle}>
|
|
78
83
|
<TimeInput
|
|
79
84
|
value={value ?? undefined}
|
|
80
85
|
onChange={handleTimeChange}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DateTimePicker styles using defineStyle with dynamic props.
|
|
3
|
+
*/
|
|
4
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
5
|
+
import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
|
|
6
|
+
import type { Theme as BaseTheme } from '@idealyst/theme';
|
|
7
|
+
|
|
8
|
+
// Required: Unistyles must see StyleSheet usage in original source to process this file
|
|
9
|
+
void StyleSheet;
|
|
10
|
+
|
|
11
|
+
// Wrap theme for $iterator support
|
|
12
|
+
type Theme = ThemeStyleWrapper<BaseTheme>;
|
|
13
|
+
|
|
14
|
+
export type DateTimePickerDynamicProps = {
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* DateTimePicker styles with theme reactivity.
|
|
20
|
+
*/
|
|
21
|
+
export const dateTimePickerStyles = defineStyle('DateTimePicker', (theme: Theme) => ({
|
|
22
|
+
// Input row for datetime picker (contains date and time inputs side by side)
|
|
23
|
+
inputRow: (_props: DateTimePickerDynamicProps) => ({
|
|
24
|
+
flexDirection: 'row' as const,
|
|
25
|
+
gap: 8,
|
|
26
|
+
_web: {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
// Label text
|
|
32
|
+
labelText: (_props: DateTimePickerDynamicProps) => ({
|
|
33
|
+
marginBottom: 4,
|
|
34
|
+
fontSize: 14,
|
|
35
|
+
fontWeight: '500' as const,
|
|
36
|
+
color: theme.colors.text.primary,
|
|
37
|
+
}),
|
|
38
|
+
|
|
39
|
+
// Input column wrapper
|
|
40
|
+
inputColumn: (_props: DateTimePickerDynamicProps) => ({
|
|
41
|
+
flex: 1,
|
|
42
|
+
}),
|
|
43
|
+
}));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
-
import { Text } from '@idealyst/components';
|
|
4
3
|
import { DateInput } from './DateInput';
|
|
5
4
|
import { TimeInput } from './TimeInput';
|
|
6
|
-
import {
|
|
5
|
+
import { dateTimePickerStyles } from './DateTimePicker.styles';
|
|
7
6
|
import type { DateTimePickerProps } from './types';
|
|
8
7
|
|
|
9
8
|
export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
@@ -18,6 +17,13 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
18
17
|
error,
|
|
19
18
|
style,
|
|
20
19
|
}) => {
|
|
20
|
+
const styles = dateTimePickerStyles;
|
|
21
|
+
|
|
22
|
+
// Get dynamic styles
|
|
23
|
+
const inputRowStyle = (styles.inputRow as any)({});
|
|
24
|
+
const labelTextStyle = (styles.labelText as any)({});
|
|
25
|
+
const inputColumnStyle = (styles.inputColumn as any)({});
|
|
26
|
+
|
|
21
27
|
const handleDateChange = (date: Date | null) => {
|
|
22
28
|
if (!date) {
|
|
23
29
|
onChange(null);
|
|
@@ -55,18 +61,16 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
55
61
|
onChange(updated);
|
|
56
62
|
};
|
|
57
63
|
|
|
58
|
-
// Get web props
|
|
59
|
-
const inputRowProps = getWebProps([
|
|
64
|
+
// Get web props
|
|
65
|
+
const inputRowProps = getWebProps([inputRowStyle]);
|
|
60
66
|
|
|
61
67
|
return (
|
|
62
68
|
<div style={style as React.CSSProperties}>
|
|
63
69
|
{label && (
|
|
64
|
-
<
|
|
65
|
-
{label}
|
|
66
|
-
</Text>
|
|
70
|
+
<span style={labelTextStyle}>{label}</span>
|
|
67
71
|
)}
|
|
68
72
|
<div {...inputRowProps}>
|
|
69
|
-
<div style={
|
|
73
|
+
<div style={inputColumnStyle}>
|
|
70
74
|
<DateInput
|
|
71
75
|
value={value ?? undefined}
|
|
72
76
|
onChange={handleDateChange}
|
|
@@ -77,7 +81,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
77
81
|
error={error}
|
|
78
82
|
/>
|
|
79
83
|
</div>
|
|
80
|
-
<div style={
|
|
84
|
+
<div style={inputColumnStyle}>
|
|
81
85
|
<TimeInput
|
|
82
86
|
value={value ?? undefined}
|
|
83
87
|
onChange={handleTimeChange}
|