@codecademy/gamut

68.2.268.2.3-alpha.794432.0
dist/DatePicker/utils/locale.js
+dist/DatePicker/utils/locale.jsNew file
+93
Index: package/dist/DatePicker/utils/locale.js
===================================================================
--- package/dist/DatePicker/utils/locale.js
+++ package/dist/DatePicker/utils/locale.js
@@ -0,0 +1,93 @@
+// Replaces `Intl.Locale` when missing or incomplete (e.g. no `getWeekInfo` in Firefox).
+// https://formatjs.github.io/docs/polyfills/intl-locale/
+import '@formatjs/intl-locale/polyfill.js';
+import { useEffect, useMemo, useState } from 'react';
+
+/**
+ * The runtime default locale string (user agent), matching what `Intl` uses when no locale is passed.
+ */
+export const getDefaultLocaleTag = () => new Intl.DateTimeFormat().resolvedOptions().locale;
+
+/**
+ * Resolves `Intl.LocalesArgument` (or `undefined`) to a stable `Intl.Locale` instance for formatting
+ * and locale metadata (e.g. `getWeekInfo()` in supporting environments).
+ *
+ * - `undefined` → default runtime locale via {@link getDefaultLocaleTag}
+ * - `Intl.Locale` → returned as-is (no duplicate allocation)
+ * - `string` / `readonly string[]` → `new Intl.Locale(...)`
+ */
+export const resolveLocale = locales => {
+  if (locales === undefined) {
+    return new Intl.Locale(getDefaultLocaleTag());
+  }
+  if (locales instanceof Intl.Locale) {
+    return locales;
+  }
+  if (typeof locales === 'string') {
+    return new Intl.Locale(locales);
+  }
+  const first = locales[0];
+  if (first === undefined) {
+    return new Intl.Locale(getDefaultLocaleTag());
+  }
+  if (typeof first === 'string') {
+    return new Intl.Locale(first);
+  }
+  return first instanceof Intl.Locale ? first : new Intl.Locale(String(first));
+};
+
+/**
+ * Memoized {@link resolveLocale} for calendar subcomponents. Pass the same `locale` prop you accept
+ * from `CalendarBaseProps` (optional `Intl.LocalesArgument`).
+ */
+export const useResolvedLocale = locale => useMemo(() => resolveLocale(locale), [locale]);
+
+/**
+ * Convert an Intl.Locale to a string. This is necessary the Intl.DateTimeFormat constructor only accepts a string in some versions of TS.
+ * @param locale - The Intl.Locale to convert to a string.
+ * @returns The stringified locale.
+ */
+export const stringifyLocale = locale => locale.toString();
+
+/** ISO weekday: 1 = Monday … 7 = Sunday (matches `Intl.Locale#getWeekInfo().firstDay`). */
+
+/** `getWeekInfo` is stage-3; typings may lag behind runtime / polyfill. */
+
+/**
+ * First calendar column weekday from `locale` (via `getWeekInfo()`), or explicit override.
+ * - `weekStartsOnOverride` — ISO weekday **1–7** (Monday … Sunday), same as `getWeekInfo().firstDay`
+ * - omitted → `locale.getWeekInfo().firstDay` when available, else **7** (Sunday)
+ */
+export const getIsoFirstDayFromLocale = (locale, weekStartsOnOverride) => {
+  if (weekStartsOnOverride) return weekStartsOnOverride;
+  try {
+    const getWeekInfo = locale.getWeekInfo?.bind(locale);
+    if (typeof getWeekInfo === 'function') {
+      const {
+        firstDay
+      } = getWeekInfo();
+      if (typeof firstDay === 'number' && firstDay >= 1 && firstDay <= 7) {
+        return firstDay;
+      }
+    }
+  } catch {
+    /* ignore */
+  }
+  return 7;
+};
+
+/**
+ * Hook: resolved first weekday for the calendar grid. Re-reads after mount so async polyfills
+ * (e.g. Firefox) can install `getWeekInfo` before the first paint in some bundles.
+ */
+export const useIsoFirstWeekday = (locale, weekStartsOnOverride) => {
+  const [firstDay, setFirstDay] = useState(() => getIsoFirstDayFromLocale(locale, weekStartsOnOverride));
+  useEffect(() => {
+    setFirstDay(getIsoFirstDayFromLocale(locale, weekStartsOnOverride));
+    const t = setTimeout(() => {
+      setFirstDay(getIsoFirstDayFromLocale(locale, weekStartsOnOverride));
+    }, 0);
+    return () => clearTimeout(t);
+  }, [locale, weekStartsOnOverride]);
+  return firstDay;
+};
\ No newline at end of file