Skip to content

Instantly share code, notes, and snippets.

@nishimweprince
Last active November 19, 2024 05:19
Show Gist options
  • Select an option

  • Save nishimweprince/9f231f85f41fe95a6128ad25bec80d25 to your computer and use it in GitHub Desktop.

Select an option

Save nishimweprince/9f231f85f41fe95a6128ad25bec80d25 to your computer and use it in GitHub Desktop.
A reusable component for Shadcn DatePicker with years and months selection
import { CalendarIcon } from '@radix-ui/react-icons';
import { format } from 'date-fns';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { ChangeEvent, FC, useState } from 'react';
import { SelectSingleEventHandler } from 'react-day-picker';
import moment from 'moment';
import Select from './Select';
interface DatePickerProps {
value: Date | undefined;
onChange:
| SelectSingleEventHandler
| ((e: ChangeEvent<HTMLInputElement>) => void)
| undefined;
selectionType?: 'date' | 'month' | 'year' | 'recurringDate';
fromDate?: Date;
placeholder?: string;
toDate?: Date;
}
const DatePicker: FC<DatePickerProps> = ({
onChange,
value = undefined,
selectionType,
fromDate = undefined,
placeholder = 'Select date',
toDate = undefined,
}) => {
// SET MONTH AND YEAR
const [year, setYear] = useState<string | undefined>(moment().format('YYYY'));
const [defaultMonth, setDefaultMonth] = useState<Date | undefined>(
moment().toDate()
);
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant={'outline'}
className={cn(
'w-full justify-start text-left font-normal py-2 h-[38px]',
!value && 'text-muted-foreground'
)}
onClick={() => setOpen(!open)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{value ? (
selectionType === 'recurringDate' ? (
moment(value).format('MMMM DD')
) : (
format(value, 'PPP')
)
) : (
<p className="text-[13px]">{placeholder}</p>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0" align="start">
<menu className="flex flex-col gap-2 p-2 w-full">
<ul
className={`w-full grid gap-3 ${
selectionType === 'recurringDate' ? 'grid-cols-1' : 'grid-cols-2'
}`}
>
{selectionType !== 'recurringDate' && (
<Select
className="!h-8"
placeholder="Year"
onChange={(e) => {
setYear(e);
setDefaultMonth(moment(e, 'YYYY-MM-DD').toDate());
}}
value={year}
options={Array.from({ length: 200 }, (_, i) => ({
value: String(2099 - i),
label: String(2099 - i),
}))}
/>
)}
<Select
className="!h-8"
placeholder="Month"
onChange={(e) => {
setDefaultMonth(moment(`${year}-${e}`, 'YYYY-MM-DD').toDate());
}}
value={moment(defaultMonth).format('MM')}
options={Array.from({ length: 12 }, (_, i) => ({
value: String(i + 1).padStart(2, '0'),
label: moment().month(i).format('MMM'),
}))}
/>
</ul>
<Calendar
fromDate={fromDate}
toDate={toDate}
mode="single"
month={defaultMonth}
onMonthChange={(e) => {
if (selectionType !== 'recurringDate') {
setDefaultMonth(e);
}
}}
selected={value}
onSelect={(e) => {
if (e) {
onChange(moment(e).format());
setOpen(false);
}
}}
initialFocus
/>
</menu>
</PopoverContent>
</Popover>
);
};
export default DatePicker;
@MaxJLerman

Copy link
Copy Markdown

Is your Select component your own? It isn't the one from ShadCN

@nishimweprince

Copy link
Copy Markdown
Author

Is your Select component your own? It isn't the one from ShadCN

Yes it is based on the one of Shadcn, I simply added a few customizations.

@jayPreak

Copy link
Copy Markdown

Can you drop the Select component code too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment