@charlite/react-temporal 0.0.0-stage → 1.1.1
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/CHANGELOG.md +106 -0
- package/LICENSE +21 -0
- package/README.md +178 -2
- package/dist/hooks/useTemporalAdd.d.ts +7 -0
- package/dist/hooks/useTemporalCalendar.d.ts +4 -0
- package/dist/hooks/useTemporalClock.d.ts +5 -0
- package/dist/hooks/useTemporalCompare.d.ts +7 -0
- package/dist/hooks/useTemporalCountdown.d.ts +5 -0
- package/dist/hooks/useTemporalCountdownStatus.d.ts +11 -0
- package/dist/hooks/useTemporalDiff.d.ts +5 -0
- package/dist/hooks/useTemporalDuration.d.ts +5 -0
- package/dist/hooks/useTemporalElapsed.d.ts +5 -0
- package/dist/hooks/useTemporalFormat.d.ts +7 -0
- package/dist/hooks/useTemporalFrom.d.ts +5 -0
- package/dist/hooks/useTemporalInterval.d.ts +6 -0
- package/dist/hooks/useTemporalLocalDate.d.ts +5 -0
- package/dist/hooks/useTemporalMonth.d.ts +5 -0
- package/dist/hooks/useTemporalNow.d.ts +6 -0
- package/dist/hooks/useTemporalParse.d.ts +3 -0
- package/dist/hooks/useTemporalRange.d.ts +5 -0
- package/dist/hooks/useTemporalRelative.d.ts +6 -0
- package/dist/hooks/useTemporalSafeParse.d.ts +5 -0
- package/dist/hooks/useTemporalSchedule.d.ts +9 -0
- package/dist/hooks/useTemporalStopwatch.d.ts +11 -0
- package/dist/hooks/useTemporalTimeZone.d.ts +4 -0
- package/dist/hooks/useTemporalWeek.d.ts +5 -0
- package/dist/hooks/useTemporalWithin.d.ts +5 -0
- package/dist/hooks/useTemporalYear.d.ts +5 -0
- package/dist/hooks/useTemporalZonedNow.d.ts +6 -0
- package/dist/index.cjs +507 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.esm.js +477 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/internal/durationKey.d.ts +5 -0
- package/dist/internal/useLatest.d.ts +2 -0
- package/dist/internal/useTemporalTicker.d.ts +11 -0
- package/dist/temporal.d.ts +20 -0
- package/dist/types.d.ts +35 -0
- package/dist/utils/formatRelative.d.ts +6 -0
- package/dist/utils/parseTemporal.d.ts +2 -0
- package/docs/README.md +53 -0
- package/docs/api-reference.md +331 -0
- package/docs/getting-started.md +111 -0
- package/docs/installation.md +87 -0
- package/docs/migration.md +143 -0
- package/docs/polyfill.md +89 -0
- package/docs/typescript.md +86 -0
- package/package.json +117 -4
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
All exports from `@charlite/react-temporal`.
|
|
4
|
+
|
|
5
|
+
## Utilities
|
|
6
|
+
|
|
7
|
+
### `Temporal`
|
|
8
|
+
|
|
9
|
+
Re-exported Temporal namespace (native or polyfill).
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { Temporal } from '@charlite/react-temporal';
|
|
13
|
+
|
|
14
|
+
const date = Temporal.PlainDate.from('2026-07-05');
|
|
15
|
+
const now = Temporal.Now.instant();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### `getTemporal()`
|
|
19
|
+
|
|
20
|
+
Returns the resolved Temporal namespace. Prefer using `Temporal` directly.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { getTemporal } from '@charlite/react-temporal';
|
|
24
|
+
|
|
25
|
+
const T = getTemporal();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### `hasNativeTemporal()`
|
|
29
|
+
|
|
30
|
+
Returns `true` when `globalThis.Temporal` is defined.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Parsing
|
|
35
|
+
|
|
36
|
+
### `useTemporalFrom(kind, input)`
|
|
37
|
+
|
|
38
|
+
Parses an ISO string into the requested Temporal type.
|
|
39
|
+
|
|
40
|
+
**Kinds:** `'instant' | 'plainDate' | 'plainTime' | 'plainDateTime' | 'zonedDateTime'`
|
|
41
|
+
|
|
42
|
+
### `useTemporalSafeParse(kind, input)`
|
|
43
|
+
|
|
44
|
+
Same as `useTemporalFrom`, but returns `{ value, error }` instead of throwing.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Clock hooks
|
|
49
|
+
|
|
50
|
+
### `useTemporalNow(options?)`
|
|
51
|
+
|
|
52
|
+
Returns the current time, updating at a configurable interval.
|
|
53
|
+
|
|
54
|
+
| Option | Type | Default | Description |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `intervalMs` | `number` | `1000` | Tick interval in milliseconds |
|
|
57
|
+
| `timeZone` | `string` | — | IANA time zone; when set, returns `ZonedDateTime` |
|
|
58
|
+
| `pauseWhenHidden` | `boolean` | `false` | Skip ticks while the document is hidden |
|
|
59
|
+
|
|
60
|
+
**Returns:** `Temporal.Instant` or `Temporal.ZonedDateTime`
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const now = useTemporalNow();
|
|
64
|
+
const madrid = useTemporalNow({ timeZone: 'Europe/Madrid', intervalMs: 5000 });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### `useTemporalClock(options?)`
|
|
70
|
+
|
|
71
|
+
Live clock returning `Temporal.Instant`. Simpler alternative to `useTemporalNow` without time zone support.
|
|
72
|
+
|
|
73
|
+
| Option | Type | Default | Description |
|
|
74
|
+
| --- | --- | --- | --- |
|
|
75
|
+
| `intervalMs` | `number` | `1000` | Tick interval in milliseconds |
|
|
76
|
+
|
|
77
|
+
**Returns:** `Temporal.Instant`
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
const now = useTemporalClock({ intervalMs: 100 });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### `useTemporalZonedNow(timeZone, options?)`
|
|
86
|
+
|
|
87
|
+
Live clock for a specific IANA time zone.
|
|
88
|
+
|
|
89
|
+
| Parameter | Type | Description |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| `timeZone` | `string` | IANA time zone identifier (e.g. `'Asia/Tokyo'`) |
|
|
92
|
+
| `options.intervalMs` | `number` | Tick interval (default `1000`) |
|
|
93
|
+
|
|
94
|
+
**Returns:** `Temporal.ZonedDateTime`
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
const tokyo = useTemporalZonedNow('Asia/Tokyo');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Duration and difference
|
|
103
|
+
|
|
104
|
+
### `useTemporalDuration(start, end)`
|
|
105
|
+
|
|
106
|
+
Returns the duration from `start` to `end` using `start.until(end)`.
|
|
107
|
+
|
|
108
|
+
**Returns:** `Temporal.Duration`
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
const duration = useTemporalDuration(startInstant, endInstant);
|
|
112
|
+
duration.total('minutes'); // 90
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### `useTemporalDiff(a, b)`
|
|
118
|
+
|
|
119
|
+
Alias-style hook returning `a.until(b)`.
|
|
120
|
+
|
|
121
|
+
**Returns:** `Temporal.Duration`
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
const diff = useTemporalDiff(earlier, later);
|
|
125
|
+
diff.total('hours');
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Formatting and parsing
|
|
131
|
+
|
|
132
|
+
### `useTemporalFormat(temporalObj, locales?, options?)`
|
|
133
|
+
|
|
134
|
+
Formats a Temporal value using `toLocaleString`.
|
|
135
|
+
|
|
136
|
+
**Accepts:** `Instant`, `PlainDate`, `PlainTime`, `PlainDateTime`, `ZonedDateTime`
|
|
137
|
+
|
|
138
|
+
**Returns:** `string`
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
const formatted = useTemporalFormat(
|
|
142
|
+
Temporal.PlainDateTime.from('2026-07-05T14:30:00'),
|
|
143
|
+
'en-US',
|
|
144
|
+
{ dateStyle: 'full', timeStyle: 'short' },
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### `useTemporalParse(isoString)`
|
|
151
|
+
|
|
152
|
+
Parses an ISO 8601 string to `Temporal.Instant`. Memoized on `isoString`.
|
|
153
|
+
|
|
154
|
+
**Returns:** `Temporal.Instant`
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
const instant = useTemporalParse('2026-07-05T12:00:00Z');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### `useTemporalRelative(from, to, locales?, options?)`
|
|
163
|
+
|
|
164
|
+
Locale-aware relative time via `Intl.RelativeTimeFormat`.
|
|
165
|
+
|
|
166
|
+
**Returns:** `string` (e.g. `"in 3 hours"`, `"2 days ago"`)
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
const label = useTemporalRelative(past, future, 'en', { numeric: 'auto' });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Calendar and ranges
|
|
175
|
+
|
|
176
|
+
### `useTemporalRange(start, end)`
|
|
177
|
+
|
|
178
|
+
Inclusive array of `PlainDate` from `start` to `end`.
|
|
179
|
+
|
|
180
|
+
**Returns:** `Temporal.PlainDate[]`
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
const dates = useTemporalRange(
|
|
184
|
+
Temporal.PlainDate.from('2026-07-01'),
|
|
185
|
+
Temporal.PlainDate.from('2026-07-07'),
|
|
186
|
+
);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `useTemporalWeek(date)`
|
|
192
|
+
|
|
193
|
+
All dates in the ISO week (Monday–Sunday) containing `date`.
|
|
194
|
+
|
|
195
|
+
**Returns:** `Temporal.PlainDate[]` (7 items)
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
const week = useTemporalWeek(Temporal.PlainDate.from('2026-07-05'));
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### `useTemporalMonth(date)`
|
|
204
|
+
|
|
205
|
+
All dates in the month of `date`.
|
|
206
|
+
|
|
207
|
+
**Returns:** `Temporal.PlainDate[]`
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
const days = useTemporalMonth(Temporal.PlainDate.from('2026-07-15'));
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### `useTemporalYear(date)`
|
|
216
|
+
|
|
217
|
+
First day of each month in the year of `date`.
|
|
218
|
+
|
|
219
|
+
**Returns:** `Temporal.PlainDate[]` (12 items)
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
const months = useTemporalYear(Temporal.PlainDate.from('2026-07-05'));
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Validation
|
|
228
|
+
|
|
229
|
+
### `useTemporalCalendar(id)`
|
|
230
|
+
|
|
231
|
+
Validates a calendar identifier and returns the resolved calendar ID.
|
|
232
|
+
|
|
233
|
+
**Returns:** `string`
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
const id = useTemporalCalendar('iso8601'); // 'iso8601'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### `useTemporalTimeZone(id)`
|
|
242
|
+
|
|
243
|
+
Validates an IANA time zone and returns the resolved time zone ID.
|
|
244
|
+
|
|
245
|
+
**Returns:** `string`
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
const id = useTemporalTimeZone('Europe/Madrid'); // 'Europe/Madrid'
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Timers and scheduling
|
|
254
|
+
|
|
255
|
+
### `useTemporalInterval(callback, duration)`
|
|
256
|
+
|
|
257
|
+
Runs `callback` repeatedly at the given `Temporal.DurationLike` interval. Uses a ref for the callback to avoid stale closures.
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
useTemporalInterval(() => refresh(), { seconds: 30 });
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
### `useTemporalCountdown(target, options?)`
|
|
266
|
+
|
|
267
|
+
Seconds remaining until `target`. Returns `0` after the target passes.
|
|
268
|
+
|
|
269
|
+
| Option | Description |
|
|
270
|
+
| --- | --- |
|
|
271
|
+
| `intervalMs` | Tick interval (default `1000`) |
|
|
272
|
+
| `onComplete` | Called once when countdown reaches zero |
|
|
273
|
+
|
|
274
|
+
### `useTemporalCountdownStatus(target, options?)`
|
|
275
|
+
|
|
276
|
+
Same as countdown but returns `{ seconds, isComplete }`.
|
|
277
|
+
|
|
278
|
+
### `useTemporalElapsed(since, options?)`
|
|
279
|
+
|
|
280
|
+
Live seconds elapsed since `since` (0 if `since` is in the future).
|
|
281
|
+
|
|
282
|
+
### `useTemporalStopwatch(start, options?)`
|
|
283
|
+
|
|
284
|
+
Returns `{ elapsed: Duration, seconds }` updating from `start`.
|
|
285
|
+
|
|
286
|
+
### `useTemporalLocalDate(timeZone, options?)`
|
|
287
|
+
|
|
288
|
+
Today's `PlainDate` in the given IANA zone (default tick: 60s).
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### `useTemporalSchedule(callback, instant, options?)`
|
|
293
|
+
|
|
294
|
+
Schedules `callback` once at `instant`. Runs immediately when already past unless `runIfPast: false`.
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
useTemporalSchedule(() => alert('Done!'), Temporal.Now.instant().add({ minutes: 5 }));
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### `useTemporalCompare(a, b)`
|
|
303
|
+
|
|
304
|
+
Memoized `Temporal.Instant.compare` or `PlainDate.compare`.
|
|
305
|
+
|
|
306
|
+
### `useTemporalWithin(value, start, end)`
|
|
307
|
+
|
|
308
|
+
Inclusive range check (matching `Instant` or `PlainDate` operands).
|
|
309
|
+
|
|
310
|
+
### `useTemporalAdd(value, duration)`
|
|
311
|
+
|
|
312
|
+
Memoized `.add(duration)` for `Instant` or `PlainDate`.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Type exports
|
|
317
|
+
|
|
318
|
+
| Export | Description |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| `TemporalInstant` | `Temporal.Instant` |
|
|
321
|
+
| `TemporalPlainDate` | `Temporal.PlainDate` |
|
|
322
|
+
| `TemporalPlainTime` | `Temporal.PlainTime` |
|
|
323
|
+
| `TemporalPlainDateTime` | `Temporal.PlainDateTime` |
|
|
324
|
+
| `TemporalZonedDateTime` | `Temporal.ZonedDateTime` |
|
|
325
|
+
| `TemporalDuration` | `Temporal.Duration` |
|
|
326
|
+
| `TemporalDurationLike` | `Temporal.DurationLike` |
|
|
327
|
+
| `TemporalNamespace` | `typeof Temporal` |
|
|
328
|
+
| `UseTemporalNowOptions` | Options for `useTemporalNow` |
|
|
329
|
+
| `UseTemporalClockOptions` | Options for `useTemporalClock` / `useTemporalZonedNow` |
|
|
330
|
+
|
|
331
|
+
See [TypeScript](./typescript.md) for usage examples.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
This guide walks you through installing **react-temporal** and using it in a React app.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js **26** or later (for local development of this repo)
|
|
8
|
+
- React **17**, **18**, or **19**
|
|
9
|
+
- A bundler that supports ES modules (Vite, Next.js, Webpack 5, etc.)
|
|
10
|
+
|
|
11
|
+
## 1. Install the package
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @charlite/react-temporal
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 2. Add a polyfill (if needed)
|
|
18
|
+
|
|
19
|
+
Skip this step if you only target browsers with native Temporal (Chrome 144+, Firefox 139+, Edge 144+) **and** run Node.js 26+ on the server without needing a fallback.
|
|
20
|
+
|
|
21
|
+
For **Safari**, **Node.js < 26**, or mixed environments:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Recommended — smaller bundle (~20 KB gzip)
|
|
25
|
+
npm install temporal-polyfill
|
|
26
|
+
|
|
27
|
+
# Or the official reference implementation
|
|
28
|
+
npm install @js-temporal/polyfill
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Add the polyfill once at your app entry point:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// main.tsx or _app.tsx
|
|
35
|
+
import 'temporal-polyfill/global';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
See the [Polyfill guide](./polyfill.md) for details.
|
|
39
|
+
|
|
40
|
+
## 3. Use a hook
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { useTemporalNow } from '@charlite/react-temporal';
|
|
44
|
+
|
|
45
|
+
export function Clock() {
|
|
46
|
+
const now = useTemporalNow();
|
|
47
|
+
return <time dateTime={now.toString()}>{now.toLocaleString()}</time>;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 4. Import Temporal types
|
|
52
|
+
|
|
53
|
+
You do not need a separate polyfill import for types or values — the package re-exports `Temporal`:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Temporal, useTemporalMonth } from '@charlite/react-temporal';
|
|
57
|
+
|
|
58
|
+
export function JulyCalendar() {
|
|
59
|
+
const dates = useTemporalMonth(Temporal.PlainDate.from('2026-07-01'));
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<ul>
|
|
63
|
+
{dates.map((date) => (
|
|
64
|
+
<li key={date.toString()}>{date.day}</li>
|
|
65
|
+
))}
|
|
66
|
+
</ul>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Common patterns
|
|
72
|
+
|
|
73
|
+
### Live clock with time zone
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { useTemporalZonedNow } from '@charlite/react-temporal';
|
|
77
|
+
|
|
78
|
+
export function LocalClock({ timeZone }: { timeZone: string }) {
|
|
79
|
+
const now = useTemporalZonedNow(timeZone, { intervalMs: 1000 });
|
|
80
|
+
return <span>{now.toLocaleString()}</span>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Countdown timer
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useTemporalCountdown, Temporal } from '@charlite/react-temporal';
|
|
88
|
+
|
|
89
|
+
export function Countdown({ iso }: { iso: string }) {
|
|
90
|
+
const target = Temporal.Instant.from(iso);
|
|
91
|
+
const seconds = useTemporalCountdown(target);
|
|
92
|
+
return <span>{seconds}s remaining</span>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Relative time label
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { useTemporalRelative, Temporal } from '@charlite/react-temporal';
|
|
100
|
+
|
|
101
|
+
export function RelativeLabel({ instant }: { instant: Temporal.Instant }) {
|
|
102
|
+
const label = useTemporalRelative(Temporal.Now.instant(), instant, 'en');
|
|
103
|
+
return <span>{label}</span>;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Next steps
|
|
108
|
+
|
|
109
|
+
- [API reference](./api-reference.md) — full hook documentation
|
|
110
|
+
- [TypeScript](./typescript.md) — type exports and annotations
|
|
111
|
+
- [Examples](../examples/README.md) — one example per hook
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## npm
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @charlite/react-temporal
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## yarn
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
yarn add @charlite/react-temporal
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## pnpm
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @charlite/react-temporal
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Peer dependencies
|
|
22
|
+
|
|
23
|
+
`@charlite/react-temporal` expects React to already be installed in your project:
|
|
24
|
+
|
|
25
|
+
| Package | Supported versions |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `react` | `^17.0.0 \|\| ^18.0.0 \|\| ^19.0.0` |
|
|
28
|
+
| `react-dom` | `^17.0.0 \|\| ^18.0.0 \|\| ^19.0.0` |
|
|
29
|
+
|
|
30
|
+
If npm warns about missing peers, install React explicitly:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install react react-dom
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Optional polyfill dependencies
|
|
37
|
+
|
|
38
|
+
For environments without native Temporal, install **one** of:
|
|
39
|
+
|
|
40
|
+
| Package | Size (gzip) | Notes |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| [`temporal-polyfill`](https://www.npmjs.com/package/temporal-polyfill) | ~20 KB | Recommended; installed automatically with `@charlite/react-temporal` |
|
|
43
|
+
| [`@js-temporal/polyfill`](https://www.npmjs.com/package/@js-temporal/polyfill) | ~44 KB | Official reference implementation (install manually if needed) |
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install temporal-polyfill
|
|
47
|
+
# or
|
|
48
|
+
npm install @js-temporal/polyfill
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Node.js version
|
|
52
|
+
|
|
53
|
+
Development and CI for this package require **Node.js 26+**. Your end-user app can run on older Node versions if you install a Temporal polyfill when native Temporal is missing.
|
|
54
|
+
|
|
55
|
+
## Package exports
|
|
56
|
+
|
|
57
|
+
The package ships ESM and CommonJS builds:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"import": "./dist/index.esm.js",
|
|
62
|
+
"require": "./dist/index.cjs",
|
|
63
|
+
"types": "./dist/index.d.ts"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Tree-shaking is supported (`"sideEffects": false`).
|
|
68
|
+
|
|
69
|
+
## Bundler notes
|
|
70
|
+
|
|
71
|
+
### Vite / CRA / Webpack
|
|
72
|
+
|
|
73
|
+
No special configuration required. Install a polyfill for SSR or test environments running in Node.js without native Temporal.
|
|
74
|
+
|
|
75
|
+
### Next.js
|
|
76
|
+
|
|
77
|
+
Add the polyfill import in `instrumentation.ts` or your root layout for server components that use Temporal hooks on the server:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import 'temporal-polyfill/global';
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Client components can use hooks normally.
|
|
84
|
+
|
|
85
|
+
### Vitest
|
|
86
|
+
|
|
87
|
+
Ensure the test environment loads a polyfill before tests run (see root `vitest.setup.ts` in this repository).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Migration guide
|
|
2
|
+
|
|
3
|
+
## Upgrading to 1.1.0 from 1.0.0
|
|
4
|
+
|
|
5
|
+
### Polyfill default
|
|
6
|
+
|
|
7
|
+
The built-in fallback is now **`temporal-polyfill`** instead of `@js-temporal/polyfill`. No app changes are required unless you relied on `@js-temporal/polyfill` being installed automatically — add it explicitly if you still need it for code outside `@charlite/react-temporal`.
|
|
8
|
+
|
|
9
|
+
### New hooks (optional)
|
|
10
|
+
|
|
11
|
+
See [API reference](./api-reference.md) for `useTemporalFrom`, `useTemporalElapsed`, `useTemporalStopwatch`, `useTemporalLocalDate`, and related utilities.
|
|
12
|
+
|
|
13
|
+
### Schedule behavior
|
|
14
|
+
|
|
15
|
+
`useTemporalSchedule` now invokes the callback immediately when the target instant is in the past (use `{ runIfPast: false }` to restore the old no-op behavior).
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Upgrading to 1.0.0 from 0.0.3
|
|
20
|
+
|
|
21
|
+
Version **1.0.0** aligns the repo with the 2026 Temporal ecosystem and modern tooling. Library APIs are unchanged; development and CI requirements changed.
|
|
22
|
+
|
|
23
|
+
### Node.js
|
|
24
|
+
|
|
25
|
+
**Before:** Node.js 22+
|
|
26
|
+
|
|
27
|
+
**After:** Node.js **26+** (native Temporal in supported releases)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
nvm install 26
|
|
31
|
+
nvm use 26
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Update GitHub Actions / CI images to Node 26. End-user apps on older Node versions can still use **@charlite/react-temporal** with a Temporal polyfill.
|
|
35
|
+
|
|
36
|
+
### Testing (contributors)
|
|
37
|
+
|
|
38
|
+
**Before:** Jest + `jest.config.json`
|
|
39
|
+
|
|
40
|
+
**After:** Vitest + `vitest.config.ts`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm test # vitest run
|
|
44
|
+
npm run test:watch
|
|
45
|
+
npm run test:coverage
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Timer mocks use `vi` instead of `jest`:
|
|
49
|
+
|
|
50
|
+
```diff
|
|
51
|
+
- jest.useFakeTimers();
|
|
52
|
+
+ vi.useFakeTimers();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Linting (contributors)
|
|
56
|
+
|
|
57
|
+
**Before:** `.eslintrc.json`
|
|
58
|
+
|
|
59
|
+
**After:** `eslint.config.js` (ESLint 10 flat config)
|
|
60
|
+
|
|
61
|
+
### Agent / knowledge layout
|
|
62
|
+
|
|
63
|
+
New OKF v0.2 bundle at [`knowledge/`](../knowledge/index.md) and [`AGENTS.md`](../AGENTS.md). No runtime impact on published npm code.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Upgrading to 0.0.3 from 0.0.1 / 0.0.2
|
|
68
|
+
|
|
69
|
+
Version **0.0.3** modernizes the library for 2026 Temporal adoption. Most changes are additive; a few APIs changed behavior.
|
|
70
|
+
|
|
71
|
+
### Install / dependency changes
|
|
72
|
+
|
|
73
|
+
**Before (0.0.2):**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm install @charlite/react-temporal
|
|
77
|
+
# react and react-dom were bundled as direct dependencies
|
|
78
|
+
# polyfill was bundled inside the package
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**After (0.0.3):**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install @charlite/react-temporal
|
|
85
|
+
npm install temporal-polyfill # recommended for SSR / Safari / Node
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Ensure `react` and `react-dom` are in your app's `package.json` (peer dependencies).
|
|
89
|
+
|
|
90
|
+
### Import changes
|
|
91
|
+
|
|
92
|
+
You can now import `Temporal` from the package instead of `@js-temporal/polyfill`:
|
|
93
|
+
|
|
94
|
+
```diff
|
|
95
|
+
- import { useTemporalMonth } from '@charlite/react-temporal';
|
|
96
|
+
- import { Temporal } from '@js-temporal/polyfill';
|
|
97
|
+
+ import { useTemporalMonth, Temporal } from '@charlite/react-temporal';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `useTemporalFormat`
|
|
101
|
+
|
|
102
|
+
Signature changed to match `toLocaleString`:
|
|
103
|
+
|
|
104
|
+
```diff
|
|
105
|
+
- useTemporalFormat(date, { dateStyle: 'full' })
|
|
106
|
+
+ useTemporalFormat(date, undefined, { dateStyle: 'full' })
|
|
107
|
+
+ useTemporalFormat(date, 'en-US', { dateStyle: 'full' })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `useTemporalRelative`
|
|
111
|
+
|
|
112
|
+
Output format changed from manual strings (`"10 seconds ago"`) to `Intl.RelativeTimeFormat` (`"in 10 seconds"`). Pass a locale as the third argument:
|
|
113
|
+
|
|
114
|
+
```diff
|
|
115
|
+
- useTemporalRelative(from, to)
|
|
116
|
+
+ useTemporalRelative(from, to, 'en')
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `useTemporalDuration` / `useTemporalDiff`
|
|
120
|
+
|
|
121
|
+
These now use `until()` internally. Access total units with `.total()`:
|
|
122
|
+
|
|
123
|
+
```diff
|
|
124
|
+
- diff.hours
|
|
125
|
+
+ diff.total('hours')
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### New hooks (optional adoption)
|
|
129
|
+
|
|
130
|
+
- `useTemporalClock({ intervalMs })` — configurable live clock
|
|
131
|
+
- `useTemporalZonedNow(timeZone, { intervalMs })` — zoned live clock
|
|
132
|
+
- `useTemporalNow({ intervalMs, timeZone })` — extended options
|
|
133
|
+
|
|
134
|
+
### No action needed
|
|
135
|
+
|
|
136
|
+
These hooks work the same way, with improved internals:
|
|
137
|
+
|
|
138
|
+
- `useTemporalParse`
|
|
139
|
+
- `useTemporalRange`
|
|
140
|
+
- `useTemporalWeek` / `useTemporalMonth` / `useTemporalYear`
|
|
141
|
+
- `useTemporalCalendar` / `useTemporalTimeZone`
|
|
142
|
+
- `useTemporalInterval`
|
|
143
|
+
- `useTemporalSchedule`
|