diff --git a/package-lock.json b/package-lock.json index d18367d04..ce2fe09ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "date-fns": "^3.6.0", + "date-fns": "^4.1.0", "dompurify": "^3.3.3", "lodash.capitalize": "^4.2.1", "lodash.get": "^4.4.2", @@ -36,7 +36,7 @@ "lodash.omitby": "^4.6.0", "lucide-react": "^1.7.0", "postcss": "^8.5.9", - "react-day-picker": "^8.10.1", + "react-day-picker": "^9.14.0", "react-flagpack": "^2.0.6", "react-hook-form": "^7.54.2", "tailwind-merge": "^3.5.0", @@ -548,6 +548,12 @@ } } }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", + "license": "MIT" + }, "node_modules/@emnapi/core": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", @@ -3355,16 +3361,6 @@ "node": ">=18.14.0" } }, - "node_modules/@remoteoss/remote-json-schema-form-kit/node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.13", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.13.tgz", @@ -3961,6 +3957,15 @@ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==" }, + "node_modules/@tabby_ai/hijri-converter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", + "integrity": "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@tailwindcss/cli": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.2.2.tgz", @@ -5380,14 +5385,21 @@ } }, "node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -7606,16 +7618,25 @@ } }, "node_modules/react-day-picker": { - "version": "8.10.1", - "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz", - "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.14.0.tgz", + "integrity": "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "@tabby_ai/hijri-converter": "1.0.5", + "date-fns": "^4.1.0", + "date-fns-jalali": "4.1.0-0" + }, + "engines": { + "node": ">=18" + }, "funding": { "type": "individual", "url": "https://github.com/sponsors/gpbl" }, "peerDependencies": { - "date-fns": "^2.28.0 || ^3.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": ">=16.8.0" } }, "node_modules/react-dom": { diff --git a/package.json b/package.json index 849821a70..65356f493 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "date-fns": "^3.6.0", + "date-fns": "^4.1.0", "dompurify": "^3.3.3", "lodash.capitalize": "^4.2.1", "lodash.get": "^4.4.2", @@ -115,7 +115,7 @@ "lodash.omitby": "^4.6.0", "lucide-react": "^1.7.0", "postcss": "^8.5.9", - "react-day-picker": "^8.10.1", + "react-day-picker": "^9.14.0", "react-flagpack": "^2.0.6", "react-hook-form": "^7.54.2", "tailwind-merge": "^3.5.0", diff --git a/src/components/form/fields/default/DatePickerFieldDefault.tsx b/src/components/form/fields/default/DatePickerFieldDefault.tsx index e9032fe81..6f34cff34 100644 --- a/src/components/form/fields/default/DatePickerFieldDefault.tsx +++ b/src/components/form/fields/default/DatePickerFieldDefault.tsx @@ -11,13 +11,13 @@ import { CalendarIcon } from 'lucide-react'; import { format } from 'date-fns'; import { Popover, - PopoverClose, PopoverContent, PopoverTrigger, } from '@/src/components/ui/popover'; import { cn } from '@/src/lib/utils'; import { Calendar } from '@/src/components/ui/calendar'; import { HelpCenter } from '@/src/components/shared/zendesk-drawer/HelpCenter'; +import { useState } from 'react'; export function DatePickerFieldDefault({ field, @@ -27,6 +27,8 @@ export function DatePickerFieldDefault({ const { name, label, description, minDate, maxDate } = fieldData; const minDateValue = minDate ? new Date(minDate) : undefined; const maxDateValue = maxDate ? new Date(maxDate) : undefined; + const [open, setOpen] = useState(false); + return ( {label} - +
@@ -62,15 +64,12 @@ export function DatePickerFieldDefault({ mode='single' className='RemoteFlows__DatepickerField__Calendar' selected={field.value ? new Date(field.value) : undefined} - onSelect={(date) => { - field.onChange(date ? format(date, 'yyyy-MM-dd') : null); + onDayClick={(day) => { + const formattedDate = format(day, 'yyyy-MM-dd'); + field.onChange(formattedDate); + setOpen(false); }} defaultMonth={minDateValue} - components={{ - DayContent: (props) => { - return {props.date.getDate()}; - }, - }} disabled={(date: Date) => { if (minDateValue && date < minDateValue) return true; if (maxDateValue && date > maxDateValue) return true; diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index e95343bac..9a0ce7f7e 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -3,71 +3,45 @@ import { ChevronLeft, ChevronRight } from 'lucide-react'; import { DayPicker } from 'react-day-picker'; import { cn } from '@/src/lib/utils'; -import { buttonVariants } from '@/src/components/ui/button'; + +export type CalendarProps = React.ComponentProps; function Calendar({ className, classNames, showOutsideDays = true, ...props -}: React.ComponentProps) { +}: CalendarProps) { return ( .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md' - : '[&:has([aria-selected])]:rounded-md', - ), - day: cn( - buttonVariants({ variant: 'ghost' }), - 'size-8 p-0 font-normal aria-selected:opacity-100', - ), - day_range_start: - 'day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground', - day_range_end: - 'day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground', - 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: - 'day-outside text-muted-foreground aria-selected:text-muted-foreground', - day_disabled: 'text-muted-foreground opacity-50', - day_range_middle: - 'aria-selected:bg-accent aria-selected:text-accent-foreground', - day_hidden: 'invisible', + months: 'flex flex-col sm:flex-row gap-4', + month: 'flex flex-col gap-y-4', + month_caption: 'flex h-8 w-full items-center justify-center relative', + caption_label: 'font-medium text-sm', + nav: 'flex items-center justify-between w-full', + month_grid: 'w-full border-collapse mt-4', + weekdays: 'flex', + weekday: + 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]', + week: 'flex w-full mt-2', + day: 'h-9 w-9 text-center text-sm p-0 relative', ...classNames, }} components={{ - IconLeft: ({ className, ...props }) => ( - - ), - IconRight: ({ className, ...props }) => ( - - ), + Chevron: ({ orientation, ...props }) => { + if (orientation === 'left') { + return ; + } + return ; + }, }} {...props} /> ); } +Calendar.displayName = 'Calendar'; export { Calendar }; diff --git a/src/styles/global.css b/src/styles/global.css index bf491f990..1a9c4486c 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -104,4 +104,123 @@ .RemoteFlows__EstimationResults__Headers__Label { @apply text-sm text-[#27272A] text-right; } + + /* React Day Picker v9 Styling */ + + /* Base day button styling */ + .rdp-button { + height: 2.25rem; + width: 2.25rem; + padding: 0; + font-weight: 400; + border-radius: 0.375rem; + cursor: pointer; + } + + /* Hover state for non-disabled, non-selected days */ + .rdp-button:hover:not([disabled]):not([aria-selected='true']) { + background-color: var(--color-accent); + color: var(--color-accent-foreground); + } + + /* Selected and today states (use same accent styling with bold weight for selected) */ + .rdp-button[aria-selected='true'], + .rdp-day_selected .rdp-button, + .rdp-day_today:not(.rdp-day_selected) .rdp-button { + background-color: var(--color-accent); + color: var(--color-accent-foreground); + } + + .rdp-button[aria-selected='true'], + .rdp-day_selected .rdp-button { + font-weight: 600; + } + + /* Disabled days */ + .rdp-button[disabled], + .rdp-day_disabled .rdp-button { + color: var(--color-muted-foreground); + opacity: 0.5; + cursor: not-allowed; + pointer-events: auto; + } + + /* Outside days (days from previous/next month) */ + .rdp-day_outside .rdp-button { + color: var(--color-muted-foreground); + opacity: 0.5; + } + + .rdp-day_outside.rdp-day_selected .rdp-button { + opacity: 0.3; + } + + /* Weekday headers */ + .rdp-head_cell { + width: 2.25rem; + font-size: 0.75rem; + font-weight: 500; + color: var(--color-muted-foreground); + text-transform: uppercase; + letter-spacing: 0.05em; + padding-bottom: 0.5rem; + } + + /* Caption container - holds title and navigation buttons */ + .rdp-caption { + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem 0; + margin-bottom: 0.5rem; + } + + /* Caption title - absolutely centered */ + .rdp-caption > div[aria-live='polite'] { + position: absolute; + left: 50%; + transform: translateX(-50%); + } + + /* Navigation container - use display: contents to not affect layout */ + .rdp-nav { + display: contents; + } + + /* Navigation buttons - positioned at far left and right */ + .rdp-nav_button { + position: absolute; + height: 1.75rem; + width: 1.75rem; + display: flex; + align-items: center; + justify-content: center; + background-color: transparent; + border: 1px solid var(--color-border); + border-radius: 0.375rem; + padding: 0; + opacity: 0.75; + cursor: pointer; + color: var(--color-foreground); + } + + .rdp-nav_button_previous { + left: 0; + } + + .rdp-nav_button_next { + right: 0; + } + + .rdp-nav_button:hover { + opacity: 1; + background-color: var(--color-accent); + } + + /* Navigation icons - smaller chevrons */ + .rdp-nav_icon { + height: 0.75rem; + width: 0.75rem; + } }