@idealyst/datepicker 1.2.36 → 1.2.37

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.36",
3
+ "version": "1.2.37",
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.36",
39
+ "@idealyst/theme": "^1.2.37",
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.36",
72
+ "@idealyst/theme": "^1.2.37",
73
73
  "@mdi/js": "^7.4.47",
74
74
  "@mdi/react": "^1.6.1",
75
75
  "@types/react": "^19.1.0",
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
+ import { useUnistyles } from 'react-native-unistyles';
3
4
  import { mdiCalendar } from '@mdi/js';
4
5
  import { PositionedPortal } from '@idealyst/components/internal';
5
6
  import { IconSvg } from './IconSvg.web';
@@ -16,6 +17,7 @@ export const DateInput: React.FC<DateInputProps> = ({
16
17
  maxDate,
17
18
  disabled = false,
18
19
  error,
20
+ size = 'md',
19
21
  style,
20
22
  }) => {
21
23
  const [open, setOpen] = useState(false);
@@ -24,14 +26,26 @@ export const DateInput: React.FC<DateInputProps> = ({
24
26
 
25
27
  const styles = dateTimeInputStyles;
26
28
 
27
- // Get dynamic styles
29
+ // Apply variants for disabled, error, and size states
30
+ styles.useVariants({
31
+ disabled,
32
+ error: !!error,
33
+ size,
34
+ });
35
+
36
+ // Get theme for icon size and color
37
+ const { theme } = useUnistyles();
38
+ const iconSize = theme.sizes.input[size].iconSize;
39
+ const iconColor = theme.colors.text.secondary;
40
+
41
+ // Get dynamic styles with size variant
28
42
  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 });
43
+ const inputContainerStyle = (styles.inputContainer as any)({ disabled, error: !!error, size });
44
+ const textInputStyle = (styles.textInput as any)({ disabled, size });
45
+ const iconButtonStyle = (styles.iconButton as any)({ disabled, size });
46
+ const iconStyle = (styles.icon as any)({ size });
32
47
  const errorTextStyle = (styles.errorText as any)({});
33
48
  const popoverContentStyle = (styles.popoverContent as any)({});
34
- const iconColorStyle = (styles.iconColor as any)({ disabled });
35
49
 
36
50
  // Format date to string
37
51
  const formatDate = (date: Date | undefined): string => {
@@ -80,17 +94,24 @@ export const DateInput: React.FC<DateInputProps> = ({
80
94
  };
81
95
 
82
96
  const handleDateSelect = (date: Date) => {
97
+ console.log('[DateInput] handleDateSelect received:', date.toISOString());
98
+ console.log('[DateInput] Calling onChange with:', date.toISOString());
83
99
  onChange(date);
84
100
  setOpen(false);
85
101
  };
86
102
 
87
- // Get web props
103
+ // Get web props for all elements
88
104
  const containerProps = getWebProps([inputContainerStyle]);
105
+ const labelProps = getWebProps([labelTextStyle]);
106
+ const inputProps = getWebProps([textInputStyle]);
107
+ const iconButtonProps = getWebProps([iconButtonStyle]);
108
+ const errorProps = getWebProps([errorTextStyle]);
109
+ const popoverProps = getWebProps([popoverContentStyle]);
89
110
 
90
111
  return (
91
112
  <div style={style as React.CSSProperties}>
92
113
  {label && (
93
- <span style={labelTextStyle}>{label}</span>
114
+ <span {...labelProps}>{label}</span>
94
115
  )}
95
116
  <div {...containerProps} ref={triggerRef}>
96
117
  <input
@@ -100,18 +121,19 @@ export const DateInput: React.FC<DateInputProps> = ({
100
121
  onBlur={handleInputBlur}
101
122
  placeholder={placeholder}
102
123
  disabled={disabled}
103
- style={textInputStyle}
124
+ {...inputProps}
104
125
  />
105
126
  <button
106
- style={iconButtonStyle}
127
+ type="button"
128
+ {...iconButtonProps}
107
129
  onClick={() => !disabled && setOpen(!open)}
108
130
  disabled={disabled}
109
131
  >
110
- <IconSvg path={mdiCalendar} size={18} color={iconColorStyle.color} />
132
+ <IconSvg path={mdiCalendar} size={iconSize} color={iconColor} />
111
133
  </button>
112
134
  </div>
113
135
  {error && (
114
- <span style={errorTextStyle}>{error}</span>
136
+ <span {...errorProps}>{error}</span>
115
137
  )}
116
138
 
117
139
  <PositionedPortal
@@ -123,7 +145,7 @@ export const DateInput: React.FC<DateInputProps> = ({
123
145
  onEscapeKey={() => setOpen(false)}
124
146
  zIndex={9999}
125
147
  >
126
- <div style={popoverContentStyle}>
148
+ <div {...popoverProps}>
127
149
  <DatePicker
128
150
  value={value ?? undefined}
129
151
  onChange={handleDateSelect}
@@ -20,12 +20,17 @@ export type DatePickerDynamicProps = {
20
20
  */
21
21
  export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme: Theme) => ({
22
22
  // Calendar container - compact
23
- calendar: ({ disabled = false }: DatePickerDynamicProps) => ({
23
+ calendar: (_props: DatePickerDynamicProps) => ({
24
24
  padding: 8,
25
25
  backgroundColor: theme.colors.surface.primary,
26
26
  borderRadius: 6,
27
27
  width: 220,
28
- opacity: disabled ? 0.6 : 1,
28
+ variants: {
29
+ disabled: {
30
+ true: { opacity: 0.6 },
31
+ false: { opacity: 1 },
32
+ },
33
+ },
29
34
  }),
30
35
 
31
36
  // Calendar header with month/year and navigation
@@ -111,27 +116,44 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
111
116
  }),
112
117
 
113
118
  // Navigation button
114
- navButton: ({ disabled = false }: DatePickerDynamicProps) => ({
119
+ navButton: (_props: DatePickerDynamicProps) => ({
115
120
  width: 28,
116
121
  height: 28,
117
122
  alignItems: 'center' as const,
118
123
  justifyContent: 'center' as const,
119
124
  borderRadius: 4,
120
- opacity: disabled ? 0.4 : 1,
125
+ backgroundColor: 'transparent',
126
+ borderWidth: 0,
121
127
  _web: {
122
128
  display: 'flex',
123
- cursor: disabled ? 'not-allowed' : 'pointer',
129
+ background: 'none',
130
+ border: 'none',
131
+ padding: 0,
132
+ },
133
+ variants: {
134
+ disabled: {
135
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
136
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
137
+ },
124
138
  },
125
139
  }),
126
140
 
127
141
  // Title button (month/year selector)
128
- titleButton: ({ disabled = false }: DatePickerDynamicProps) => ({
142
+ titleButton: (_props: DatePickerDynamicProps) => ({
129
143
  paddingHorizontal: 8,
130
144
  paddingVertical: 4,
131
145
  borderRadius: 4,
132
- opacity: disabled ? 0.6 : 1,
146
+ backgroundColor: 'transparent',
147
+ borderWidth: 0,
133
148
  _web: {
134
- cursor: disabled ? 'not-allowed' : 'pointer',
149
+ background: 'none',
150
+ border: 'none',
151
+ },
152
+ variants: {
153
+ disabled: {
154
+ true: { opacity: 0.6, _web: { cursor: 'not-allowed' } },
155
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
156
+ },
135
157
  },
136
158
  }),
137
159
 
@@ -141,23 +163,36 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
141
163
  color: theme.colors.text.primary,
142
164
  }),
143
165
 
144
- // Day button
145
- dayButton: ({ disabled = false }: DatePickerDynamicProps) => ({
146
- width: 24,
147
- height: 24,
166
+ // Day button - fills entire cell for better click handling
167
+ dayButton: (_props: DatePickerDynamicProps) => ({
168
+ width: 28,
169
+ height: 28,
148
170
  alignItems: 'center' as const,
149
171
  justifyContent: 'center' as const,
150
- borderRadius: 12,
151
- opacity: disabled ? 0.4 : 1,
172
+ borderRadius: 14,
173
+ backgroundColor: 'transparent',
174
+ borderWidth: 0,
152
175
  _web: {
153
176
  display: 'flex',
154
- cursor: disabled ? 'not-allowed' : 'pointer',
177
+ background: 'none',
178
+ border: 'none',
179
+ padding: 0,
180
+ cursor: 'pointer',
181
+ },
182
+ variants: {
183
+ disabled: {
184
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
185
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
186
+ },
155
187
  },
156
188
  }),
157
189
 
158
190
  dayText: (_props: DatePickerDynamicProps) => ({
159
191
  fontSize: 12,
160
192
  color: theme.colors.text.primary,
193
+ _web: {
194
+ pointerEvents: 'none',
195
+ },
161
196
  }),
162
197
 
163
198
  weekdayText: (_props: DatePickerDynamicProps) => ({
@@ -166,7 +201,7 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
166
201
  }),
167
202
 
168
203
  // Month/Year selector item
169
- selectorItem: ({ disabled = false }: DatePickerDynamicProps) => ({
204
+ selectorItem: (_props: DatePickerDynamicProps) => ({
170
205
  minWidth: 48,
171
206
  paddingVertical: 6,
172
207
  paddingHorizontal: 8,
@@ -174,41 +209,66 @@ export const datePickerCalendarStyles = defineStyle('DatePickerCalendar', (theme
174
209
  alignItems: 'center' as const,
175
210
  justifyContent: 'center' as const,
176
211
  borderRadius: 4,
177
- opacity: disabled ? 0.4 : 1,
212
+ backgroundColor: 'transparent',
213
+ borderWidth: 0,
178
214
  _web: {
179
215
  display: 'flex',
180
- cursor: disabled ? 'not-allowed' : 'pointer',
216
+ background: 'none',
217
+ border: 'none',
218
+ },
219
+ variants: {
220
+ disabled: {
221
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
222
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
223
+ },
181
224
  },
182
225
  }),
183
226
 
184
227
  selectorItemSelected: (_props: DatePickerDynamicProps) => ({
185
228
  backgroundColor: theme.intents.primary.primary,
229
+ _web: {
230
+ background: theme.intents.primary.primary,
231
+ },
186
232
  }),
187
233
 
188
234
  selectorItemText: (_props: DatePickerDynamicProps) => ({
189
235
  fontSize: 12,
190
236
  color: theme.colors.text.primary,
237
+ _web: {
238
+ pointerEvents: 'none',
239
+ },
191
240
  }),
192
241
 
193
242
  selectorItemTextSelected: (_props: DatePickerDynamicProps) => ({
194
243
  color: theme.intents.primary.contrast,
244
+ _web: {
245
+ pointerEvents: 'none',
246
+ },
195
247
  }),
196
248
 
197
249
  // Selected day styling
198
250
  selectedDay: (_props: DatePickerDynamicProps) => ({
199
251
  backgroundColor: theme.intents.primary.primary,
200
- borderRadius: 14,
252
+ borderRadius: 6,
253
+ _web: {
254
+ background: theme.intents.primary.primary,
255
+ },
201
256
  }),
202
257
 
203
258
  selectedDayText: (_props: DatePickerDynamicProps) => ({
204
259
  color: theme.intents.primary.contrast,
260
+ _web: {
261
+ pointerEvents: 'none',
262
+ },
205
263
  }),
206
264
 
207
- // Today styling
265
+ // Today styling - subtle background highlight
208
266
  todayDay: (_props: DatePickerDynamicProps) => ({
209
- borderWidth: 1,
210
- borderColor: theme.intents.primary.primary,
211
- borderRadius: 14,
267
+ backgroundColor: theme.intents.primary.muted,
268
+ borderRadius: 6,
269
+ _web: {
270
+ background: theme.intents.primary.muted,
271
+ },
212
272
  }),
213
273
 
214
274
  // Icon color helper
@@ -23,6 +23,11 @@ export const DatePicker: React.FC<DatePickerProps> = ({
23
23
 
24
24
  const styles = datePickerCalendarStyles;
25
25
 
26
+ // Apply variants for disabled state
27
+ styles.useVariants({
28
+ disabled,
29
+ });
30
+
26
31
  const { days, monthShort, year } = useMemo(() => {
27
32
  const year = currentMonth.getFullYear();
28
33
  const month = currentMonth.getMonth();
@@ -78,8 +83,16 @@ export const DatePicker: React.FC<DatePickerProps> = ({
78
83
 
79
84
  const isDisabled = (date: Date): boolean => {
80
85
  if (disabled) return true;
81
- if (minDate && date < new Date(minDate.setHours(0, 0, 0, 0))) return true;
82
- if (maxDate && date > new Date(maxDate.setHours(23, 59, 59, 999))) return true;
86
+ // Normalize the date to start of day for comparison
87
+ const normalizedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
88
+ if (minDate) {
89
+ const min = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
90
+ if (normalizedDate < min) return true;
91
+ }
92
+ if (maxDate) {
93
+ const max = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate());
94
+ if (normalizedDate > max) return true;
95
+ }
83
96
  return false;
84
97
  };
85
98
 
@@ -92,8 +105,13 @@ export const DatePicker: React.FC<DatePickerProps> = ({
92
105
  };
93
106
 
94
107
  const handleDayPress = (date: Date) => {
108
+ console.log('[DatePicker] handleDayPress called with:', date.toISOString());
109
+ console.log('[DatePicker] isDisabled:', isDisabled(date));
95
110
  if (!isDisabled(date)) {
96
- onChange(date);
111
+ // Create a new date to avoid mutating the calendar's date objects
112
+ const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
113
+ console.log('[DatePicker] Calling onChange with:', newDate.toISOString());
114
+ onChange(newDate);
97
115
  }
98
116
  };
99
117
 
@@ -140,13 +158,22 @@ export const DatePicker: React.FC<DatePickerProps> = ({
140
158
  const selectorItemTextSelectedStyle = (styles.selectorItemTextSelected as any)({});
141
159
  const iconColorStyle = (styles.iconColor as any)({});
142
160
 
143
- // Get web props
161
+ // Get web props for all elements
144
162
  const calendarProps = getWebProps([calendarStyle, style as any]);
145
163
  const headerProps = getWebProps([headerStyle]);
146
164
  const weekdayRowProps = getWebProps([weekdayRowStyle]);
165
+ const weekdayCellProps = getWebProps([weekdayCellStyle]);
147
166
  const gridProps = getWebProps([gridStyle]);
148
167
  const monthGridProps = getWebProps([monthGridStyle]);
149
168
  const yearGridProps = getWebProps([yearGridStyle]);
169
+ const navButtonProps = getWebProps([navButtonStyle]);
170
+ const titleButtonProps = getWebProps([titleButtonStyle]);
171
+ const titleTextProps = getWebProps([titleTextStyle]);
172
+ const weekdayTextProps = getWebProps([weekdayTextStyle]);
173
+ const selectorItemProps = getWebProps([selectorItemStyle]);
174
+ const selectorItemSelectedProps = getWebProps([selectorItemStyle, selectorItemSelectedStyle]);
175
+ const selectorItemTextProps = getWebProps([selectorItemTextStyle]);
176
+ const selectorItemTextSelectedProps = getWebProps([selectorItemTextStyle, selectorItemTextSelectedStyle]);
150
177
 
151
178
  // Render month selector
152
179
  if (viewMode === 'months') {
@@ -154,18 +181,20 @@ export const DatePicker: React.FC<DatePickerProps> = ({
154
181
  <div {...calendarProps}>
155
182
  <div {...headerProps}>
156
183
  <button
157
- style={navButtonStyle}
184
+ type="button"
185
+ {...navButtonProps}
158
186
  onClick={() => setViewMode('calendar')}
159
187
  disabled={disabled}
160
188
  >
161
189
  <IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
162
190
  </button>
163
191
  <button
164
- style={titleButtonStyle}
192
+ type="button"
193
+ {...titleButtonProps}
165
194
  onClick={() => setViewMode('years')}
166
195
  disabled={disabled}
167
196
  >
168
- <span style={titleTextStyle}>{year}</span>
197
+ <span {...titleTextProps}>{year}</span>
169
198
  </button>
170
199
  <div style={{ width: 28 }} />
171
200
  </div>
@@ -174,20 +203,13 @@ export const DatePicker: React.FC<DatePickerProps> = ({
174
203
  const isCurrentMonth = index === currentMonth.getMonth();
175
204
  return (
176
205
  <button
206
+ type="button"
177
207
  key={month}
178
- style={{
179
- ...selectorItemStyle,
180
- ...(isCurrentMonth ? selectorItemSelectedStyle : {}),
181
- }}
208
+ {...(isCurrentMonth ? selectorItemSelectedProps : selectorItemProps)}
182
209
  onClick={() => handleMonthSelect(index)}
183
210
  disabled={disabled}
184
211
  >
185
- <span
186
- style={{
187
- ...selectorItemTextStyle,
188
- ...(isCurrentMonth ? selectorItemTextSelectedStyle : {}),
189
- }}
190
- >
212
+ <span {...(isCurrentMonth ? selectorItemTextSelectedProps : selectorItemTextProps)}>
191
213
  {month}
192
214
  </span>
193
215
  </button>
@@ -204,17 +226,19 @@ export const DatePicker: React.FC<DatePickerProps> = ({
204
226
  <div {...calendarProps}>
205
227
  <div {...headerProps}>
206
228
  <button
207
- style={navButtonStyle}
229
+ type="button"
230
+ {...navButtonProps}
208
231
  onClick={goToPrevYearRange}
209
232
  disabled={disabled}
210
233
  >
211
234
  <IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
212
235
  </button>
213
- <span style={titleTextStyle}>
236
+ <span {...titleTextProps}>
214
237
  {yearRange[0]} - {yearRange[yearRange.length - 1]}
215
238
  </span>
216
239
  <button
217
- style={navButtonStyle}
240
+ type="button"
241
+ {...navButtonProps}
218
242
  onClick={goToNextYearRange}
219
243
  disabled={disabled}
220
244
  >
@@ -226,20 +250,13 @@ export const DatePicker: React.FC<DatePickerProps> = ({
226
250
  const isCurrentYear = yr === currentMonth.getFullYear();
227
251
  return (
228
252
  <button
253
+ type="button"
229
254
  key={yr}
230
- style={{
231
- ...selectorItemStyle,
232
- ...(isCurrentYear ? selectorItemSelectedStyle : {}),
233
- }}
255
+ {...(isCurrentYear ? selectorItemSelectedProps : selectorItemProps)}
234
256
  onClick={() => handleYearSelect(yr)}
235
257
  disabled={disabled}
236
258
  >
237
- <span
238
- style={{
239
- ...selectorItemTextStyle,
240
- ...(isCurrentYear ? selectorItemTextSelectedStyle : {}),
241
- }}
242
- >
259
+ <span {...(isCurrentYear ? selectorItemTextSelectedProps : selectorItemTextProps)}>
243
260
  {yr}
244
261
  </span>
245
262
  </button>
@@ -250,29 +267,49 @@ export const DatePicker: React.FC<DatePickerProps> = ({
250
267
  );
251
268
  }
252
269
 
270
+ // Get day cell props
271
+ const dayCellStyle = (styles.dayCell as any)({});
272
+ const selectedDayStyle = (styles.selectedDay as any)({});
273
+ const todayDayStyle = (styles.todayDay as any)({});
274
+ const dayButtonStyle = (styles.dayButton as any)({ disabled: false });
275
+ const dayButtonDisabledStyle = (styles.dayButton as any)({ disabled: true });
276
+ const dayTextStyle = (styles.dayText as any)({});
277
+ const selectedDayTextStyle = (styles.selectedDayText as any)({});
278
+
279
+ const dayCellProps = getWebProps([dayCellStyle]);
280
+ const dayButtonProps = getWebProps([dayButtonStyle]);
281
+ const dayButtonSelectedProps = getWebProps([dayButtonStyle, selectedDayStyle]);
282
+ const dayButtonTodayProps = getWebProps([dayButtonStyle, todayDayStyle]);
283
+ const dayButtonDisabledProps = getWebProps([dayButtonDisabledStyle]);
284
+ const dayTextProps = getWebProps([dayTextStyle]);
285
+ const selectedDayTextProps = getWebProps([dayTextStyle, selectedDayTextStyle]);
286
+
253
287
  // Render calendar (default)
254
288
  return (
255
289
  <div {...calendarProps}>
256
290
  {/* Header */}
257
291
  <div {...headerProps}>
258
292
  <button
259
- style={navButtonStyle}
293
+ type="button"
294
+ {...navButtonProps}
260
295
  onClick={goToPrevMonth}
261
296
  disabled={disabled}
262
297
  >
263
298
  <IconSvg path={mdiChevronLeft} size={16} color={iconColorStyle.color} />
264
299
  </button>
265
300
  <button
266
- style={titleButtonStyle}
301
+ type="button"
302
+ {...titleButtonProps}
267
303
  onClick={() => setViewMode('months')}
268
304
  disabled={disabled}
269
305
  >
270
- <span style={titleTextStyle}>
306
+ <span {...titleTextProps}>
271
307
  {monthShort} {year}
272
308
  </span>
273
309
  </button>
274
310
  <button
275
- style={navButtonStyle}
311
+ type="button"
312
+ {...navButtonProps}
276
313
  onClick={goToNextMonth}
277
314
  disabled={disabled}
278
315
  >
@@ -283,51 +320,41 @@ export const DatePicker: React.FC<DatePickerProps> = ({
283
320
  {/* Weekday headers */}
284
321
  <div {...weekdayRowProps}>
285
322
  {WEEKDAYS.map((day, i) => (
286
- <div key={i} style={weekdayCellStyle}>
287
- <span style={weekdayTextStyle}>{day}</span>
323
+ <div key={i} {...weekdayCellProps}>
324
+ <span {...weekdayTextProps}>{day}</span>
288
325
  </div>
289
326
  ))}
290
327
  </div>
291
328
 
292
329
  {/* Calendar grid */}
293
330
  <div {...gridProps}>
294
- {days.map(({ date, isCurrentMonth }, index) => {
331
+ {days.map(({ date, isCurrentMonth: isCurrentMonthDay }, index) => {
295
332
  const selected = isSelected(date);
296
333
  const today = isToday(date);
297
334
  const dayDisabled = isDisabled(date);
298
335
 
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)({});
336
+ // Get appropriate button props (className and ref only)
337
+ const buttonProps = dayDisabled
338
+ ? dayButtonDisabledProps
339
+ : selected
340
+ ? dayButtonSelectedProps
341
+ : today
342
+ ? dayButtonTodayProps
343
+ : dayButtonProps;
305
344
 
306
345
  return (
307
- <div
346
+ <button
308
347
  key={index}
309
- style={{
310
- ...dayCellStyle,
311
- ...(selected ? selectedDayStyle : {}),
312
- ...(today && !selected ? todayDayStyle : {}),
313
- opacity: (!isCurrentMonth || dayDisabled) ? 0.3 : 1,
314
- }}
348
+ type="button"
349
+ className={buttonProps.className}
350
+ style={{ opacity: (!isCurrentMonthDay || dayDisabled) ? 0.3 : 1 }}
351
+ onClick={() => handleDayPress(date)}
352
+ disabled={dayDisabled}
315
353
  >
316
- <button
317
- style={dayButtonStyle}
318
- onClick={() => handleDayPress(date)}
319
- disabled={dayDisabled}
320
- >
321
- <span
322
- style={{
323
- ...dayTextStyle,
324
- ...(selected ? selectedDayTextStyle : {}),
325
- }}
326
- >
327
- {date.getDate()}
328
- </span>
329
- </button>
330
- </div>
354
+ <span {...(selected ? selectedDayTextProps : dayTextProps)}>
355
+ {date.getDate()}
356
+ </span>
357
+ </button>
331
358
  );
332
359
  })}
333
360
  </div>
@@ -15,6 +15,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
15
15
  minuteStep = 1,
16
16
  disabled = false,
17
17
  error,
18
+ size = 'md',
18
19
  style,
19
20
  }) => {
20
21
  const styles = dateTimePickerStyles;
@@ -25,6 +26,8 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
25
26
  const inputColumnStyle = (styles.inputColumn as any)({});
26
27
 
27
28
  const handleDateChange = (date: Date | null) => {
29
+ console.log('[DateTimePicker] handleDateChange received:', date?.toISOString());
30
+ console.log('[DateTimePicker] Current value:', value?.toISOString());
28
31
  if (!date) {
29
32
  onChange(null);
30
33
  return;
@@ -32,12 +35,18 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
32
35
  // Preserve time from current value, or use noon as default
33
36
  const hours = value?.getHours() ?? 12;
34
37
  const minutes = value?.getMinutes() ?? 0;
38
+ console.log('[DateTimePicker] Preserving hours:', hours, 'minutes:', minutes);
35
39
  const updated = new Date(date);
40
+ console.log('[DateTimePicker] After new Date(date):', updated.toISOString());
36
41
  updated.setHours(hours, minutes, 0, 0);
42
+ console.log('[DateTimePicker] After setHours:', updated.toISOString());
43
+ console.log('[DateTimePicker] Calling onChange with:', updated.toISOString());
37
44
  onChange(updated);
38
45
  };
39
46
 
40
47
  const handleTimeChange = (time: Date | null) => {
48
+ console.log('[DateTimePicker] handleTimeChange received:', time?.toISOString());
49
+ console.log('[DateTimePicker] Current value:', value?.toISOString());
41
50
  if (!time) {
42
51
  // Only clear time component, keep date if it exists
43
52
  if (value) {
@@ -58,19 +67,22 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
58
67
  0,
59
68
  0
60
69
  );
70
+ console.log('[DateTimePicker] Time change - calling onChange with:', updated.toISOString());
61
71
  onChange(updated);
62
72
  };
63
73
 
64
- // Get web props
74
+ // Get web props for all elements
65
75
  const inputRowProps = getWebProps([inputRowStyle]);
76
+ const labelProps = getWebProps([labelTextStyle]);
77
+ const inputColumnProps = getWebProps([inputColumnStyle]);
66
78
 
67
79
  return (
68
80
  <div style={style as React.CSSProperties}>
69
81
  {label && (
70
- <span style={labelTextStyle}>{label}</span>
82
+ <span {...labelProps}>{label}</span>
71
83
  )}
72
84
  <div {...inputRowProps}>
73
- <div style={inputColumnStyle}>
85
+ <div {...inputColumnProps}>
74
86
  <DateInput
75
87
  value={value ?? undefined}
76
88
  onChange={handleDateChange}
@@ -79,9 +91,10 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
79
91
  maxDate={maxDate}
80
92
  disabled={disabled}
81
93
  error={error}
94
+ size={size}
82
95
  />
83
96
  </div>
84
- <div style={inputColumnStyle}>
97
+ <div {...inputColumnProps}>
85
98
  <TimeInput
86
99
  value={value ?? undefined}
87
100
  onChange={handleTimeChange}
@@ -89,6 +102,7 @@ export const DateTimePicker: React.FC<DateTimePickerProps> = ({
89
102
  mode={timeMode}
90
103
  minuteStep={minuteStep}
91
104
  disabled={disabled}
105
+ size={size}
92
106
  />
93
107
  </div>
94
108
  </div>
@@ -22,11 +22,14 @@ export const IconSvg: React.FC<IconSvgProps> = ({
22
22
  }) => {
23
23
  if (!path) return null;
24
24
 
25
+ // Convert numeric sizes to pixel strings - @mdi/react interprets bare numbers as rem
26
+ const sizeValue = typeof size === 'number' ? `${size}px` : size;
27
+
25
28
  return (
26
29
  <MdiIcon
27
30
  style={style}
28
31
  path={path}
29
- size={size}
32
+ size={sizeValue}
30
33
  color={color}
31
34
  aria-label={ariaLabel}
32
35
  />
@@ -18,51 +18,103 @@ export type InputDynamicProps = {
18
18
 
19
19
  /**
20
20
  * Shared input styles with theme reactivity.
21
+ * Uses $iterator pattern to support size variants (xs, sm, md, lg, xl).
21
22
  */
22
23
  export const dateTimeInputStyles = defineStyle('DateTimeInput', (theme: Theme) => ({
23
24
  // Input container for DateInput/TimeInput
24
- inputContainer: ({ disabled = false, error = false }: InputDynamicProps) => ({
25
+ inputContainer: (_props: InputDynamicProps) => ({
25
26
  flexDirection: 'row' as const,
26
27
  alignItems: 'center' as const,
27
28
  borderWidth: 1,
28
- borderRadius: 6,
29
+ borderStyle: 'solid' as const,
30
+ borderRadius: theme.radii.md,
29
31
  overflow: 'hidden' as const,
30
- borderColor: error ? theme.intents.danger.primary : theme.colors.border.primary,
31
- backgroundColor: disabled ? theme.colors.surface.secondary : theme.colors.surface.primary,
32
+ borderColor: theme.colors.border.primary,
33
+ backgroundColor: theme.colors.surface.primary,
32
34
  _web: {
33
35
  display: 'flex',
34
36
  flexDirection: 'row',
35
37
  alignItems: 'center',
36
- border: `1px solid ${error ? theme.intents.danger.primary : theme.colors.border.primary}`,
38
+ boxSizing: 'border-box',
39
+ },
40
+ variants: {
41
+ disabled: {
42
+ true: { backgroundColor: theme.colors.surface.secondary },
43
+ false: { backgroundColor: theme.colors.surface.primary },
44
+ },
45
+ error: {
46
+ true: { borderColor: theme.intents.danger.primary },
47
+ false: { borderColor: theme.colors.border.primary },
48
+ },
49
+ // $iterator expands for each input size (xs, sm, md, lg, xl)
50
+ size: {
51
+ height: theme.sizes.$input.height,
52
+ paddingHorizontal: theme.sizes.$input.paddingHorizontal,
53
+ },
37
54
  },
38
55
  }),
39
56
 
40
57
  // Text input inside the input container
41
- textInput: ({ disabled = false }: InputDynamicProps) => ({
58
+ textInput: (_props: InputDynamicProps) => ({
42
59
  flex: 1,
43
- padding: 8,
44
- paddingHorizontal: 12,
45
- fontSize: 14,
60
+ minWidth: 0,
46
61
  backgroundColor: 'transparent',
47
- color: disabled ? theme.colors.text.tertiary : theme.colors.text.primary,
62
+ color: theme.colors.text.primary,
63
+ fontWeight: '400' as const,
48
64
  _web: {
49
65
  outline: 'none',
50
66
  border: 'none',
67
+ fontFamily: 'inherit',
68
+ },
69
+ variants: {
70
+ disabled: {
71
+ true: { color: theme.colors.text.tertiary },
72
+ false: { color: theme.colors.text.primary },
73
+ },
74
+ // $iterator expands for each input size
75
+ size: {
76
+ fontSize: theme.sizes.$input.fontSize,
77
+ },
51
78
  },
52
79
  }),
53
80
 
54
81
  // Icon button inside input
55
- iconButton: ({ disabled = false }: InputDynamicProps) => ({
56
- width: 32,
57
- height: 32,
82
+ iconButton: (_props: InputDynamicProps) => ({
58
83
  alignItems: 'center' as const,
59
84
  justifyContent: 'center' as const,
60
- marginRight: 4,
61
85
  borderRadius: 4,
62
- opacity: disabled ? 0.4 : 1,
86
+ backgroundColor: 'transparent',
87
+ borderWidth: 0,
88
+ flexShrink: 0,
63
89
  _web: {
64
90
  display: 'flex',
65
- cursor: disabled ? 'not-allowed' : 'pointer',
91
+ background: 'none',
92
+ border: 'none',
93
+ padding: 0,
94
+ },
95
+ variants: {
96
+ disabled: {
97
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
98
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
99
+ },
100
+ // $iterator expands for each input size
101
+ size: {
102
+ width: theme.sizes.$input.iconSize,
103
+ height: theme.sizes.$input.iconSize,
104
+ marginLeft: theme.sizes.$input.iconMargin,
105
+ },
106
+ },
107
+ }),
108
+
109
+ // Icon inside button - sized based on input size
110
+ icon: (_props: InputDynamicProps) => ({
111
+ color: theme.colors.text.secondary,
112
+ variants: {
113
+ // $iterator expands for each input size
114
+ size: {
115
+ width: theme.sizes.$input.iconSize,
116
+ height: theme.sizes.$input.iconSize,
117
+ },
66
118
  },
67
119
  }),
68
120
 
@@ -101,17 +153,21 @@ export const dateTimeInputStyles = defineStyle('DateTimeInput', (theme: Theme) =
101
153
  }),
102
154
 
103
155
  // Close button
104
- closeButton: ({ disabled = false }: InputDynamicProps) => ({
156
+ closeButton: (_props: InputDynamicProps) => ({
105
157
  marginTop: 8,
106
158
  paddingVertical: 8,
107
159
  paddingHorizontal: 16,
108
160
  alignItems: 'center' as const,
109
161
  justifyContent: 'center' as const,
110
162
  borderRadius: 4,
111
- opacity: disabled ? 0.4 : 1,
112
163
  _web: {
113
164
  display: 'flex',
114
- cursor: disabled ? 'not-allowed' : 'pointer',
165
+ },
166
+ variants: {
167
+ disabled: {
168
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
169
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
170
+ },
115
171
  },
116
172
  }),
117
173
 
@@ -121,7 +177,7 @@ export const dateTimeInputStyles = defineStyle('DateTimeInput', (theme: Theme) =
121
177
  }),
122
178
 
123
179
  // Icon color helper
124
- iconColor: ({ disabled = false }: InputDynamicProps) => ({
125
- color: theme.colors.text.primary,
180
+ iconColor: (_props: InputDynamicProps) => ({
181
+ color: theme.colors.text.secondary,
126
182
  }),
127
183
  }));
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
+ import { useUnistyles } from 'react-native-unistyles';
3
4
  import { mdiClockOutline } from '@mdi/js';
4
5
  import { PositionedPortal } from '@idealyst/components/internal';
5
6
  import { IconSvg } from './IconSvg.web';
@@ -16,6 +17,7 @@ export const TimeInput: React.FC<TimeInputProps> = ({
16
17
  minuteStep = 1,
17
18
  disabled = false,
18
19
  error,
20
+ size = 'md',
19
21
  style,
20
22
  }) => {
21
23
  const [open, setOpen] = useState(false);
@@ -24,14 +26,26 @@ export const TimeInput: React.FC<TimeInputProps> = ({
24
26
 
25
27
  const styles = dateTimeInputStyles;
26
28
 
27
- // Get dynamic styles
29
+ // Apply variants for disabled, error, and size states
30
+ styles.useVariants({
31
+ disabled,
32
+ error: !!error,
33
+ size,
34
+ });
35
+
36
+ // Get theme for icon size and color
37
+ const { theme } = useUnistyles();
38
+ const iconSize = theme.sizes.input[size].iconSize;
39
+ const iconColor = theme.colors.text.secondary;
40
+
41
+ // Get dynamic styles with size variant
28
42
  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 });
43
+ const inputContainerStyle = (styles.inputContainer as any)({ disabled, error: !!error, size });
44
+ const textInputStyle = (styles.textInput as any)({ disabled, size });
45
+ const iconButtonStyle = (styles.iconButton as any)({ disabled, size });
46
+ const iconStyle = (styles.icon as any)({ size });
32
47
  const errorTextStyle = (styles.errorText as any)({});
33
48
  const popoverContentStyle = (styles.popoverContent as any)({});
34
- const iconColorStyle = (styles.iconColor as any)({ disabled });
35
49
 
36
50
  // Format time to string
37
51
  const formatTime = (date: Date | undefined): string => {
@@ -109,13 +123,18 @@ export const TimeInput: React.FC<TimeInputProps> = ({
109
123
  onChange(date);
110
124
  };
111
125
 
112
- // Get web props
126
+ // Get web props for all elements
113
127
  const containerProps = getWebProps([inputContainerStyle]);
128
+ const labelProps = getWebProps([labelTextStyle]);
129
+ const inputProps = getWebProps([textInputStyle]);
130
+ const iconButtonProps = getWebProps([iconButtonStyle]);
131
+ const errorProps = getWebProps([errorTextStyle]);
132
+ const popoverProps = getWebProps([popoverContentStyle]);
114
133
 
115
134
  return (
116
135
  <div style={style as React.CSSProperties}>
117
136
  {label && (
118
- <span style={labelTextStyle}>{label}</span>
137
+ <span {...labelProps}>{label}</span>
119
138
  )}
120
139
  <div {...containerProps} ref={triggerRef}>
121
140
  <input
@@ -125,18 +144,19 @@ export const TimeInput: React.FC<TimeInputProps> = ({
125
144
  onBlur={handleInputBlur}
126
145
  placeholder={placeholder}
127
146
  disabled={disabled}
128
- style={textInputStyle}
147
+ {...inputProps}
129
148
  />
130
149
  <button
131
- style={iconButtonStyle}
150
+ type="button"
151
+ {...iconButtonProps}
132
152
  onClick={() => !disabled && setOpen(!open)}
133
153
  disabled={disabled}
134
154
  >
135
- <IconSvg path={mdiClockOutline} size={18} color={iconColorStyle.color} />
155
+ <IconSvg path={mdiClockOutline} size={iconSize} color={iconColor} />
136
156
  </button>
137
157
  </div>
138
158
  {error && (
139
- <span style={errorTextStyle}>{error}</span>
159
+ <span {...errorProps}>{error}</span>
140
160
  )}
141
161
 
142
162
  <PositionedPortal
@@ -148,7 +168,7 @@ export const TimeInput: React.FC<TimeInputProps> = ({
148
168
  onEscapeKey={() => setOpen(false)}
149
169
  zIndex={9999}
150
170
  >
151
- <div style={popoverContentStyle}>
171
+ <div {...popoverProps}>
152
172
  <TimePicker
153
173
  value={value ?? undefined}
154
174
  onChange={handleTimeChange}
@@ -20,11 +20,16 @@ export type TimePickerDynamicProps = {
20
20
  */
21
21
  export const timePickerStyles = defineStyle('TimePicker', (theme: Theme) => ({
22
22
  // Time picker container
23
- timePicker: ({ disabled = false }: TimePickerDynamicProps) => ({
23
+ timePicker: (_props: TimePickerDynamicProps) => ({
24
24
  padding: 12,
25
25
  backgroundColor: theme.colors.surface.primary,
26
26
  borderRadius: 6,
27
- opacity: disabled ? 0.6 : 1,
27
+ variants: {
28
+ disabled: {
29
+ true: { opacity: 0.6 },
30
+ false: { opacity: 1 },
31
+ },
32
+ },
28
33
  }),
29
34
 
30
35
  // Time columns container
@@ -62,32 +67,51 @@ export const timePickerStyles = defineStyle('TimePicker', (theme: Theme) => ({
62
67
  fontSize: 24,
63
68
  fontWeight: '600' as const,
64
69
  color: theme.colors.text.primary,
70
+ _web: {
71
+ pointerEvents: 'none',
72
+ },
65
73
  }),
66
74
 
67
75
  // Arrow button (up/down)
68
- arrowButton: ({ disabled = false }: TimePickerDynamicProps) => ({
76
+ arrowButton: (_props: TimePickerDynamicProps) => ({
69
77
  width: 32,
70
78
  height: 32,
71
79
  alignItems: 'center' as const,
72
80
  justifyContent: 'center' as const,
73
81
  borderRadius: 4,
74
- opacity: disabled ? 0.4 : 1,
82
+ backgroundColor: 'transparent',
83
+ borderWidth: 0,
75
84
  _web: {
76
85
  display: 'flex',
77
- cursor: disabled ? 'not-allowed' : 'pointer',
86
+ background: 'none',
87
+ border: 'none',
88
+ padding: 0,
89
+ },
90
+ variants: {
91
+ disabled: {
92
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
93
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
94
+ },
78
95
  },
79
96
  }),
80
97
 
81
98
  // Period toggle button (AM/PM)
82
- periodButton: ({ disabled = false }: TimePickerDynamicProps) => ({
99
+ periodButton: (_props: TimePickerDynamicProps) => ({
83
100
  paddingVertical: 6,
84
101
  paddingHorizontal: 12,
85
102
  borderWidth: 1,
103
+ borderStyle: 'solid' as const,
86
104
  borderColor: theme.colors.border.primary,
87
105
  borderRadius: 4,
88
- opacity: disabled ? 0.4 : 1,
106
+ backgroundColor: 'transparent',
89
107
  _web: {
90
- cursor: disabled ? 'not-allowed' : 'pointer',
108
+ background: 'none',
109
+ },
110
+ variants: {
111
+ disabled: {
112
+ true: { opacity: 0.4, _web: { cursor: 'not-allowed' } },
113
+ false: { opacity: 1, _web: { cursor: 'pointer' } },
114
+ },
91
115
  },
92
116
  }),
93
117
 
@@ -95,6 +119,9 @@ export const timePickerStyles = defineStyle('TimePicker', (theme: Theme) => ({
95
119
  fontSize: 14,
96
120
  fontWeight: '500' as const,
97
121
  color: theme.colors.text.primary,
122
+ _web: {
123
+ pointerEvents: 'none',
124
+ },
98
125
  }),
99
126
 
100
127
  // Icon color helper
@@ -15,6 +15,11 @@ export const TimePicker: React.FC<TimePickerProps> = ({
15
15
  }) => {
16
16
  const styles = timePickerStyles;
17
17
 
18
+ // Apply variants for disabled state
19
+ styles.useVariants({
20
+ disabled,
21
+ });
22
+
18
23
  const currentDate = value || new Date();
19
24
  const hours = currentDate.getHours();
20
25
  const minutes = currentDate.getMinutes();
@@ -67,10 +72,16 @@ export const TimePicker: React.FC<TimePickerProps> = ({
67
72
  const periodButtonTextStyle = (styles.periodButtonText as any)({});
68
73
  const iconColorStyle = (styles.iconColor as any)({});
69
74
 
70
- // Get web props
75
+ // Get web props for all elements
71
76
  const timePickerProps = getWebProps([timePickerStyle, style as any]);
72
77
  const timeColumnsProps = getWebProps([timeColumnsStyle]);
73
78
  const timeColumnProps = getWebProps([timeColumnStyle]);
79
+ const timeSeparatorProps = getWebProps([timeSeparatorStyle]);
80
+ const separatorTextProps = getWebProps([separatorTextStyle]);
81
+ const timeValueProps = getWebProps([timeValueStyle]);
82
+ const arrowButtonProps = getWebProps([arrowButtonStyle]);
83
+ const periodButtonProps = getWebProps([periodButtonStyle]);
84
+ const periodButtonTextProps = getWebProps([periodButtonTextStyle]);
74
85
 
75
86
  return (
76
87
  <div {...timePickerProps}>
@@ -78,17 +89,19 @@ export const TimePicker: React.FC<TimePickerProps> = ({
78
89
  {/* Hours column */}
79
90
  <div {...timeColumnProps}>
80
91
  <button
81
- style={arrowButtonStyle}
92
+ type="button"
93
+ {...arrowButtonProps}
82
94
  onClick={incrementHours}
83
95
  disabled={disabled}
84
96
  >
85
97
  <IconSvg path={mdiChevronUp} size={20} color={iconColorStyle.color} />
86
98
  </button>
87
- <span style={timeValueStyle}>
99
+ <span {...timeValueProps}>
88
100
  {String(displayHours).padStart(2, '0')}
89
101
  </span>
90
102
  <button
91
- style={arrowButtonStyle}
103
+ type="button"
104
+ {...arrowButtonProps}
92
105
  onClick={decrementHours}
93
106
  disabled={disabled}
94
107
  >
@@ -97,24 +110,26 @@ export const TimePicker: React.FC<TimePickerProps> = ({
97
110
  </div>
98
111
 
99
112
  {/* Separator */}
100
- <div style={timeSeparatorStyle}>
101
- <span style={separatorTextStyle}>:</span>
113
+ <div {...timeSeparatorProps}>
114
+ <span {...separatorTextProps}>:</span>
102
115
  </div>
103
116
 
104
117
  {/* Minutes column */}
105
118
  <div {...timeColumnProps}>
106
119
  <button
107
- style={arrowButtonStyle}
120
+ type="button"
121
+ {...arrowButtonProps}
108
122
  onClick={incrementMinutes}
109
123
  disabled={disabled}
110
124
  >
111
125
  <IconSvg path={mdiChevronUp} size={20} color={iconColorStyle.color} />
112
126
  </button>
113
- <span style={timeValueStyle}>
127
+ <span {...timeValueProps}>
114
128
  {String(minutes).padStart(2, '0')}
115
129
  </span>
116
130
  <button
117
- style={arrowButtonStyle}
131
+ type="button"
132
+ {...arrowButtonProps}
118
133
  onClick={decrementMinutes}
119
134
  disabled={disabled}
120
135
  >
@@ -126,11 +141,12 @@ export const TimePicker: React.FC<TimePickerProps> = ({
126
141
  {is12Hour && (
127
142
  <div {...timeColumnProps}>
128
143
  <button
129
- style={periodButtonStyle}
144
+ type="button"
145
+ {...periodButtonProps}
130
146
  onClick={togglePeriod}
131
147
  disabled={disabled}
132
148
  >
133
- <span style={periodButtonTextStyle}>
149
+ <span {...periodButtonTextProps}>
134
150
  {isPM ? 'PM' : 'AM'}
135
151
  </span>
136
152
  </button>
@@ -79,8 +79,8 @@ export const DatePickerExamples = () => {
79
79
  <View gap="md">
80
80
  <Text typography="h4" weight="semibold">With Min/Max Date</Text>
81
81
  <DateInput
82
- value={undefined}
83
- onChange={() => {}}
82
+ value={date ?? undefined}
83
+ onChange={setDate}
84
84
  label="Future Dates Only"
85
85
  placeholder="MM/DD/YYYY"
86
86
  minDate={tomorrow}
@@ -91,6 +91,129 @@ export const DatePickerExamples = () => {
91
91
  </Text>
92
92
  </View>
93
93
 
94
+ {/* Size Variants */}
95
+ <View gap="md">
96
+ <Text typography="h4" weight="semibold">Size Variants</Text>
97
+ <Text typography="caption" color="secondary">
98
+ DateInput, TimeInput, and DateTimePicker support different sizes
99
+ </Text>
100
+
101
+ <View gap="sm">
102
+ <Text typography="body2" weight="medium">Extra Small (xs)</Text>
103
+ <View gap="xs">
104
+ <DateInput
105
+ value={date ?? undefined}
106
+ onChange={setDate}
107
+ placeholder="MM/DD/YYYY"
108
+ size="xs"
109
+ />
110
+ <TimeInput
111
+ value={time ?? undefined}
112
+ onChange={setTime}
113
+ placeholder="12:00 PM"
114
+ size="xs"
115
+ />
116
+ </View>
117
+ </View>
118
+
119
+ <View gap="sm">
120
+ <Text typography="body2" weight="medium">Small (sm)</Text>
121
+ <View gap="xs">
122
+ <DateInput
123
+ value={date ?? undefined}
124
+ onChange={setDate}
125
+ placeholder="MM/DD/YYYY"
126
+ size="sm"
127
+ />
128
+ <TimeInput
129
+ value={time ?? undefined}
130
+ onChange={setTime}
131
+ placeholder="12:00 PM"
132
+ size="sm"
133
+ />
134
+ </View>
135
+ </View>
136
+
137
+ <View gap="sm">
138
+ <Text typography="body2" weight="medium">Medium (md) - Default</Text>
139
+ <View gap="xs">
140
+ <DateInput
141
+ value={date ?? undefined}
142
+ onChange={setDate}
143
+ placeholder="MM/DD/YYYY"
144
+ size="md"
145
+ />
146
+ <TimeInput
147
+ value={time ?? undefined}
148
+ onChange={setTime}
149
+ placeholder="12:00 PM"
150
+ size="md"
151
+ />
152
+ </View>
153
+ </View>
154
+
155
+ <View gap="sm">
156
+ <Text typography="body2" weight="medium">Large (lg)</Text>
157
+ <View gap="xs">
158
+ <DateInput
159
+ value={date ?? undefined}
160
+ onChange={setDate}
161
+ placeholder="MM/DD/YYYY"
162
+ size="lg"
163
+ />
164
+ <TimeInput
165
+ value={time ?? undefined}
166
+ onChange={setTime}
167
+ placeholder="12:00 PM"
168
+ size="lg"
169
+ />
170
+ </View>
171
+ </View>
172
+
173
+ <View gap="sm">
174
+ <Text typography="body2" weight="medium">Extra Large (xl)</Text>
175
+ <View gap="xs">
176
+ <DateInput
177
+ value={date ?? undefined}
178
+ onChange={setDate}
179
+ placeholder="MM/DD/YYYY"
180
+ size="xl"
181
+ />
182
+ <TimeInput
183
+ value={time ?? undefined}
184
+ onChange={setTime}
185
+ placeholder="12:00 PM"
186
+ size="xl"
187
+ />
188
+ </View>
189
+ </View>
190
+ </View>
191
+
192
+ {/* DateTimePicker Sizes */}
193
+ <View gap="md">
194
+ <Text typography="h4" weight="semibold">DateTimePicker Sizes</Text>
195
+ <View gap="sm">
196
+ <DateTimePicker
197
+ value={dateTime ?? undefined}
198
+ onChange={setDateTime}
199
+ label="Small"
200
+ size="sm"
201
+ />
202
+ <DateTimePicker
203
+ value={dateTime ?? undefined}
204
+ onChange={setDateTime}
205
+ label="Medium (Default)"
206
+ size="md"
207
+ />
208
+ <DateTimePicker
209
+ value={dateTime ?? undefined}
210
+ onChange={setDateTime}
211
+ label="Large"
212
+ size="lg"
213
+ />
214
+ </View>
215
+ </View>
216
+
94
217
  {/* Inline DatePicker */}
95
218
  <View gap="md">
96
219
  <Text typography="h4" weight="semibold">Inline DatePicker</Text>
@@ -120,8 +243,8 @@ export const DatePickerExamples = () => {
120
243
  <View gap="md">
121
244
  <Text typography="h4" weight="semibold">24-hour Format</Text>
122
245
  <TimeInput
123
- value={undefined}
124
- onChange={() => {}}
246
+ value={time ?? undefined}
247
+ onChange={setTime}
125
248
  label="24-hour Time"
126
249
  placeholder="14:30"
127
250
  mode="24h"
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ViewStyle } from 'react-native';
2
+ import type { Size } from '@idealyst/theme';
2
3
 
3
4
  export interface DatePickerProps {
4
5
  value?: Date;
@@ -27,6 +28,7 @@ export interface DateInputProps {
27
28
  maxDate?: Date;
28
29
  disabled?: boolean;
29
30
  error?: string;
31
+ size?: Size;
30
32
  style?: ViewStyle;
31
33
  }
32
34
 
@@ -39,6 +41,7 @@ export interface TimeInputProps {
39
41
  minuteStep?: number;
40
42
  disabled?: boolean;
41
43
  error?: string;
44
+ size?: Size;
42
45
  style?: ViewStyle;
43
46
  }
44
47
 
@@ -52,5 +55,6 @@ export interface DateTimePickerProps {
52
55
  minuteStep?: number;
53
56
  disabled?: boolean;
54
57
  error?: string;
58
+ size?: Size;
55
59
  style?: ViewStyle;
56
60
  }