Skip to content
Open
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
79 changes: 79 additions & 0 deletions apps/www/src/content/docs/components/calendar-preview/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,82 @@ export const dateInfoDemo = {
}
]
};

export const pickerDemo = {
type: 'code',
tabs: [
{
name: 'Basic',
code: `<CalendarPreview defaultMonth={new Date(2024, 3, 1)}>
<CalendarPreview.Trigger>
<CalendarPreview.Input />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>`
},
{
name: 'Disabled',
code: `<CalendarPreview defaultMonth={new Date(2024, 3, 1)} disabled>
<CalendarPreview.Trigger>
<CalendarPreview.Input />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>`
},
{
name: 'Disabled dates',
code: `<CalendarPreview
defaultMonth={new Date(2024, 3, 1)}
minDate={new Date(2024, 3, 10)}
isDateUnavailable={date => date.getDay() === 0 || date.getDay() === 6}
>
<CalendarPreview.Trigger>
<CalendarPreview.Input />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>`
},
{
name: 'Without calendar icon',
code: `<CalendarPreview defaultMonth={new Date(2024, 3, 1)}>
<CalendarPreview.Trigger>
<CalendarPreview.Input trailingIcon={null} />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>`
},
{
name: 'With Field',
code: `<Field label="Start date" required>
<CalendarPreview defaultMonth={new Date(2024, 3, 1)}>
<CalendarPreview.Trigger>
<CalendarPreview.Input />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>
</Field>`
},
{
name: 'Custom trigger',
code: `<CalendarPreview
defaultMonth={new Date(2024, 3, 1)}
defaultValue={new Date(2024, 3, 17)}
>
<CalendarPreview.Trigger render={<Button variant="outline" />} />
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>`
}
]
};
37 changes: 37 additions & 0 deletions apps/www/src/content/docs/components/calendar-preview/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
boundsDemo,
gridDemo,
dateInfoDemo,
pickerDemo,
} from "./demo.ts";

<Demo data={preview} />
Expand Down Expand Up @@ -88,6 +89,18 @@ Step the view one month. Never disabled by `minDate` or `maxDate` — bounds lim

Restores `defaultDate`. Renders only when there is something to restore.

### CalendarPreview.Trigger

Anchors the popover and owns opening it. Renders the formatted value, or the placeholder, when given no children — wrap an `.Input` in it for a typeable field. Never renders a `button`, so the control inside stays focusable. Takes `render`, `className` and `ref`.

### CalendarPreview.Content

The portaled popover surface. Takes `Popover.Content` props — `side`, `align`, `sideOffset` and the rest — and flips above the trigger on collision.

### CalendarPreview.Input

<auto-type-table path="./props.ts" name="CalendarPreviewInputProps" />

### CalendarPreview.Footer

The row below the calendar. A bare string is wrapped in `Text`; anything else renders as given.
Expand All @@ -113,6 +126,9 @@ Every rendered part carries a stable `data-slot` attribute for [styling and test
| Slot | Element |
|------|---------|
| `calendar-preview` | The root, a column wrapping the parts |
| `calendar-preview-trigger` | The popover anchor |
| `calendar-preview-content` | The portaled popover surface |
| `calendar-preview-input` | The typeable date field |
| `calendar-preview-days` | The day view surface |
| `calendar-preview-header` | The header row, single-month layout |
| `calendar-preview-month-header` | One month's header, when several months are shown |
Expand Down Expand Up @@ -186,6 +202,27 @@ Outside days are **off by default**, so a grid ends on the last day of its month

`<CalendarPreview.Caption dropdown />` turns the caption into a filled chip that opens two adjacent scrolling columns. It is a plain popover of buttons, not a `Select` — picking from either column moves the view and never selects a value.

### Date picker

The date picker is not a separate export — it is this composition:

```tsx
<CalendarPreview value={date} onValueChange={setDate}>
<CalendarPreview.Trigger>
<CalendarPreview.Input />
</CalendarPreview.Trigger>
<CalendarPreview.Content>
<CalendarPreview.Days />
</CalendarPreview.Content>
</CalendarPreview>
```

The popover opens when the input takes focus. Enter, blur and an outside click all commit — there is no Apply button. Dismissal is Base UI's, so escape and outside press behave like every other popover in the library.

"Without calendar icon" is composition rather than a prop: pass `trailingIcon={null}` to `.Input`.

<Demo data={pickerDemo} />

## Accessibility

- Arrow keys move between days; the focused cell carries `data-draft` until it is committed
Expand Down
27 changes: 27 additions & 0 deletions apps/www/src/content/docs/components/calendar-preview/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,30 @@ export interface CalendarPreviewGridProps {
/** Cover the grid with a skeleton and stop navigation. */
loading?: boolean;
}

export interface CalendarPreviewInputProps {
/**
* Placeholder shown when there is no value.
* @default "Select date"
*/
placeholder?: string;

/**
* Icon at the end of the field. Pass `null` for a picker with no calendar
* glyph — that variant is composition, not a prop.
* @default <CalendarIcon />
*/
trailingIcon?: ReactNode;

/**
* Called when the typed text starts or stops being a usable date.
* @example onValidityChange={({ valid, reason }) => setError(reason)}
*/
onValidityChange?: (validity: {
valid: boolean;
reason?: 'unparseable' | 'out-of-bounds' | 'unavailable';
}) => void;

/** Read and navigable, but not typeable. */
readOnly?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -915,14 +915,17 @@ describe('CalendarPreview public surface', () => {
expect(partNames.sort()).toEqual(
[
'Caption',
'Content',
'Day',
'Days',
'Footer',
'Grid',
'Header',
'NextMonth',
'PrevMonth',
'Input',
'Reset',
'Trigger',
'Weekday'
].sort()
);
Expand Down
Loading
Loading