@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/datepicker",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
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/components": "^1.1.8",
40
- "@idealyst/theme": "^1.1.8",
39
+ "@idealyst/theme": "^1.2.0",
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/components": "^1.1.8",
65
- "@idealyst/theme": "^1.1.8",
72
+ "@idealyst/theme": "^1.2.0",
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": [
@@ -1,8 +1,8 @@
1
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';
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 { datePickerStyles } from './styles';
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 typography="body2" weight="medium" style={datePickerStyles.labelText}>
88
+ <Text style={labelTextStyle}>
82
89
  {label}
83
90
  </Text>
84
91
  )}
85
- <View style={datePickerStyles.inputContainer}>
86
- <RNTextInput
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={datePickerStyles.textInput}
99
+ style={textInputStyle}
93
100
  />
94
- <Button
95
- type="text"
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
- <Icon name="calendar" size={18} />
102
- </Button>
106
+ <MaterialCommunityIcons name="calendar" size={18} style={iconStyle} />
107
+ </TouchableOpacity>
103
108
  </View>
104
109
  {error && (
105
- <Text typography="caption" style={datePickerStyles.errorText}>
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={datePickerStyles.modalBackdrop}>
117
- <View style={datePickerStyles.popoverContent}>
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
- <Button
126
- type="text"
130
+ <TouchableOpacity
131
+ style={closeButtonStyle}
127
132
  onPress={() => setOpen(false)}
128
- style={{ marginTop: 8 }}
129
133
  >
130
- Close
131
- </Button>
134
+ <Text style={closeButtonTextStyle}>Close</Text>
135
+ </TouchableOpacity>
132
136
  </View>
133
137
  </View>
134
138
  </Modal>
@@ -1,9 +1,10 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
- import { View, Text, Button, Icon } from '@idealyst/components';
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 { datePickerStyles } from './styles';
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
- // Apply variants for input container
76
- datePickerStyles.useVariants({
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
- <View style={style}>
91
+ <div style={style as React.CSSProperties}>
87
92
  {label && (
88
- <Text typography="body2" weight="medium" style={datePickerStyles.labelText}>
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
- {...inputProps}
103
+ style={textInputStyle}
101
104
  />
102
- <Button
103
- type="text"
104
- size="sm"
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
- <Icon name="calendar" size={18} />
110
- </Button>
110
+ <IconSvg path={mdiCalendar} size={18} color={iconColorStyle.color} />
111
+ </button>
111
112
  </div>
112
113
  {error && (
113
- <Text typography="caption" style={datePickerStyles.errorText}>
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
- <View style={datePickerStyles.popoverContent}>
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
- </View>
134
+ </div>
136
135
  </PositionedPortal>
137
- </View>
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 { Text, Button, Icon } from '@idealyst/components';
4
- import { datePickerStyles } from './styles';
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
- datePickerStyles.useVariants({ disabled });
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={[datePickerStyles.calendar, style]}>
128
- <View style={datePickerStyles.calendarHeader}>
129
- <Button type="text" size="sm" onPress={() => setViewMode('calendar')} disabled={disabled}>
130
- <Icon name="chevron-left" size={16} />
131
- </Button>
132
- <Button type="text" size="sm" onPress={() => setViewMode('years')} disabled={disabled}>
133
- <Text typography="body2" weight="semibold">{year}</Text>
134
- </Button>
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={datePickerStyles.monthGrid}>
168
+ <View style={monthGridStyle}>
138
169
  {MONTHS.map((month, index) => {
139
170
  const isCurrentMonth = index === currentMonth.getMonth();
140
171
  return (
141
- <Button
172
+ <TouchableOpacity
142
173
  key={month}
143
- type={isCurrentMonth ? 'contained' : 'text'}
144
- size="sm"
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 typography="caption" color={isCurrentMonth ? 'inverse' : 'primary'}>
181
+ <Text
182
+ style={[
183
+ selectorItemTextStyle,
184
+ isCurrentMonth && selectorItemTextSelectedStyle,
185
+ ]}
186
+ >
150
187
  {month}
151
188
  </Text>
152
- </Button>
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={[datePickerStyles.calendar, style]}>
164
- <View style={datePickerStyles.calendarHeader}>
165
- <Button type="text" size="sm" onPress={goToPrevYearRange} disabled={disabled}>
166
- <Icon name="chevron-left" size={16} />
167
- </Button>
168
- <Text typography="body2" weight="semibold">
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
- <Button type="text" size="sm" onPress={goToNextYearRange} disabled={disabled}>
172
- <Icon name="chevron-right" size={16} />
173
- </Button>
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={datePickerStyles.yearGrid}>
220
+ <View style={yearGridStyle}>
176
221
  {yearRange.map((yr) => {
177
222
  const isCurrentYear = yr === currentMonth.getFullYear();
178
223
  return (
179
- <Button
224
+ <TouchableOpacity
180
225
  key={yr}
181
- type={isCurrentYear ? 'contained' : 'text'}
182
- size="sm"
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 typography="caption" color={isCurrentYear ? 'inverse' : 'primary'}>
233
+ <Text
234
+ style={[
235
+ selectorItemTextStyle,
236
+ isCurrentYear && selectorItemTextSelectedStyle,
237
+ ]}
238
+ >
188
239
  {yr}
189
240
  </Text>
190
- </Button>
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={[datePickerStyles.calendar, style]}>
251
+ <View style={[calendarStyle, style]}>
201
252
  {/* Header */}
202
- <View style={datePickerStyles.calendarHeader}>
203
- <Button type="text" size="sm" onPress={goToPrevMonth} disabled={disabled}>
204
- <Icon name="chevron-left" size={16} />
205
- </Button>
206
- <Button type="text" size="sm" onPress={() => setViewMode('months')} disabled={disabled}>
207
- <Text typography="body2" weight="semibold">
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
- </Button>
211
- <Button type="text" size="sm" onPress={goToNextMonth} disabled={disabled}>
212
- <Icon name="chevron-right" size={16} />
213
- </Button>
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={datePickerStyles.weekdayRow}>
280
+ <View style={weekdayRowStyle}>
218
281
  {WEEKDAYS.map((day, i) => (
219
- <View key={i} style={datePickerStyles.weekdayCell}>
220
- <Text typography="caption" color="secondary">
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={datePickerStyles.calendarGrid}>
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
- datePickerStyles.dayCell,
239
- selected && datePickerStyles.selectedDay,
300
+ dayCellStyle,
301
+ selected && selectedDayStyle,
240
302
  !isCurrentMonth && { opacity: 0.3 },
241
- today && !selected && datePickerStyles.todayDay,
303
+ today && !selected && todayDayStyle,
242
304
  dayDisabled && { opacity: 0.3 },
243
305
  ]}
244
306
  >
245
- <Button
246
- type="text"
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
- typography="caption"
254
- color={selected ? 'inverse' : 'primary'}
313
+ style={[
314
+ dayTextStyle,
315
+ selected && selectedDayTextStyle,
316
+ ]}
255
317
  >
256
318
  {date.getDate()}
257
319
  </Text>
258
- </Button>
320
+ </TouchableOpacity>
259
321
  </View>
260
322
  );
261
323
  })}