125 lines
3.7 KiB
TypeScript
125 lines
3.7 KiB
TypeScript
"use client"
|
|
import { parseDate } from "@internationalized/date"
|
|
import { useController, useFormContext, useWatch } from "react-hook-form"
|
|
|
|
import { _ } from "@/lib/translation"
|
|
import { dt } from "@/lib/dt"
|
|
import { rangeArray } from "@/utils/rangeArray"
|
|
|
|
import {
|
|
DateInput,
|
|
DatePicker,
|
|
DateSegment,
|
|
Group,
|
|
} from "react-aria-components"
|
|
import Select from "./Select"
|
|
|
|
import styles from "./date.module.css"
|
|
|
|
import { DateName } from "./Select/select"
|
|
import type { DateProps } from "./date"
|
|
import type { Key } from "react-aria-components"
|
|
|
|
/** TODO: Get selecting with Enter-key to work */
|
|
export default function DateSelect({
|
|
control,
|
|
name,
|
|
registerOptions,
|
|
}: DateProps) {
|
|
const d = useWatch({ name })
|
|
const { setValue } = useFormContext()
|
|
const { field } = useController({
|
|
control,
|
|
name,
|
|
rules: registerOptions,
|
|
})
|
|
const currentYear = new Date().getFullYear()
|
|
const months = rangeArray(1, 12)
|
|
const years = rangeArray(1900, currentYear).reverse()
|
|
|
|
function handleOnSelect(select: Key, selector: DateName) {
|
|
/**
|
|
* Months are 0 index based and therefore we
|
|
* must subtract by 1 to get the selected month
|
|
*/
|
|
if (selector === DateName.month) {
|
|
select = Number(select) - 1
|
|
}
|
|
const newDate = dt(d).set(selector, Number(select))
|
|
setValue(name, newDate.format("YYYY-MM-DD"))
|
|
}
|
|
|
|
return (
|
|
<DatePicker
|
|
aria-label="Select date of birth"
|
|
granularity="day"
|
|
isRequired={!!registerOptions.required}
|
|
name={name}
|
|
ref={field.ref}
|
|
value={parseDate(d)}
|
|
>
|
|
<Group>
|
|
<DateInput className={styles.container}>
|
|
{(segment) => {
|
|
switch (segment.type) {
|
|
case "day":
|
|
let days = []
|
|
if (segment.maxValue && segment.minValue) {
|
|
days = rangeArray(segment.minValue, segment.maxValue)
|
|
} else {
|
|
days = Array.from(Array(segment.maxValue).keys()).map(
|
|
(i) => i + 1
|
|
)
|
|
}
|
|
return (
|
|
<DateSegment className={styles.day} segment={segment}>
|
|
<Select
|
|
aria-label={_("Day")}
|
|
items={days}
|
|
label={_("Day")}
|
|
name={DateName.date}
|
|
onSelect={handleOnSelect}
|
|
placeholder={_("DD")}
|
|
value={segment.value}
|
|
/>
|
|
</DateSegment>
|
|
)
|
|
case "month":
|
|
return (
|
|
<DateSegment className={styles.month} segment={segment}>
|
|
<Select
|
|
aria-label={_("Month")}
|
|
items={months}
|
|
label={_("Month")}
|
|
name={DateName.month}
|
|
onSelect={handleOnSelect}
|
|
placeholder={_("MM")}
|
|
value={segment.value}
|
|
/>
|
|
</DateSegment>
|
|
)
|
|
case "year":
|
|
return (
|
|
<DateSegment className={styles.year} segment={segment}>
|
|
<Select
|
|
aria-label={_("Year")}
|
|
items={years}
|
|
label={_("Year")}
|
|
name={DateName.year}
|
|
onSelect={handleOnSelect}
|
|
placeholder={_("YYYY")}
|
|
value={segment.value}
|
|
/>
|
|
</DateSegment>
|
|
)
|
|
default:
|
|
/** DateInput forces return of ReactElement */
|
|
return <></>
|
|
}
|
|
}}
|
|
</DateInput>
|
|
</Group>
|
|
</DatePicker>
|
|
)
|
|
}
|