Date Picker
A component that allows users to select a date from a calendar.
'use client'
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
import { Button } from '~/components/ui/button'
import { DatePicker } from '~/components/ui/date-picker'
import { IconButton } from '~/components/ui/icon-button'
import { Input } from '~/components/ui/input'
export const Demo = (props: DatePicker.RootProps) => {
return (
<DatePicker.Root
positioning={{ sameWidth: true }}
startOfWeek={1}
selectionMode="range"
{...props}
>
<DatePicker.Label>Date Picker</DatePicker.Label>
<DatePicker.Control>
<DatePicker.Input index={0} asChild>
<Input />
</DatePicker.Input>
<DatePicker.Input index={1} asChild>
<Input />
</DatePicker.Input>
<DatePicker.Trigger asChild>
<IconButton variant="outline" aria-label="Open date picker">
<CalendarIcon />
</IconButton>
</DatePicker.Trigger>
</DatePicker.Control>
<DatePicker.Positioner>
<DatePicker.Content>
<DatePicker.View view="day">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronLeftIcon />
</IconButton>
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger asChild>
<Button variant="ghost" size="sm">
<DatePicker.RangeText />
</Button>
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronRightIcon />
</IconButton>
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api.weekDays.map((weekDay, id) => (
<DatePicker.TableHeader key={id}>{weekDay.narrow}</DatePicker.TableHeader>
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api.weeks.map((week, id) => (
<DatePicker.TableRow key={id}>
{week.map((day, id) => (
<DatePicker.TableCell key={id} value={day}>
<DatePicker.TableCellTrigger asChild>
<IconButton variant="ghost">{day.day}</IconButton>
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="month">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronLeftIcon />
</IconButton>
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger asChild>
<Button variant="ghost" size="sm">
<DatePicker.RangeText />
</Button>
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronRightIcon />
</IconButton>
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api.getMonthsGrid({ columns: 4, format: 'short' }).map((months, id) => (
<DatePicker.TableRow key={id}>
{months.map((month, id) => (
<DatePicker.TableCell key={id} value={month.value}>
<DatePicker.TableCellTrigger asChild>
<Button variant="ghost">{month.label}</Button>
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="year">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronLeftIcon />
</IconButton>
</DatePicker.PrevTrigger>
<DatePicker.ViewTrigger asChild>
<Button variant="ghost" size="sm">
<DatePicker.RangeText />
</Button>
</DatePicker.ViewTrigger>
<DatePicker.NextTrigger asChild>
<IconButton variant="ghost" size="sm">
<ChevronRightIcon />
</IconButton>
</DatePicker.NextTrigger>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
{api.getYearsGrid({ columns: 4 }).map((years, id) => (
<DatePicker.TableRow key={id}>
{years.map((year, id) => (
<DatePicker.TableCell key={id} value={year.value}>
<DatePicker.TableCellTrigger asChild>
<Button variant="ghost">{year.label}</Button>
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</DatePicker.Root>
)
}
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-solid'
import { For } from 'solid-js'
import { Button } from '~/components/ui/button'
import { DatePicker } from '~/components/ui/date-picker'
import { IconButton } from '~/components/ui/icon-button'
import { Input } from '~/components/ui/input'
export const Demo = (props: DatePicker.RootProps) => {
return (
<DatePicker.Root
positioning={{ sameWidth: true }}
startOfWeek={1}
selectionMode="range"
{...props}
>
<DatePicker.Label>Date Picker</DatePicker.Label>
<DatePicker.Control>
<DatePicker.Input index={0} asChild={(inputProps) => <Input {...inputProps()} />} />
<DatePicker.Input index={1} asChild={(inputProps) => <Input {...inputProps()} />} />
<DatePicker.Trigger
asChild={(triggerProps) => (
<IconButton variant="outline" aria-label="Open date picker" {...triggerProps()}>
<CalendarIcon />
</IconButton>
)}
/>
</DatePicker.Control>
<DatePicker.Positioner>
<DatePicker.Content>
<DatePicker.View view="day">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronLeftIcon />
</IconButton>
)}
/>
<DatePicker.ViewTrigger
asChild={(triggerProps) => (
<Button variant="ghost" size="sm" {...triggerProps()}>
<DatePicker.RangeText />
</Button>
)}
/>
<DatePicker.NextTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronRightIcon />
</IconButton>
)}
/>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
<For each={api().weekDays}>
{(weekDay) => (
<DatePicker.TableHeader>{weekDay.narrow}</DatePicker.TableHeader>
)}
</For>
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
<For each={api().weeks}>
{(week) => (
<DatePicker.TableRow>
<For each={week}>
{(day) => (
<DatePicker.TableCell value={day}>
<DatePicker.TableCellTrigger
asChild={(cellProps) => (
<IconButton variant="ghost" {...cellProps()}>
{day.day}
</IconButton>
)}
/>
</DatePicker.TableCell>
)}
</For>
</DatePicker.TableRow>
)}
</For>
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="month">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronLeftIcon />
</IconButton>
)}
/>
<DatePicker.ViewTrigger
asChild={(triggerProps) => (
<Button variant="ghost" size="sm" {...triggerProps()}>
<DatePicker.RangeText />
</Button>
)}
/>
<DatePicker.NextTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronRightIcon />
</IconButton>
)}
/>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
<For each={api().getMonthsGrid({ columns: 4, format: 'short' })}>
{(months) => (
<DatePicker.TableRow>
<For each={months}>
{(month) => (
<DatePicker.TableCell value={month.value}>
<DatePicker.TableCellTrigger
asChild={(cellProps) => (
<Button variant="ghost" {...cellProps()}>
{month.label}
</Button>
)}
/>
</DatePicker.TableCell>
)}
</For>
</DatePicker.TableRow>
)}
</For>
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="year">
<DatePicker.Context>
{(api) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronLeftIcon />
</IconButton>
)}
/>
<DatePicker.ViewTrigger
asChild={(triggerProps) => (
<Button variant="ghost" size="sm" {...triggerProps()}>
<DatePicker.RangeText />
</Button>
)}
/>
<DatePicker.NextTrigger
asChild={(triggerProps) => (
<IconButton variant="ghost" size="sm" {...triggerProps()}>
<ChevronRightIcon />
</IconButton>
)}
/>
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableBody>
<For each={api().getYearsGrid({ columns: 4 })}>
{(years) => (
<DatePicker.TableRow>
<For each={years}>
{(year) => (
<DatePicker.TableCell value={year.value}>
<DatePicker.TableCellTrigger
asChild={(cellProps) => (
<Button variant="ghost" {...cellProps()}>
{year.label}
</Button>
)}
/>
</DatePicker.TableCell>
)}
</For>
</DatePicker.TableRow>
)}
</For>
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</DatePicker.Root>
)
}
Usage
import { DatePicker } from '~/components/ui/date-picker'
Installation
npx @park-ui/cli components add date-picker
1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/date-picker.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { DatePicker } from '@ark-ui/react/date-picker'
import { type DatePickerVariantProps, datePicker } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(datePicker)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, DatePicker.RootProviderBaseProps>, DatePickerVariantProps>
>(DatePicker.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, DatePicker.RootBaseProps>, DatePickerVariantProps>
>(DatePicker.Root, 'root')
export const ClearTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.ClearTriggerBaseProps>
>(DatePicker.ClearTrigger, 'clearTrigger')
export const Content = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.ContentBaseProps>
>(DatePicker.Content, 'content')
export const Control = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.ControlBaseProps>
>(DatePicker.Control, 'control')
export const Input = withContext<
HTMLInputElement,
Assign<HTMLStyledProps<'input'>, DatePicker.InputBaseProps>
>(DatePicker.Input, 'input')
export const Label = withContext<
HTMLLabelElement,
Assign<HTMLStyledProps<'label'>, DatePicker.LabelBaseProps>
>(DatePicker.Label, 'label')
export const MonthSelect = withContext<
HTMLSelectElement,
Assign<HTMLStyledProps<'select'>, DatePicker.MonthSelectBaseProps>
>(DatePicker.MonthSelect, 'monthSelect')
export const NextTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.NextTriggerBaseProps>
>(DatePicker.NextTrigger, 'nextTrigger')
export const Positioner = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.PositionerBaseProps>
>(DatePicker.Positioner, 'positioner')
export const PresetTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.PresetTriggerBaseProps>
>(DatePicker.PresetTrigger, 'presetTrigger')
export const PrevTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.PrevTriggerBaseProps>
>(DatePicker.PrevTrigger, 'prevTrigger')
export const RangeText = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.RangeTextBaseProps>
>(DatePicker.RangeText, 'rangeText')
export const TableBody = withContext<
HTMLTableSectionElement,
Assign<HTMLStyledProps<'tbody'>, DatePicker.TableBodyBaseProps>
>(DatePicker.TableBody, 'tableBody')
export const TableCell = withContext<
HTMLTableCellElement,
Assign<HTMLStyledProps<'td'>, DatePicker.TableCellBaseProps>
>(DatePicker.TableCell, 'tableCell')
export const TableCellTrigger = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.TableCellTriggerBaseProps>
>(DatePicker.TableCellTrigger, 'tableCellTrigger')
export const TableHead = withContext<
HTMLTableSectionElement,
Assign<HTMLStyledProps<'thead'>, DatePicker.TableHeadBaseProps>
>(DatePicker.TableHead, 'tableHead')
export const TableHeader = withContext<
HTMLTableCellElement,
Assign<HTMLStyledProps<'th'>, DatePicker.TableHeaderBaseProps>
>(DatePicker.TableHeader, 'tableHeader')
export const Table = withContext<
HTMLTableElement,
Assign<HTMLStyledProps<'table'>, DatePicker.TableBaseProps>
>(DatePicker.Table, 'table')
export const TableRow = withContext<
HTMLTableRowElement,
Assign<HTMLStyledProps<'tr'>, DatePicker.TableRowBaseProps>
>(DatePicker.TableRow, 'tableRow')
export const Trigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.TriggerBaseProps>
>(DatePicker.Trigger, 'trigger')
export const ViewControl = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.ViewControlBaseProps>
>(DatePicker.ViewControl, 'viewControl')
export const View = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, DatePicker.ViewBaseProps>
>(DatePicker.View, 'view')
export const ViewTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, DatePicker.ViewTriggerBaseProps>
>(DatePicker.ViewTrigger, 'viewTrigger')
export const YearSelect = withContext<
HTMLSelectElement,
Assign<HTMLStyledProps<'select'>, DatePicker.YearSelectBaseProps>
>(DatePicker.YearSelect, 'yearSelect')
export { DatePickerContext as Context } from '@ark-ui/react/date-picker'
import { type Assign, DatePicker } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type DatePickerVariantProps, datePicker } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(datePicker)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, DatePicker.RootProviderBaseProps>, DatePickerVariantProps>
>(DatePicker.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, DatePicker.RootBaseProps>, DatePickerVariantProps>
>(DatePicker.Root, 'root')
export const ClearTrigger = withContext<
Assign<HTMLStyledProps<'button'>, DatePicker.ClearTriggerBaseProps>
>(DatePicker.ClearTrigger, 'clearTrigger')
export const Content = withContext<Assign<HTMLStyledProps<'div'>, DatePicker.ContentBaseProps>>(
DatePicker.Content,
'content',
)
export const Control = withContext<Assign<HTMLStyledProps<'div'>, DatePicker.ControlBaseProps>>(
DatePicker.Control,
'control',
)
export const Input = withContext<Assign<HTMLStyledProps<'input'>, DatePicker.InputBaseProps>>(
DatePicker.Input,
'input',
)
export const Label = withContext<Assign<HTMLStyledProps<'label'>, DatePicker.LabelBaseProps>>(
DatePicker.Label,
'label',
)
export const MonthSelect = withContext<
Assign<HTMLStyledProps<'select'>, DatePicker.MonthSelectBaseProps>
>(DatePicker.MonthSelect, 'monthSelect')
export const NextTrigger = withContext<
Assign<HTMLStyledProps<'button'>, DatePicker.NextTriggerBaseProps>
>(DatePicker.NextTrigger, 'nextTrigger')
export const Positioner = withContext<
Assign<HTMLStyledProps<'div'>, DatePicker.PositionerBaseProps>
>(DatePicker.Positioner, 'positioner')
export const PresetTrigger = withContext<
Assign<HTMLStyledProps<'button'>, DatePicker.PresetTriggerBaseProps>
>(DatePicker.PresetTrigger, 'presetTrigger')
export const PrevTrigger = withContext<
Assign<HTMLStyledProps<'button'>, DatePicker.PrevTriggerBaseProps>
>(DatePicker.PrevTrigger, 'prevTrigger')
export const RangeText = withContext<Assign<HTMLStyledProps<'div'>, DatePicker.RangeTextBaseProps>>(
DatePicker.RangeText,
'rangeText',
)
export const TableBody = withContext<
Assign<HTMLStyledProps<'tbody'>, DatePicker.TableBodyBaseProps>
>(DatePicker.TableBody, 'tableBody')
export const TableCell = withContext<Assign<HTMLStyledProps<'td'>, DatePicker.TableCellBaseProps>>(
DatePicker.TableCell,
'tableCell',
)
export const TableCellTrigger = withContext<
Assign<HTMLStyledProps<'div'>, DatePicker.TableCellTriggerBaseProps>
>(DatePicker.TableCellTrigger, 'tableCellTrigger')
export const TableHead = withContext<
Assign<HTMLStyledProps<'thead'>, DatePicker.TableHeadBaseProps>
>(DatePicker.TableHead, 'tableHead')
export const TableHeader = withContext<
Assign<HTMLStyledProps<'th'>, DatePicker.TableHeaderBaseProps>
>(DatePicker.TableHeader, 'tableHeader')
export const Table = withContext<Assign<HTMLStyledProps<'table'>, DatePicker.TableBaseProps>>(
DatePicker.Table,
'table',
)
export const TableRow = withContext<Assign<HTMLStyledProps<'tr'>, DatePicker.TableRowBaseProps>>(
DatePicker.TableRow,
'tableRow',
)
export const Trigger = withContext<Assign<HTMLStyledProps<'button'>, DatePicker.TriggerBaseProps>>(
DatePicker.Trigger,
'trigger',
)
export const ViewControl = withContext<
Assign<HTMLStyledProps<'div'>, DatePicker.ViewControlBaseProps>
>(DatePicker.ViewControl, 'viewControl')
export const View = withContext<Assign<HTMLStyledProps<'div'>, DatePicker.ViewBaseProps>>(
DatePicker.View,
'view',
)
export const ViewTrigger = withContext<
Assign<HTMLStyledProps<'button'>, DatePicker.ViewTriggerBaseProps>
>(DatePicker.ViewTrigger, 'viewTrigger')
export const YearSelect = withContext<
Assign<HTMLStyledProps<'select'>, DatePicker.YearSelectBaseProps>
>(DatePicker.YearSelect, 'yearSelect')
export { DatePickerContext as Context } from '@ark-ui/solid'
No snippet found
2
Add Re-Export
To improve the developer experience, re-export the styled primitives in~/components/ui/date-picker.tsx
.
export * as DatePicker from './styled/date-picker'
export * as DatePicker from './styled/date-picker'
3
Integrate Recipe
If you're not using @park-ui/preset
, add the following recipe to yourpanda.config.ts
:
import { datePickerAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const datePicker = defineSlotRecipe({
className: 'datePicker',
slots: [...datePickerAnatomy.keys()],
base: {
root: {
display: 'flex',
flexDirection: 'column',
gap: '1.5',
},
content: {
background: 'bg.default',
borderRadius: 'l3',
boxShadow: 'lg',
display: 'flex',
flexDirection: 'column',
gap: '3',
p: '4',
width: '344px',
zIndex: 'dropdown',
_open: {
animation: 'fadeIn 0.25s ease-out',
},
_closed: {
animation: 'fadeOut 0.2s ease-out',
},
_hidden: {
display: 'none',
},
},
control: {
display: 'flex',
flexDirection: 'row',
gap: '2',
},
label: {
color: 'fg.default',
fontWeight: 'medium',
textStyle: 'sm',
},
tableHeader: {
color: 'fg.muted',
fontWeight: 'semibold',
height: '10',
textStyle: 'sm',
},
viewControl: {
display: 'flex',
gap: '2',
justifyContent: 'space-between',
},
table: {
width: 'full',
borderCollapse: 'separate',
borderSpacing: '1',
m: '-1',
},
tableCell: {
textAlign: 'center',
},
tableCellTrigger: {
width: '100%',
_today: {
_before: {
content: "'−'",
color: 'colorPalette.default',
position: 'absolute',
marginTop: '6',
},
},
'&[data-in-range]': {
background: 'bg.muted',
},
_selected: {
_before: {
color: 'colorPalette.fg',
},
},
},
view: {
display: 'flex',
flexDirection: 'column',
gap: '3',
_hidden: {
display: 'none',
},
},
},
})