-
-
Save mjbalcueva/1fbcb1be9ef68a82c14d778b686a04fa to your computer and use it in GitHub Desktop.
"use client" | |
import * as React from "react" | |
import { buttonVariants } from "@/components/ui/button" | |
import { ScrollArea } from "@/components/ui/scroll-area" | |
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" | |
import { cn } from "@/lib/utils" | |
import { ChevronLeft, ChevronRight } from "lucide-react" | |
import { DayPicker, DropdownProps } from "react-day-picker" | |
export type CalendarProps = React.ComponentProps<typeof DayPicker> | |
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) { | |
return ( | |
<DayPicker | |
showOutsideDays={showOutsideDays} | |
className={cn("p-3", className)} | |
classNames={{ | |
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0", | |
month: "space-y-4", | |
caption: "flex justify-center pt-1 relative items-center", | |
caption_label: "text-sm font-medium", | |
caption_dropdowns: "flex justify-center gap-1", | |
nav: "space-x-1 flex items-center", | |
nav_button: cn( | |
buttonVariants({ variant: "outline" }), | |
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" | |
), | |
nav_button_previous: "absolute left-1", | |
nav_button_next: "absolute right-1", | |
table: "w-full border-collapse space-y-1", | |
head_row: "flex", | |
head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]", | |
row: "flex w-full mt-2", | |
cell: "text-center text-sm p-0 relative [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20", | |
day: cn(buttonVariants({ variant: "ghost" }), "h-9 w-9 p-0 font-normal aria-selected:opacity-100"), | |
day_selected: | |
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", | |
day_today: "bg-accent text-accent-foreground", | |
day_outside: "text-muted-foreground opacity-50", | |
day_disabled: "text-muted-foreground opacity-50", | |
day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground", | |
day_hidden: "invisible", | |
...classNames, | |
}} | |
components={{ | |
Dropdown: ({ value, onChange, children, ...props }: DropdownProps) => { | |
const options = React.Children.toArray(children) as React.ReactElement<React.HTMLProps<HTMLOptionElement>>[] | |
const selected = options.find((child) => child.props.value === value) | |
const handleChange = (value: string) => { | |
const changeEvent = { | |
target: { value }, | |
} as React.ChangeEvent<HTMLSelectElement> | |
onChange?.(changeEvent) | |
} | |
return ( | |
<Select | |
value={value?.toString()} | |
onValueChange={(value) => { | |
handleChange(value) | |
}} | |
> | |
<SelectTrigger className="pr-1.5 focus:ring-0"> | |
<SelectValue>{selected?.props?.children}</SelectValue> | |
</SelectTrigger> | |
<SelectContent position="popper"> | |
<ScrollArea className="h-80"> | |
{options.map((option, id: number) => ( | |
<SelectItem key={`${option.props.value}-${id}`} value={option.props.value?.toString() ?? ""}> | |
{option.props.children} | |
</SelectItem> | |
))} | |
</ScrollArea> | |
</SelectContent> | |
</Select> | |
) | |
}, | |
IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />, | |
IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />, | |
}} | |
{...props} | |
/> | |
) | |
} | |
Calendar.displayName = "Calendar" | |
export { Calendar } |
/* add this snippet in your globals.css file */ | |
.rdp-vhidden { | |
@apply hidden; | |
} |
"use client" | |
import * as React from "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 { format } from "date-fns" | |
import { CalendarIcon } from "lucide-react" | |
export function SampleDatePicker() { | |
const [date, setDate] = React.useState<Date>() | |
return ( | |
<Popover> | |
<PopoverTrigger asChild> | |
<Button | |
variant={"outline"} | |
className={cn("w-[240px] justify-start text-left font-normal", !date && "text-muted-foreground")} | |
> | |
<CalendarIcon className="mr-2 h-4 w-4" /> | |
{date ? format(date, "PPP") : <span>Pick a date</span>} | |
</Button> | |
</PopoverTrigger> | |
<PopoverContent align="start" className=" w-auto p-0"> | |
<Calendar | |
mode="single" | |
captionLayout="dropdown-buttons" | |
selected={date} | |
onSelect={setDate} | |
fromYear={1960} | |
toYear={2030} | |
/> | |
</PopoverContent> | |
</Popover> | |
) | |
} |
can close the modal when the user selects a date, instead of having to click outside again?
I have just incorporated the key prop as shown: <Popover key={date?.getDate()}>
. This adjustment ensures the component will re-render whenever the date changes.
Great work man
Thank you man, great work 🥇 !
Very useful, thank you very much
Thank you man you saved my life
that's awesome man, thanks a lot
Thanks a lot @mjbalcueva! great work 👊
This is awesome, thanks a lot mark @mjbalcueva
Thanks @mjbalcueva 🔥🔥
Thank you!
When I add captionLayout="dropdown" and fromYear and toYear to my Calendar component it always looks as if there is a double render/glitch when the calendar is opening. Do you perhaps know why?
Having the same issue. Adding dropwodn or dropdown-buttons causes a double render.
Thank you, it helps me a lot!
Great job!
Thank you very much! Looks great!
@mjbalcueva man you're the best bro. Saved me a ton of time! Can't express in words how much grateful I am. God bless you.
Since you like helping others, I wanna share something I've done: a Combobox with an auto-complete feature.
If you want to check it out and share with the community, here it is:
shadcn-ui/ui#2577 (comment)
@mjbalcueva man you're the best bro. Saved me a ton of time! Can't express in words how much grateful I am. God bless you. Since you like helping others, I wanna share something I've done: a Combobox with an auto-complete feature. If you want to check it out and share with the community, here it is: shadcn-ui/ui#2577 (comment)
Hi man, does your combobox work with multi-select like headless ui?
awesome thanks
@mjbalcueva thank you , this is so good !
thanks for sharing this
@mjbalcueva thank you for this. Has anyone used this in a modal?? Having some weird behaviors on the dropdowns
Using this component, after selecting current date selectedValue = undefined. To solve this issue add to calendar prop required
according https://react-day-picker.js.org/basics/selecting-days
A small change, instead of adding to the CSS file
.rdp-vhidden {
@apply hidden;
}
Apply the Tailwind selector directly in the component declaration:
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
//...
vhidden: "vhidden hidden", // Add this line
//..
}}
@jramiresbrito Try adding this classname caption_dropdowns: "flex justify-center gap-x-2"
for fixing the dropdowns in a row. If you put the code in a live example, I can try to help with the popover issue.
@jramiresbrito Try adding this classname
caption_dropdowns: "flex justify-center gap-x-2"
for fixing the dropdowns in a row. If you put the code in a live example, I can try to help with the popover issue.
Thanks for replying. I went for an easier solution by just adding classes instead of modifying the component broadly
Thank you!
Thank you for sharing this
Thanks a lot
When I add captionLayout="dropdown" and fromYear and toYear to my Calendar component it always looks as if there is a double render/glitch when the calendar is opening. Do you perhaps know why?