Skip to content

Instantly share code, notes, and snippets.

@fernandops26
Created May 18, 2023 18:37
Show Gist options
  • Save fernandops26/da681c4b12e52191803b4fcb040cdebb to your computer and use it in GitHub Desktop.
Save fernandops26/da681c4b12e52191803b4fcb040cdebb to your computer and use it in GitHub Desktop.
DatetimePicker example using https://ui.shadcn.com
import * as React from 'react';
import { DateTime } from 'luxon';
import { Calendar as CalendarIcon } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Calendar } from '@/components/ui/Calendar';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover';
import { cn } from '@/lib/utils';
import { SelectSingleEventHandler } from 'react-day-picker';
import { Label } from '@/components/ui/Label';
import { Input } from '@/components/ui/Input';
interface DateTimePickerProps {
date: Date;
setDate: (date: Date) => void;
}
export function DateTimePicker({ date, setDate }: DateTimePickerProps) {
const [selectedDateTime, setSelectedDateTime] = React.useState<DateTime>(
DateTime.fromJSDate(date)
);
const handleSelect: SelectSingleEventHandler = (day, selected) => {
const selectedDay = DateTime.fromJSDate(selected);
const modifiedDay = selectedDay.set({
hour: selectedDateTime.hour,
minute: selectedDateTime.minute,
});
setSelectedDateTime(modifiedDay);
setDate(modifiedDay.toJSDate());
};
const handleTimeChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
const { value } = e.target;
const hours = Number.parseInt(value.split(':')[0] || '00', 10);
const minutes = Number.parseInt(value.split(':')[1] || '00', 10);
const modifiedDay = selectedDateTime.set({ hour: hours, minute: minutes });
setSelectedDateTime(modifiedDay);
setDate(modifiedDay.toJSDate());
};
const footer = (
<>
<div className="px-4 pt-0 pb-4">
<Label>Time</Label>
<Input
type="time"
onChange={handleTimeChange}
value={selectedDateTime.toFormat('HH:mm')}
/>
</div>
{!selectedDateTime && <p>Please pick a day.</p>}
</>
);
return (
<Popover>
<PopoverTrigger asChild className="z-10">
<Button
variant={'outline'}
className={cn(
'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? (
selectedDateTime.toFormat('DDD HH:mm')
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={selectedDateTime.toJSDate()}
onSelect={handleSelect}
initialFocus
/>
{footer}
</PopoverContent>
</Popover>
);
}
@mxkaske
Copy link

mxkaske commented Aug 4, 2023

Amazing! Thanks @fernandops26 🚀.

When using Nextjs, I had to use suppressHydrationWarning on the button as server and client dates are differently!

@mamlzy
Copy link

mamlzy commented Sep 14, 2023

Thanks @fernandops26 !

@Ivan275
Copy link

Ivan275 commented Nov 3, 2023

how to add date range picker using mode="range"?

@nicolashedoire
Copy link

nicolashedoire commented Nov 20, 2023

it's a version to use in a dialog. add modal props on popover.
otherwise you will lose focus on the element and the popover will disappear

import * as React from "react";
import { DateTime } from "luxon";
import { Calendar as CalendarIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { SelectSingleEventHandler } from "react-day-picker";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";

interface DateTimePickerProps {
date: Date;
setDate: (date: Date) => void;
locale: any;
}

export function DateTimePicker({ date, setDate, locale }: DateTimePickerProps) {
const [selectedDateTime, setSelectedDateTime] = React.useState(
DateTime.fromJSDate(date),
);

React.useEffect(() => {
setSelectedDateTime(DateTime.fromJSDate(date));
}, [date]);

const handleSelect: SelectSingleEventHandler = (day, selected) => {
if (!selected) return;
const selectedDay = DateTime.fromJSDate(selected);
const modifiedDay = selectedDay.set({
hour: selectedDateTime.hour,
minute: selectedDateTime.minute,
});

setSelectedDateTime(modifiedDay);
setDate(modifiedDay.toJSDate());

};

const handleTimeChange: React.ChangeEventHandler = (e) => {
e.stopPropagation();
const { value } = e.target;
const hours = Number.parseInt(value.split(":")[0], 10);
const minutes = Number.parseInt(value.split(":")[1], 10);
const modifiedDay = selectedDateTime.set({ hour: hours, minute: minutes });

setSelectedDateTime(modifiedDay);
setDate(modifiedDay.toJSDate());

};

const footer = (


Heure
<Input
type="time"
onChange={handleTimeChange}
value={selectedDateTime.toFormat("HH:mm")}
/>

);

return (


<Button
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal",
!date && "text-muted-foreground",
)}
>

{date ? selectedDateTime.toFormat("DDD HH:mm") : Selectionne une date}




{footer}


);
}

@congtrieu98
Copy link

Hello @fernandops26 and every one, thank you for the tutorial on creating dateTimePicker with shadcn ui. I have applied, everything is fine on windows and android, but I have a problem when it displays on IOS, I have searched everywhere to solve the problem but still can't solve it, do you have any way to solve it? solve my problem? Thanks a lot!
412157998_1533750767464652_1747389952825417145_n

@errorstudent
Copy link

Thanks @fernandops26!

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