@campxdev/react-blueprint 3.0.0-alpha.3 → 3.0.0-alpha.5

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.
Files changed (60) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/types/src/components/Input/DatePicker/DatePicker.d.ts +2 -1
  3. package/dist/cjs/types/src/components/Input/DatePicker/components/DatePickerFilter.d.ts +2 -0
  4. package/dist/cjs/types/src/components/Input/DatePicker/components/DatePickerInput.d.ts +2 -0
  5. package/dist/cjs/types/src/components/Input/DateTimePicker/DateTimePicker.d.ts +2 -1
  6. package/dist/cjs/types/src/components/Input/DateTimePicker/components/DateTimePickerFilter.d.ts +2 -0
  7. package/dist/cjs/types/src/components/Input/DateTimePicker/components/DateTimePickerInput.d.ts +2 -0
  8. package/dist/cjs/types/src/components/Input/MultiSelect/MultiSelect.d.ts +2 -0
  9. package/dist/cjs/types/src/components/Input/MultiSelect/components/MultiSelectFilter.d.ts +1 -1
  10. package/dist/cjs/types/src/components/Input/MultiSelect/components/MultiSelectInput.d.ts +1 -1
  11. package/dist/cjs/types/src/components/Input/Select/Select.d.ts +2 -1
  12. package/dist/cjs/types/src/components/Input/SingleSelect/SingleSelect.d.ts +2 -0
  13. package/dist/cjs/types/src/components/Input/SingleSelect/components/SingleFilter.d.ts +1 -1
  14. package/dist/cjs/types/src/components/Input/SingleSelect/components/SingleInput.d.ts +1 -1
  15. package/dist/cjs/types/src/shadcn-components/Input/Select/Select.d.ts +2 -2
  16. package/dist/esm/index.js +2 -2
  17. package/dist/esm/types/src/components/Input/DatePicker/DatePicker.d.ts +2 -1
  18. package/dist/esm/types/src/components/Input/DatePicker/components/DatePickerFilter.d.ts +2 -0
  19. package/dist/esm/types/src/components/Input/DatePicker/components/DatePickerInput.d.ts +2 -0
  20. package/dist/esm/types/src/components/Input/DateTimePicker/DateTimePicker.d.ts +2 -1
  21. package/dist/esm/types/src/components/Input/DateTimePicker/components/DateTimePickerFilter.d.ts +2 -0
  22. package/dist/esm/types/src/components/Input/DateTimePicker/components/DateTimePickerInput.d.ts +2 -0
  23. package/dist/esm/types/src/components/Input/MultiSelect/MultiSelect.d.ts +2 -0
  24. package/dist/esm/types/src/components/Input/MultiSelect/components/MultiSelectFilter.d.ts +1 -1
  25. package/dist/esm/types/src/components/Input/MultiSelect/components/MultiSelectInput.d.ts +1 -1
  26. package/dist/esm/types/src/components/Input/Select/Select.d.ts +2 -1
  27. package/dist/esm/types/src/components/Input/SingleSelect/SingleSelect.d.ts +2 -0
  28. package/dist/esm/types/src/components/Input/SingleSelect/components/SingleFilter.d.ts +1 -1
  29. package/dist/esm/types/src/components/Input/SingleSelect/components/SingleInput.d.ts +1 -1
  30. package/dist/esm/types/src/shadcn-components/Input/Select/Select.d.ts +2 -2
  31. package/dist/index.d.ts +13 -6
  32. package/dist/styles.css +372 -4
  33. package/package.json +1 -1
  34. package/src/components/DataDisplay/DataTable/components/TableView.tsx +31 -5
  35. package/src/components/Feedback/Tooltip/Tooltip.tsx +17 -3
  36. package/src/components/Input/Button/ButtonLoader.css +2 -2
  37. package/src/components/Input/DatePicker/DatePicker.tsx +9 -188
  38. package/src/components/Input/DatePicker/components/DatePickerFilter.tsx +178 -0
  39. package/src/components/Input/DatePicker/components/DatePickerInput.tsx +192 -0
  40. package/src/components/Input/DateTimePicker/DateTimePicker.tsx +8 -294
  41. package/src/components/Input/DateTimePicker/components/DateTimePickerFilter.tsx +292 -0
  42. package/src/components/Input/DateTimePicker/components/DateTimePickerInput.tsx +297 -0
  43. package/src/components/Input/MultiSelect/MultiSelect.tsx +2 -0
  44. package/src/components/Input/MultiSelect/components/MultiSelectFilter.tsx +7 -3
  45. package/src/components/Input/MultiSelect/components/MultiSelectInput.tsx +8 -3
  46. package/src/components/Input/Select/Select.tsx +22 -12
  47. package/src/components/Input/SingleSelect/SingleSelect.tsx +2 -0
  48. package/src/components/Input/SingleSelect/components/SingleFilter.tsx +7 -3
  49. package/src/components/Input/SingleSelect/components/SingleInput.tsx +8 -3
  50. package/src/components/Layout/AppLayout/components/Sidebar/MenuItem.tsx +2 -2
  51. package/src/components/Navigation/Breadcrumbs/Breadcrumbs.tsx +1 -1
  52. package/src/components/Navigation/DialogButton/DialogButton.tsx +6 -1
  53. package/src/components/Navigation/DropDownMenu/DropDownMenu.tsx +1 -1
  54. package/src/components/Navigation/TabsContainer/TabsContainer.tsx +1 -1
  55. package/src/shadcn-components/DataDisplay/Dialog/Dialog.tsx +2 -2
  56. package/src/shadcn-components/Input/Popover/Popover.tsx +1 -1
  57. package/src/shadcn-components/Input/Select/Select.tsx +8 -8
  58. package/src/shadcn-components/Navigation/DropdownMenu/DropdownMenu.tsx +2 -2
  59. package/src/styles/globals.css +4 -2
  60. package/src/styles/index.css +5 -0
@@ -1,17 +1,5 @@
1
- import { cn } from '@/lib/utils';
2
- import { Calendar } from '@/shadcn-components/Input/Calendar/Calendar';
3
- import { Input } from '@/shadcn-components/Input/Input/Input';
4
- import {
5
- Popover,
6
- PopoverContent,
7
- PopoverTrigger,
8
- } from '@/shadcn-components/Input/Popover/Popover';
9
- import { format as DateFnsFormat } from 'date-fns';
10
- import { CalendarDays } from 'lucide-react';
11
- import { cloneElement, useEffect, useMemo, useRef, useState } from 'react';
12
- import { Typography } from '../../DataDisplay/Typography/Typography';
13
- import { Button } from '../Button/Button';
14
- import { LabelWrapper } from '../LabelWrapper/LabelWrapper';
1
+ import { DateTimePickerFilter } from './components/DateTimePickerFilter';
2
+ import { DateTimePickerInput } from './components/DateTimePickerInput';
15
3
 
16
4
  type DateTimePickerViews =
17
5
  | 'year'
@@ -69,291 +57,17 @@ export type DateTimePickerProps = {
69
57
  onClose?: () => void;
70
58
  onBlur?: React.FocusEventHandler;
71
59
  fullWidth?: boolean;
60
+ type?: 'input' | 'filter';
72
61
  [key: string]: any;
73
62
  };
74
63
 
75
64
  export const DateTimePicker = ({
76
- label,
77
- name,
78
- value,
79
- onChange,
80
- required = false,
81
- format = 'dd MMM yyyy hh:mm a',
82
- views = ['year', 'month', 'day', 'hours', 'minutes', 'seconds'],
83
- helperText,
84
- placeholder = 'Pick a date and time',
85
- shortcutsItems = [],
86
- openPickerIcon: Icon = <CalendarDays />,
87
- containerProps,
88
- error,
89
- disabled = false,
90
- minDate,
91
- maxDate,
92
- disablePast = false,
93
- disableFuture = false,
94
- shouldDisableDate,
95
- shouldDisableMonth,
96
- shouldDisableYear,
97
- className,
98
- onOpen,
99
- onClose,
100
- fullWidth,
101
- ...rest
65
+ type = 'input',
66
+ ...props
102
67
  }: DateTimePickerProps) => {
103
- const [open, setOpen] = useState(false);
104
- const [dateTime, setDateTime] = useState<Date | undefined>(value);
105
- const [timeValue, setTimeValue] = useState<string>('');
106
- const wrapperRef = useRef<HTMLSpanElement>(null);
107
-
108
- // Determine view configurations based on views prop
109
- const viewConfig = useMemo(() => {
110
- const hasTime =
111
- views.includes('hours') ||
112
- views.includes('minutes') ||
113
- views.includes('seconds');
114
- const hasDate =
115
- views.includes('day') ||
116
- views.includes('month') ||
117
- views.includes('year');
118
-
119
- return {
120
- showTime: hasTime,
121
- showDate: hasDate,
122
- };
123
- }, [views]);
124
-
125
- // Update time value when dateTime changes
126
- useEffect(() => {
127
- if (dateTime && viewConfig.showTime) {
128
- const hours = dateTime.getHours(),
129
- minutes = dateTime.getMinutes(),
130
- seconds = dateTime.getSeconds(),
131
- timeStr = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(
132
- 2,
133
- '0',
134
- )}:${String(seconds).padStart(2, '0')}`;
135
- setTimeValue(timeStr);
136
- }
137
- }, [dateTime, viewConfig.showTime]);
138
-
139
- const formatDateTimeString = (date: Date | undefined) => {
140
- if (!date) return '';
141
-
142
- try {
143
- return DateFnsFormat(date, format);
144
- } catch {
145
- return DateFnsFormat(date, 'dd/MM/yyyy hh:mm a');
146
- }
147
- };
148
-
149
- const handleOpenChange = (newOpen: boolean) => {
150
- setOpen(newOpen);
151
- if (newOpen && onOpen) {
152
- onOpen();
153
- } else if (!newOpen && onClose) {
154
- onClose();
155
- }
156
- };
157
-
158
- const handleDateSelect = (date: Date | undefined) => {
159
- if (date) {
160
- const newDateTime = new Date(date);
161
- // Preserve existing time if we have one
162
- if (dateTime) {
163
- newDateTime.setHours(
164
- dateTime.getHours(),
165
- dateTime.getMinutes(),
166
- dateTime.getSeconds(),
167
- );
168
- }
169
- setDateTime(newDateTime);
170
- onChange?.(newDateTime);
171
- } else {
172
- setDateTime(undefined);
173
- onChange?.(undefined);
174
- }
175
- };
176
-
177
- const handleTimeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
178
- const timeStr = e.target.value;
179
- setTimeValue(timeStr);
180
-
181
- if (timeStr) {
182
- const [hours, minutes, seconds] = timeStr.split(':').map(Number);
183
- const newDateTime = dateTime ? new Date(dateTime) : new Date();
184
- newDateTime.setHours(hours || 0, minutes || 0, seconds || 0);
185
- setDateTime(newDateTime);
186
- onChange?.(newDateTime);
187
- }
188
- };
189
-
190
- const [, forceUpdate] = useState({});
191
- useEffect(() => {
192
- forceUpdate({});
193
- }, []);
194
-
195
- // If only time selection (no date), render native time input directly
196
- if (viewConfig.showTime && !viewConfig.showDate) {
197
- return (
198
- <LabelWrapper
199
- label={label}
200
- required={required}
201
- name={name}
202
- containerProps={containerProps}
203
- >
204
- <Input
205
- type="time"
206
- step="1"
207
- value={timeValue}
208
- onChange={handleTimeChange}
209
- disabled={disabled}
210
- placeholder={placeholder}
211
- className={cn(
212
- 'bg-input-background border-none text-muted-foreground',
213
- error && 'border-destructive',
214
- className,
215
- )}
216
- {...rest}
217
- />
218
-
219
- {/* Helper Text / Error */}
220
- {(helperText || error) && (
221
- <Typography
222
- variant="small"
223
- className={cn('ml-1 mt-1', error && 'text-destructive')}
224
- >
225
- {typeof error === 'string' ? error : error?.message || helperText}
226
- </Typography>
227
- )}
228
- </LabelWrapper>
229
- );
68
+ if (type === 'filter') {
69
+ return <DateTimePickerFilter {...props} />;
230
70
  }
231
71
 
232
- return (
233
- <LabelWrapper
234
- label={label}
235
- required={required}
236
- name={name}
237
- containerProps={{
238
- ...(fullWidth && { style: { width: '100%' } }),
239
- ...containerProps,
240
- }}
241
- >
242
- <Popover open={open} onOpenChange={handleOpenChange}>
243
- <PopoverTrigger asChild>
244
- <span
245
- ref={wrapperRef}
246
- style={{ display: fullWidth ? 'block' : 'inline-block' }}
247
- >
248
- <Button
249
- variant="input"
250
- className={cn(
251
- 'w-full justify-between text-left font-normal',
252
- !dateTime && 'text-muted-foreground',
253
- error && 'border-destructive',
254
- fullWidth ? 'w-full' : 'w-auto',
255
- className,
256
- )}
257
- disabled={disabled}
258
- {...rest}
259
- >
260
- <span>
261
- {dateTime ? formatDateTimeString(dateTime) : placeholder}
262
- </span>
263
- {Icon &&
264
- cloneElement(Icon as React.ReactElement, {
265
- className: 'ml-2 h-4 w-4 ',
266
- })}
267
- </Button>
268
- </span>
269
- </PopoverTrigger>
270
- <PopoverContent className="w-auto p-0" align="start">
271
- <div className="flex flex-col">
272
- {/* Shortcuts */}
273
- {shortcutsItems.length > 0 && (
274
- <div className="border-b p-2">
275
- <div className="flex flex-wrap gap-1">
276
- {shortcutsItems.map((shortcut, index) => (
277
- <Button
278
- key={index}
279
- variant="outline"
280
- size="sm"
281
- onClick={() => {
282
- const newDate = shortcut.getValue();
283
- onChange?.(newDate);
284
- setDateTime(newDate);
285
- setOpen(false);
286
- }}
287
- >
288
- {shortcut.label}
289
- </Button>
290
- ))}
291
- </div>
292
- </div>
293
- )}
294
-
295
- {/* Calendar */}
296
- {viewConfig.showDate && (
297
- <Calendar
298
- mode="single"
299
- selected={dateTime}
300
- onSelect={handleDateSelect}
301
- disabled={(date) => {
302
- const today = new Date();
303
- today.setHours(0, 0, 0, 0);
304
- const compareDate = new Date(date);
305
- compareDate.setHours(0, 0, 0, 0);
306
-
307
- if (minDate) {
308
- const min = new Date(minDate);
309
- min.setHours(0, 0, 0, 0);
310
- if (compareDate < min) return true;
311
- }
312
- if (maxDate) {
313
- const max = new Date(maxDate);
314
- max.setHours(0, 0, 0, 0);
315
- if (compareDate > max) return true;
316
- }
317
-
318
- if (disablePast && compareDate < today) return true;
319
- if (disableFuture && compareDate > today) return true;
320
- if (shouldDisableDate && shouldDisableDate(date)) return true;
321
- if (shouldDisableMonth && shouldDisableMonth(date))
322
- return true;
323
- if (shouldDisableYear && shouldDisableYear(date)) return true;
324
-
325
- return false;
326
- }}
327
- captionLayout={'dropdown'}
328
- defaultMonth={dateTime}
329
- />
330
- )}
331
-
332
- {/* Time Picker */}
333
- {viewConfig.showTime && (
334
- <div className={cn('p-3', viewConfig.showDate && 'border-t')}>
335
- <Input
336
- type="time"
337
- step="1"
338
- value={timeValue}
339
- onChange={handleTimeChange}
340
- className="bg-input-background border-none text-muted-foreground appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
341
- />
342
- </div>
343
- )}
344
- </div>
345
- </PopoverContent>
346
- </Popover>
347
-
348
- {/* Helper Text / Error */}
349
- {(helperText || error) && (
350
- <Typography
351
- variant="small"
352
- className={cn('ml-1 mt-1', error && 'text-destructive')}
353
- >
354
- {typeof error === 'string' ? error : error?.message || helperText}
355
- </Typography>
356
- )}
357
- </LabelWrapper>
358
- );
72
+ return <DateTimePickerInput {...props} />;
359
73
  };
@@ -0,0 +1,292 @@
1
+ import { Separator } from '@/components/DataDisplay/Separator/Separator';
2
+ import { cn } from '@/lib/utils';
3
+ import { Calendar } from '@/shadcn-components/Input/Calendar/Calendar';
4
+ import { Input } from '@/shadcn-components/Input/Input/Input';
5
+ import {
6
+ Popover,
7
+ PopoverContent,
8
+ PopoverTrigger,
9
+ } from '@/shadcn-components/Input/Popover/Popover';
10
+ import { format as DateFnsFormat } from 'date-fns';
11
+ import { CalendarDays } from 'lucide-react';
12
+ import { useEffect, useMemo, useRef, useState } from 'react';
13
+ import { Button } from '../../Button/Button';
14
+ import { DateTimePickerProps } from '../DateTimePicker';
15
+
16
+ export const DateTimePickerFilter = ({
17
+ label,
18
+ value,
19
+ onChange,
20
+ format = 'dd MMM yyyy hh:mm a',
21
+ views = ['year', 'month', 'day', 'hours', 'minutes', 'seconds'],
22
+ placeholder = 'Pick a date and time',
23
+ shortcutsItems = [],
24
+ disabled = false,
25
+ minDate,
26
+ maxDate,
27
+ disablePast = false,
28
+ disableFuture = false,
29
+ shouldDisableDate,
30
+ shouldDisableMonth,
31
+ shouldDisableYear,
32
+ className,
33
+ onOpen,
34
+ onClose,
35
+ fullWidth,
36
+ type,
37
+ ...rest
38
+ }: DateTimePickerProps) => {
39
+ const [open, setOpen] = useState(false);
40
+ const [timeValue, setTimeValue] = useState<string>('');
41
+ const wrapperRef = useRef<HTMLSpanElement>(null);
42
+
43
+ // Determine view configurations based on views prop
44
+ const viewConfig = useMemo(() => {
45
+ const hasTime =
46
+ views.includes('hours') ||
47
+ views.includes('minutes') ||
48
+ views.includes('seconds');
49
+ const hasDate =
50
+ views.includes('day') ||
51
+ views.includes('month') ||
52
+ views.includes('year');
53
+
54
+ return {
55
+ showTime: hasTime,
56
+ showDate: hasDate,
57
+ };
58
+ }, [views]);
59
+
60
+ // Update time value when value changes
61
+ useEffect(() => {
62
+ if (value && viewConfig.showTime) {
63
+ const hours = value.getHours(),
64
+ minutes = value.getMinutes(),
65
+ seconds = value.getSeconds(),
66
+ timeStr = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(
67
+ 2,
68
+ '0',
69
+ )}:${String(seconds).padStart(2, '0')}`;
70
+ setTimeValue(timeStr);
71
+ }
72
+ }, [value, viewConfig.showTime]);
73
+
74
+ const formatDateTimeString = (date: Date | undefined) => {
75
+ if (!date) return '';
76
+
77
+ try {
78
+ return DateFnsFormat(date, format);
79
+ } catch {
80
+ return DateFnsFormat(date, 'dd/MM/yyyy hh:mm a');
81
+ }
82
+ };
83
+
84
+ const handleOpenChange = (newOpen: boolean) => {
85
+ setOpen(newOpen);
86
+ if (newOpen && onOpen) {
87
+ onOpen();
88
+ } else if (!newOpen && onClose) {
89
+ onClose();
90
+ }
91
+ };
92
+
93
+ const handleDateSelect = (date: Date | undefined) => {
94
+ if (date) {
95
+ const newDateTime = new Date(date);
96
+ // Preserve existing time if we have one
97
+ if (value) {
98
+ newDateTime.setHours(
99
+ value.getHours(),
100
+ value.getMinutes(),
101
+ value.getSeconds(),
102
+ );
103
+ }
104
+ onChange?.(newDateTime);
105
+ } else {
106
+ onChange?.(undefined);
107
+ }
108
+ };
109
+
110
+ const handleTimeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
111
+ const timeStr = e.target.value;
112
+ setTimeValue(timeStr);
113
+
114
+ if (timeStr) {
115
+ const [hours, minutes, seconds] = timeStr.split(':').map(Number);
116
+ const newDateTime = value ? new Date(value) : new Date();
117
+ newDateTime.setHours(hours || 0, minutes || 0, seconds || 0);
118
+ onChange?.(newDateTime);
119
+ }
120
+ };
121
+
122
+ const handleClear = () => {
123
+ setTimeValue('');
124
+ onChange?.(undefined);
125
+ setOpen(false);
126
+ };
127
+
128
+ // Force re-render on mount to ensure positioning works
129
+ const [, forceUpdate] = useState({});
130
+ useEffect(() => {
131
+ forceUpdate({});
132
+ }, []);
133
+
134
+ const hasValue = !!value;
135
+ const displayText = hasValue
136
+ ? `${label} = ${formatDateTimeString(value)}`
137
+ : label;
138
+
139
+ // If only time selection (no date), render simplified filter for time only
140
+ if (viewConfig.showTime && !viewConfig.showDate) {
141
+ return (
142
+ <Popover open={open} onOpenChange={handleOpenChange}>
143
+ <PopoverTrigger asChild>
144
+ <span
145
+ ref={wrapperRef}
146
+ style={{ display: fullWidth ? 'block' : 'inline-block' }}
147
+ >
148
+ <Button
149
+ variant={hasValue ? 'default' : 'input'}
150
+ className={cn(
151
+ 'justify-between',
152
+ fullWidth ? 'w-full' : '',
153
+ className,
154
+ )}
155
+ disabled={disabled}
156
+ {...rest}
157
+ >
158
+ {displayText || placeholder}
159
+ <CalendarDays className="ml-2 h-4 w-4 shrink-0 opacity-50" />
160
+ </Button>
161
+ </span>
162
+ </PopoverTrigger>
163
+ <PopoverContent className="w-auto p-0" align="start">
164
+ <div className="flex flex-col">
165
+ <div className="p-3">
166
+ <Input
167
+ type="time"
168
+ step="1"
169
+ value={timeValue}
170
+ onChange={handleTimeChange}
171
+ className="bg-input-background border-none text-muted-foreground appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
172
+ />
173
+ </div>
174
+ <Separator />
175
+ <div className="flex flex-row items-center justify-left">
176
+ <Button variant="link" onClick={handleClear} className="min-w-0">
177
+ Clear
178
+ </Button>
179
+ </div>
180
+ </div>
181
+ </PopoverContent>
182
+ </Popover>
183
+ );
184
+ }
185
+
186
+ return (
187
+ <Popover open={open} onOpenChange={handleOpenChange}>
188
+ <PopoverTrigger asChild>
189
+ <span
190
+ ref={wrapperRef}
191
+ style={{ display: fullWidth ? 'block' : 'inline-block' }}
192
+ >
193
+ <Button
194
+ variant={hasValue ? 'default' : 'input'}
195
+ className={cn(
196
+ 'justify-between',
197
+ fullWidth ? 'w-full' : '',
198
+ className,
199
+ )}
200
+ disabled={disabled}
201
+ {...rest}
202
+ >
203
+ {displayText || placeholder}
204
+ <CalendarDays className="ml-2 h-4 w-4 shrink-0 opacity-50" />
205
+ </Button>
206
+ </span>
207
+ </PopoverTrigger>
208
+ <PopoverContent className="w-auto p-0" align="start">
209
+ <div className="flex flex-col">
210
+ {/* Shortcuts */}
211
+ {shortcutsItems.length > 0 && (
212
+ <div className="border-b p-2">
213
+ <div className="flex flex-wrap gap-1">
214
+ {shortcutsItems.map((shortcut, index) => (
215
+ <Button
216
+ key={index}
217
+ variant="outline"
218
+ size="sm"
219
+ onClick={() => {
220
+ const newDate = shortcut.getValue();
221
+ onChange?.(newDate);
222
+ setOpen(false);
223
+ }}
224
+ >
225
+ {shortcut.label}
226
+ </Button>
227
+ ))}
228
+ </div>
229
+ </div>
230
+ )}
231
+
232
+ {/* Calendar */}
233
+ {viewConfig.showDate && (
234
+ <Calendar
235
+ mode="single"
236
+ selected={value}
237
+ onSelect={handleDateSelect}
238
+ disabled={(date) => {
239
+ const today = new Date();
240
+ today.setHours(0, 0, 0, 0);
241
+ const compareDate = new Date(date);
242
+ compareDate.setHours(0, 0, 0, 0);
243
+
244
+ if (minDate) {
245
+ const min = new Date(minDate);
246
+ min.setHours(0, 0, 0, 0);
247
+ if (compareDate < min) return true;
248
+ }
249
+ if (maxDate) {
250
+ const max = new Date(maxDate);
251
+ max.setHours(0, 0, 0, 0);
252
+ if (compareDate > max) return true;
253
+ }
254
+
255
+ if (disablePast && compareDate < today) return true;
256
+ if (disableFuture && compareDate > today) return true;
257
+ if (shouldDisableDate && shouldDisableDate(date)) return true;
258
+ if (shouldDisableMonth && shouldDisableMonth(date)) return true;
259
+ if (shouldDisableYear && shouldDisableYear(date)) return true;
260
+
261
+ return false;
262
+ }}
263
+ captionLayout={'dropdown'}
264
+ defaultMonth={value}
265
+ />
266
+ )}
267
+
268
+ {/* Time Picker */}
269
+ {viewConfig.showTime && (
270
+ <div className={cn('p-3', viewConfig.showDate && 'border-t')}>
271
+ <Input
272
+ type="time"
273
+ step="1"
274
+ value={timeValue}
275
+ onChange={handleTimeChange}
276
+ className="bg-input-background border-none text-muted-foreground appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
277
+ />
278
+ </div>
279
+ )}
280
+
281
+ {/* Clear Button */}
282
+ <Separator />
283
+ <div className="flex flex-row items-center justify-left">
284
+ <Button variant="link" onClick={handleClear} className="min-w-0">
285
+ Clear
286
+ </Button>
287
+ </div>
288
+ </div>
289
+ </PopoverContent>
290
+ </Popover>
291
+ );
292
+ };