fix(datepicker): avoid offscreen calendar placement
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react"
|
import React, { useEffect, useRef, useState } from "react"
|
||||||
import DatePicker, { DateObject } from "react-multi-date-picker"
|
import DatePicker, { DateObject } from "react-multi-date-picker"
|
||||||
import persian from "react-date-object/calendars/persian"
|
import persian from "react-date-object/calendars/persian"
|
||||||
import persian_fa from "react-date-object/locales/persian_fa"
|
import persian_fa from "react-date-object/locales/persian_fa"
|
||||||
@@ -16,8 +16,10 @@ interface JalaliDatePickerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function JalaliDatePicker({ value, onChange, label, disabled, inputClassName = "", placeholder }: JalaliDatePickerProps) {
|
export default function JalaliDatePicker({ value, onChange, label, disabled, inputClassName = "", placeholder }: JalaliDatePickerProps) {
|
||||||
const isFa = document.documentElement.dir === 'rtl'
|
const isFa = document.documentElement.dir === 'rtl'
|
||||||
const [isDark, setIsDark] = useState(document.documentElement.classList.contains('dark'))
|
const [isDark, setIsDark] = useState(document.documentElement.classList.contains('dark'))
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [calendarPosition, setCalendarPosition] = useState("bottom-right")
|
||||||
|
|
||||||
// Listen for dark mode changes dynamically (optional but good for UX)
|
// Listen for dark mode changes dynamically (optional but good for UX)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -28,17 +30,28 @@ export default function JalaliDatePicker({ value, onChange, label, disabled, inp
|
|||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleChange = (date: DateObject | null) => {
|
const handleChange = (date: DateObject | null) => {
|
||||||
if (!date) {
|
if (!date) {
|
||||||
onChange("")
|
onChange("")
|
||||||
} else {
|
} else {
|
||||||
// Always output standard Gregorian "YYYY-MM-DD" for backend
|
// Always output standard Gregorian "YYYY-MM-DD" for backend
|
||||||
onChange(date.convert(gregorian, gregorian_en).format("YYYY-MM-DD"))
|
onChange(date.convert(gregorian, gregorian_en).format("YYYY-MM-DD"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const updateCalendarPosition = () => {
|
||||||
<div className="w-full">
|
const rect = containerRef.current?.getBoundingClientRect()
|
||||||
|
if (!rect) return
|
||||||
|
|
||||||
|
const estimatedHeight = 340
|
||||||
|
const hasMoreSpaceAbove = rect.top > window.innerHeight - rect.bottom
|
||||||
|
const shouldOpenTop = window.innerHeight - rect.bottom < estimatedHeight && hasMoreSpaceAbove
|
||||||
|
const horizontal = isFa ? "left" : "right"
|
||||||
|
setCalendarPosition(`${shouldOpenTop ? "top" : "bottom"}-${horizontal}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="w-full">
|
||||||
{label && (
|
{label && (
|
||||||
<label className="text-sm font-medium dark:text-slate-300 mb-1 block">
|
<label className="text-sm font-medium dark:text-slate-300 mb-1 block">
|
||||||
{label}
|
{label}
|
||||||
@@ -52,13 +65,14 @@ export default function JalaliDatePicker({ value, onChange, label, disabled, inp
|
|||||||
format="YYYY/MM/DD"
|
format="YYYY/MM/DD"
|
||||||
placeholder={placeholder || "YYYY/MM/DD"}
|
placeholder={placeholder || "YYYY/MM/DD"}
|
||||||
onOpenPickNewDate={false}
|
onOpenPickNewDate={false}
|
||||||
|
onOpen={updateCalendarPosition}
|
||||||
inputClass={`w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm dark:border-slate-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed ${inputClassName}`}
|
inputClass={`w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm dark:border-slate-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed ${inputClassName}`}
|
||||||
containerClassName="w-full"
|
containerClassName="w-full"
|
||||||
className={isDark ? "bg-dark" : ""}
|
className={isDark ? "bg-dark" : ""}
|
||||||
calendarPosition="bottom-right"
|
calendarPosition={calendarPosition}
|
||||||
fixMainPosition
|
fixMainPosition
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user