chrona-react 0.3.5 → 0.3.6

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/AGENTS.md CHANGED
@@ -11,10 +11,11 @@ https://github.com/devlinduldulao/chrona#readme
11
11
  `temporal-spec/global`, so `Temporal.PlainDate` resolves as an ambient global
12
12
  in any file that imports from Chrona. A file that uses Temporal types without
13
13
  importing Chrona needs `import "temporal-spec/global"` of its own.
14
- 3. **Server Components.** These components hold state: mark their module
15
- `"use client"`. The polyfill import is a side effect, so it must reach the
16
- client bundle put it at the top of a `"use client"` module that always
17
- loads (a providers file), not in a Server Component. That covers hydration.
14
+ 3. **Server Components.** The package carries a `"use client"` directive, so
15
+ *importing* it from a Server Component is safe; the module that **renders**
16
+ it still has to be a client module. The polyfill import is a side effect and
17
+ must reach the client bundle put it at the top of a `"use client"` module
18
+ that always loads (a providers file), not in a Server Component.
18
19
  4. **SSR module init.** The providers import does not order Node module
19
20
  evaluation. If another `"use client"` file reads `Temporal` at module scope
20
21
  (`const reference = Temporal.PlainDate.from(...)` at the top of `page.tsx`),
@@ -49,17 +50,53 @@ what the field and calendar open on before a value exists.
49
50
  - Typed digits are literal, stepping clamps. 29 February is reachable before the
50
51
  year is typed; a finished date that cannot exist reports
51
52
  `onInvalid("nonexistent")` rather than clamping to the 28th.
52
- - Validation waits for a segment to finish. A half-typed year emits nothing
53
- until it is complete or the field blurs.
53
+ - A segment is a draft until its last digit lands, and a draft reports nothing:
54
+ typing `2026` calls `onChange` once with that year, not four times through
55
+ 2, 20 and 202. Blur settles a draft literally — `20` becomes the year 20.
54
56
  - Empty fields are `null`. Partial drafts stay internal; `onChange` only sees
55
57
  complete, valid values.
58
+ - Segments accept input from soft keyboards, which send no usable `key` and
59
+ often arrive as a composition. The AM/PM segment also accepts its locale's own
60
+ label, so it can be set without a hardware keyboard — but it has no pointer
61
+ affordance, so leave readers a keyboard path to it.
62
+ - A segment announces `aria-valuemin`/`aria-valuemax` narrowed to
63
+ `minValue`/`maxValue` where that is certain, while still accepting
64
+ out-of-range input so it can be reported rather than silently refused.
56
65
 
57
66
  ## SSR determinism
58
67
 
59
- Pass the same reference date, value, locale, and `timeZone` on server and
60
- client — today is resolved at render time. Segment literals are normalized
61
- (U+202F and U+00A0 collapse to a plain space), so a 12-hour `TimeField` hydrates
62
- cleanly across differing ICU versions; `hourCycle="h23"` is not a requirement.
68
+ Pass the same reference date, value, locale and `timeZone` on server and client.
69
+
70
+ **`today` is resolved on the client, not at render time.** A server cannot know
71
+ the reader's day it may be in another zone, and a prerendered page can be
72
+ served days after it was built — so `data-today` and `aria-current="date"` are
73
+ absent from the server HTML and appear just after hydration. Pass `today`
74
+ yourself to put the marker in the server HTML.
75
+
76
+ **Give every server-rendered calendar a reference date.** With no `value`,
77
+ `defaultValue`, `focusedValue`, `defaultFocusedValue` or `placeholderValue`, a
78
+ calendar opens on whichever day the renderer thinks it is, and React will not
79
+ patch up the mismatch that follows. A development build warns once about this
80
+ from the server render.
81
+
82
+ **Keep values in `iso8601` unless the server is known to manage more.** Node's
83
+ native Temporal cannot do arithmetic on other calendars — `gregory` included —
84
+ so `calendar="gregory"`, or a value carrying that calendar, renders in the
85
+ browser and raises `CALENDAR_UNSUPPORTED` during SSR.
86
+
87
+ Segment literals are normalized (U+202F and U+00A0 collapse to a plain space),
88
+ so a 12-hour `TimeField` hydrates cleanly across differing ICU versions;
89
+ `hourCycle="h23"` is not a requirement. Two things that normalization cannot
90
+ cover: dates before 1582, which ICU renders through the Julian calendar and
91
+ runtimes disagree about, and `dateStyle` in `ja-JP`/`zh-CN`, where V8 and
92
+ `temporal-polyfill` differ — which only shows when the two sides of a page run
93
+ different implementations.
94
+
95
+ ## Styling
96
+
97
+ No CSS ships. Style through `data-scope`, `data-part` and state attributes.
98
+ `docs/starter.css` in the repository is a copy-and-edit stylesheet covering every
99
+ part these components emit.
63
100
 
64
101
  ## Not implemented
65
102
 
package/README.md CHANGED
@@ -6,7 +6,7 @@ values, no free-form date parsing, and no CSS — you own every pixel.
6
6
  Part of [Chrona](https://github.com/devlinduldulao/chrona). Built on
7
7
  [`chrona-core`](https://www.npmjs.com/package/chrona-core).
8
8
 
9
- > **Status: experimental 0.3.5.** Public APIs and styling attributes are not
9
+ > **Status: experimental 0.3.6.** Public APIs and styling attributes are not
10
10
  > frozen for v1. 0.3.0 changes when a field reports a value and leaves `today`
11
11
  > to the client; 0.2.0 changed the Calendar cell anatomy. See the
12
12
  > [changelog](./CHANGELOG.md). Automated axe checks pass, but no screen-reader
@@ -120,7 +120,7 @@ return focus to the trigger.
120
120
  | `DateField` | `chrona-react/date-field` | Locale-ordered segments; ISO/Gregorian years 1–9999 |
121
121
  | `TimeField` | `chrona-react/time-field` | `hourCycle` h11/h12/h23/h24, minute or second granularity |
122
122
  | `DatePicker` | `chrona-react/date-picker` | Field plus calendar in a native dialog |
123
- | `ChronaProvider` | — | Ambient `locale` and `timeZone` |
123
+ | `ChronaProvider` | — | Ambient `locale`, `timeZone`, `dir` and `translations` |
124
124
 
125
125
  A barrel export is also available. Matching hooks — `useCalendar`,
126
126
  `useRangeCalendar`, `useDateField`, `useTimeField`, `useDatePicker` — expose
@@ -154,6 +154,12 @@ Hidden inputs serialize with `.toString()` and are never read back.
154
154
  inputs do not take part in browser constraint validation — validate required,
155
155
  incomplete, or invalid values in your form layer before submission.
156
156
 
157
+ Every failure is a `ChronaError` carrying a stable `code`, listed in
158
+ [`chrona-core`'s README](https://www.npmjs.com/package/chrona-core#errors).
159
+ `TEMPORAL_MISSING`, `TEMPORAL_MISMATCH` and `CALENDAR_UNSUPPORTED` are the three
160
+ worth handling: each means the page's Temporal runtime is not what the component
161
+ needs, and each usually shows up only on the server.
162
+
157
163
  ## With shadcn/ui
158
164
 
159
165
  Chrona does not replace shadcn. Keep `Button`, `Popover`, `Label`, and `cn`.
@@ -5,14 +5,14 @@ import { F as FieldProps } from './field-DayikvR6.js';
5
5
 
6
6
  type DateFieldProps = FieldProps<"date">;
7
7
  declare const DateField: {
8
- Root: ({ children, asChild, className, style, ...props }: FieldProps<"date"> & Pick<PartProps, "children" | "asChild" | "className" | "style">) => React.JSX.Element;
8
+ Root: ({ children, asChild, className, style, ...props }: FieldProps<"date"> & Pick<PartProps, "className" | "style" | "children" | "asChild">) => React.JSX.Element;
9
9
  Label: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & {
10
10
  asChild?: boolean;
11
11
  } & React.RefAttributes<HTMLElement>>;
12
12
  Field: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & {
13
13
  asChild?: boolean;
14
14
  } & React.RefAttributes<HTMLElement>>;
15
- Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "children" | "defaultValue" | "onChange" | "value" | "type"> & {
15
+ Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultValue" | "children" | "onChange" | "type" | "value"> & {
16
16
  type: chrona_core.SegmentType;
17
17
  } & React.RefAttributes<HTMLInputElement>>;
18
18
  HiddenInput: (props: HiddenInputProps) => React.JSX.Element;
@@ -47,7 +47,7 @@ declare const DatePicker: {
47
47
  Field: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & {
48
48
  asChild?: boolean;
49
49
  } & React.RefAttributes<HTMLElement>>;
50
- Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "children" | "defaultValue" | "onChange" | "value" | "type"> & {
50
+ Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultValue" | "children" | "onChange" | "type" | "value"> & {
51
51
  type: chrona_core.SegmentType;
52
52
  } & React.RefAttributes<HTMLInputElement>>;
53
53
  HiddenInput: (props: HiddenInputProps) => React.JSX.Element;
@@ -5,14 +5,14 @@ import { F as FieldProps } from './field-DayikvR6.js';
5
5
 
6
6
  type TimeFieldProps = FieldProps<"time">;
7
7
  declare const TimeField: {
8
- Root: ({ children, asChild, className, style, ...props }: FieldProps<"time"> & Pick<PartProps, "children" | "asChild" | "className" | "style">) => React.JSX.Element;
8
+ Root: ({ children, asChild, className, style, ...props }: FieldProps<"time"> & Pick<PartProps, "className" | "style" | "children" | "asChild">) => React.JSX.Element;
9
9
  Label: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & {
10
10
  asChild?: boolean;
11
11
  } & React.RefAttributes<HTMLElement>>;
12
12
  Field: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLElement> & {
13
13
  asChild?: boolean;
14
14
  } & React.RefAttributes<HTMLElement>>;
15
- Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "children" | "defaultValue" | "onChange" | "value" | "type"> & {
15
+ Segment: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultValue" | "children" | "onChange" | "type" | "value"> & {
16
16
  type: chrona_core.SegmentType;
17
17
  } & React.RefAttributes<HTMLInputElement>>;
18
18
  HiddenInput: (props: HiddenInputProps) => React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrona-react",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Headless Temporal-native React date and time primitives",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,7 +61,7 @@
61
61
  }
62
62
  },
63
63
  "dependencies": {
64
- "chrona-core": "^0.3.5"
64
+ "chrona-core": "^0.3.6"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": ">=18.0.0",