1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
'use client';
/* IMPORT */
import { format } from 'date-fns';
import { Button, type ButtonProps } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import { CalendarIcon, X } from 'lucide-react';
import { cn } from '@/lib/utils';
import { parseAsString, useQueryStates } from 'nuqs';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { type ComponentPropsWithoutRef, type MouseEvent, useMemo } from 'react';
import { type DateRange } from 'react-day-picker';
// ----------------------------------------------------------------------------------------------------
/* TYPES */
interface RisksDateRangePickerProps extends ComponentPropsWithoutRef<typeof PopoverContent> {
defaultDateRange?: DateRange;
placeholder?: string;
triggerVariant?: Exclude<ButtonProps['variant'], 'destructive' | 'link'>;
triggerSize?: Exclude<ButtonProps['size'], 'icon'>;
triggerClassName?: string;
shallow?: boolean;
showClearButton?: boolean;
}
// ----------------------------------------------------------------------------------------------------
/* RISKS DATE RANGE PICKER COMPONENT */
function RisksDateRangePicker(props: RisksDateRangePickerProps) {
const {
defaultDateRange,
placeholder = '날짜를 선택하십시오.',
triggerVariant = 'outline',
triggerSize = 'default',
triggerClassName,
showClearButton = false,
shallow = true,
className,
...otherProps
} = props;
const [dateParams, setDateParams] = useQueryStates(
{
from: parseAsString.withDefault(
defaultDateRange?.from?.toISOString() ?? ''
),
to: parseAsString.withDefault(defaultDateRange?.to?.toISOString() ?? ''),
},
{
clearOnDefault: true,
shallow,
}
)
const date = useMemo(() => {
function parseDate(dateString: string | null) {
if (!dateString) {
return undefined;
}
const parsedDate = new Date(dateString);
return isNaN(parsedDate.getTime()) ? undefined : parsedDate;
}
return {
from: parseDate(dateParams.from) ?? defaultDateRange?.from,
to: parseDate(dateParams.to) ?? defaultDateRange?.to,
};
}, [dateParams, defaultDateRange]);
const clearDates = (e: MouseEvent) => {
e.stopPropagation();
void setDateParams({
from: "",
to: "",
});
};
const hasSelectedDate = Boolean(date?.from || date?.to);
return (
<div className="grid gap-2">
<Popover>
<PopoverTrigger asChild>
<Button
variant={triggerVariant}
size={triggerSize}
className={cn(
'relative w-full justify-start gap-2 truncate text-left font-normal',
!date && 'text-muted-foreground',
triggerClassName
)}
>
<CalendarIcon className="size-4" />
{date?.from ? (
date.to ? (
<>
{format(date.from, 'LLL dd, y')} -{' '}
{format(date.to, 'LLL dd, y')}
</>
) : (
format(date.from, 'LLL dd, y')
)
) : (
<span>{placeholder}</span>
)}
{showClearButton && hasSelectedDate && (
<Button
type="button"
variant="ghost"
size="icon"
className="absolute right-0 top-0 h-full rounded-l-none px-3 hover:bg-background"
onClick={clearDates}
>
<X className="size-4" />
<span className="sr-only">초기화</span>
</Button>
)}
</Button>
</PopoverTrigger>
<PopoverContent className={cn("w-auto p-0", className)} {...otherProps}>
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date?.from && date?.to ? date : undefined}
onSelect={(newDateRange) => {
const from = newDateRange?.from;
const to = newDateRange?.to;
if (from && to && from > to) {
void setDateParams({
from: to.toISOString(),
to: from.toISOString(),
});
} else {
void setDateParams({
from: from?.toISOString() ?? '',
to: to?.toISOString() ?? '',
});
}
}}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
)
}
// ----------------------------------------------------------------------------------------------------
export default RisksDateRangePicker;
|