cupertino-datetime-picker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Klaus Fan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # cupertino-datetime-picker
2
+
3
+ **[Documentation](https://cupertino-datetime-picker-docs.vercel.app)** ·
4
+ **[Installation](https://cupertino-datetime-picker-docs.vercel.app/docs/installation)** ·
5
+ **[npm](https://www.npmjs.com/package/cupertino-datetime-picker)**
6
+
7
+ The iOS 14+ compact date & time picker, for the web. A date pill and a time
8
+ pill; the date pill opens the inline calendar whose title turns into month and
9
+ year wheels, the time pill opens hour / minute / AM–PM wheels with a typed
10
+ field above them. Every part takes touch, mouse, trackpad and keyboard.
11
+
12
+ Built for [shadcn/ui](https://ui.shadcn.com) on
13
+ [Base UI](https://base-ui.com) popovers and Tailwind v4. No date library:
14
+ `Intl` supplies month names, weekday names, the 12/24-hour clock and the
15
+ first day of the week.
16
+
17
+ ## Install
18
+
19
+ As a shadcn registry item (the source lands in your project, under
20
+ `components/ui/cupertino/`):
21
+
22
+ ```sh
23
+ npx shadcn@latest add https://cupertino-datetime-picker-docs.vercel.app/r/cupertino-datetime-picker.json
24
+ ```
25
+
26
+ Or from npm:
27
+
28
+ ```sh
29
+ pnpm add cupertino-datetime-picker @base-ui/react
30
+ ```
31
+
32
+ ```css
33
+ /* your Tailwind entry */
34
+ @import "tailwindcss";
35
+ @import "cupertino-datetime-picker/styles.css";
36
+ @source "../node_modules/cupertino-datetime-picker/dist";
37
+ ```
38
+
39
+ ## Use
40
+
41
+ ```tsx
42
+ import { DateTimePicker } from "cupertino-datetime-picker";
43
+
44
+ <DateTimePicker value={date} onChange={setDate} />
45
+ <DateTimePicker mode="date" />
46
+ <DateTimePicker mode="time" minuteInterval={5} />
47
+ <DateTimePicker display="inline" locale="de-DE" />
48
+ ```
49
+
50
+ | Prop | Default | |
51
+ | ------------------------------------- | -------------------- | ------------------------------------------------------ |
52
+ | `value` / `defaultValue` / `onChange` | | `Date \| null`. Controlled when `value` is given. |
53
+ | `mode` | `"dateTime"` | `"date"`, `"time"`, or both — `UIDatePicker.Mode`. |
54
+ | `display` | `"compact"` | `"compact"` pills with popovers, or `"inline"` panels. |
55
+ | `locale` | `navigator.language` | Drives names, clock, week start and the pill formats. |
56
+ | `hourCycle` | from locale | `"h12"` or `"h23"`. |
57
+ | `minuteInterval` | `1` | Minute wheel step, like `UIDatePicker.minuteInterval`. |
58
+ | `min` / `max` | | Days outside the range are disabled. |
59
+
60
+ The panels are exported on their own (`CalendarPanel`, `TimePanel`) as are
61
+ the primitives (`Wheel`, `WheelHighlight`, `SegmentedControl`).
62
+
63
+ Re-tint with `--cdp-tint` (and `--cdp-on-tint` for text on it); dark mode
64
+ follows a `.dark` (or `data-theme="dark"`) ancestor. Accessible names are
65
+ English by default; pass `labels` to `CalendarPanel` / `TimePanel` to
66
+ translate them — the visible text is already the locale's.
67
+
68
+ ## What matches iOS
69
+
70
+ - Pills: 34pt, 8pt radius, tertiary fill, tint text while open.
71
+ - Calendar: "September 2026 ›" title, next/previous in tint, single-letter
72
+ weekdays, 20pt days in 40pt circles. Selected day is a tinted circle; today
73
+ is tint text; selected today is a filled tint circle. Only the rows the
74
+ month needs. Months slide in; the title cross-fades the grid into month and
75
+ year wheels. Horizontal swipe changes month.
76
+ - Wheels: native scroll with snap points, so touch and trackpad flings are
77
+ the platform's own. Mouse drag has its own deceleration. Rows tilt away
78
+ from the centre and fade at the edges; hour and minute wheels wrap.
79
+ - Time field: hour and minute segments. Digits auto-advance (`945` is 9:45,
80
+ `12` waits for the second digit, `13` becomes 3), arrows step, `a`/`p` set
81
+ the period, and the wheels spin to follow. Spin a wheel and the digits
82
+ follow back. Mobile keypads insert through `beforeinput`, so IMEs work.
83
+ - Segmented control for AM/PM with the sliding thumb.
84
+ - Popover springs in from its pill.
85
+
86
+ ## Keyboard
87
+
88
+ | Where | Keys |
89
+ | ------------- | ------------------------------------------------------------------------------------------------- |
90
+ | Calendar grid | ← → ↑ ↓ move a day / week, Home/End week bounds, PageUp/PageDown month (Shift: year), Enter picks |
91
+ | Any wheel | ↑ ↓ one row, PageUp/PageDown five, Home/End, type a value to jump |
92
+ | Time field | digits, ↑ ↓ step, ← → between segments, `a` / `p`, Backspace clears |
93
+ | Popover | Escape closes |
94
+
95
+ All wheels are `spinbutton`s with `aria-valuenow` / `aria-valuetext`; the
96
+ grid is a `grid` of `gridcell`s with full-date names.
97
+
98
+ ## Develop
99
+
100
+ ```sh
101
+ pnpm install
102
+ pnpm dev # demo at http://localhost:5173
103
+ pnpm test # vitest: calendar math, clock, segment typing, fling math
104
+ pnpm e2e # Playwright: desktop Chrome + iPhone 15 (touch flings via CDP)
105
+ pnpm check # typecheck, lint, tests, build
106
+ pnpm registry:build # shadcn registry JSON into docs/public/r
107
+ pnpm docs # documentation site (Fumadocs) at http://localhost:3000
108
+ ```
109
+
110
+ ## Not yet
111
+
112
+ - RTL locales.
113
+ - `wheels` display for the date (day / month / year drums).
114
+ - Ranges.
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ export type CalendarPanelLabels = {
3
+ previousMonth: string;
4
+ nextMonth: string;
5
+ month: string;
6
+ year: string;
7
+ };
8
+ export declare const CALENDAR_LABELS: CalendarPanelLabels;
9
+ export type CalendarPanelProps = {
10
+ value: Date | null;
11
+ onChange: (date: Date) => void;
12
+ locale?: string;
13
+ min?: Date;
14
+ max?: Date;
15
+ /** Accessible names; visible text comes from `Intl`. */
16
+ labels?: Partial<CalendarPanelLabels>;
17
+ /** Injected for tests and stories. */
18
+ today?: Date;
19
+ className?: string;
20
+ };
21
+ /**
22
+ * The iOS inline calendar: month title that opens month/year wheels, next
23
+ * and previous month, a grid of circles. Months slide, the wheels cross-fade
24
+ * in over the grid, a horizontal swipe changes month, and the keyboard walks
25
+ * the grid (arrows, Home/End, PageUp/PageDown, Enter).
26
+ */
27
+ export declare function CalendarPanel({ value, onChange, locale, min, max, labels: labelsProp, today: todayProp, className, }: CalendarPanelProps): React.JSX.Element;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Calendar arithmetic on local `Date`s, with no library. A picker only ever
3
+ * needs the month grid, day-level comparisons, and "same clock, other day".
4
+ */
5
+ export type YearMonth = {
6
+ year: number;
7
+ month: number;
8
+ };
9
+ export declare function startOfDay(date: Date): Date;
10
+ export declare function isSameDay(a: Date | null | undefined, b: Date | null | undefined): boolean;
11
+ export declare function daysInMonth(year: number, month: number): number;
12
+ export declare function yearMonthOf(date: Date): YearMonth;
13
+ /** Months between `a` and `b`; negative when `b` is earlier. */
14
+ export declare function monthDiff(a: YearMonth, b: YearMonth): number;
15
+ export declare function shiftMonth(ym: YearMonth, by: number): YearMonth;
16
+ /** The same clock on another calendar day; the day is clamped into the month. */
17
+ export declare function withDay(base: Date, year: number, month: number, day: number): Date;
18
+ export declare function withTime(base: Date, hours: number, minutes: number): Date;
19
+ export declare function addDays(date: Date, days: number): Date;
20
+ export declare function addMonths(date: Date, months: number): Date;
21
+ /** Whether the whole day lies outside [min, max]. */
22
+ export declare function isDayDisabled(date: Date, min?: Date, max?: Date): boolean;
23
+ export type DayCell = {
24
+ date: Date;
25
+ inMonth: boolean;
26
+ };
27
+ /**
28
+ * Rows of seven for one month, starting on `weekStart` (0 = Sunday). Only as
29
+ * many rows as the month needs, like the iOS inline calendar.
30
+ */
31
+ export declare function monthGrid(year: number, month: number, weekStart: number): DayCell[][];
32
+ /** First day of the week for a locale, 0 = Sunday … 6 = Saturday. */
33
+ export declare function weekStartFor(locale: string): number;
@@ -0,0 +1,31 @@
1
+ import { HourCycle } from './time';
2
+ export type DateTimePickerProps = {
3
+ value?: Date | null;
4
+ defaultValue?: Date | null;
5
+ onChange?: (date: Date | null) => void;
6
+ /** Which parts are editable — `UIDatePicker.Mode`. */
7
+ mode?: "date" | "time" | "dateTime";
8
+ /** Pills that open popovers (`.compact`), or the panels laid out in place (`.inline`). */
9
+ display?: "compact" | "inline";
10
+ locale?: string;
11
+ hourCycle?: HourCycle;
12
+ minuteInterval?: number;
13
+ min?: Date;
14
+ max?: Date;
15
+ disabled?: boolean;
16
+ className?: string;
17
+ /** Placeholder labels when there is no value. */
18
+ labels?: {
19
+ date?: string;
20
+ time?: string;
21
+ };
22
+ };
23
+ /** The compact pill and its popover, for composing your own rows. */
24
+ export declare const pillClass = "inline-flex h-[34px] items-center rounded-lg bg-[var(--cdp-fill)] px-3 text-[17px] leading-none text-[var(--cdp-label)] transition-[background-color,color] outline-none hover:bg-[var(--cdp-fill-hover)] focus-visible:ring-2 focus-visible:ring-[var(--cdp-tint)] active:opacity-60 disabled:opacity-40 data-popup-open:text-[var(--cdp-tint)]";
25
+ export declare const popupClass = "cdp origin-(--transform-origin) rounded-[13px] bg-[var(--cdp-bg)] shadow-[var(--cdp-shadow)] outline-none data-open:animate-[cdp-pop-in_260ms_cubic-bezier(0.18,0.9,0.32,1.15)] data-closed:animate-[cdp-pop-out_140ms_ease-in]";
26
+ /**
27
+ * The iOS 14+ date picker for the web. Compact display shows the date and
28
+ * time as pills; each opens a popover — the calendar or the time panel —
29
+ * anchored to it. Inline display lays the panels out in place.
30
+ */
31
+ export declare function DateTimePicker({ value: valueProp, defaultValue, onChange, mode, display, locale, hourCycle: hourCycleProp, minuteInterval, min, max, disabled, className, labels, }: DateTimePickerProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ /** Controlled when `value` is given, otherwise owned here. */
2
+ export declare function useControlled<T>(value: T | undefined, defaultValue: T, onChange: ((next: T) => void) | undefined): [T, (next: T) => void];
3
+ export declare function usePrefersReducedMotion(): boolean;
@@ -0,0 +1,8 @@
1
+ export { DateTimePicker, pillClass, popupClass, type DateTimePickerProps, } from './date-time-picker';
2
+ export { CalendarPanel, CALENDAR_LABELS, type CalendarPanelLabels, type CalendarPanelProps, } from './calendar-panel';
3
+ export { TimePanel, TIME_LABELS, type TimePanelLabels, type TimePanelProps } from './time-panel';
4
+ export { Wheel, WheelHighlight, type WheelOption, type WheelProps } from './wheel';
5
+ export { SegmentedControl, type SegmentedOption } from './segmented-control';
6
+ export * from './calendar';
7
+ export * from './time';
8
+ export { typeDigit, stepValue, type DigitStep } from './segments';