[InputDate] Implement component
https://www.shadcn-vue.com/docs/components/date-picker.html
I think we can just use a Button + Popover + Calendar for this instead of implementing:
- https://www.radix-vue.com/components/date-field.html
- https://www.radix-vue.com/components/date-picker.html
- https://www.radix-vue.com/components/date-range-field.html
- https://www.radix-vue.com/components/date-range-picker.html
Why are you not considering adding a DateField to the DatePicker?
I'm a bit hesitant on this actually, are date pickers not mostly used with buttons instead of inputs? 🤔
I'm a bit hesitant on this actually, are date pickers not mostly used with buttons instead of inputs? 🤔
If buttons do have the edge, it's not by much, in my experience. In companies related to booking trains and flights, it's about even - some allow you to fill in fields directly, others use only buttons.
I agree with @mehaac
My vision of the component: DateField with scope slot #default, by default it is UInput, whoever needs it will pass UButton to it.
<script setup lang="ts">
const date = ref(new Date())
</script>
<template>
<!-- Default -->
<UDatePicker v-model="date" />
<!-- Replace UInput to UButton -->
<UDatePicker>
<template #default="{ open, date }">
<UButton :label="date.toLocaleDateString()" @click="open = true" />
</template>
</UDatePicker>
</template>
Renaming this InputDate for consistency with InputNumber.
For ergonomics and speed on desktop, the keyboard input and navigation of the Reka UI Date Picker and Date Range Picker is way superior to just a calendar in a popup, IMO.
In terms of accessibility, i think the inputs are also a must have 👍 Even if the calendar is keyboard enabled, its way easier to set the value through the inputs.
Hi, Do we have any time estimate for this?
Hi,
What is the update on this? The Calendar seems to be very limited in terms of output. There is no way to get a date with current time it seems. Also no option for a time picker at the moment.
I'll be starting working on this soon to ship it in the next minor 😊
Isn't this "done by example" with https://ui.nuxt.com/components/calendar#as-a-datepicker ?
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
</script>
<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
{{ modelValue ? df.format(modelValue.toDate(getLocalTimeZone())) : 'Select a date' }}
</UButton>
<template #content>
<UCalendar v-model="modelValue" class="p-2" />
</template>
</UPopover>
</template>
Hello! Do we have any updates on this?
Here is a component I created that recreates the calendar with input field.
<template>
<UPopover
v-model:open="isOpen"
:content="{ side: 'bottom', align: 'start', sideOffset: 8 }"
mode="click">
<UInput
:model-value="formattedDate"
:placeholder="placeholder"
:icon="icon"
readonly
class="cursor-pointer"
:ui="{ base: 'text-left' }" />
<template #content>
<div class="p-4">
<UCalendar
:model-value="calendarValue"
:number-of-months="3"
@update:model-value="handleDateChange" />
</div>
</template>
</UPopover>
</template>
<script setup lang="ts">
import { format } from 'date-fns'
import { CalendarDate } from '@internationalized/date'
import type { DateValue } from '@internationalized/date'
/**
* Component Props Interface
*/
interface Props {
placeholder?: string
icon?: string
}
// Define props with destructuring and defaults
const { placeholder = 'Select date', icon = 'ph:calendar-dots-duotone' } =
defineProps<Props>()
// Use defineModel for v-model - accepts Date objects or ISO strings
const modelValue = defineModel<Date | string | null>({ default: null })
// Popover open state
const isOpen = ref(false)
/**
* Computed: Calendar Value
* Converts JavaScript Date or ISO string to DateValue for UCalendar
*/
const calendarValue = computed(() => {
if (!modelValue.value) return undefined
try {
// Handle both Date objects and ISO strings
const date = typeof modelValue.value === 'string'
? new Date(modelValue.value)
: modelValue.value
return new CalendarDate(
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
)
} catch (error) {
return undefined
}
})
/**
* Computed: Formatted Date Display
* Formats the selected date for display in the input
*/
const formattedDate = computed(() => {
if (!modelValue.value) return ''
try {
// Handle both Date objects and ISO strings
const date = typeof modelValue.value === 'string'
? new Date(modelValue.value)
: modelValue.value
return format(date, 'MMM dd, yyyy')
} catch (error) {
return ''
}
})
/**
* Date Change Handler
* Handles the calendar date selection and converts DateValue to Date
*/
const handleDateChange = (newDate: any) => {
// Handle only single date selection (DateValue)
if (
!newDate ||
Array.isArray(newDate) ||
(typeof newDate === 'object' && 'start' in newDate)
) {
modelValue.value = null
isOpen.value = false
return
}
// Convert DateValue to Date object
try {
const dateValue = newDate as DateValue
const jsDate = new Date(
dateValue.year,
dateValue.month - 1,
dateValue.day
)
modelValue.value = jsDate
isOpen.value = false // Close popover after selection
} catch (error) {
modelValue.value = null
isOpen.value = false
}
}
</script>
Use like this
<InputCalendar v-model="date" icon="icon-name" />