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
2 changes: 2 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Added

- **Date Picker**: Added `ValueText` component for displaying selected date value(s) with placeholder support and render
prop for custom formatting
- **Scroll Area**: Added overflow CSS variables (`--scroll-area-overflow-{x,y}-{start,end}`) for scroll fade effects
- **Slider**: Added `thumbCollisionBehavior` prop (`none`, `push`, `swap`)
- **Steps**: Added `isStepValid`, `isStepSkippable`, and `onStepInvalid` for validation support
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { DateValue } from '@zag-js/date-picker'
import { Fragment, forwardRef } from 'react'
import type { Assign } from '../../types'
import { type HTMLProps, type PolymorphicProps, ark } from '../factory'
import { datePickerAnatomy } from './date-picker.anatomy'
import { useDatePickerContext } from './use-date-picker-context'

export interface DatePickerValueTextRenderProps {
value: DateValue
index: number
valueAsString: string
remove: () => void
}

export interface DatePickerValueTextBaseProps extends PolymorphicProps {
/**
* Text to display when no date is selected.
*/
placeholder?: string | undefined
/**
* A function to render each selected date value.
* When provided, each date in the selection will be rendered using this function.
*/
children?: ((props: DatePickerValueTextRenderProps) => React.ReactNode) | undefined
/**
* The separator to use between multiple date values when using default rendering.
* @default ", "
*/
separator?: string | undefined
}

export interface DatePickerValueTextProps extends Assign<HTMLProps<'span'>, DatePickerValueTextBaseProps> {}

export const DatePickerValueText = forwardRef<HTMLSpanElement, DatePickerValueTextProps>((props, ref) => {
const { children, placeholder, separator = ', ', ...localProps } = props
const datePicker = useDatePickerContext()

const hasValue = datePicker.value.length > 0

if (typeof children === 'function') {
return (
<Fragment>
{hasValue
? datePicker.value.map((value, index) => (
<Fragment key={index}>
{children({
value,
index,
valueAsString: datePicker.valueAsString[index],
remove: () => {
datePicker.setValue(datePicker.value.filter((_, i) => i !== index))
},
})}
</Fragment>
))
: placeholder}
</Fragment>
)
}

return (
<ark.span {...datePickerAnatomy.build().valueText.attrs} {...localProps} ref={ref}>
{hasValue ? datePicker.valueAsString.join(separator) : placeholder}
</ark.span>
)
})

DatePickerValueText.displayName = 'DatePickerValueText'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { anatomy } from '@zag-js/date-picker'

export const datePickerAnatomy = anatomy.extendWith('view')
export const datePickerAnatomy = anatomy.extendWith('view', 'valueText')
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { MultiSelection } from './examples/multi-selection'
export { Presets } from './examples/presets'
export { RangeSelection } from './examples/range-selection'
export { RootProvider } from './examples/root-provider'
export { TriggerValue } from './examples/trigger-value'
export { Unavailable } from './examples/unavailable'
export { YearPicker } from './examples/year-picker'
export { YearPickerRange } from './examples/year-picker-range'
6 changes: 6 additions & 0 deletions packages/react/src/components/date-picker/date-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export {
type DatePickerRangeTextBaseProps as RangeTextBaseProps,
type DatePickerRangeTextProps as RangeTextProps,
} from './date-picker-range-text'
export {
DatePickerValueText as ValueText,
type DatePickerValueTextBaseProps as ValueTextBaseProps,
type DatePickerValueTextProps as ValueTextProps,
type DatePickerValueTextRenderProps as ValueTextRenderProps,
} from './date-picker-value-text'
export {
DatePickerRoot as Root,
type DatePickerRootBaseProps as RootBaseProps,
Expand Down
136 changes: 136 additions & 0 deletions packages/react/src/components/date-picker/examples/trigger-value.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { DatePicker } from '@ark-ui/react/date-picker'
import { Portal } from '@ark-ui/react/portal'
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/date-picker.module.css'

export const TriggerValue = () => {
return (
<DatePicker.Root className={styles.Root}>
<DatePicker.Label className={styles.Label}>Date of birth</DatePicker.Label>
<DatePicker.Control className={styles.Control}>
<DatePicker.Trigger className={button.Root} style={{ width: '100%', justifyContent: 'space-between' }}>
<DatePicker.ValueText placeholder="Select date" />
<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>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="month" 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.TableBody className={styles.TableBody}>
{datePicker.getMonthsGrid({ columns: 4, format: 'short' }).map((months, id) => (
<DatePicker.TableRow className={styles.TableRow} key={id}>
{months.map((month, id) => (
<DatePicker.TableCell className={styles.TableCell} key={id} value={month.value}>
<DatePicker.TableCellTrigger className={styles.TableCellTrigger}>
{month.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
<DatePicker.View view="year" 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.TableBody className={styles.TableBody}>
{datePicker.getYearsGrid({ columns: 4 }).map((years, id) => (
<DatePicker.TableRow className={styles.TableRow} key={id}>
{years.map((year, id) => (
<DatePicker.TableCell className={styles.TableCell} key={id} value={year.value}>
<DatePicker.TableCellTrigger className={styles.TableCellTrigger}>
{year.label}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.Context>
</DatePicker.View>
</DatePicker.Content>
</DatePicker.Positioner>
</Portal>
</DatePicker.Root>
)
}
6 changes: 6 additions & 0 deletions packages/react/src/components/date-picker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export {
type DatePickerRangeTextBaseProps,
type DatePickerRangeTextProps,
} from './date-picker-range-text'
export {
DatePickerValueText,
type DatePickerValueTextBaseProps,
type DatePickerValueTextProps,
type DatePickerValueTextRenderProps,
} from './date-picker-value-text'
export { DatePickerRoot, type DatePickerRootBaseProps, type DatePickerRootProps } from './date-picker-root'
export {
DatePickerRootProvider,
Expand Down
2 changes: 2 additions & 0 deletions packages/solid/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Added

- **Date Picker**: Added `ValueText` component for displaying selected date value(s) with placeholder support and render
prop for custom formatting
- **Scroll Area**: Added overflow CSS variables (`--scroll-area-overflow-{x,y}-{start,end}`) for scroll fade effects
- **Slider**: Added `thumbCollisionBehavior` prop (`none`, `push`, `swap`)
- **Steps**: Added `isStepValid`, `isStepSkippable`, and `onStepInvalid` for validation support
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { DateValue } from '@zag-js/date-picker'
import { type JSX, For, Show } from 'solid-js'
import type { Assign } from '../../types'
import { type HTMLProps, type PolymorphicProps, ark } from '../factory'
import { datePickerAnatomy } from './date-picker.anatomy'
import { useDatePickerContext } from './use-date-picker-context'

export interface DatePickerValueTextRenderProps {
value: DateValue
index: number
valueAsString: string
remove: () => void
}

export interface DatePickerValueTextBaseProps extends PolymorphicProps<'span'> {
/**
* Text to display when no date is selected.
*/
placeholder?: string | undefined
/**
* A function to render each selected date value.
* When provided, each date in the selection will be rendered using this function.
*/
children?: ((props: DatePickerValueTextRenderProps) => JSX.Element) | undefined
/**
* The separator to use between multiple date values when using default rendering.
* @default ", "
*/
separator?: string | undefined
}

export interface DatePickerValueTextProps extends Assign<HTMLProps<'span'>, DatePickerValueTextBaseProps> {}

export const DatePickerValueText = (props: DatePickerValueTextProps) => {
const api = useDatePickerContext()

const hasValue = () => api().value.length > 0

return (
<Show
when={typeof props.children === 'function'}
fallback={
<ark.span {...datePickerAnatomy.build().valueText.attrs} {...props}>
{hasValue() ? api().valueAsString.join(props.separator ?? ', ') : props.placeholder}
</ark.span>
}
>
<Show when={hasValue()} fallback={props.placeholder}>
<For each={api().value}>
{(value, index) => (
<>
{(props.children as (props: DatePickerValueTextRenderProps) => JSX.Element)({
value,
index: index(),
valueAsString: api().valueAsString[index()],
remove: () => {
api().setValue(api().value.filter((_, i) => i !== index()))
},
})}
</>
)}
</For>
</Show>
</Show>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { anatomy } from '@zag-js/date-picker'

export const datePickerAnatomy = anatomy.extendWith('view')
export const datePickerAnatomy = anatomy.extendWith('view', 'valueText')
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export { MultiSelection } from './examples/multi-selection'
export { Presets } from './examples/presets'
export { RangeSelection } from './examples/range-selection'
export { RootProvider } from './examples/root-provider'
export { TriggerValue } from './examples/trigger-value'
export { Unavailable } from './examples/unavailable'
export { YearPicker } from './examples/year-picker'
6 changes: 6 additions & 0 deletions packages/solid/src/components/date-picker/date-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export {
type DatePickerRangeTextBaseProps as RangeTextBaseProps,
type DatePickerRangeTextProps as RangeTextProps,
} from './date-picker-range-text'
export {
DatePickerValueText as ValueText,
type DatePickerValueTextBaseProps as ValueTextBaseProps,
type DatePickerValueTextProps as ValueTextProps,
type DatePickerValueTextRenderProps as ValueTextRenderProps,
} from './date-picker-value-text'
export {
DatePickerRoot as Root,
type DatePickerRootBaseProps as RootBaseProps,
Expand Down
Loading
Loading