ui
ui copied to clipboard
feat: :sparkles: new date picker v9
Added support for react-day-picker v9.
Currently using this version in conjunction with the calendar component leads to this broken view:
The fix leads to this view (should be the same as before):
Had to change quite a bit in the calendar component, but tried to keep the visual aspect as close to before as possible.
Year Picker
Also added a new feature that is especially useful when trying to select a birth date. Before, a user would have to navigate through each month at a time. This obviously is not very good UX, the older the user, the worse the experience. ;)
Here is a demo of the new functionality, to add it to the date picker component, the user only needs to pass the showYearSwitcher prop to the calendar component.
notice this label can now be a button. When clicking this button, a new view will appear.
This is the year view, the user can now navigate and switch between years instead of only one month.
The amount of years shown in this view can also be customised, by passing the number of years desired to the yearRange prop. By default this is 12.
In the meantime, this component can be used and tested by anyone using this link:
:sparkles: https://date-picker.luca-felix.com/ :sparkles:
@flixlix is attempting to deploy a commit to the shadcn-pro Team on Vercel.
A member of the Team first needs to authorize it.
Added support for
react-day-pickerv9.Currently using this version in conjunction with the calendar component leads to this broken view:
The fix leads to this view (should be the same as before):
Had to change quite a bit in the calendar component, but tried to keep the visual aspect as close to before as possible.
Year Picker
Also added a new feature that is especially useful when trying to select a birth date. Before, a user would have to navigate through each month at a time. This obviously is not very good UX, the older the user, the worse the experience. ;)
Here is a demo of the new functionality, to add it to the date picker component, the user only needs to pass the
showYearSwitcherprop to the calendar component.
notice this label can now be a button. When clicking this button, a new view will appear.
This is the year view, the user can now navigate and switch between years instead of only one month.
The amount of years shown in this view can also be customised, by passing the number of years desired to the
yearRangeprop. By default this is 12.In the meantime, this component can be used and tested by anyone using this link:
✨ https://date-picker.luca-felix.com/ ✨
ajusting style
Style issue
issue
og
React daypicker already has functionality for month/year select. So it would probably be better to use that: https://daypicker.dev/docs/navigation#hidenavigation
React daypicker already has functionality for month/year select. So it would probably be better to use that: https://daypicker.dev/docs/navigation#hidenavigation
I did see this, but in my opinion the select dropdown is not as user friendly, especially considering might want to enter their birth dates in some cases
React daypicker already has functionality for month/year select. So it would probably be better to use that: https://daypicker.dev/docs/navigation#hidenavigation
I did see this, but in my opinion the select dropdown is not as user friendly, especially considering might want to enter their birth dates in some cases
While i agree that it isn't as clean as your solution. It still exists in the original component, and since this library is a wrapper it feels a bit weird to not support that. Since this library gives you the code, we can extend it ourselves for better UX like you did, but not sure it should be in the base component. Not hating on your work because it looks great!
React daypicker already has functionality for month/year select. So it would probably be better to use that: https://daypicker.dev/docs/navigation#hidenavigation
I did see this, but in my opinion the select dropdown is not as user friendly, especially considering might want to enter their birth dates in some cases
While i agree that it isn't as clean as your solution. It still exists in the original component, and since this library is a wrapper it feels a bit weird to not support that. Since this library gives you the code, we can extend it ourselves for better UX like you did, but not sure it should be in the base component. Not hating on your work because it looks great!
Alright I see your point, maybe I'll try contributing to RDP directly and hopefully we can use it directly in shadcn after that :)
@flixlix The vue version already made an implementation i think: https://www.shadcn-vue.com/docs/components/calendar.html#advanced-customization
| Image | Code |
|---|---|
Make sure to fix this as well, when "showOutsideDays" is set to false, the UI breaks a little bit
Any update?
ajusting style
Hi @flixlix The fix which I'm using after copying the code from PR apps/www/registry/default/ui/calendar.tsx
className={cn('p-2', className)}
month_caption: 'relative mx-10 mb-3 mt-1 flex h-7 items-center justify-center',
month_grid: 'm-1',
After above fix
This is probably my favorite among the previous attempts to extend the date picker. It feels really nice and intuitive to use.
Btw, not sure if react-day-picker does anything special to prevent this issue, but I'm getting this lint error:
Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “Calendar” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true. eslintreact/no-unstable-nested-components
Maybe it'd be better to move the components outside.
I'll try contributing to RDP directly and hopefully we can use it directly in shadcn after that :)
That would be great. Nice job man.
but not sure it should be in the base component
It would be nice to have this functionality, it's modern and other libraries are doing something similar. If not as the base component, maybe a variant of it like Toast and Sonner?
Great job!
Here's an improved version of this calendar. This code fixes the following:
- the errors shown in the screenshots below
- a bug in the navigation of year view
"use client"
import * as React from "react"
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
import { differenceInCalendarDays } from "date-fns"
import {
DayPicker,
labelNext,
labelPrevious,
useDayPicker,
} from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "./button"
import { omit } from "lodash"
export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
/**
* In the year view, the number of years to display at once.
* @default 12
*/
yearRange?: number
/**
* Wether to let user switch between months and years view.
* @default false
*/
showYearSwitcher?: boolean
}
function Calendar({
className,
classNames,
showOutsideDays = true,
yearRange = 12,
showYearSwitcher = false,
numberOfMonths,
...props
}: CalendarProps) {
const [navView, setNavView] = React.useState<"days" | "years">("days")
const [displayYears, setDisplayYears] = React.useState<{
from: number
to: number
}>(
React.useMemo(() => {
const currentYear = new Date().getFullYear()
return {
from: currentYear - Math.floor(yearRange / 2 - 1),
to: currentYear + Math.ceil(yearRange / 2),
}
}, [yearRange])
)
const { onNextClick, onPrevClick, startMonth, endMonth } = props
const columnsDisplayed = navView === "years" ? 1 : numberOfMonths
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
style={{
width: 248.8 * (columnsDisplayed ?? 1) + "px",
}}
classNames={{
months: "relative flex flex-col gap-y-4 sm:flex-row sm:gap-y-0",
month_caption: "relative mx-10 flex h-7 items-center justify-center",
weekdays: "flex flex-row",
weekday: "w-8 text-[0.8rem] font-normal text-muted-foreground",
month: "w-full gap-y-4 overflow-x-hidden",
caption: "relative flex items-center justify-center pt-1",
caption_label: "truncate text-sm font-medium",
button_next: cn(
buttonVariants({
variant: "outline",
className:
"absolute right-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
})
),
button_previous: cn(
buttonVariants({
variant: "outline",
className:
"absolute left-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
})
),
nav: "flex items-start",
month_grid: "mt-4",
week: "mt-2 flex w-full",
day: "flex size-8 flex-1 items-center justify-center rounded-md p-0 text-sm [&:has(button)]:hover:!bg-accent [&:has(button)]:hover:text-accent-foreground [&:has(button)]:hover:aria-selected:!bg-primary [&:has(button)]:hover:aria-selected:text-primary-foreground",
day_button: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal transition-none hover:bg-transparent hover:text-inherit aria-selected:opacity-100"
),
range_start: "day-range-start rounded-s-md",
range_end: "day-range-end rounded-e-md",
selected:
"bg-primary text-primary-foreground hover:!bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
today: "bg-accent text-accent-foreground",
outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
disabled: "text-muted-foreground opacity-50",
range_middle:
"rounded-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:aria-selected:!bg-accent hover:aria-selected:text-accent-foreground",
hidden: "invisible hidden",
...classNames,
}}
components={{
Chevron: ({ orientation }) => {
const Icon =
orientation === "left" ? ChevronLeftIcon : ChevronRightIcon
return <Icon className="h-4 w-4" />
},
Nav: ({ className, children, ...props }) => {
const navProps = omit(props, [
"onPreviousClick",
"onNextClick",
"previousMonth",
"nextMonth"
]);
const { nextMonth, previousMonth, goToMonth } = useDayPicker()
const isPreviousDisabled = (() => {
if (navView === "years") {
return (
(startMonth &&
differenceInCalendarDays(
new Date(displayYears.from - 1, 0, 1),
startMonth
) < 0) ||
(endMonth &&
differenceInCalendarDays(
new Date(displayYears.from - 1, 0, 1),
endMonth
) > 0)
)
}
return !previousMonth
})()
const isNextDisabled = (() => {
if (navView === "years") {
return (
(startMonth &&
differenceInCalendarDays(
new Date(displayYears.to + 1, 0, 1),
startMonth
) < 0) ||
(endMonth &&
differenceInCalendarDays(
new Date(displayYears.to + 1, 0, 1),
endMonth
) > 0)
)
}
return !nextMonth
})()
const handlePreviousClick = React.useCallback(() => {
if (navView === "years") {
setDisplayYears((prev) => ({
from: prev.from - (prev.to - prev.from + 1),
to: prev.to - (prev.to - prev.from + 1),
}))
onPrevClick?.(
new Date(
displayYears.from - (displayYears.to - displayYears.from),
0,
1
)
)
return
}
if (!previousMonth) return
goToMonth(previousMonth)
onPrevClick?.(previousMonth)
}, [previousMonth, goToMonth])
const handleNextClick = React.useCallback(() => {
if (navView === "years") {
setDisplayYears((prev) => ({
from: prev.from + (prev.to - prev.from + 1),
to: prev.to + (prev.to - prev.from + 1),
}))
onNextClick?.(
new Date(
displayYears.from + (displayYears.to - displayYears.from),
0,
1
)
)
return
}
if (!nextMonth) return
goToMonth(nextMonth)
onNextClick?.(nextMonth)
}, [goToMonth, nextMonth])
return (
<nav className={cn("flex items-center", className)} {...navProps}>
<Button
variant="outline"
className="absolute left-0 h-7 w-7 bg-transparent p-0 opacity-80 hover:opacity-100"
type="button"
tabIndex={isPreviousDisabled ? undefined : -1}
disabled={isPreviousDisabled}
aria-label={
navView === "years"
? `Go to the previous ${displayYears.to - displayYears.from + 1
} years`
: labelPrevious(previousMonth)
}
onClick={handlePreviousClick}
>
<ChevronLeftIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="absolute right-0 h-7 w-7 bg-transparent p-0 opacity-80 hover:opacity-100"
type="button"
tabIndex={isNextDisabled ? undefined : -1}
disabled={isNextDisabled}
aria-label={
navView === "years"
? `Go to the next ${displayYears.to - displayYears.from + 1
} years`
: labelNext(nextMonth)
}
onClick={handleNextClick}
>
<ChevronRightIcon className="h-4 w-4" />
</Button>
</nav>
)
},
CaptionLabel: ({ children, ...props }) => {
if (!showYearSwitcher) return <span {...props}>{children}</span>
return (
<Button
className="h-7 w-full truncate text-sm font-medium"
variant="ghost"
size="sm"
onClick={() =>
setNavView((prev) => (prev === "days" ? "years" : "days"))
}
>
{navView === "days"
? children
: displayYears.from + " - " + displayYears.to}
</Button>
)
},
MonthGrid: ({ className, children, ...props }) => {
const { goToMonth } = useDayPicker()
if (navView === "years") {
return (
<div
className={cn("grid grid-cols-4 gap-y-2", className)}
{...props}
>
{Array.from(
{ length: displayYears.to - displayYears.from + 1 },
(_, i) => {
const isBefore =
differenceInCalendarDays(
new Date(displayYears.from + i, 12, 31),
startMonth!
) < 0
const isAfter =
differenceInCalendarDays(
new Date(displayYears.from + i, 0, 0),
endMonth!
) > 0
const isDisabled = isBefore || isAfter
return (
<Button
key={i}
className={cn(
"h-7 w-full text-sm font-normal text-foreground",
displayYears.from + i === new Date().getFullYear() &&
"bg-accent font-medium text-accent-foreground"
)}
variant="ghost"
onClick={() => {
setNavView("days")
goToMonth(
new Date(
displayYears.from + i,
new Date().getMonth()
)
)
}}
disabled={navView === "years" ? isDisabled : undefined}
>
{displayYears.from + i}
</Button>
)
}
)}
</div>
)
}
return (
<table className={className} {...props}>
{children}
</table>
)
},
}}
numberOfMonths={
// we need to override the number of months if we are in years view to 1
columnsDisplayed
}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }
Error screenshots:
Hello, do we know when the fix will be released? Thanks!
@musthafa1996 your component lacks some styles polish-for example when I hover the corners are not rounded:
"use client"
import "react-day-picker/style.css";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
import {
DayPicker,
labelNext,
labelPrevious,
useDayPicker,
} from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/ui/button"
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectGroup } from '@/components/ui/select'
function Calendar({
className,
classNames,
showOutsideDays = true,
numberOfMonths,
...props
}: React.ComponentProps<typeof DayPicker>) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("py-2", className)}
classNames={{
months: "relative flex flex-col gap-y-4 sm:flex-row sm:gap-y-0",
month_caption: "relative mx-10 flex h-7 items-center justify-center",
weekdays: "flex flex-row",
weekday: "w-8 text-[0.8rem] font-normal text-muted-foreground",
month: "w-full gap-y-4 overflow-x-hidden",
caption: "relative flex items-center justify-center pt-1",
caption_label: "truncate text-sm font-medium",
button_next: cn(
buttonVariants({
variant: "outline",
className:
"absolute right-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
})
),
button_previous: cn(
buttonVariants({
variant: "outline",
className:
"absolute left-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
})
),
nav: "flex items-start",
month_grid: "my-2 mx-2",
week: "mt-2 flex w-full",
day: "flex h-9 w-9 flex-1 items-center justify-center rounded-md p-0 text-sm [&:has(button)]:hover:!bg-accent [&:has(button)]:hover:text-accent-foreground [&:has(button)]:hover:aria-selected:!bg-primary [&:has(button)]:hover:aria-selected:text-primary-foreground",
day_button: cn(
buttonVariants({ variant: "ghost" }),
"h-9 w-9 p-0 font-normal transition-none hover:bg-transparent hover:text-inherit aria-selected:opacity-100"
),
range_start: "day-range-start rounded-s-md",
range_end: "day-range-end rounded-e-md",
selected:
"bg-primary text-primary-foreground hover:!bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
today: "bg-accent text-accent-foreground",
outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
disabled: "text-muted-foreground opacity-50",
range_middle:
"rounded-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:aria-selected:!bg-accent hover:aria-selected:text-accent-foreground",
hidden: "invisible hidden",
chevron: `inline-block fill-muted-foreground`,
...classNames,
}}
components={{
Dropdown: ({ children, ...props }) => {
const { options, className, disabled } = props;
const { goToMonth, months } = useDayPicker();
const currentShown = months[0].date;
const currentSelection =
className === "rdp-years_dropdown"
? currentShown.getFullYear().toString()
: currentShown.getMonth().toString();
const updateDayPickerState = (value: string) => {
const newDate = new Date(currentShown);
if (className === "rdp-years_dropdown") {
newDate.setFullYear(parseInt(value));
} else if (className === "rdp-months_dropdown") {
newDate.setMonth(parseInt(value));
}
goToMonth(newDate);
};
return (
<Select
value={currentSelection}
onValueChange={updateDayPickerState}
disabled={disabled}
>
<SelectTrigger className="w-full border-0 ring-0 focus:ring-0 px-2 py-1">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{options?.map((option) => (
<SelectItem
key={option.value}
value={option.value.toString()}
>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
},
YearsDropdown: ({ children, ...props }) => {
const { components } = useDayPicker()
// sort years in descending order
const sortedOptions = props.options?.sort((a, b) => b.value - a.value)
return <components.Dropdown {...props} options={sortedOptions} />
},
PreviousMonthButton: ({ className, children, ...props }) => {
const previousMonth = useDayPicker().previousMonth
return (
<Button
variant="outline"
className={cn(
buttonVariants({ variant: "outline" }),
"absolute left-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 ml-2",
className
)}
type="button"
tabIndex={previousMonth ? -1 : undefined}
disabled={!previousMonth}
aria-label={labelPrevious(previousMonth)}
onClick={() => {
props.onClick()
}}
>
<ChevronLeftIcon className="h-4 w-4" />
</Button>
)
},
NextMonthButton: ({ className, children, ...props }) => {
const nextMonth = useDayPicker().nextMonth
return (
<Button
variant="outline"
className={cn(
buttonVariants({ variant: "outline" }),
"absolute right-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 mr-2",
className
)}
type="button"
tabIndex={nextMonth ? -1 : undefined}
disabled={!nextMonth}
aria-label={labelNext(nextMonth)}
onClick={() => {
props.onClick()
}}
>
<ChevronRightIcon className="h-4 w-4" />
</Button>
)
},
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }
"use client" import "react-day-picker/style.css"; import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react" import { DayPicker, labelNext, labelPrevious, useDayPicker, } from "react-day-picker" import { cn } from "@/lib/utils" import { Button, buttonVariants } from "@/components/ui/button" import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectGroup } from '@/components/ui/select' function Calendar({ className, classNames, showOutsideDays = true, numberOfMonths, ...props }: React.ComponentProps<typeof DayPicker>) { return ( <DayPicker showOutsideDays={showOutsideDays} className={cn("py-2", className)} classNames={{ months: "relative flex flex-col gap-y-4 sm:flex-row sm:gap-y-0", month_caption: "relative mx-10 flex h-7 items-center justify-center", weekdays: "flex flex-row", weekday: "w-8 text-[0.8rem] font-normal text-muted-foreground", month: "w-full gap-y-4 overflow-x-hidden", caption: "relative flex items-center justify-center pt-1", caption_label: "truncate text-sm font-medium", button_next: cn( buttonVariants({ variant: "outline", className: "absolute right-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100", }) ), button_previous: cn( buttonVariants({ variant: "outline", className: "absolute left-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100", }) ), nav: "flex items-start", month_grid: "my-2 mx-2", week: "mt-2 flex w-full", day: "flex h-9 w-9 flex-1 items-center justify-center rounded-md p-0 text-sm [&:has(button)]:hover:!bg-accent [&:has(button)]:hover:text-accent-foreground [&:has(button)]:hover:aria-selected:!bg-primary [&:has(button)]:hover:aria-selected:text-primary-foreground", day_button: cn( buttonVariants({ variant: "ghost" }), "h-9 w-9 p-0 font-normal transition-none hover:bg-transparent hover:text-inherit aria-selected:opacity-100" ), range_start: "day-range-start rounded-s-md", range_end: "day-range-end rounded-e-md", selected: "bg-primary text-primary-foreground hover:!bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", today: "bg-accent text-accent-foreground", outside: "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", disabled: "text-muted-foreground opacity-50", range_middle: "rounded-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:aria-selected:!bg-accent hover:aria-selected:text-accent-foreground", hidden: "invisible hidden", chevron: `inline-block fill-muted-foreground`, ...classNames, }} components={{ Dropdown: ({ children, ...props }) => { const { options, className, disabled } = props; const { goToMonth, months } = useDayPicker(); const currentShown = months[0].date; const currentSelection = className === "rdp-years_dropdown" ? currentShown.getFullYear().toString() : currentShown.getMonth().toString(); const updateDayPickerState = (value: string) => { const newDate = new Date(currentShown); if (className === "rdp-years_dropdown") { newDate.setFullYear(parseInt(value)); } else if (className === "rdp-months_dropdown") { newDate.setMonth(parseInt(value)); } goToMonth(newDate); }; return ( <Select value={currentSelection} onValueChange={updateDayPickerState} disabled={disabled} > <SelectTrigger className="w-full border-0 ring-0 focus:ring-0 px-2 py-1"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectGroup> {options?.map((option) => ( <SelectItem key={option.value} value={option.value.toString()} > {option.label} </SelectItem> ))} </SelectGroup> </SelectContent> </Select> ); }, YearsDropdown: ({ children, ...props }) => { const { components } = useDayPicker() // sort years in descending order const sortedOptions = props.options?.sort((a, b) => b.value - a.value) return <components.Dropdown {...props} options={sortedOptions} /> }, PreviousMonthButton: ({ className, children, ...props }) => { const previousMonth = useDayPicker().previousMonth return ( <Button variant="outline" className={cn( buttonVariants({ variant: "outline" }), "absolute left-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 ml-2", className )} type="button" tabIndex={previousMonth ? -1 : undefined} disabled={!previousMonth} aria-label={labelPrevious(previousMonth)} onClick={() => { props.onClick() }} > <ChevronLeftIcon className="h-4 w-4" /> </Button> ) }, NextMonthButton: ({ className, children, ...props }) => { const nextMonth = useDayPicker().nextMonth return ( <Button variant="outline" className={cn( buttonVariants({ variant: "outline" }), "absolute right-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 mr-2", className )} type="button" tabIndex={nextMonth ? -1 : undefined} disabled={!nextMonth} aria-label={labelNext(nextMonth)} onClick={() => { props.onClick() }} > <ChevronRightIcon className="h-4 w-4" /> </Button> ) }, }} {...props} /> ) } Calendar.displayName = "Calendar" export { Calendar }
Thanks, you saved me some work. Finally a version which works with captureLayout="dropdown" <3
@Blackvz works, but does not display two calendars side by side in range select mode :-1:
Style issue
issue
og
fixed this in the latest commits
@Blackvz works, but does not display two calendars side by side in range select mode 👎
fixed this in https://github.com/flixlix/shadcn-date-picker/commit/57f5684d7a5fc65f67b5f7f6bb3817f407b20fce#diff-111083d11e1f991cf2764d274afd3378246acb82aaff5dfe1d61f55d0ebdde47
@musthafa1996 your component lacks some styles polish-for example when I hover the corners are not rounded:
fixed this in https://github.com/flixlix/shadcn-date-picker/commit/57f5684d7a5fc65f67b5f7f6bb3817f407b20fce#diff-111083d11e1f991cf2764d274afd3378246acb82aaff5dfe1d61f55d0ebdde47
Will this be released someday?
Best proposal, +1
The version here fixes the console warnings https://github.com/shadcn-ui/ui/pull/4421#issuecomment-2456403155
as well as the version here https://github.com/shadcn-ui/ui/pull/4421#issuecomment-2477597372
The current version by @flixlix doesn't fix those errors, or am I missing something?
Is this or #4371 better upgraded to v9? I want to bump the cal version, disappointed that something has not been merged on this yet.
I find the confusion regarding a good calendar component rather unfortunate. Some people are creating pull requests, that don't get merged. On X you can read that a new calendar component is developed by shadcn himself, but you never heard anything again from that. Here on Github are a lot of discussions about how to upgrade to v9 as well. The lack of communication is really frustrating.
@shadcn any general updates from the core team here, at minimum to help set/manage expectations?





ajusting style
og 
