@idealyst/datepicker 1.2.119 → 11.2.120

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.119",
3
+ "version": "11.2.120",
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.119",
39
+ "@idealyst/theme": "^11.2.120",
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.119",
72
+ "@idealyst/theme": "^11.2.120",
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,37 @@ 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}
104
+ style={inputContainerStyle}
112
105
  onPress={() => !disabled && setOpen(true)}
113
106
  disabled={disabled}
107
+ activeOpacity={0.7}
114
108
  >
109
+ <Text style={[textInputStyle, !value && { color: (styles.pressableText as any)({ disabled, size }).placeholderColor }]}>
110
+ {value ? formatDate(value) : placeholder}
111
+ </Text>
115
112
  <MaterialDesignIcons name="calendar" size={iconStyle.width} style={iconStyle} />
116
113
  </TouchableOpacity>
117
- </View>
114
+ ) : (
115
+ <View style={inputContainerStyle}>
116
+ <TextInput
117
+ value={inputValue}
118
+ onChangeText={handleInputChange}
119
+ onBlur={handleInputBlur}
120
+ placeholder={placeholder}
121
+ editable={!disabled}
122
+ style={textInputStyle}
123
+ />
124
+ <TouchableOpacity
125
+ style={iconButtonStyle}
126
+ onPress={() => !disabled && setOpen(true)}
127
+ disabled={disabled}
128
+ >
129
+ <MaterialDesignIcons name="calendar" size={iconStyle.width} style={iconStyle} />
130
+ </TouchableOpacity>
131
+ </View>
132
+ )}
118
133
  {error && (
119
134
  <Text style={errorTextStyle}>
120
135
  {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,
@@ -108,30 +109,50 @@ export const DateInput: React.FC<DateInputProps> = ({
108
109
  const errorProps = getWebProps([errorTextStyle]);
109
110
  const popoverProps = getWebProps([popoverContentStyle]);
110
111
 
112
+ // Pressable text style (reuses input style but renders as a button)
113
+ const pressableTextStyle = (styles.pressableText as any)({ disabled, size });
114
+ const pressableTextProps = getWebProps([pressableTextStyle]);
115
+
111
116
  return (
112
117
  <div style={flattenStyle(style)}>
113
118
  {label && (
114
119
  <span {...labelProps}>{label}</span>
115
120
  )}
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
- />
121
+ {pressable ? (
126
122
  <button
127
123
  type="button"
128
- {...iconButtonProps}
124
+ {...containerProps}
125
+ ref={triggerRef as any}
126
+ style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
129
127
  onClick={() => !disabled && setOpen(!open)}
130
128
  disabled={disabled}
131
129
  >
130
+ <span {...pressableTextProps} style={!value ? { color: pressableTextStyle.placeholderColor } : undefined}>
131
+ {value ? formatDate(value) : placeholder}
132
+ </span>
132
133
  <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
133
134
  </button>
134
- </div>
135
+ ) : (
136
+ <div {...containerProps} ref={triggerRef}>
137
+ <input
138
+ type="text"
139
+ value={inputValue}
140
+ onChange={handleInputChange}
141
+ onBlur={handleInputBlur}
142
+ placeholder={placeholder}
143
+ disabled={disabled}
144
+ {...inputProps}
145
+ />
146
+ <button
147
+ type="button"
148
+ {...iconButtonProps}
149
+ onClick={() => !disabled && setOpen(!open)}
150
+ disabled={disabled}
151
+ >
152
+ <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
153
+ </button>
154
+ </div>
155
+ )}
135
156
  {error && (
136
157
  <span {...errorProps}>{error}</span>
137
158
  )}
@@ -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();
@@ -71,13 +81,11 @@ export const DatePicker: React.FC<DatePickerProps> = ({
71
81
  days.push({ date: new Date(year, month, day, 12, 0, 0, 0), isCurrentMonth: true });
72
82
  }
73
83
 
74
- // Next month padding (fill to complete last week)
84
+ // Next month padding (always fill to 42 days = 6 rows for consistent height)
75
85
  // Use noon (12:00) to avoid timezone issues when date crosses day boundaries
76
- const remaining = 7 - (days.length % 7);
77
- if (remaining < 7) {
78
- for (let day = 1; day <= remaining; day++) {
79
- days.push({ date: new Date(year, month + 1, day, 12, 0, 0, 0), isCurrentMonth: false });
80
- }
86
+ const totalCells = 42;
87
+ for (let day = 1; days.length < totalCells; day++) {
88
+ days.push({ date: new Date(year, month + 1, day, 12, 0, 0, 0), isCurrentMonth: false });
81
89
  }
82
90
 
83
91
  return { days, monthShort: MONTHS[month], year };
@@ -157,6 +165,13 @@ export const DatePicker: React.FC<DatePickerProps> = ({
157
165
  setCurrentMonth(new Date(currentMonth.getFullYear() + 10, currentMonth.getMonth(), 1));
158
166
  };
159
167
 
168
+ // Helper to get indicators for a date
169
+ const getIndicators = (date: Date): DayIndicator[] => {
170
+ if (!indicators) return [];
171
+ const key = formatDateKey(date);
172
+ return indicators[key] || [];
173
+ };
174
+
160
175
  // Render month selector
161
176
  if (viewMode === 'months') {
162
177
  return (
@@ -176,7 +191,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
176
191
  >
177
192
  <Text style={titleTextStyle}>{year}</Text>
178
193
  </TouchableOpacity>
179
- <View style={{ width: 28 }} />
194
+ <View style={{ width: 32 }} />
180
195
  </View>
181
196
  <View style={monthGridStyle}>
182
197
  {MONTHS.map((month, index) => {
@@ -305,20 +320,24 @@ export const DatePicker: React.FC<DatePickerProps> = ({
305
320
  const today = isToday(date);
306
321
  const dayDisabled = isDisabled(date);
307
322
  const disabledDayButtonStyle = (styles.dayButton as any)({ disabled: dayDisabled });
323
+ const dayIndicators = getIndicators(date);
308
324
 
309
325
  return (
310
326
  <View
311
327
  key={index}
312
328
  style={[
313
329
  dayCellStyle,
314
- selected && selectedDayStyle,
315
330
  !isCurrentMonth && { opacity: 0.3 },
316
- today && !selected && todayDayStyle,
317
331
  dayDisabled && { opacity: 0.3 },
318
332
  ]}
319
333
  >
320
334
  <TouchableOpacity
321
- style={[dayButtonStyle, dayDisabled && disabledDayButtonStyle]}
335
+ style={[
336
+ dayButtonStyle,
337
+ dayDisabled && disabledDayButtonStyle,
338
+ selected && selectedDayStyle,
339
+ today && !selected && todayDayStyle,
340
+ ]}
322
341
  onPress={() => handleDayPress(date)}
323
342
  disabled={dayDisabled}
324
343
  >
@@ -331,6 +350,14 @@ export const DatePicker: React.FC<DatePickerProps> = ({
331
350
  {date.getDate()}
332
351
  </Text>
333
352
  </TouchableOpacity>
353
+ <View style={indicatorRowStyle}>
354
+ {dayIndicators.slice(0, 3).map((ind, i) => (
355
+ <View
356
+ key={ind.key ?? i}
357
+ style={[indicatorStyle, { backgroundColor: ind.color }]}
358
+ />
359
+ ))}
360
+ </View>
334
361
  </View>
335
362
  );
336
363
  })}
@@ -263,12 +263,15 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
263
263
  },
264
264
  }),
265
265
 
266
- // Today styling - subtle background highlight
266
+ // Today styling - outlined circle (no fill)
267
267
  todayDay: (_props: DatePickerDynamicProps) => ({
268
- backgroundColor: theme.intents.primary.light,
268
+ borderWidth: 1,
269
+ borderColor: theme.intents.primary.primary,
269
270
  borderRadius: 16,
271
+ backgroundColor: 'transparent',
270
272
  _web: {
271
- background: theme.intents.primary.light,
273
+ background: 'transparent',
274
+ boxSizing: 'border-box',
272
275
  },
273
276
  }),
274
277
 
@@ -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());
@@ -54,13 +62,11 @@ export const DatePicker: React.FC<DatePickerProps> = ({
54
62
  days.push({ date: new Date(year, month, day, 12, 0, 0, 0), isCurrentMonth: true });
55
63
  }
56
64
 
57
- // Next month padding (fill to complete last week)
65
+ // Next month padding (always fill to 42 days = 6 rows for consistent height)
58
66
  // Use noon (12:00) to avoid timezone issues when date crosses day boundaries
59
- const remaining = 7 - (days.length % 7);
60
- if (remaining < 7) {
61
- for (let day = 1; day <= remaining; day++) {
62
- days.push({ date: new Date(year, month + 1, day, 12, 0, 0, 0), isCurrentMonth: false });
63
- }
67
+ const totalCells = 42;
68
+ for (let day = 1; days.length < totalCells; day++) {
69
+ days.push({ date: new Date(year, month + 1, day, 12, 0, 0, 0), isCurrentMonth: false });
64
70
  }
65
71
 
66
72
  return { days, monthShort: MONTHS[month], year };
@@ -157,6 +163,8 @@ export const DatePicker: React.FC<DatePickerProps> = ({
157
163
  const selectorItemTextStyle = (styles.selectorItemText as any)({});
158
164
  const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
159
165
  const iconColorStyle = (styles.iconColor as any)({});
166
+ const indicatorRowStyle = (styles.indicatorRow as any)({});
167
+ const indicatorStyle = (styles.indicator as any)({});
160
168
 
161
169
  // Get web props for all elements
162
170
  const calendarProps = getWebProps([calendarStyle, style as any]);
@@ -174,6 +182,14 @@ export const DatePicker: React.FC<DatePickerProps> = ({
174
182
  const selectorItemSelectedProps = getWebProps([selectorItemStyle, selectorItemSelectedStyle]);
175
183
  const selectorItemTextProps = getWebProps([selectorItemTextStyle]);
176
184
  const selectorItemTextSelectedProps = getWebProps([selectorItemTextStyle, selectorItemTextSelectedStyle]);
185
+ const indicatorRowProps = getWebProps([indicatorRowStyle]);
186
+
187
+ // Helper to get indicators for a date
188
+ const getIndicators = (date: Date): DayIndicator[] => {
189
+ if (!indicators) return [];
190
+ const key = formatDateKey(date);
191
+ return indicators[key] || [];
192
+ };
177
193
 
178
194
  // Render month selector
179
195
  if (viewMode === 'months') {
@@ -196,7 +212,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
196
212
  >
197
213
  <span {...titleTextProps}>{year}</span>
198
214
  </button>
199
- <div style={{ width: 28 }} />
215
+ <div style={{ width: 32 }} />
200
216
  </div>
201
217
  <div {...monthGridProps}>
202
218
  {MONTHS.map((month, index) => {
@@ -332,6 +348,7 @@ export const DatePicker: React.FC<DatePickerProps> = ({
332
348
  const selected = isSelected(date);
333
349
  const today = isToday(date);
334
350
  const dayDisabled = isDisabled(date);
351
+ const dayIndicators = getIndicators(date);
335
352
 
336
353
  // Get appropriate button props (className and ref only)
337
354
  const buttonProps = dayDisabled
@@ -343,18 +360,28 @@ export const DatePicker: React.FC<DatePickerProps> = ({
343
360
  : dayButtonProps;
344
361
 
345
362
  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>
363
+ <div key={index} {...dayCellProps}>
364
+ <button
365
+ type="button"
366
+ className={buttonProps.className}
367
+ style={{ opacity: (!isCurrentMonthDay || dayDisabled) ? 0.3 : 1 }}
368
+ onClick={() => handleDayPress(date)}
369
+ disabled={dayDisabled}
370
+ >
371
+ <span {...(selected ? selectedDayTextProps : dayTextProps)}>
372
+ {date.getDate()}
373
+ </span>
374
+ </button>
375
+ <div {...indicatorRowProps}>
376
+ {dayIndicators.slice(0, 3).map((ind, i) => (
377
+ <div
378
+ key={ind.key ?? i}
379
+ className={getWebProps([indicatorStyle]).className}
380
+ style={{ backgroundColor: ind.color }}
381
+ />
382
+ ))}
383
+ </div>
384
+ </div>
358
385
  );
359
386
  })}
360
387
  </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>
@@ -195,6 +195,35 @@ export const dateTimeInputStyles = defineStyle('DateTimeInput', (theme: Theme) =
195
195
  color: theme.intents.primary.primary,
196
196
  }),
197
197
 
198
+ // Pressable text (used when pressable=true instead of a text input)
199
+ pressableText: (_props: InputDynamicProps) => ({
200
+ flex: 1,
201
+ minWidth: 0,
202
+ color: theme.colors.text.primary,
203
+ fontWeight: '400' as const,
204
+ textAlign: 'left' as const,
205
+ // Default font size for when size variant isn't specified
206
+ fontSize: theme.sizes.input.md.fontSize,
207
+ // Placeholder color for native when no value is set
208
+ placeholderColor: theme.colors.text.tertiary,
209
+ _web: {
210
+ overflow: 'hidden',
211
+ textOverflow: 'ellipsis',
212
+ whiteSpace: 'nowrap',
213
+ pointerEvents: 'none',
214
+ },
215
+ variants: {
216
+ disabled: {
217
+ true: { color: theme.colors.text.tertiary },
218
+ false: { color: theme.colors.text.primary },
219
+ },
220
+ // $iterator expands for each input size
221
+ size: {
222
+ fontSize: theme.sizes.$input.fontSize,
223
+ },
224
+ },
225
+ }),
226
+
198
227
  // Icon color helper
199
228
  iconColor: (_props: InputDynamicProps) => ({
200
229
  color: theme.colors.text.secondary,
@@ -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,37 @@ 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}
124
+ style={inputContainerStyle}
132
125
  onPress={() => !disabled && setOpen(true)}
133
126
  disabled={disabled}
127
+ activeOpacity={0.7}
134
128
  >
129
+ <Text style={[textInputStyle, !value && { color: (styles.pressableText as any)({ disabled, size }).placeholderColor }]}>
130
+ {value ? formatTime(value) : placeholder}
131
+ </Text>
135
132
  <MaterialDesignIcons name="clock-outline" size={iconStyle.width} style={iconStyle} />
136
133
  </TouchableOpacity>
137
- </View>
134
+ ) : (
135
+ <View style={inputContainerStyle}>
136
+ <TextInput
137
+ value={inputValue}
138
+ onChangeText={handleInputChange}
139
+ onBlur={handleInputBlur}
140
+ placeholder={placeholder}
141
+ editable={!disabled}
142
+ style={textInputStyle}
143
+ />
144
+ <TouchableOpacity
145
+ style={iconButtonStyle}
146
+ onPress={() => !disabled && setOpen(true)}
147
+ disabled={disabled}
148
+ >
149
+ <MaterialDesignIcons name="clock-outline" size={iconStyle.width} style={iconStyle} />
150
+ </TouchableOpacity>
151
+ </View>
152
+ )}
138
153
  {error && (
139
154
  <Text style={errorTextStyle}>
140
155
  {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,
@@ -132,30 +133,50 @@ export const TimeInput: React.FC<TimeInputProps> = ({
132
133
  const errorProps = getWebProps([errorTextStyle]);
133
134
  const popoverProps = getWebProps([popoverContentStyle]);
134
135
 
136
+ // Pressable text style (reuses input style but renders as a button)
137
+ const pressableTextStyle = (styles.pressableText as any)({ disabled, size });
138
+ const pressableTextProps = getWebProps([pressableTextStyle]);
139
+
135
140
  return (
136
141
  <div style={style as React.CSSProperties}>
137
142
  {label && (
138
143
  <span {...labelProps}>{label}</span>
139
144
  )}
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
- />
145
+ {pressable ? (
150
146
  <button
151
147
  type="button"
152
- {...iconButtonProps}
148
+ {...containerProps}
149
+ ref={triggerRef as any}
150
+ style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
153
151
  onClick={() => !disabled && setOpen(!open)}
154
152
  disabled={disabled}
155
153
  >
154
+ <span {...pressableTextProps} style={!value ? { color: pressableTextStyle.placeholderColor } : undefined}>
155
+ {value ? formatTime(value) : placeholder}
156
+ </span>
156
157
  <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
157
158
  </button>
158
- </div>
159
+ ) : (
160
+ <div {...containerProps} ref={triggerRef}>
161
+ <input
162
+ type="text"
163
+ value={inputValue}
164
+ onChange={handleInputChange}
165
+ onBlur={handleInputBlur}
166
+ placeholder={placeholder}
167
+ disabled={disabled}
168
+ {...inputProps}
169
+ />
170
+ <button
171
+ type="button"
172
+ {...iconButtonProps}
173
+ onClick={() => !disabled && setOpen(!open)}
174
+ disabled={disabled}
175
+ >
176
+ <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
177
+ </button>
178
+ </div>
179
+ )}
159
180
  {error && (
160
181
  <span {...errorProps}>{error}</span>
161
182
  )}
@@ -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
@@ -34,6 +34,8 @@ export interface DateInputProps {
34
34
  minDate?: Date;
35
35
  maxDate?: Date;
36
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;
37
39
  error?: string;
38
40
  size?: Size;
39
41
  style?: ViewStyle;
@@ -47,6 +49,8 @@ export interface TimeInputProps {
47
49
  mode?: '12h' | '24h';
48
50
  minuteStep?: number;
49
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;
50
54
  error?: string;
51
55
  size?: Size;
52
56
  style?: ViewStyle;
@@ -61,6 +65,8 @@ export interface DateTimePickerProps {
61
65
  timeMode?: '12h' | '24h';
62
66
  minuteStep?: number;
63
67
  disabled?: boolean;
68
+ /** When true, both input areas are pressable to open their pickers instead of being text inputs. */
69
+ pressable?: boolean;
64
70
  error?: string;
65
71
  size?: Size;
66
72
  style?: ViewStyle;