Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .storybook/modules/date-picker.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,25 @@
margin-bottom: 0.25rem;
}

.TimeInput {
width: 100%;
height: 2.5rem;
padding: 0 0.75rem;
margin-top: 0.75rem;
font-size: 0.875rem;
font-family: inherit;
background: transparent;
border: 1px solid var(--demo-border);
border-radius: 0.375rem;
color: var(--demo-neutral-fg);
outline: none;

&:focus {
border-color: var(--demo-coral-solid);
box-shadow: 0 0 0 1px var(--demo-coral-solid);
}
}

.Table {
width: 100%;
border-collapse: separate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export { RangeSelection } from './examples/range-selection'
export { RootProvider } from './examples/root-provider'
export { TriggerValue } from './examples/trigger-value'
export { Unavailable } from './examples/unavailable'
export { WithTime } from './examples/with-time'
export { YearPicker } from './examples/year-picker'
export { YearPickerRange } from './examples/year-picker-range'
100 changes: 100 additions & 0 deletions packages/react/src/components/date-picker/examples/with-time.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { CalendarDateTime, DateFormatter, getLocalTimeZone } from '@internationalized/date'
import { DatePicker, type DatePickerValueChangeDetails } from '@ark-ui/react/date-picker'
import { Portal } from '@ark-ui/react/portal'
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/date-picker.module.css'

const formatter = new DateFormatter('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
})

export const WithTime = () => {
const [value, setValue] = useState<CalendarDateTime[]>([new CalendarDateTime(2025, 1, 29, 14, 30)])

const timeValue = value[0]
? `${String(value[0].hour).padStart(2, '0')}:${String(value[0].minute).padStart(2, '0')}`
: ''

const onTimeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const [hours, minutes] = e.currentTarget.value.split(':').map(Number)
setValue((prev) => {
const current = prev[0] ?? new CalendarDateTime(2025, 1, 1, 0, 0)
return [current.set({ hour: hours, minute: minutes })]
})
}

const onDateChange = (details: DatePickerValueChangeDetails) => {
const newDate = details.value[0]
if (!newDate) return setValue([])
const prevTime = value[0] ?? { hour: 0, minute: 0 }
setValue([new CalendarDateTime(newDate.year, newDate.month, newDate.day, prevTime.hour, prevTime.minute)])
}

return (
<DatePicker.Root className={styles.Root} value={value} onValueChange={onDateChange} closeOnSelect={false}>
<DatePicker.Label className={styles.Label}>Date and time</DatePicker.Label>
<DatePicker.Control className={styles.Control}>
<DatePicker.Trigger className={button.Root} style={{ width: '100%', justifyContent: 'space-between' }}>
{value[0] ? formatter.format(value[0].toDate(getLocalTimeZone())) : 'Select date and time'}
<CalendarIcon />
</DatePicker.Trigger>
</DatePicker.Control>
<Portal>
<DatePicker.Positioner>
<DatePicker.Content className={styles.Content}>
<DatePicker.View view="day" className={styles.View}>
<DatePicker.Context>
{(datePicker) => (
<>
<DatePicker.ViewControl className={styles.ViewControl}>
<DatePicker.PrevTrigger className={styles.PrevTrigger}>
<ChevronLeftIcon />
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger className={styles.ViewTrigger}>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger className={styles.NextTrigger}>
<ChevronRightIcon />
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table className={styles.Table}>
<DatePicker.TableHead className={styles.TableHead}>
<DatePicker.TableRow className={styles.TableRow}>
{datePicker.weekDays.map((weekDay, id) => (
<DatePicker.TableHeader className={styles.TableHeader} key={id}>
{weekDay.short}
</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody className={styles.TableBody}>
{datePicker.weeks.map((week, id) => (
<DatePicker.TableRow className={styles.TableRow} key={id}>
{week.map((day, id) => (
<DatePicker.TableCell className={styles.TableCell} key={id} value={day}>
<DatePicker.TableCellTrigger className={styles.TableCellTrigger}>
{day.day}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
<input type="time" value={timeValue} onChange={onTimeChange} className={styles.TimeInput} />
</>
)}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export { RangeSelection } from './examples/range-selection'
export { RootProvider } from './examples/root-provider'
export { TriggerValue } from './examples/trigger-value'
export { Unavailable } from './examples/unavailable'
export { WithTime } from './examples/with-time'
export { YearPicker } from './examples/year-picker'
107 changes: 107 additions & 0 deletions packages/solid/src/components/date-picker/examples/with-time.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { CalendarDateTime, DateFormatter, getLocalTimeZone } from '@internationalized/date'
import { DatePicker, type DatePickerValueChangeDetails } from '@ark-ui/solid/date-picker'
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { Index, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/date-picker.module.css'

const formatter = new DateFormatter('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
})

export const WithTime = () => {
const [value, setValue] = createSignal<CalendarDateTime[]>([new CalendarDateTime(2025, 1, 29, 14, 30)])

const timeValue = () => {
const v = value()[0]
return v ? `${String(v.hour).padStart(2, '0')}:${String(v.minute).padStart(2, '0')}` : ''
}

const onTimeChange = (e: Event & { currentTarget: HTMLInputElement }) => {
const [hours, minutes] = e.currentTarget.value.split(':').map(Number)
setValue((prev) => {
const current = prev[0] ?? new CalendarDateTime(2025, 1, 1, 0, 0)
return [current.set({ hour: hours, minute: minutes })]
})
}

const onDateChange = (details: DatePickerValueChangeDetails) => {
const newDate = details.value[0]
if (!newDate) return setValue([])
const prevTime = value()[0] ?? { hour: 0, minute: 0 }
setValue([new CalendarDateTime(newDate.year, newDate.month, newDate.day, prevTime.hour, prevTime.minute)])
}

return (
<DatePicker.Root class={styles.Root} value={value()} onValueChange={onDateChange} closeOnSelect={false}>
<DatePicker.Label class={styles.Label}>Date and time</DatePicker.Label>
<DatePicker.Control class={styles.Control}>
<DatePicker.Trigger class={button.Root} style={{ width: '100%', 'justify-content': 'space-between' }}>
{value()[0] ? formatter.format(value()[0].toDate(getLocalTimeZone())) : 'Select date and time'}
<CalendarIcon />
</DatePicker.Trigger>
</DatePicker.Control>
<Portal>
<DatePicker.Positioner>
<DatePicker.Content class={styles.Content}>
<DatePicker.View view="day" class={styles.View}>
<DatePicker.Context>
{(context) => (
<>
<DatePicker.ViewControl class={styles.ViewControl}>
<DatePicker.PrevTrigger class={styles.PrevTrigger}>
<ChevronLeftIcon />
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger class={styles.ViewTrigger}>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger class={styles.NextTrigger}>
<ChevronRightIcon />
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table class={styles.Table}>
<DatePicker.TableHead class={styles.TableHead}>
<DatePicker.TableRow class={styles.TableRow}>
<Index each={context().weekDays}>
{(weekDay) => (
<DatePicker.TableHeader class={styles.TableHeader}>
{weekDay().short}
</DatePicker.TableHeader>
)}
</Index>
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody class={styles.TableBody}>
<Index each={context().weeks}>
{(week) => (
<DatePicker.TableRow class={styles.TableRow}>
<Index each={week()}>
{(day) => (
<DatePicker.TableCell class={styles.TableCell} value={day()}>
<DatePicker.TableCellTrigger class={styles.TableCellTrigger}>
{day().day}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
)}
</Index>
</DatePicker.TableRow>
)}
</Index>
</DatePicker.TableBody>
</DatePicker.Table>
<input type="time" value={timeValue()} onInput={onTimeChange} class={styles.TimeInput} />
</>
)}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import RangeSelectionExample from './examples/range-selection.svelte'
import RootProviderExample from './examples/root-provider.svelte'
import TriggerValueExample from './examples/trigger-value.svelte'
import UnavailableExample from './examples/unavailable.svelte'
import WithTimeExample from './examples/with-time.svelte'
import YearPickerExample from './examples/year-picker.svelte'

const meta: Meta = {
Expand Down Expand Up @@ -119,6 +120,12 @@ export const Unavailable = {
}),
}

export const WithTime = {
render: () => ({
Component: WithTimeExample,
}),
}

export const YearPicker = {
render: () => ({
Component: YearPickerExample,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<script lang="ts">
import { CalendarDateTime, DateFormatter, getLocalTimeZone } from '@internationalized/date'
import { DatePicker, type DatePickerValueChangeDetails } from '@ark-ui/svelte/date-picker'
import { Portal } from '@ark-ui/svelte/portal'
import CalendarIcon from 'lucide-svelte/icons/calendar'
import ChevronLeftIcon from 'lucide-svelte/icons/chevron-left'
import ChevronRightIcon from 'lucide-svelte/icons/chevron-right'
import button from 'styles/button.module.css'
import styles from 'styles/date-picker.module.css'

const formatter = new DateFormatter('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
})

let value = $state<CalendarDateTime[]>([new CalendarDateTime(2025, 1, 29, 14, 30)])

const timeValue = $derived(
value[0] ? `${String(value[0].hour).padStart(2, '0')}:${String(value[0].minute).padStart(2, '0')}` : '',
)

const onTimeChange = (e: Event & { currentTarget: HTMLInputElement }) => {
const [hours, minutes] = e.currentTarget.value.split(':').map(Number)
const current = value[0] ?? new CalendarDateTime(2025, 1, 1, 0, 0)
value = [current.set({ hour: hours, minute: minutes })]
}

const onDateChange = (details: DatePickerValueChangeDetails) => {
const newDate = details.value[0]
if (!newDate) {
value = []
return
}
const prevTime = value[0] ?? { hour: 0, minute: 0 }
value = [new CalendarDateTime(newDate.year, newDate.month, newDate.day, prevTime.hour, prevTime.minute)]
}
</script>

<DatePicker.Root class={styles.Root} {value} onValueChange={onDateChange} closeOnSelect={false}>
<DatePicker.Label class={styles.Label}>Date and time</DatePicker.Label>
<DatePicker.Control class={styles.Control}>
<DatePicker.Trigger class={button.Root} style="width: 100%; justify-content: space-between">
{value[0] ? formatter.format(value[0].toDate(getLocalTimeZone())) : 'Select date and time'}
<CalendarIcon />
</DatePicker.Trigger>
</DatePicker.Control>
<Portal>
<DatePicker.Positioner>
<DatePicker.Content class={styles.Content}>
<DatePicker.View view="day" class={styles.View}>
<DatePicker.Context>
{#snippet render(datePicker)}
<DatePicker.ViewControl class={styles.ViewControl}>
<DatePicker.PrevTrigger class={styles.PrevTrigger}>
<ChevronLeftIcon />
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger class={styles.ViewTrigger}>
<DatePicker.RangeText />
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger class={styles.NextTrigger}>
<ChevronRightIcon />
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table class={styles.Table}>
<DatePicker.TableHead class={styles.TableHead}>
<DatePicker.TableRow class={styles.TableRow}>
{#each datePicker().weekDays as weekDay}
<DatePicker.TableHeader class={styles.TableHeader}>{weekDay.short}</DatePicker.TableHeader>
{/each}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody class={styles.TableBody}>
{#each datePicker().weeks as week}
<DatePicker.TableRow class={styles.TableRow}>
{#each week as day}
<DatePicker.TableCell class={styles.TableCell} value={day}>
<DatePicker.TableCellTrigger class={styles.TableCellTrigger}>{day.day}</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
{/each}
</DatePicker.TableRow>
{/each}
</DatePicker.TableBody>
</DatePicker.Table>
<input type="time" value={timeValue} oninput={onTimeChange} class={styles.TimeInput} />
{/snippet}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import RangeSelectionExample from './examples/range-selection.vue'
import RootProviderExample from './examples/root-provider.vue'
import TriggerValueExample from './examples/trigger-value.vue'
import UnavailableExample from './examples/unavailable.vue'
import WithTimeExample from './examples/with-time.vue'
import YearPickerExample from './examples/year-picker.vue'

const meta: Meta = {
Expand Down Expand Up @@ -136,6 +137,13 @@ export const Unavailable = {
}),
}

export const WithTime = {
render: () => ({
components: { Component: WithTimeExample },
template: '<Component />',
}),
}

export const YearPicker = {
render: () => ({
components: { Component: YearPickerExample },
Expand Down
Loading
Loading