A date input that uses the phone's native date picker on touch devices and the segmented date field with a calendar on desktop, with one ISO value. Use this when the same form runs on phones and desktops and each should get the date control it is best at.
registry/patterns/adaptive-date-input/adaptive-date-input.tsx
"use client"
import * as React from "react"
import { DateField } from "@/components/ui/date-field"
import { resolveLocale } from "@/components/ui/date-time-core"
export interface AdaptiveDateInputProps {
value?: string | null
defaultValue?: string | null
onValueChange?: (value: string | null) => void
min?: string
max?: string
forceNative?: boolean
label?: string
locale?: string
}
/** True on a device whose main pointer is a finger. */
function useCoarsePointer(force?: boolean) {
const [coarse, setCoarse] = React.useState(false)
React.useEffect(() => {
if (force !== undefined) return
const mq = window.matchMedia("(pointer: coarse)")
setCoarse(mq.matches)
const on = (e: MediaQueryListEvent) => setCoarse(e.matches)
mq.addEventListener("change", on)
return () => mq.removeEventListener("change", on)
}, [force])
return force ?? coarse
}
export function AdaptiveDateInput({
value,
defaultValue = null,
onValueChange,
min,
max,
forceNative,
label = "Date",
locale,
}: AdaptiveDateInputProps) {
const loc = resolveLocale(locale)
const native = useCoarsePointer(forceNative)
const [inner, setInner] = React.useState<string | null>(defaultValue)
const date = value === undefined ? inner : value
function change(next: string | null) {
if (value === undefined) setInner(next)
onValueChange?.(next)
}
return native ? (
<input
type="date"
aria-label={label}
min={min}
max={max}
value={date ?? ""}
onChange={(e) => change(e.target.value || null)}
className="border-input bg-background focus-visible:border-ring focus-visible:ring-ring/50 h-11 w-full max-w-60 rounded-md border px-3 text-base shadow-xs outline-none focus-visible:ring-[3px]"
/>
) : (
<DateField
label={label}
locale={loc}
min={min}
max={max}
value={date}
onValueChange={change}
/>
)
}