Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
ec7c580
feat(base-ui): add date picker theming tokens
joshuapare Apr 12, 2026
25b8e3e
feat(base-ui): pure date utilities for DatePicker
joshuapare Apr 12, 2026
27143f0
feat(base-ui): Intl-based formatters for DatePicker
joshuapare Apr 12, 2026
5c62deb
feat(base-ui): Calendar grid with WAI-ARIA keyboard nav
joshuapare Apr 12, 2026
65e035b
feat(base-ui): DatePicker component with compound API
joshuapare Apr 12, 2026
5228945
feat(base-ui): TimePicker with 12/24 hour support and minute step
joshuapare Apr 12, 2026
9c60c83
chore(base-ui): remove unused useRef import from TimePicker
joshuapare Apr 12, 2026
b2b5aa3
feat(base-ui): DateTimePicker composing DatePicker and TimePicker
joshuapare Apr 12, 2026
e8b7630
docs(base-ui): Storybook stories for date/time pickers
joshuapare Apr 12, 2026
ae8c649
docs(base-ui): note date/time pickers in component status
joshuapare Apr 12, 2026
039dd97
feat(showcase): add date/time pickers interactive demo
joshuapare Apr 17, 2026
827839a
fix(base-ui): input-styled triggers + solid popover surface for pickers
joshuapare Apr 17, 2026
0f31883
fix(base-ui): drop DatePicker.Root/Trigger/Popup aliases to fix dts emit
joshuapare Apr 17, 2026
86d2e37
feat(base-ui): DatePicker trigger becomes text input with live parse
joshuapare Apr 17, 2026
cadd96c
chore(base-ui): fix strict-mode index access in DatePicker tests
joshuapare Apr 17, 2026
c02beb7
feat(base-ui): TimePicker column-selector popover alongside text entry
joshuapare Apr 17, 2026
1f2db0f
feat(base-ui): Calendar supports range selection mode
joshuapare Apr 17, 2026
f916c7f
fix(base-ui): handle Calendar discriminated union in DatePicker
joshuapare Apr 17, 2026
44b216d
feat(base-ui): DateRangePicker with range calendar and text entry
joshuapare Apr 17, 2026
9da8c66
docs(base-ui): DateRangePicker stories and update picker status
joshuapare Apr 17, 2026
7ed297e
fix(base-ui): resolve pre-existing TS errors in test files
joshuapare Apr 17, 2026
d839c07
feat(base-ui): DateField sectioned input primitive (MUI-style useField)
joshuapare Apr 17, 2026
8f32e62
chore(base-ui): clean up date-field diagnostics
joshuapare Apr 17, 2026
f5cdded
feat(base-ui): DatePicker uses sectioned DateField for guided input
joshuapare Apr 17, 2026
219e7be
revert(showcase): remove date-time-pickers demo (storybook is the tar…
joshuapare Apr 17, 2026
40080d6
feat(base-ui): TimePicker uses sectioned DateField for guided time input
joshuapare Apr 17, 2026
b463ca3
feat(base-ui): DateTimePicker uses sectioned DateField for guided input
joshuapare Apr 17, 2026
6ec67b7
feat(base-ui): DateRangePicker uses two sectioned DateFields
joshuapare Apr 17, 2026
a2492a8
chore(base-ui): drop deprecated placeholder/format props from picker …
joshuapare Apr 17, 2026
3bbefd8
docs(base-ui): document DateField + update picker status for sectione…
joshuapare Apr 17, 2026
2a202d7
fix(base-ui): bare DateField inside pickers; DateTimePicker popup use…
joshuapare Apr 17, 2026
1143216
feat(base-ui): DateTimePicker popup renders time columns inline
joshuapare Apr 17, 2026
5df7b2d
fix(base-ui): time columns fill popup height in DateTimePicker
joshuapare Apr 17, 2026
0d9af06
feat(base-ui): picker popups gain Clear/Done footer; fix DateField no…
joshuapare Apr 17, 2026
0fae1c3
fix(base-ui): suppress TimeColumns re-center on user click/keyboard s…
joshuapare Apr 17, 2026
db14f74
fix(base-ui): time columns stretch via flex instead of percentage height
joshuapare Apr 17, 2026
ac1b947
fix(base-ui): drop fill mechanism; use fixed column height for consis…
joshuapare Apr 17, 2026
687d5f1
fix(base-ui): time columns stretch to Calendar height via overflow:hi…
joshuapare Apr 17, 2026
42cf5ea
build(showcase): auto-build library deps before dev and build
joshuapare Apr 17, 2026
6721caf
fix(base-ui): address CodeRabbit review findings
joshuapare Apr 20, 2026
c952e6f
fix(base-ui): second round of CodeRabbit review fixes
joshuapare Apr 20, 2026
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
6 changes: 4 additions & 2 deletions apps/showcase/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"description": "Demo showcase app built entirely from Omniview UI packages.",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -p tsconfig.json --noEmit && vite build",
"build:deps": "pnpm --filter \"@omniviewdev/showcase^...\" build",
"dev": "pnpm build:deps && vite",
"dev:fast": "vite",
"build": "pnpm build:deps && tsc -p tsconfig.json --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
Expand Down
6 changes: 6 additions & 0 deletions docs/COMPONENT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ These are the only components currently in scope and exported:
- `Breadcrumbs`
- `Button`
- `ButtonGroup`
- `Calendar` (Beta) — calendar grid with full keyboard nav, min/max bounds, and custom disabled-cell support (internal to `DatePicker`; also exported standalone)
- `Card`
- `Checkbox`
- `CheckboxGroup`
Expand All @@ -30,6 +31,10 @@ These are the only components currently in scope and exported:
- `ConfirmButton`
- `Container`
- `ContextMenu`
- `DateField` (Beta) — sectioned guided date/time input primitive with per-section validation, paste-to-fill, and locale-aware section ordering
- `DatePicker` (Beta) — calendar popover input with full keyboard navigation, min/max constraints, and custom disabled-cell callback
- `DateRangePicker` (Beta) — dual `DateField` trigger with range-selecting calendar popover; min/max and custom separators
- `DateTimePicker` (Beta) — composition of `DatePicker` + `TimePicker` producing a combined date-and-time value
- `DescriptionList`
- `Dialog`
- `DockLayout`
Expand Down Expand Up @@ -69,6 +74,7 @@ These are the only components currently in scope and exported:
- `TagInput`
- `TextField`
- `TextArea`
- `TimePicker` (Beta) — 12/24-hour time input with optional seconds display and configurable minute step
- `Toast`
- `Tooltip`
- `ToggleButton`
Expand Down
14 changes: 14 additions & 0 deletions packages/base-ui/docs/COMPONENT_STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Component Status — @omniviewdev/base-ui

Tracks the implementation status of components in the package.

| Component | Status | Description |
|---|---|---|
| Calendar | Beta[^1] | Single-date and range-mode calendar grid; supports `min`/`max`, custom `isDateDisabled`, `weekStartsOn`, `locale`, and `autoFocus` |
| DateField | Beta[^1] | Sectioned guided input primitive; tabbable sections, per-section validation, arrow-key increment, paste-to-fill; supports `mode` (`date`/`time`/`datetime`), `locale`, `hourCycle`, `showSeconds`, `min`/`max`, `disabled`, `readOnly` |
| DatePicker | Beta[^1] | DateField-powered date input + calendar popover; supports `min`/`max`, `locale`, `hourCycle`, `disabled`, `readOnly` |
| DateRangePicker | Beta[^1] | Dual DateField trigger + range-selecting calendar popover; supports `min`/`max`, `locale`, `rangeSeparator`, `disabled`, `readOnly` |
| DateTimePicker | Beta[^1] | DateField-powered combined date+time input with calendar+time popover; supports `min`/`max`, `showSeconds`, `hourCycle`, `minuteStep`, `disabled`, `readOnly` |
| TimePicker | Beta[^1] | DateField-powered time input + column-selector popover; supports `showSeconds`, `hourCycle`, `minuteStep`, `disabled`, `readOnly` |

[^1]: Date and time components are feature-complete and unit-tested, but final UX verification (cross-browser behavior, locale exotica, keyboard-only flows) is still pending — treat as Beta until that sign-off lands.
107 changes: 107 additions & 0 deletions packages/base-ui/src/components/date-field/DateField.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* ─── DateField: sectioned input primitive ──────────────────────────────── */

.root {
--_ov-control-height: var(--ov-control-height-md);
--_ov-font-size: var(--ov-font-size-body);
--_ov-padding-inline: var(--ov-space-inline-control);
--_ov-bg: var(--ov-color-bg-surface-raised);
--_ov-fg: var(--ov-color-fg-default);
--_ov-border: var(--ov-color-border-default);
--_ov-focus: var(--ov-color-border-focus);
--_ov-placeholder: var(--ov-color-fg-subtle);

display: inline-flex;
align-items: center;
min-height: var(--_ov-control-height);
padding-inline: var(--_ov-padding-inline);
border: 1px solid var(--_ov-border);
border-radius: var(--ov-radius-control);
background: var(--_ov-bg);
color: var(--_ov-fg);
font-family: var(--ov-font-sans);
font-size: var(--_ov-font-size);
line-height: 1.2;
/* Tabular numerals so sections align cleanly as values change */
font-variant-numeric: tabular-nums;
white-space: nowrap;
cursor: text;
outline: none;
transition:
background-color var(--ov-duration-interactive) var(--ov-ease-standard),
border-color var(--ov-duration-interactive) var(--ov-ease-standard),
box-shadow var(--ov-duration-interactive) var(--ov-ease-standard);
}

.root:hover:not([data-disabled]) {
background: color-mix(in srgb, var(--_ov-bg) 86%, var(--ov-color-state-hover) 14%);
}

.root:focus-within:not([data-disabled]) {
border-color: var(--_ov-focus);
box-shadow: 0 0 0 1px var(--ov-color-state-focus-ring);
}

.root[data-disabled] {
opacity: var(--ov-opacity-disabled, 0.45);
cursor: not-allowed;
}

/* ─── Bare mode — strip shell styling when embedded inside a picker shell ─── */

.root[data-bare] {
border: none;
background: transparent;
padding-inline: 0;
min-height: unset;
}

.root[data-bare]:hover {
background: transparent;
}

.root[data-bare]:focus-within {
border: none;
box-shadow: none;
}

/* ─── Section span (editable) ────────────────────────────────────────────── */

.section {
display: inline-block;
padding: 0 1px;
border-radius: 2px;
min-width: 1ch;
text-align: center;
white-space: nowrap;
outline: none;
caret-color: transparent;
user-select: none;
-webkit-user-select: none;
cursor: text;
}

.section[data-focused] {
background: var(--ov-color-brand-500);
color: var(--ov-color-fg-inverse, var(--ov-color-fg-default));
}

.section[data-placeholder] {
color: var(--ov-color-fg-muted);
}

/* When focused and also placeholder, preserve readable contrast on brand bg */
.section[data-focused][data-placeholder] {
color: var(--ov-color-fg-inverse, var(--ov-color-fg-default));
opacity: 0.85;
}

/* ─── Literal separators ─────────────────────────────────────────────────── */

.literal {
display: inline-block;
color: var(--ov-color-fg-muted);
user-select: none;
-webkit-user-select: none;
pointer-events: none;
white-space: pre;
}
101 changes: 101 additions & 0 deletions packages/base-ui/src/components/date-field/DateField.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react';
import { DateField, type DateFieldProps } from './DateField';

const meta = {
title: 'Components/DateField',
component: DateField,
tags: ['autodocs'],
args: {
mode: 'date',
disabled: false,
readOnly: false,
},
argTypes: {
disabled: { control: 'boolean' },
readOnly: { control: 'boolean' },
mode: { control: 'select', options: ['date', 'time', 'datetime'] },
hourCycle: { control: 'inline-radio', options: [12, 24] },
showSeconds: { control: 'boolean' },
locale: { control: 'text' },
},
} satisfies Meta<typeof DateField>;

export default meta;
type Story = StoryObj<typeof meta>;

// ─── Story components (keep hooks legal) ──────────────────────────────────

function DateModeStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(null);
return <DateField {...args} mode="date" locale="en-US" value={value} onChange={setValue} />;
}

function TimeMode24hStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(null);
return <DateField {...args} mode="time" hourCycle={24} value={value} onChange={setValue} />;
}

function TimeMode12hStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(null);
return (
<DateField
{...args}
mode="time"
hourCycle={12}
showSeconds
value={value}
onChange={setValue}
/>
);
}

function DateTimeModeStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(null);
return <DateField {...args} mode="datetime" value={value} onChange={setValue} />;
}

function LocaleGBStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(new Date());
return <DateField {...args} mode="date" locale="en-GB" value={value} onChange={setValue} />;
}

function DisabledStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(new Date());
return <DateField {...args} disabled value={value} onChange={setValue} />;
}

function ReadOnlyStory(args: DateFieldProps) {
const [value, setValue] = useState<Date | null>(new Date());
return <DateField {...args} readOnly value={value} onChange={setValue} />;
}

// ─── Story exports ────────────────────────────────────────────────────────

export const DateMode: Story = {
render: (args) => <DateModeStory {...args} />,
};

export const TimeMode24h: Story = {
render: (args) => <TimeMode24hStory {...args} />,
};

export const TimeMode12h: Story = {
render: (args) => <TimeMode12hStory {...args} />,
};

export const DateTimeMode: Story = {
render: (args) => <DateTimeModeStory {...args} />,
};

export const LocaleGB: Story = {
render: (args) => <LocaleGBStory {...args} />,
};

export const Disabled: Story = {
render: (args) => <DisabledStory {...args} />,
};

export const ReadOnly: Story = {
render: (args) => <ReadOnlyStory {...args} />,
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading
Loading