@idealyst/datepicker 1.2.118 → 1.2.121

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.2.118",
3
+ "version": "1.2.121",
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,7 +36,7 @@
36
36
  "publish:npm": "npm publish"
37
37
  },
38
38
  "peerDependencies": {
39
- "@idealyst/theme": "^1.2.118",
39
+ "@idealyst/theme": "^1.2.121",
40
40
  "@mdi/js": ">=7.0.0",
41
41
  "@mdi/react": ">=1.6.0",
42
42
  "react": ">=16.8.0",
@@ -69,7 +69,7 @@
69
69
  }
70
70
  },
71
71
  "devDependencies": {
72
- "@idealyst/theme": "^1.2.118",
72
+ "@idealyst/theme": "^1.2.121",
73
73
  "@mdi/js": "^7.4.47",
74
74
  "@mdi/react": "^1.6.1",
75
75
  "@types/react": "^19.1.0",
@@ -13,6 +13,7 @@ export const DateInput: React.FC<DateInputProps> = ({
13
13
  minDate,
14
14
  maxDate,
15
15
  disabled = false,
16
+ pressable = false,
16
17
  error,
17
18
  size = 'md',
18
19
  style,
@@ -98,23 +99,43 @@ export const DateInput: React.FC<DateInputProps> = ({
98
99
  {label}
99
100
  </Text>
100
101
  )}
101
- <View style={inputContainerStyle}>
102
- <TextInput
103
- value={inputValue}
104
- onChangeText={handleInputChange}
105
- onBlur={handleInputBlur}
106
- placeholder={placeholder}
107
- editable={!disabled}
108
- style={textInputStyle}
109
- />
102
+ {pressable ? (
110
103
  <TouchableOpacity
111
- style={iconButtonStyle}
112
104
  onPress={() => !disabled && setOpen(true)}
113
105
  disabled={disabled}
106
+ activeOpacity={0.7}
114
107
  >
115
- <MaterialDesignIcons name="calendar" size={iconStyle.width} style={iconStyle} />
108
+ <View style={inputContainerStyle} pointerEvents="none">
109
+ <TextInput
110
+ value={formatDate(value ?? undefined)}
111
+ placeholder={placeholder}
112
+ editable={false}
113
+ style={textInputStyle}
114
+ />
115
+ <View style={iconButtonStyle}>
116
+ <MaterialDesignIcons name="calendar" size={iconStyle.width} style={iconStyle} />
117
+ </View>
118
+ </View>
116
119
  </TouchableOpacity>
117
- </View>
120
+ ) : (
121
+ <View style={inputContainerStyle}>
122
+ <TextInput
123
+ value={inputValue}
124
+ onChangeText={handleInputChange}
125
+ onBlur={handleInputBlur}
126
+ placeholder={placeholder}
127
+ editable={!disabled}
128
+ style={textInputStyle}
129
+ />
130
+ <TouchableOpacity
131
+ style={iconButtonStyle}
132
+ onPress={() => !disabled && setOpen(true)}
133
+ disabled={disabled}
134
+ >
135
+ <MaterialDesignIcons name="calendar" size={iconStyle.width} style={iconStyle} />
136
+ </TouchableOpacity>
137
+ </View>
138
+ )}
118
139
  {error && (
119
140
  <Text style={errorTextStyle}>
120
141
  {error}
@@ -18,6 +18,7 @@ export const DateInput: React.FC<DateInputProps> = ({
18
18
  minDate,
19
19
  maxDate,
20
20
  disabled = false,
21
+ pressable = false,
21
22
  error,
22
23
  size = 'md',
23
24
  style,
@@ -113,25 +114,47 @@ export const DateInput: React.FC<DateInputProps> = ({
113
114
  {label && (
114
115
  <span {...labelProps}>{label}</span>
115
116
  )}
116
- <div {...containerProps} ref={triggerRef}>
117
- <input
118
- type="text"
119
- value={inputValue}
120
- onChange={handleInputChange}
121
- onBlur={handleInputBlur}
122
- placeholder={placeholder}
123
- disabled={disabled}
124
- {...inputProps}
125
- />
126
- <button
127
- type="button"
128
- {...iconButtonProps}
117
+ {pressable ? (
118
+ <div
119
+ ref={triggerRef}
129
120
  onClick={() => !disabled && setOpen(!open)}
130
- disabled={disabled}
121
+ style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
131
122
  >
132
- <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
133
- </button>
134
- </div>
123
+ <div {...containerProps} style={{ pointerEvents: 'none' }}>
124
+ <input
125
+ type="text"
126
+ value={formatDate(value ?? undefined)}
127
+ placeholder={placeholder}
128
+ readOnly
129
+ tabIndex={-1}
130
+ {...inputProps}
131
+ />
132
+ <div {...iconButtonProps}>
133
+ <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
134
+ </div>
135
+ </div>
136
+ </div>
137
+ ) : (
138
+ <div {...containerProps} ref={triggerRef}>
139
+ <input
140
+ type="text"
141
+ value={inputValue}
142
+ onChange={handleInputChange}
143
+ onBlur={handleInputBlur}
144
+ placeholder={placeholder}
145
+ disabled={disabled}
146
+ {...inputProps}
147
+ />
148
+ <button
149
+ type="button"
150
+ {...iconButtonProps}
151
+ onClick={() => !disabled && setOpen(!open)}
152
+ disabled={disabled}
153
+ >
154
+ <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
155
+ </button>
156
+ </div>
157
+ )}
135
158
  {error && (
136
159
  <span {...errorProps}>{error}</span>
137
160
  )}
@@ -2,19 +2,27 @@ import React, { useMemo, useState } from 'react';
2
2
  import { View, Text, TouchableOpacity } from 'react-native';
3
3
  import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons';
4
4
  import { datePickerCalendarStyles } from './DatePicker.styles';
5
- import type { DatePickerProps } from './types';
5
+ import type { DatePickerProps, DayIndicator } from './types';
6
6
 
7
7
  const WEEKDAYS = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
8
8
  const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
9
9
 
10
10
  type ViewMode = 'calendar' | 'months' | 'years';
11
11
 
12
+ function formatDateKey(date: Date): string {
13
+ const y = date.getFullYear();
14
+ const m = String(date.getMonth() + 1).padStart(2, '0');
15
+ const d = String(date.getDate()).padStart(2, '0');
16
+ return `${y}-${m}-${d}`;
17
+ }
18
+
12
19
  export const DatePicker: React.FC<DatePickerProps> = ({
13
20
  value,
14
21
  onChange,
15
22
  minDate,
16
23
  maxDate,
17
24
  disabled = false,
25
+ indicators,
18
26
  style,
19
27
  }) => {
20
28
  const [currentMonth, setCurrentMonth] = useState(() => value || new Date());
@@ -44,6 +52,8 @@ export const DatePicker: React.FC<DatePickerProps> = ({
44
52
  const selectorItemTextStyle = (styles.selectorItemText as any)({});
45
53
  const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
46
54
  const iconStyle = (styles.iconColor as any)({});
55
+ const indicatorRowStyle = (styles.indicatorRow as any)({});
56
+ const indicatorStyle = (styles.indicator as any)({});
47
57
 
48
58
  const { days, monthShort, year } = useMemo(() => {
49
59
  const year = currentMonth.getFullYear();
@@ -157,6 +167,13 @@ export const DatePicker: React.FC<DatePickerProps> = ({
157
167
  setCurrentMonth(new Date(currentMonth.getFullYear() + 10, currentMonth.getMonth(), 1));
158
168
  };
159
169
 
170
+ // Helper to get indicators for a date
171
+ const getIndicators = (date: Date): DayIndicator[] => {
172
+ if (!indicators) return [];
173
+ const key = formatDateKey(date);
174
+ return indicators[key] || [];
175
+ };
176
+
160
177
  // Render month selector
161
178
  if (viewMode === 'months') {
162
179
  return (
@@ -176,7 +193,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
176
193
  >
177
194
  <Text style={titleTextStyle}>{year}</Text>
178
195
  </TouchableOpacity>
179
- <View style={{ width: 28 }} />
196
+ <View style={{ width: 32 }} />
180
197
  </View>
181
198
  <View style={monthGridStyle}>
182
199
  {MONTHS.map((month, index) => {
@@ -305,20 +322,24 @@ export const DatePicker: React.FC<DatePickerProps> = ({
305
322
  const today = isToday(date);
306
323
  const dayDisabled = isDisabled(date);
307
324
  const disabledDayButtonStyle = (styles.dayButton as any)({ disabled: dayDisabled });
325
+ const dayIndicators = getIndicators(date);
308
326
 
309
327
  return (
310
328
  <View
311
329
  key={index}
312
330
  style={[
313
331
  dayCellStyle,
314
- selected && selectedDayStyle,
315
332
  !isCurrentMonth && { opacity: 0.3 },
316
- today && !selected && todayDayStyle,
317
333
  dayDisabled && { opacity: 0.3 },
318
334
  ]}
319
335
  >
320
336
  <TouchableOpacity
321
- style={[dayButtonStyle, dayDisabled && disabledDayButtonStyle]}
337
+ style={[
338
+ dayButtonStyle,
339
+ dayDisabled && disabledDayButtonStyle,
340
+ selected && selectedDayStyle,
341
+ today && !selected && todayDayStyle,
342
+ ]}
322
343
  onPress={() => handleDayPress(date)}
323
344
  disabled={dayDisabled}
324
345
  >
@@ -331,6 +352,14 @@ export const DatePicker: React.FC<DatePickerProps> = ({
331
352
  {date.getDate()}
332
353
  </Text>
333
354
  </TouchableOpacity>
355
+ <View style={indicatorRowStyle}>
356
+ {dayIndicators.slice(0, 3).map((ind, i) => (
357
+ <View
358
+ key={ind.key ?? i}
359
+ style={[indicatorStyle, { backgroundColor: ind.color }]}
360
+ />
361
+ ))}
362
+ </View>
334
363
  </View>
335
364
  );
336
365
  })}
@@ -19,12 +19,12 @@ export type DatePickerDynamicProps = {
19
19
  * DatePicker calendar styles with theme reactivity.
20
20
  */
21
21
  export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme: Theme) => ({
22
- // Calendar container - compact
22
+ // Calendar container
23
23
  calendar: (_props: DatePickerDynamicProps) => ({
24
- padding: 8,
24
+ padding: 12,
25
25
  backgroundColor: theme.colors.surface.primary,
26
26
  borderRadius: 6,
27
- width: 220,
27
+ width: 280,
28
28
  variants: {
29
29
  disabled: {
30
30
  true: { opacity: 0.6 },
@@ -38,7 +38,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
38
38
  flexDirection: 'row' as const,
39
39
  alignItems: 'center' as const,
40
40
  justifyContent: 'space-between' as const,
41
- marginBottom: 4,
41
+ marginBottom: 8,
42
42
  paddingHorizontal: 2,
43
43
  _web: {
44
44
  display: 'flex',
@@ -57,15 +57,15 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
57
57
  // Weekday header row
58
58
  weekdayRow: (_props: DatePickerDynamicProps) => ({
59
59
  flexDirection: 'row' as const,
60
- marginBottom: 2,
60
+ marginBottom: 4,
61
61
  _web: {
62
62
  display: 'flex',
63
63
  },
64
64
  }),
65
65
 
66
66
  weekdayCell: (_props: DatePickerDynamicProps) => ({
67
- width: 28,
68
- height: 20,
67
+ width: 36,
68
+ height: 24,
69
69
  alignItems: 'center' as const,
70
70
  justifyContent: 'center' as const,
71
71
  _web: {
@@ -73,10 +73,12 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
73
73
  },
74
74
  }),
75
75
 
76
- // Calendar grid
76
+ // Calendar grid - fixed height so rows flex evenly regardless of 5 or 6 weeks
77
77
  calendarGrid: (_props: DatePickerDynamicProps) => ({
78
78
  flexDirection: 'row' as const,
79
79
  flexWrap: 'wrap' as const,
80
+ height: 252, // 6 rows × 42px
81
+ alignContent: 'space-evenly' as const,
80
82
  _web: {
81
83
  display: 'flex',
82
84
  },
@@ -104,12 +106,13 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
104
106
  },
105
107
  }),
106
108
 
107
- // Individual day cell - compact
109
+ // Individual day cell - contains button + indicator row
108
110
  dayCell: (_props: DatePickerDynamicProps) => ({
109
- width: 28,
110
- height: 28,
111
+ width: 36,
112
+ height: 42,
111
113
  alignItems: 'center' as const,
112
- justifyContent: 'center' as const,
114
+ justifyContent: 'flex-start' as const,
115
+ paddingTop: 2,
113
116
  _web: {
114
117
  display: 'flex',
115
118
  },
@@ -117,8 +120,8 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
117
120
 
118
121
  // Navigation button
119
122
  navButton: (_props: DatePickerDynamicProps) => ({
120
- width: 28,
121
- height: 28,
123
+ width: 32,
124
+ height: 32,
122
125
  alignItems: 'center' as const,
123
126
  justifyContent: 'center' as const,
124
127
  borderRadius: 4,
@@ -158,18 +161,18 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
158
161
  }),
159
162
 
160
163
  titleText: (_props: DatePickerDynamicProps) => ({
161
- fontSize: 14,
164
+ fontSize: 15,
162
165
  fontWeight: '600' as const,
163
166
  color: theme.colors.text.primary,
164
167
  }),
165
168
 
166
- // Day button - fills entire cell for better click handling
169
+ // Day button - fills cell for better click handling
167
170
  dayButton: (_props: DatePickerDynamicProps) => ({
168
- width: 28,
169
- height: 28,
171
+ width: 32,
172
+ height: 32,
170
173
  alignItems: 'center' as const,
171
174
  justifyContent: 'center' as const,
172
- borderRadius: 14,
175
+ borderRadius: 16,
173
176
  backgroundColor: 'transparent',
174
177
  borderWidth: 0,
175
178
  _web: {
@@ -188,7 +191,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
188
191
  }),
189
192
 
190
193
  dayText: (_props: DatePickerDynamicProps) => ({
191
- fontSize: 12,
194
+ fontSize: 14,
192
195
  color: theme.colors.text.primary,
193
196
  _web: {
194
197
  pointerEvents: 'none',
@@ -196,7 +199,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
196
199
  }),
197
200
 
198
201
  weekdayText: (_props: DatePickerDynamicProps) => ({
199
- fontSize: 11,
202
+ fontSize: 12,
200
203
  color: theme.colors.text.secondary,
201
204
  }),
202
205
 
@@ -232,7 +235,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
232
235
  }),
233
236
 
234
237
  selectorItemText: (_props: DatePickerDynamicProps) => ({
235
- fontSize: 12,
238
+ fontSize: 13,
236
239
  color: theme.colors.text.primary,
237
240
  _web: {
238
241
  pointerEvents: 'none',
@@ -249,7 +252,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
249
252
  // Selected day styling
250
253
  selectedDay: (_props: DatePickerDynamicProps) => ({
251
254
  backgroundColor: theme.intents.primary.primary,
252
- borderRadius: 6,
255
+ borderRadius: 16,
253
256
  _web: {
254
257
  background: theme.intents.primary.primary,
255
258
  },
@@ -262,15 +265,34 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
262
265
  },
263
266
  }),
264
267
 
265
- // Today styling - subtle background highlight
268
+ // Today styling - subtle gray background
266
269
  todayDay: (_props: DatePickerDynamicProps) => ({
267
- backgroundColor: theme.intents.primary.light,
268
- borderRadius: 6,
270
+ borderRadius: 16,
271
+ backgroundColor: theme.colors.pallet.gray?.[200] ?? theme.colors.surface.secondary,
269
272
  _web: {
270
- background: theme.intents.primary.light,
273
+ background: theme.colors.pallet.gray?.[200] ?? theme.colors.surface.secondary,
271
274
  },
272
275
  }),
273
276
 
277
+ // Indicator row below the day number
278
+ indicatorRow: (_props: DatePickerDynamicProps) => ({
279
+ flexDirection: 'row' as const,
280
+ alignItems: 'center' as const,
281
+ justifyContent: 'center' as const,
282
+ height: 6,
283
+ gap: 2,
284
+ _web: {
285
+ display: 'flex',
286
+ },
287
+ }),
288
+
289
+ // Individual indicator dot
290
+ indicator: (_props: DatePickerDynamicProps) => ({
291
+ width: 4,
292
+ height: 4,
293
+ borderRadius: 2,
294
+ }),
295
+
274
296
  // Icon color helper
275
297
  iconColor: (_props: DatePickerDynamicProps) => ({
276
298
  color: theme.colors.text.primary,
@@ -3,19 +3,27 @@ import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
4
4
  import { IconSvg } from './IconSvg.web';
5
5
  import { datePickerCalendarStyles } from './DatePicker.styles';
6
- import type { DatePickerProps } from './types';
6
+ import type { DatePickerProps, DayIndicator } from './types';
7
7
 
8
8
  const WEEKDAYS = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
9
9
  const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
10
10
 
11
11
  type ViewMode = 'calendar' | 'months' | 'years';
12
12
 
13
+ function formatDateKey(date: Date): string {
14
+ const y = date.getFullYear();
15
+ const m = String(date.getMonth() + 1).padStart(2, '0');
16
+ const d = String(date.getDate()).padStart(2, '0');
17
+ return `${y}-${m}-${d}`;
18
+ }
19
+
13
20
  export const DatePicker: React.FC<DatePickerProps> = ({
14
21
  value,
15
22
  onChange,
16
23
  minDate,
17
24
  maxDate,
18
25
  disabled = false,
26
+ indicators,
19
27
  style,
20
28
  }) => {
21
29
  const [currentMonth, setCurrentMonth] = useState(() => value || new Date());
@@ -157,6 +165,8 @@ export const DatePicker: React.FC<DatePickerProps> = ({
157
165
  const selectorItemTextStyle = (styles.selectorItemText as any)({});
158
166
  const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
159
167
  const iconColorStyle = (styles.iconColor as any)({});
168
+ const indicatorRowStyle = (styles.indicatorRow as any)({});
169
+ const indicatorStyle = (styles.indicator as any)({});
160
170
 
161
171
  // Get web props for all elements
162
172
  const calendarProps = getWebProps([calendarStyle, style as any]);
@@ -174,6 +184,14 @@ export const DatePicker: React.FC<DatePickerProps> = ({
174
184
  const selectorItemSelectedProps = getWebProps([selectorItemStyle, selectorItemSelectedStyle]);
175
185
  const selectorItemTextProps = getWebProps([selectorItemTextStyle]);
176
186
  const selectorItemTextSelectedProps = getWebProps([selectorItemTextStyle, selectorItemTextSelectedStyle]);
187
+ const indicatorRowProps = getWebProps([indicatorRowStyle]);
188
+
189
+ // Helper to get indicators for a date
190
+ const getIndicators = (date: Date): DayIndicator[] => {
191
+ if (!indicators) return [];
192
+ const key = formatDateKey(date);
193
+ return indicators[key] || [];
194
+ };
177
195
 
178
196
  // Render month selector
179
197
  if (viewMode === 'months') {
@@ -196,7 +214,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
196
214
  >
197
215
  <span {...titleTextProps}>{year}</span>
198
216
  </button>
199
- <div style={{ width: 28 }} />
217
+ <div style={{ width: 32 }} />
200
218
  </div>
201
219
  <div {...monthGridProps}>
202
220
  {MONTHS.map((month, index) => {
@@ -332,6 +350,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
332
350
  const selected = isSelected(date);
333
351
  const today = isToday(date);
334
352
  const dayDisabled = isDisabled(date);
353
+ const dayIndicators = getIndicators(date);
335
354
 
336
355
  // Get appropriate button props (className and ref only)
337
356
  const buttonProps = dayDisabled
@@ -343,18 +362,28 @@ export const DatePicker: React.FC<DatePickerProps> = ({
343
362
  : dayButtonProps;
344
363
 
345
364
  return (
346
- <button
347
- key={index}
348
- type="button"
349
- className={buttonProps.className}
350
- style={{ opacity: (!isCurrentMonthDay || dayDisabled) ? 0.3 : 1 }}
351
- onClick={() => handleDayPress(date)}
352
- disabled={dayDisabled}
353
- >
354
- <span {...(selected ? selectedDayTextProps : dayTextProps)}>
355
- {date.getDate()}
356
- </span>
357
- </button>
365
+ <div key={index} {...dayCellProps}>
366
+ <button
367
+ type="button"
368
+ className={buttonProps.className}
369
+ style={{ opacity: (!isCurrentMonthDay || dayDisabled) ? 0.3 : 1 }}
370
+ onClick={() => handleDayPress(date)}
371
+ disabled={dayDisabled}
372
+ >
373
+ <span {...(selected ? selectedDayTextProps : dayTextProps)}>
374
+ {date.getDate()}
375
+ </span>
376
+ </button>
377
+ <div {...indicatorRowProps}>
378
+ {dayIndicators.slice(0, 3).map((ind, i) => (
379
+ <div
380
+ key={ind.key ?? i}
381
+ className={getWebProps([indicatorStyle]).className}
382
+ style={{ backgroundColor: ind.color }}
383
+ />
384
+ ))}
385
+ </div>
386
+ </div>
358
387
  );
359
388
  })}
360
389
  </div>
@@ -14,6 +14,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
14
14
  timeMode = '12h',
15
15
  minuteStep = 1,
16
16
  disabled = false,
17
+ pressable = false,
17
18
  error,
18
19
  size = 'md',
19
20
  style,
@@ -91,6 +92,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
91
92
  minDate={minDate}
92
93
  maxDate={maxDate}
93
94
  disabled={disabled}
95
+ pressable={pressable}
94
96
  error={error}
95
97
  size={size}
96
98
  />
@@ -103,6 +105,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
103
105
  mode={timeMode}
104
106
  minuteStep={minuteStep}
105
107
  disabled={disabled}
108
+ pressable={pressable}
106
109
  size={size}
107
110
  />
108
111
  </View>
@@ -14,6 +14,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
14
14
  timeMode = '12h',
15
15
  minuteStep = 1,
16
16
  disabled = false,
17
+ pressable = false,
17
18
  error,
18
19
  size = 'md',
19
20
  style,
@@ -95,6 +96,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
95
96
  minDate={minDate}
96
97
  maxDate={maxDate}
97
98
  disabled={disabled}
99
+ pressable={pressable}
98
100
  error={error}
99
101
  size={size}
100
102
  />
@@ -107,6 +109,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
107
109
  mode={timeMode}
108
110
  minuteStep={minuteStep}
109
111
  disabled={disabled}
112
+ pressable={pressable}
110
113
  size={size}
111
114
  />
112
115
  </div>
@@ -13,6 +13,7 @@ export const TimeInput: React.FC<TimeInputProps> = ({
13
13
  mode = '12h',
14
14
  minuteStep = 1,
15
15
  disabled = false,
16
+ pressable = false,
16
17
  error,
17
18
  size = 'md',
18
19
  style,
@@ -118,23 +119,43 @@ export const TimeInput: React.FC<TimeInputProps> = ({
118
119
  {label}
119
120
  </Text>
120
121
  )}
121
- <View style={inputContainerStyle}>
122
- <TextInput
123
- value={inputValue}
124
- onChangeText={handleInputChange}
125
- onBlur={handleInputBlur}
126
- placeholder={placeholder}
127
- editable={!disabled}
128
- style={textInputStyle}
129
- />
122
+ {pressable ? (
130
123
  <TouchableOpacity
131
- style={iconButtonStyle}
132
124
  onPress={() => !disabled && setOpen(true)}
133
125
  disabled={disabled}
126
+ activeOpacity={0.7}
134
127
  >
135
- <MaterialDesignIcons name="clock-outline" size={iconStyle.width} style={iconStyle} />
128
+ <View style={inputContainerStyle} pointerEvents="none">
129
+ <TextInput
130
+ value={formatTime(value ?? undefined)}
131
+ placeholder={placeholder}
132
+ editable={false}
133
+ style={textInputStyle}
134
+ />
135
+ <View style={iconButtonStyle}>
136
+ <MaterialDesignIcons name="clock-outline" size={iconStyle.width} style={iconStyle} />
137
+ </View>
138
+ </View>
136
139
  </TouchableOpacity>
137
- </View>
140
+ ) : (
141
+ <View style={inputContainerStyle}>
142
+ <TextInput
143
+ value={inputValue}
144
+ onChangeText={handleInputChange}
145
+ onBlur={handleInputBlur}
146
+ placeholder={placeholder}
147
+ editable={!disabled}
148
+ style={textInputStyle}
149
+ />
150
+ <TouchableOpacity
151
+ style={iconButtonStyle}
152
+ onPress={() => !disabled && setOpen(true)}
153
+ disabled={disabled}
154
+ >
155
+ <MaterialDesignIcons name="clock-outline" size={iconStyle.width} style={iconStyle} />
156
+ </TouchableOpacity>
157
+ </View>
158
+ )}
138
159
  {error && (
139
160
  <Text style={errorTextStyle}>
140
161
  {error}
@@ -17,6 +17,7 @@ export const TimeInput: React.FC<TimeInputProps> = ({
17
17
  mode = '12h',
18
18
  minuteStep = 1,
19
19
  disabled = false,
20
+ pressable = false,
20
21
  error,
21
22
  size = 'md',
22
23
  style,
@@ -137,25 +138,47 @@ export const TimeInput: React.FC<TimeInputProps> = ({
137
138
  {label && (
138
139
  <span {...labelProps}>{label}</span>
139
140
  )}
140
- <div {...containerProps} ref={triggerRef}>
141
- <input
142
- type="text"
143
- value={inputValue}
144
- onChange={handleInputChange}
145
- onBlur={handleInputBlur}
146
- placeholder={placeholder}
147
- disabled={disabled}
148
- {...inputProps}
149
- />
150
- <button
151
- type="button"
152
- {...iconButtonProps}
141
+ {pressable ? (
142
+ <div
143
+ ref={triggerRef}
153
144
  onClick={() => !disabled && setOpen(!open)}
154
- disabled={disabled}
145
+ style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
155
146
  >
156
- <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
157
- </button>
158
- </div>
147
+ <div {...containerProps} style={{ pointerEvents: 'none' }}>
148
+ <input
149
+ type="text"
150
+ value={formatTime(value ?? undefined)}
151
+ placeholder={placeholder}
152
+ readOnly
153
+ tabIndex={-1}
154
+ {...inputProps}
155
+ />
156
+ <div {...iconButtonProps}>
157
+ <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
158
+ </div>
159
+ </div>
160
+ </div>
161
+ ) : (
162
+ <div {...containerProps} ref={triggerRef}>
163
+ <input
164
+ type="text"
165
+ value={inputValue}
166
+ onChange={handleInputChange}
167
+ onBlur={handleInputBlur}
168
+ placeholder={placeholder}
169
+ disabled={disabled}
170
+ {...inputProps}
171
+ />
172
+ <button
173
+ type="button"
174
+ {...iconButtonProps}
175
+ onClick={() => !disabled && setOpen(!open)}
176
+ disabled={disabled}
177
+ >
178
+ <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
179
+ </button>
180
+ </div>
181
+ )}
159
182
  {error && (
160
183
  <span {...errorProps}>{error}</span>
161
184
  )}
@@ -12,4 +12,5 @@ export type {
12
12
  DateInputProps,
13
13
  TimeInputProps,
14
14
  DateTimePickerProps,
15
+ DayIndicator,
15
16
  } from './types';
package/src/index.ts CHANGED
@@ -12,4 +12,5 @@ export type {
12
12
  DateInputProps,
13
13
  TimeInputProps,
14
14
  DateTimePickerProps,
15
+ DayIndicator,
15
16
  } from './types';
package/src/types.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  import type { ViewStyle } from 'react-native';
2
2
  import type { Size } from '@idealyst/theme';
3
3
 
4
+ export interface DayIndicator {
5
+ color: string;
6
+ key?: string;
7
+ }
8
+
4
9
  export interface DatePickerProps {
5
10
  value?: Date;
6
11
  onChange: (date: Date) => void;
7
12
  minDate?: Date;
8
13
  maxDate?: Date;
9
14
  disabled?: boolean;
15
+ /** Indicators to render below specific dates. Key format: "YYYY-MM-DD" */
16
+ indicators?: Record<string, DayIndicator[]>;
10
17
  style?: ViewStyle;
11
18
  }
12
19
 
@@ -27,6 +34,8 @@ export interface DateInputProps {
27
34
  minDate?: Date;
28
35
  maxDate?: Date;
29
36
  disabled?: boolean;
37
+ /** When true, the entire input area is pressable to open the calendar instead of being a text input. */
38
+ pressable?: boolean;
30
39
  error?: string;
31
40
  size?: Size;
32
41
  style?: ViewStyle;
@@ -40,6 +49,8 @@ export interface TimeInputProps {
40
49
  mode?: '12h' | '24h';
41
50
  minuteStep?: number;
42
51
  disabled?: boolean;
52
+ /** When true, the entire input area is pressable to open the time picker instead of being a text input. */
53
+ pressable?: boolean;
43
54
  error?: string;
44
55
  size?: Size;
45
56
  style?: ViewStyle;
@@ -54,6 +65,8 @@ export interface DateTimePickerProps {
54
65
  timeMode?: '12h' | '24h';
55
66
  minuteStep?: number;
56
67
  disabled?: boolean;
68
+ /** When true, both input areas are pressable to open their pickers instead of being text inputs. */
69
+ pressable?: boolean;
57
70
  error?: string;
58
71
  size?: Size;
59
72
  style?: ViewStyle;