@astryxdesign/core 0.7.9-canary.2ea8f68 → 0.7.9-canary.4e7823a

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.
@@ -1,8 +1,14 @@
1
1
  import { getResolve } from './resolve.js';
2
2
  import type { Locale, MessagesByLocale, Overrides } from './types.js';
3
+ import type { CalendarSystem } from '../utils/calendarSystem.js';
3
4
  export interface InternationalizationContextValue {
4
5
  locale: Locale;
5
6
  direction: 'ltr' | 'rtl';
7
+ /**
8
+ * The provider's resolved calendar system. A context without it means
9
+ * Gregorian dates.
10
+ */
11
+ calendar?: CalendarSystem;
6
12
  messages: MessagesByLocale;
7
13
  overrides?: Overrides;
8
14
  translate: ReturnType<typeof getResolve>;
@@ -1 +1 @@
1
- {"version":3,"file":"InternationalizationContext.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationContext.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,UAAU,EAAC,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEjE,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,2DAMpC,CAAC"}
1
+ {"version":3,"file":"InternationalizationContext.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationContext.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,UAAU,EAAC,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AACjE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAC;AAE5D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,2DAMpC,CAAC"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @file InternationalizationProvider.tsx
3
- * @input React, InternationalizationContext, i18n types
3
+ * @input React, InternationalizationContext, i18n types, calendar resolution
4
4
  * @output Exports InternationalizationProvider component and props type
5
- * @position Provider component for astryx i18n locale + messages
5
+ * @position Provider component for astryx i18n locale, messages, and calendar
6
6
  *
7
7
  * Wraps a subtree with a locale and (optional) additional message catalogs +
8
8
  * overrides. Astryx components inside the subtree resolve their strings via
@@ -15,6 +15,7 @@
15
15
  * - /packages/core/src/i18n/InternationalizationProvider.doc.mjs
16
16
  */
17
17
  import { type ReactNode } from 'react';
18
+ import type { CalendarSystem } from '../utils/calendarSystem.js';
18
19
  import type { Locale, Overrides, ProviderMessagesByLocale } from './types.js';
19
20
  export interface InternationalizationProviderProps {
20
21
  /**
@@ -63,13 +64,30 @@ export interface InternationalizationProviderProps {
63
64
  * in sync (e.g. `dir={getLocaleDirection(locale)}` on both).
64
65
  */
65
66
  dir?: 'ltr' | 'rtl';
67
+ /**
68
+ * The calendar system of this subtree, such as `'persian'` or `'hebrew'`. The
69
+ * provider resolves it and exposes it on the context as `calendar`. Astryx
70
+ * date components show Gregorian dates. Values stay Gregorian ISO dates. When
71
+ * omitted, the calendar is Gregorian: a locale never selects it, neither by
72
+ * its default calendar nor by a `-u-ca-` keyword. Calendars other than
73
+ * `gregory`, `iso8601`, `buddhist`, `roc`, and `japanese` need `Temporal`.
74
+ * The context keeps the requested calendar; where `Temporal` is missing, a
75
+ * date component that resolves it shows Gregorian dates and warns once in
76
+ * development.
77
+ *
78
+ * @example
79
+ * ```
80
+ * <InternationalizationProvider locale="fa-IR" calendar="persian">
81
+ * ```
82
+ */
83
+ calendar?: CalendarSystem;
66
84
  children: ReactNode;
67
85
  }
68
86
  /**
69
87
  * Provides locale + additional messages + overrides to all astryx
70
88
  * components in the subtree.
71
89
  */
72
- export declare function InternationalizationProvider({ locale, messages, overrides, dir, children, }: InternationalizationProviderProps): import("react").JSX.Element;
90
+ export declare function InternationalizationProvider({ locale, messages, overrides, dir, calendar: calendarProp, children, }: InternationalizationProviderProps): import("react").JSX.Element;
73
91
  export declare namespace InternationalizationProvider {
74
92
  var displayName: string;
75
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"InternationalizationProvider.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationProvider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAG9C,OAAO,KAAK,EACV,MAAM,EAEN,SAAS,EACT,wBAAwB,EACzB,MAAM,SAAS,CAAC;AA0BjB,MAAM,WAAW,iCAAiC;IAChD;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,MAAM,EACN,QAAQ,EACR,SAAS,EACT,GAAG,EACH,QAAQ,GACT,EAAE,iCAAiC,+BAiBnC;yBAvBe,4BAA4B"}
1
+ {"version":3,"file":"InternationalizationProvider.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationProvider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAI9C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EACV,MAAM,EAEN,SAAS,EACT,wBAAwB,EACzB,MAAM,SAAS,CAAC;AA4BjB,MAAM,WAAW,iCAAiC;IAChD;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,MAAM,EACN,QAAQ,EACR,SAAS,EACT,GAAG,EACH,QAAQ,EAAE,YAAY,EACtB,QAAQ,GACT,EAAE,iCAAiC,+BAyBnC;yBAhCe,4BAA4B"}
@@ -4,9 +4,9 @@
4
4
 
5
5
  /**
6
6
  * @file InternationalizationProvider.tsx
7
- * @input React, InternationalizationContext, i18n types
7
+ * @input React, InternationalizationContext, i18n types, calendar resolution
8
8
  * @output Exports InternationalizationProvider component and props type
9
- * @position Provider component for astryx i18n locale + messages
9
+ * @position Provider component for astryx i18n locale, messages, and calendar
10
10
  *
11
11
  * Wraps a subtree with a locale and (optional) additional message catalogs +
12
12
  * overrides. Astryx components inside the subtree resolve their strings via
@@ -21,8 +21,10 @@
21
21
  import { useMemo } from 'react';
22
22
  import { InternationalizationContext } from "./InternationalizationContext.js";
23
23
  import { getLocaleDirection } from "./getLocaleDirection.js";
24
+ import { resolveCalendarSystem } from "./useCalendarSystem.js";
24
25
  import { getResolve } from "./resolve.js";
25
26
  import { jsx as _jsx } from "react/jsx-runtime";
27
+ const PROVIDER_NAME = 'InternationalizationProvider';
26
28
  function normalizeMessages(messages) {
27
29
  const hasRuntimeCatalog = Object.values(messages).some(catalog => Object.values(catalog).some(entry => typeof entry === 'string'));
28
30
  if (!hasRuntimeCatalog) {
@@ -41,19 +43,22 @@ export function InternationalizationProvider({
41
43
  messages,
42
44
  overrides,
43
45
  dir,
46
+ calendar: calendarProp,
44
47
  children
45
48
  }) {
46
49
  const direction = dir ?? getLocaleDirection(locale);
50
+ const calendar = useMemo(() => calendarProp != null ? resolveCalendarSystem(calendarProp, PROVIDER_NAME) : 'gregory', [calendarProp]);
47
51
  const value = useMemo(() => {
48
52
  const providedMessages = messages ?? {};
49
53
  return {
50
54
  locale,
51
55
  direction,
56
+ calendar,
52
57
  messages: normalizeMessages(providedMessages),
53
58
  overrides,
54
59
  translate: getResolve(locale, providedMessages, overrides)
55
60
  };
56
- }, [locale, direction, messages, overrides]);
61
+ }, [locale, direction, calendar, messages, overrides]);
57
62
  return /*#__PURE__*/_jsx(InternationalizationContext, {
58
63
  value: value,
59
64
  children: children
@@ -13,6 +13,7 @@
13
13
  * - useDirection — hook returning the ambient text direction
14
14
  * - getLocaleDirection — server-safe locale → direction helper
15
15
  * - Translator — interface for consumer-injected runtimes
16
+ * - CalendarSystem — the calendars date components can show
16
17
  *
17
18
  * `useTranslator` and `InternationalizationContext` are the public paths
18
19
  * that resolve any Astryx key, so they register the whole English catalog.
@@ -36,6 +37,7 @@ export { useLocale } from './useLocale.js';
36
37
  export { useCollator } from './useCollator.js';
37
38
  export { useDirection } from './useDirection.js';
38
39
  export { getLocaleDirection } from './getLocaleDirection.js';
40
+ export type { CalendarSystem } from '../utils/calendarSystem.js';
39
41
  export type { Translator } from './translator.js';
40
42
  export type { Locale, Catalog, MessageEntry, RuntimeCatalog, MessagesByLocale, ProviderMessagesByLocale, Overrides, } from './types.js';
41
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAC,4BAA4B,EAAC,MAAM,gCAAgC,CAAC;AAC5E,YAAY,EAAC,iCAAiC,EAAC,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAC,2BAA2B,EAAC,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAC,gCAAgC,EAAC,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,YAAY,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,SAAS,GACV,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAC,4BAA4B,EAAC,MAAM,gCAAgC,CAAC;AAC5E,YAAY,EAAC,iCAAiC,EAAC,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAC,2BAA2B,EAAC,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAC,gCAAgC,EAAC,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,YAAY,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,SAAS,GACV,MAAM,SAAS,CAAC"}
@@ -15,6 +15,7 @@
15
15
  * - useDirection — hook returning the ambient text direction
16
16
  * - getLocaleDirection — server-safe locale → direction helper
17
17
  * - Translator — interface for consumer-injected runtimes
18
+ * - CalendarSystem — the calendars date components can show
18
19
  *
19
20
  * `useTranslator` and `InternationalizationContext` are the public paths
20
21
  * that resolve any Astryx key, so they register the whole English catalog.
@@ -0,0 +1,13 @@
1
+ import { type CalendarSystem } from '../utils/calendarSystem.js';
2
+ /**
3
+ * Normalizes a calendar identifier. An identifier outside `CalendarSystem`
4
+ * resolves to `gregory` and warns once in development.
5
+ */
6
+ export declare function resolveCalendarSystem(id: string, source: string): CalendarSystem;
7
+ /**
8
+ * The calendar a date component shows: its own `calendar`, else the
9
+ * provider's resolved calendar, else `gregory`. It resolves during render, so
10
+ * the server and the browser agree when both have `Temporal` or both lack it.
11
+ */
12
+ export declare function useCalendarSystem(calendar: CalendarSystem | undefined, source: string): CalendarSystem;
13
+ //# sourceMappingURL=useCalendarSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCalendarSystem.d.ts","sourceRoot":"","sources":["../../src/i18n/useCalendarSystem.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,yBAAyB,CAAC;AAGjC;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,GACb,cAAc,CAUhB;AAyBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,MAAM,EAAE,MAAM,GACb,cAAc,CAOhB"}
@@ -0,0 +1,52 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useCalendarSystem.ts
7
+ * @input InternationalizationContext, an optional component `calendar`
8
+ * @output The calendar system a date component shows, and the helper that
9
+ * normalizes a calendar identifier
10
+ * @position Internal i18n hook; read by InternationalizationProvider and date
11
+ * components. Not re-exported from the i18n barrel.
12
+ */
13
+ import { use } from 'react';
14
+ import { InternationalizationContext } from "./InternationalizationContext.js";
15
+ import { isCalendarSystem, isCalendarSystemAvailable } from "../utils/calendarSystem.js";
16
+ import { warnOnce } from "../utils/devWarning.js";
17
+
18
+ /**
19
+ * Normalizes a calendar identifier. An identifier outside `CalendarSystem`
20
+ * resolves to `gregory` and warns once in development.
21
+ */
22
+ export function resolveCalendarSystem(id, source) {
23
+ if (isCalendarSystem(id)) {
24
+ return id;
25
+ }
26
+ warnOnce(`calendar-unsupported:${id}`, source, `The calendar "${id}" is not supported; showing Gregorian dates.`);
27
+ return 'gregory';
28
+ }
29
+
30
+ /**
31
+ * A calendar this runtime can show, else `gregory`. Calendars outside the
32
+ * Gregorian-aligned set need a `Temporal` that supports them. The fallback
33
+ * warns once for each calendar, on the server and in the browser.
34
+ */
35
+ function availableCalendarOrGregorian(calendar, source) {
36
+ if (isCalendarSystemAvailable(calendar)) {
37
+ return calendar;
38
+ }
39
+ warnOnce(`calendar-unavailable:${calendar}`, source, `The "${calendar}" calendar needs a Temporal that supports it, and this ` + 'runtime has none; showing Gregorian dates. Load a Temporal polyfill ' + "with every calendar, such as 'temporal-polyfill/full/global', on the " + 'server and in the browser.');
40
+ return 'gregory';
41
+ }
42
+
43
+ /**
44
+ * The calendar a date component shows: its own `calendar`, else the
45
+ * provider's resolved calendar, else `gregory`. It resolves during render, so
46
+ * the server and the browser agree when both have `Temporal` or both lack it.
47
+ */
48
+ export function useCalendarSystem(calendar, source) {
49
+ const context = use(InternationalizationContext);
50
+ const requested = calendar != null ? resolveCalendarSystem(calendar, source) : context.calendar ?? 'gregory';
51
+ return availableCalendarOrGregorian(requested, source);
52
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @file calendarSystem.ts
3
+ * @input A calendar identifier and Gregorian PlainDates
4
+ * @output Calendar months located by their Gregorian days, month and year
5
+ * steps, dates from calendar fields, availability of each calendar, and
6
+ * calendar labels and digits
7
+ * @position Shared date utility; read by the i18n calendar resolution. Values
8
+ * stay Gregorian PlainDates. Temporal owns the arithmetic of every calendar
9
+ * whose months are not Gregorian.
10
+ */
11
+ import type { Locale } from '../i18n/types.js';
12
+ import type { PlainDate } from './dateTypes.js';
13
+ /**
14
+ * A calendar system that date components can show: the calendars that
15
+ * ECMA-402 and Temporal require. Values stay Gregorian ISO dates in every
16
+ * calendar.
17
+ */
18
+ export type CalendarSystem = 'gregory' | 'iso8601' | 'buddhist' | 'roc' | 'japanese' | 'persian' | 'indian' | 'hebrew' | 'chinese' | 'dangi' | 'coptic' | 'ethiopic' | 'ethioaa' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura';
19
+ export declare const CALENDAR_SYSTEMS: ReadonlyArray<CalendarSystem>;
20
+ /** Whether the calendar's months and days are the Gregorian ones. */
21
+ export declare function isGregorianAligned(calendar: CalendarSystem): boolean;
22
+ export declare function isCalendarSystem(value: string): value is CalendarSystem;
23
+ /**
24
+ * Whether this runtime can show the calendar. Gregorian-aligned calendars
25
+ * always can. The others need a `Temporal` that knows the calendar: a
26
+ * polyfill build without non-ISO calendars throws for them.
27
+ */
28
+ export declare function isCalendarSystemAvailable(calendar: CalendarSystem): boolean;
29
+ /** A month of a calendar, located by its first and last Gregorian days. */
30
+ export interface CalendarMonth {
31
+ readonly start: PlainDate;
32
+ readonly end: PlainDate;
33
+ readonly length: number;
34
+ }
35
+ /** The day of the calendar month that a Gregorian date falls on. */
36
+ export declare function calendarDayOfMonth(calendar: CalendarSystem, pd: PlainDate): number;
37
+ /** The calendar month that contains a Gregorian date. */
38
+ export declare function calendarMonthOf(calendar: CalendarSystem, pd: PlainDate): CalendarMonth;
39
+ /** The calendar month `n` months after `month`, or before it when negative. */
40
+ export declare function calendarAddMonths(calendar: CalendarSystem, month: CalendarMonth, n: number): CalendarMonth;
41
+ /**
42
+ * Moves a date by `n` calendar months. It keeps the day of the month and
43
+ * clamps it to the length of the target month.
44
+ */
45
+ export declare function calendarShiftMonths(calendar: CalendarSystem, pd: PlainDate, n: number): PlainDate;
46
+ /**
47
+ * Moves a date by `n` calendar years, to the same month of the target year.
48
+ * It clamps the day of the month. A leap month that the target year does not
49
+ * have becomes its common counterpart, because Temporal keeps the month code
50
+ * and constrains it.
51
+ */
52
+ export declare function calendarShiftYears(calendar: CalendarSystem, pd: PlainDate, n: number): PlainDate;
53
+ /** Whether a Gregorian date falls inside a calendar month. */
54
+ export declare function isInCalendarMonth(pd: PlainDate, month: CalendarMonth): boolean;
55
+ /**
56
+ * The stand-alone month and year label of a calendar month, such as
57
+ * "Aban 1405 AP". It formats a day in the middle of the month: in the
58
+ * Japanese calendar an era can start inside a month, and most days of that
59
+ * month then belong to the era of its middle day.
60
+ */
61
+ export declare function formatCalendarMonthLabel(calendar: CalendarSystem, month: CalendarMonth, locale: Locale): string;
62
+ /**
63
+ * Formats a Gregorian date in the calendar, such as "Friday, Aban 8, 1405 AP".
64
+ * The label always names the month and day that the grid shows. Intl and the
65
+ * runtime's Temporal can disagree on where a month of the Chinese or Dangi
66
+ * calendar starts; the label then takes its month and year from the middle of
67
+ * the Temporal month, its day from Temporal, and its weekday from the date.
68
+ */
69
+ export declare function formatCalendarDate(calendar: CalendarSystem, pd: PlainDate, options: Intl.DateTimeFormatOptions, locale: Locale): string;
70
+ /**
71
+ * The fields of a date written in a calendar. A field gives either `year` or
72
+ * `era` with `eraYear`, and either `monthCode` (such as `M05L` for a leap
73
+ * month) or `month`, the month's position in its year.
74
+ */
75
+ export interface CalendarDateFields {
76
+ readonly year?: number;
77
+ readonly era?: string;
78
+ readonly eraYear?: number;
79
+ readonly monthCode?: string;
80
+ readonly month?: number;
81
+ readonly day: number;
82
+ }
83
+ /**
84
+ * The Gregorian date for fields written in a calendar that needs Temporal,
85
+ * or null when the calendar does not have that date. Temporal validates the
86
+ * fields and never adjusts them: 30 Esfand in a common Persian year is null.
87
+ */
88
+ export declare function calendarDateFromFields(calendar: CalendarSystem, fields: CalendarDateFields): PlainDate | null;
89
+ /** The number of months in the calendar year that contains a date. */
90
+ export declare function calendarMonthsInYear(calendar: CalendarSystem, pd: PlainDate): number;
91
+ //# sourceMappingURL=calendarSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendarSystem.d.ts","sourceRoot":"","sources":["../../src/utils/calendarSystem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,290 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file calendarSystem.ts
5
+ * @input A calendar identifier and Gregorian PlainDates
6
+ * @output Calendar months located by their Gregorian days, month and year
7
+ * steps, dates from calendar fields, availability of each calendar, and
8
+ * calendar labels and digits
9
+ * @position Shared date utility; read by the i18n calendar resolution. Values
10
+ * stay Gregorian PlainDates. Temporal owns the arithmetic of every calendar
11
+ * whose months are not Gregorian.
12
+ */
13
+
14
+ import { dateTimeFormatFor, plainDateAddDays, plainDateAddMonths, plainDateFormat, plainDateFormatDayNumber, plainDateFormatStandaloneMonthYear, plainDateFormatToParts, plainDateIsInRange, getDaysInMonth } from "./plainDate.js";
15
+
16
+ /**
17
+ * A calendar system that date components can show: the calendars that
18
+ * ECMA-402 and Temporal require. Values stay Gregorian ISO dates in every
19
+ * calendar.
20
+ */
21
+
22
+ // A record, so the compiler checks that it lists every calendar exactly once.
23
+ const KNOWN_CALENDARS = {
24
+ gregory: true,
25
+ iso8601: true,
26
+ buddhist: true,
27
+ roc: true,
28
+ japanese: true,
29
+ persian: true,
30
+ indian: true,
31
+ hebrew: true,
32
+ chinese: true,
33
+ dangi: true,
34
+ coptic: true,
35
+ ethiopic: true,
36
+ ethioaa: true,
37
+ 'islamic-civil': true,
38
+ 'islamic-tbla': true,
39
+ 'islamic-umalqura': true
40
+ };
41
+ export const CALENDAR_SYSTEMS = Object.keys(KNOWN_CALENDARS);
42
+ const GREGORIAN_ALIGNED = new Set(['gregory', 'iso8601', 'buddhist', 'roc', 'japanese']);
43
+
44
+ /** Whether the calendar's months and days are the Gregorian ones. */
45
+ export function isGregorianAligned(calendar) {
46
+ return GREGORIAN_ALIGNED.has(calendar);
47
+ }
48
+ export function isCalendarSystem(value) {
49
+ return Object.hasOwn(KNOWN_CALENDARS, value);
50
+ }
51
+
52
+ // The published types must not need the `esnext.temporal` library.
53
+ // This module declares only the Temporal members it reads.
54
+
55
+ function getTemporal() {
56
+ const temporal = globalThis.Temporal;
57
+ return typeof temporal?.PlainDate === 'function' ? temporal : undefined;
58
+ }
59
+
60
+ // The key is the Temporal object, so a polyfill that loads later gets its
61
+ // own probe.
62
+ const supportedCalendars = new WeakMap();
63
+
64
+ /**
65
+ * Whether this runtime can show the calendar. Gregorian-aligned calendars
66
+ * always can. The others need a `Temporal` that knows the calendar: a
67
+ * polyfill build without non-ISO calendars throws for them.
68
+ */
69
+ export function isCalendarSystemAvailable(calendar) {
70
+ if (isGregorianAligned(calendar)) {
71
+ return true;
72
+ }
73
+ const temporal = getTemporal();
74
+ if (temporal == null) {
75
+ return false;
76
+ }
77
+ let known = supportedCalendars.get(temporal);
78
+ if (known == null) {
79
+ known = new Map();
80
+ supportedCalendars.set(temporal, known);
81
+ }
82
+ let isSupported = known.get(calendar);
83
+ if (isSupported == null) {
84
+ try {
85
+ new temporal.PlainDate(2000, 1, 1, calendar);
86
+ isSupported = true;
87
+ } catch {
88
+ isSupported = false;
89
+ }
90
+ known.set(calendar, isSupported);
91
+ }
92
+ return isSupported;
93
+ }
94
+ function inCalendar(calendar, pd) {
95
+ const temporal = getTemporal();
96
+ if (temporal == null) {
97
+ throw new Error(`The "${calendar}" calendar needs Temporal.`);
98
+ }
99
+ return new temporal.PlainDate(pd.year, pd.month, pd.day, calendar);
100
+ }
101
+ function toGregorian(date) {
102
+ const iso = date.withCalendar('iso8601');
103
+ return {
104
+ year: iso.year,
105
+ month: iso.month,
106
+ day: iso.day
107
+ };
108
+ }
109
+
110
+ /** A month of a calendar, located by its first and last Gregorian days. */
111
+
112
+ function makeMonth(start, length) {
113
+ return {
114
+ start,
115
+ end: plainDateAddDays(start, length - 1),
116
+ length
117
+ };
118
+ }
119
+ function gregorianMonth(year, month) {
120
+ return makeMonth({
121
+ year,
122
+ month,
123
+ day: 1
124
+ }, getDaysInMonth(year, month));
125
+ }
126
+ function temporalMonth(firstDay) {
127
+ return makeMonth(toGregorian(firstDay), firstDay.daysInMonth);
128
+ }
129
+
130
+ /** The day of the calendar month that a Gregorian date falls on. */
131
+ export function calendarDayOfMonth(calendar, pd) {
132
+ return isGregorianAligned(calendar) ? pd.day : inCalendar(calendar, pd).day;
133
+ }
134
+
135
+ /** The calendar month that contains a Gregorian date. */
136
+ export function calendarMonthOf(calendar, pd) {
137
+ if (isGregorianAligned(calendar)) {
138
+ return gregorianMonth(pd.year, pd.month);
139
+ }
140
+ return temporalMonth(inCalendar(calendar, pd).with({
141
+ day: 1
142
+ }));
143
+ }
144
+
145
+ /** The calendar month `n` months after `month`, or before it when negative. */
146
+ export function calendarAddMonths(calendar, month, n) {
147
+ if (n === 0) {
148
+ return month;
149
+ }
150
+ if (isGregorianAligned(calendar)) {
151
+ const {
152
+ year,
153
+ month: monthOfYear
154
+ } = plainDateAddMonths(month.start, n);
155
+ return gregorianMonth(year, monthOfYear);
156
+ }
157
+ return temporalMonth(inCalendar(calendar, month.start).add({
158
+ months: n
159
+ }));
160
+ }
161
+
162
+ /**
163
+ * Moves a date by `n` calendar months. It keeps the day of the month and
164
+ * clamps it to the length of the target month.
165
+ */
166
+ export function calendarShiftMonths(calendar, pd, n) {
167
+ if (isGregorianAligned(calendar)) {
168
+ return plainDateAddMonths(pd, n);
169
+ }
170
+ return toGregorian(inCalendar(calendar, pd).add({
171
+ months: n
172
+ }));
173
+ }
174
+
175
+ /**
176
+ * Moves a date by `n` calendar years, to the same month of the target year.
177
+ * It clamps the day of the month. A leap month that the target year does not
178
+ * have becomes its common counterpart, because Temporal keeps the month code
179
+ * and constrains it.
180
+ */
181
+ export function calendarShiftYears(calendar, pd, n) {
182
+ if (isGregorianAligned(calendar)) {
183
+ return plainDateAddMonths(pd, n * 12);
184
+ }
185
+ return toGregorian(inCalendar(calendar, pd).add({
186
+ years: n
187
+ }));
188
+ }
189
+
190
+ /** Whether a Gregorian date falls inside a calendar month. */
191
+ export function isInCalendarMonth(pd, month) {
192
+ return plainDateIsInRange(pd, [month.start, month.end]);
193
+ }
194
+
195
+ /**
196
+ * The stand-alone month and year label of a calendar month, such as
197
+ * "Aban 1405 AP". It formats a day in the middle of the month: in the
198
+ * Japanese calendar an era can start inside a month, and most days of that
199
+ * month then belong to the era of its middle day.
200
+ */
201
+ export function formatCalendarMonthLabel(calendar, month, locale) {
202
+ return plainDateFormatStandaloneMonthYear(plainDateAddDays(month.start, Math.floor(month.length / 2)), locale, labelCalendar(calendar));
203
+ }
204
+
205
+ // Intl has no month names for `iso8601`. Labels use `gregory`, which has the
206
+ // same months and days.
207
+ function labelCalendar(calendar) {
208
+ return calendar === 'iso8601' ? 'gregory' : calendar;
209
+ }
210
+ function formatToParts(calendar, pd, options, locale) {
211
+ return plainDateFormatToParts(pd, {
212
+ ...options,
213
+ calendar
214
+ }, locale);
215
+ }
216
+ const MONTH_PART_TYPES = new Set(['era', 'year', 'relatedYear', 'yearName', 'month']);
217
+ function monthPartsKey(parts) {
218
+ return parts.filter(part => MONTH_PART_TYPES.has(part.type)).map(part => part.value).join('|');
219
+ }
220
+
221
+ /**
222
+ * Formats a Gregorian date in the calendar, such as "Friday, Aban 8, 1405 AP".
223
+ * The label always names the month and day that the grid shows. Intl and the
224
+ * runtime's Temporal can disagree on where a month of the Chinese or Dangi
225
+ * calendar starts; the label then takes its month and year from the middle of
226
+ * the Temporal month, its day from Temporal, and its weekday from the date.
227
+ */
228
+ export function formatCalendarDate(calendar, pd, options, locale) {
229
+ if (isGregorianAligned(calendar)) {
230
+ return plainDateFormat(pd, {
231
+ ...options,
232
+ calendar: labelCalendar(calendar)
233
+ }, locale);
234
+ }
235
+ const date = inCalendar(calendar, pd);
236
+ const parts = formatToParts(calendar, pd, options, locale);
237
+ const middle = plainDateAddDays(pd, Math.floor(date.daysInMonth / 2) - (date.day - 1));
238
+ const middleParts = formatToParts(calendar, middle, options, locale);
239
+ const intlDay = formatToParts(calendar, pd, {
240
+ day: 'numeric'
241
+ }, 'en-u-nu-latn').find(part => part.type === 'day')?.value;
242
+ if (Number(intlDay) === date.day && monthPartsKey(parts) === monthPartsKey(middleParts)) {
243
+ return parts.map(part => part.value).join('');
244
+ }
245
+ const {
246
+ numberingSystem
247
+ } = dateTimeFormatFor(locale, {
248
+ ...options,
249
+ calendar,
250
+ timeZone: 'UTC'
251
+ }).resolvedOptions();
252
+ const weekday = parts.find(part => part.type === 'weekday')?.value;
253
+ return middleParts.map(part => part.type === 'day' ? plainDateFormatDayNumber(date.day, locale, numberingSystem) : part.type === 'weekday' && weekday != null ? weekday : part.value).join('');
254
+ }
255
+
256
+ /**
257
+ * The fields of a date written in a calendar. A field gives either `year` or
258
+ * `era` with `eraYear`, and either `monthCode` (such as `M05L` for a leap
259
+ * month) or `month`, the month's position in its year.
260
+ */
261
+
262
+ /**
263
+ * The Gregorian date for fields written in a calendar that needs Temporal,
264
+ * or null when the calendar does not have that date. Temporal validates the
265
+ * fields and never adjusts them: 30 Esfand in a common Persian year is null.
266
+ */
267
+ export function calendarDateFromFields(calendar, fields) {
268
+ const temporal = getTemporal();
269
+ if (temporal == null) {
270
+ throw new Error(`The "${calendar}" calendar needs Temporal.`);
271
+ }
272
+ try {
273
+ return toGregorian(temporal.PlainDate.from({
274
+ ...fields,
275
+ calendar
276
+ }, {
277
+ overflow: 'reject'
278
+ }));
279
+ } catch (error) {
280
+ if (error instanceof RangeError || error instanceof TypeError) {
281
+ return null;
282
+ }
283
+ throw error;
284
+ }
285
+ }
286
+
287
+ /** The number of months in the calendar year that contains a date. */
288
+ export function calendarMonthsInYear(calendar, pd) {
289
+ return isGregorianAligned(calendar) ? 12 : inCalendar(calendar, pd).monthsInYear;
290
+ }
@@ -80,7 +80,7 @@ export declare const DATE_FORMAT_MONTH_YEAR: Intl.DateTimeFormatOptions;
80
80
  * stand-alone forms (`component:Calendar` FR1). Internal: not re-exported from
81
81
  * `utils/index.ts`.
82
82
  */
83
- export declare function plainDateFormatStandaloneMonthYear(pd: PlainDate, locale: Locale): string;
83
+ export declare function plainDateFormatStandaloneMonthYear(pd: PlainDate, locale: Locale, calendar?: Intl.DateTimeFormatOptions['calendar']): string;
84
84
  export declare const DATE_FORMAT_WEEKDAY_ONLY: Intl.DateTimeFormatOptions;
85
85
  /**
86
86
  * The platform's weekday names for a locale, Sunday first, in `Intl`'s
@@ -104,6 +104,11 @@ export declare const DATE_FORMAT_SHORT_WITH_YEAR: Intl.DateTimeFormatOptions;
104
104
  * serialization, or any Astryx component's calendar semantics.
105
105
  */
106
106
  export declare function plainDateFormat(pd: PlainDate, options: Intl.DateTimeFormatOptions, locale?: Locale): string;
107
+ /**
108
+ * The parts of `plainDateFormat`'s output, from the same formatter and date.
109
+ * Internal: not re-exported from `utils/index.ts`.
110
+ */
111
+ export declare function plainDateFormatToParts(pd: PlainDate, options: Intl.DateTimeFormatOptions, locale: Locale): Intl.DateTimeFormatPart[];
107
112
  /** @internal plainDateFormat's formatter cache; not part of the public API. */
108
113
  export declare function dateTimeFormatFor(locale: Locale, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat;
109
114
  /**
@@ -120,12 +125,13 @@ export declare function dateTimeFormatFor(locale: Locale, options: Intl.DateTime
120
125
  export declare function plainDateWeekFieldName(locale?: Locale): string;
121
126
  /**
122
127
  * A day of the month or a week number in the locale's own digits: `15` in
123
- * English, `۱۵` in Persian, `١٥` in Egyptian Arabic. Only the digits follow the
124
- * locale. The number stays the Gregorian day or ISO week that it names, so no
125
- * other calendar applies, and no grouping separator or unit word is added.
128
+ * English, `۱۵` in Persian, `١٥` in Egyptian Arabic. A `numberingSystem`
129
+ * replaces the locale's digits. Only the digits change. The number stays the
130
+ * value that it is given, so no calendar applies, and no grouping separator or
131
+ * unit word is added.
126
132
  * Internal: not re-exported from `utils/index.ts`.
127
133
  */
128
- export declare function plainDateFormatDayNumber(value: number, locale: Locale): string;
134
+ export declare function plainDateFormatDayNumber(value: number, locale: Locale, numberingSystem?: string): string;
129
135
  /**
130
136
  * The date-only members of Timestamp's `format` vocabulary that a
131
137
  * calendar-date field (no time-of-day) can render. Both Timestamp and