import React, { createContext, PropsWithChildren, useContext, useEffect, useMemo, } from "react"; import * as enUS from ":/locales/en-US.json"; import * as frFR from ":/locales/fr-FR.json"; import { PartialNested } from ":/types"; import { Locales } from ":/components/Provider/Locales"; import { ToastProvider } from ":/components/Toast/ToastProvider"; import { ModalProvider } from ":/components/Modal/ModalProvider"; type TranslationSet = PartialNested; const CunninghamContext = createContext< | undefined | { t: (key: string, vars?: Record) => string; currentLocale: string; } >(undefined); export const useCunningham = () => { const context = useContext(CunninghamContext); if (context === undefined) { throw new Error("useCunningham must be used within a CunninghamProvider."); } return context; }; interface Props extends PropsWithChildren { customLocales?: Record; currentLocale?: string; theme?: string; modalParentSelector?: () => HTMLElement; } export const DEFAULT_LOCALE = Locales.enUS; export const DEFAULT_THEME = "default"; export const SUPPORTED_LOCALES = Object.values(Locales); const THEME_CLASSNAME_PREFIX = "cunningham-theme--"; const findTranslation = ( key: string, locale: TranslationSet, ): string | undefined => { const [namespace, ...keys] = key.split("."); return keys.reduce((acc, subKey) => acc[subKey], (locale as any)[namespace]); }; export const CunninghamProvider = ({ currentLocale = DEFAULT_LOCALE, customLocales, theme = DEFAULT_THEME, modalParentSelector, children, }: Props) => { const locales: Record = useMemo( () => ({ [DEFAULT_LOCALE]: enUS, "fr-FR": frFR, ...customLocales, }), [customLocales], ); const locale = useMemo(() => { return (locales[currentLocale] && currentLocale) || DEFAULT_LOCALE; }, [currentLocale, locales]); const context = useMemo( () => ({ t: (key: string, vars?: Record) => { let message: string = findTranslation(key, locales[locale]) ?? findTranslation(key, locales[DEFAULT_LOCALE]) ?? key; // Replace vars in message from vars in form of {varName}. if (vars) { Object.keys(vars).forEach((varName) => { message = message?.replace(`{${varName}}`, "" + vars[varName]); }); } return message; }, currentLocale: locale, }), [currentLocale, locales], ); useEffect(() => { const root = document.querySelector(":root")!; root.classList.forEach((className) => { if (className.startsWith(THEME_CLASSNAME_PREFIX)) { root.classList.remove(className); } }); root.classList.add(THEME_CLASSNAME_PREFIX + theme); }, [theme]); return (
{children}
); };