fix(SW-1011): when only choosing one date on mobile * fix(SW-1011): when only choosing one date on mobile When choosing only from date on mobile the to date is set to the next day. This was only the case on desktop before, no it's on mobile too. * Remove unnecessary dependency Approved-by: Pontus Dreij Approved-by: Hrishikesh Vaipurkar
165 lines
4.6 KiB
TypeScript
165 lines
4.6 KiB
TypeScript
"use client"
|
|
import { da, de, fi, nb, sv } from "date-fns/locale"
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { useFormContext, useWatch } from "react-hook-form"
|
|
|
|
import { Lang } from "@/constants/languages"
|
|
import { dt } from "@/lib/dt"
|
|
|
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
import useLang from "@/hooks/useLang"
|
|
|
|
import DatePickerDesktop from "./Screen/Desktop"
|
|
import DatePickerMobile from "./Screen/Mobile"
|
|
|
|
import styles from "./date-picker.module.css"
|
|
|
|
import type { DatePickerFormProps } from "@/types/components/datepicker"
|
|
|
|
const locales = {
|
|
[Lang.da]: da,
|
|
[Lang.de]: de,
|
|
[Lang.fi]: fi,
|
|
[Lang.no]: nb,
|
|
[Lang.sv]: sv,
|
|
}
|
|
|
|
export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
|
const lang = useLang()
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const selectedDate = useWatch({ name })
|
|
const { register, setValue } = useFormContext()
|
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
|
|
const [isSelectingFrom, setIsSelectingFrom] = useState(true)
|
|
|
|
const close = useCallback(() => {
|
|
if (!selectedDate.toDate) {
|
|
setValue(name, {
|
|
fromDate: selectedDate.fromDate,
|
|
toDate: dt(selectedDate.fromDate).add(1, "day").format("YYYY-MM-DD"),
|
|
})
|
|
|
|
setIsSelectingFrom(true)
|
|
}
|
|
|
|
setIsOpen(false)
|
|
}, [name, setValue, selectedDate])
|
|
|
|
function showOnFocus() {
|
|
setIsOpen(true)
|
|
}
|
|
|
|
function handleSelectDate(selected: Date) {
|
|
/* check if selected date is not before todays date,
|
|
which happens when "Enter" key is pressed in any other input field of the form */
|
|
if (!dt(selected).isBefore(dt(), "day")) {
|
|
if (isSelectingFrom) {
|
|
setValue(name, {
|
|
fromDate: dt(selected).format("YYYY-MM-DD"),
|
|
toDate: undefined,
|
|
})
|
|
setIsSelectingFrom(false)
|
|
} else if (!dt(selectedDate.fromDate).isSame(dt(selected))) {
|
|
const fromDate = dt(selectedDate.fromDate)
|
|
const toDate = dt(selected)
|
|
if (toDate.isAfter(fromDate)) {
|
|
setValue(name, {
|
|
fromDate: selectedDate.fromDate,
|
|
toDate: toDate.format("YYYY-MM-DD"),
|
|
})
|
|
} else {
|
|
setValue(name, {
|
|
fromDate: toDate.format("YYYY-MM-DD"),
|
|
toDate: selectedDate.fromDate,
|
|
})
|
|
}
|
|
setIsSelectingFrom(true)
|
|
}
|
|
}
|
|
}
|
|
const closeIfOutside = useCallback(
|
|
(target: HTMLElement) => {
|
|
if (ref.current && target && !ref.current.contains(target)) {
|
|
close()
|
|
}
|
|
},
|
|
[close, ref]
|
|
)
|
|
|
|
function closeOnBlur(evt: FocusEvent) {
|
|
if (isOpen) {
|
|
const target = evt.relatedTarget as HTMLElement
|
|
closeIfOutside(target)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(evt: Event) {
|
|
if (isOpen) {
|
|
const target = evt.target as HTMLElement
|
|
closeIfOutside(target)
|
|
}
|
|
}
|
|
document.body.addEventListener("click", handleClickOutside)
|
|
return () => {
|
|
document.body.removeEventListener("click", handleClickOutside)
|
|
}
|
|
}, [closeIfOutside, isOpen])
|
|
|
|
const selectedFromDate = dt(selectedDate.fromDate)
|
|
.locale(lang)
|
|
.format("ddd D MMM")
|
|
const selectedToDate = !!selectedDate.toDate
|
|
? dt(selectedDate.toDate).locale(lang).format("ddd D MMM")
|
|
: ""
|
|
|
|
return (
|
|
<div
|
|
className={styles.container}
|
|
onBlur={(e) => {
|
|
closeOnBlur(e.nativeEvent)
|
|
}}
|
|
data-isopen={isOpen}
|
|
ref={ref}
|
|
>
|
|
<button
|
|
className={styles.btn}
|
|
onFocus={showOnFocus}
|
|
onClick={() => setIsOpen(true)}
|
|
type="button"
|
|
>
|
|
<Body className={styles.body} asChild>
|
|
<span>
|
|
{selectedFromDate} - {selectedToDate}
|
|
</span>
|
|
</Body>
|
|
</button>
|
|
<input {...register("date.fromDate")} type="hidden" />
|
|
<input {...register("date.toDate")} type="hidden" />
|
|
<div aria-modal className={styles.hideWrapper} role="dialog">
|
|
<DatePickerDesktop
|
|
close={close}
|
|
handleOnSelect={handleSelectDate}
|
|
locales={locales}
|
|
// DayPicker lib needs Daterange in form as below to show appropriate UI
|
|
selectedDate={{
|
|
from: selectedDate.fromDate,
|
|
to: selectedDate.toDate,
|
|
}}
|
|
/>
|
|
<DatePickerMobile
|
|
close={close}
|
|
handleOnSelect={handleSelectDate}
|
|
locales={locales}
|
|
// DayPicker lib needs Daterange in form as below to show appropriate UI
|
|
selectedDate={{
|
|
from: selectedDate.fromDate,
|
|
to: selectedDate.toDate,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|