@cfpb/cfpb-design-system
Version:
CFPB's UI framework
8 lines (7 loc) • 10.2 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../../packages/cfpb-design-system/src/elements/cfpb-utilities/i18n-service.js", "../../../../../packages/cfpb-design-system/src/elements/cfpb-utilities/media-query-service.js"],
"sourcesContent": ["/**\n * A service for embedding and using translations via markup.\n *\n * Expect markup like:\n * <i18n-service>\n * <template>\n * {\n * \"en\": { \"hello\": \"Hello\", \"good afternoon\": \"Good afternoon\" },\n * \"es\": { \"hello\": \"Hola\", \"good afternoon\": \"Buenas tardes\" }\n * }\n * </template>\n * </i18n-service>\n */\nexport class I18nService extends HTMLElement {\n #translations;\n #language;\n\n constructor() {\n super();\n this.#translations = {};\n this.#language = '';\n }\n\n connectedCallback() {\n const errors = [];\n const template = this.querySelector('template');\n if (!template) {\n errors.push([\n 'missing-template',\n 'No <template> found inside <i18n-service>',\n ]);\n return;\n }\n\n try {\n const json = template.innerHTML.trim();\n const data = JSON.parse(json);\n\n if (typeof data !== 'object' || Array.isArray(data)) {\n errors.push([\n 'invalid-format',\n 'Translations JSON must be an object keyed by the language codes',\n ]);\n }\n\n this.#translations = data;\n\n // Set default lanugage to the first available one, if not already set.\n const available = this.availableLanguages;\n if (available.length > 0 && !this.#language) {\n this.language = available[0];\n }\n } catch (err) {\n errors.push([\n 'parse-error',\n 'Failed to parse i18n template JSON',\n { err },\n ]);\n }\n\n // Aggregate any errors together and dispatch them when the app has\n // refreshed, which gives other components a chance to listen for them.\n requestAnimationFrame(() => {\n errors.forEach((error) => {\n this.#dispatchError(...error);\n });\n });\n }\n\n /**\n * Set the current language\n * @param {string} lang - Language code (e.g. \"en\", \"es\", \"ar\", \"ru\", etc.)\n */\n set language(lang) {\n if (!this.#translations[lang]) {\n this.#dispatchError('invalid-language', `Unsupported language: ${lang}`, {\n lang,\n });\n return;\n }\n\n if (this.#language !== lang) {\n this.#language = lang;\n this.dispatchEvent(\n new CustomEvent('i18n-change', {\n detail: { language: lang },\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n /**\n * @returns {string} The currently set language.\n */\n get language() {\n return this.#language;\n }\n\n get availableLanguages() {\n return Object.keys(this.#translations);\n }\n\n /**\n * Translate a language key.\n * @param {string} key - An arbitrary key from the <template>.\n * @returns {string} The translation.\n */\n translate(key) {\n return this.#translations[this.#language]?.[key] || key;\n }\n\n #dispatchError(type, message, extra = {}) {\n this.dispatchEvent(\n new CustomEvent('i18n-error', {\n detail: { type, message, ...extra },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n static init() {\n window.customElements.get('i18n-service') ||\n window.customElements.define('i18n-service', I18nService);\n }\n}\n", "/**\n * A service for observing named breakpoints via matchMedia.\n * Emits a `change` event (CustomEvent) whenever any breakpoint match status changes.\n * Consumers can also call `matches` or `is(breakpointName)` to get current state.\n */\nexport class MediaQueryService extends EventTarget {\n #breakpoints;\n #mqls = new Map(); // Map<key, MediaQueryList>\n #handlers = new Map(); // Map<key, listener>\n #matches = new Map(); // Map<key, boolean>\n #pendingUpdate = false;\n\n /**\n *\n * @param {Record<string, {min: number, max?: number}} [breakpoints]\n * A map of breakpoint name -> { min: px, optional max: px }.\n * If not provided, default breakpoints are used.\n */\n constructor(breakpoints) {\n super();\n\n this.#breakpoints = breakpoints ?? {\n xs: { min: 0, max: 600 },\n sm: { min: 601, max: 900 },\n med: { min: 901, max: 1020 },\n lg: { min: 1021, max: 1200 },\n xl: { min: 1201 },\n };\n\n // Setup each media query.\n for (const [key, range] of Object.entries(this.#breakpoints)) {\n const mqString = this.#rangeToMediaQuery(range);\n const mql = window.matchMedia(mqString);\n\n this.#mqls.set(key, mql);\n this.#matches.set(key, mql.matches);\n\n const listener = (evt) => {\n this.#matches.set(key, evt.matches);\n\n if (!this.#pendingUpdate) {\n this.#pendingUpdate = true;\n\n requestAnimationFrame(() => {\n this.#pendingUpdate = false;\n this.#dispatchChange(); // Dispatch only once per frame.\n });\n }\n\n /*\n const prev = this.#matches.get(key);\n if (prev !== evt.matches) {\n this.#matches.set(key, evt.matches);\n this.#dispatchChange();\n }\n */\n };\n\n mql.addEventListener('change', listener);\n\n this.#handlers.set(key, listener);\n }\n\n // Emit initial state.\n this.#dispatchChange();\n }\n\n #rangeToMediaQuery(range) {\n const parts = [];\n if (range.min != null) {\n parts.push(`(min-width: ${range.min}px)`);\n }\n if (range.max != null) {\n parts.push(`(max-width: ${range.max}px)`);\n }\n return parts.join(' and ');\n }\n\n #dispatchChange() {\n const detail = {\n matches: Object.fromEntries(this.#matches),\n };\n\n this.dispatchEvent(new CustomEvent('change', { detail }));\n }\n\n get matches() {\n return Object.fromEntries(this.#matches);\n }\n\n destroy() {\n for (const [key, mql] of this.#mqls.entries()) {\n const listener = this.#handlers.get(key);\n if (!listener) continue;\n mql.removeEventListener('change', listener);\n }\n\n this.#mqls.clear();\n this.#handlers.clear();\n this.#matches.clear();\n }\n}\n"],
"mappings": "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",
"names": ["_translations", "_language", "_I18nService_instances", "dispatchError_fn", "_I18nService", "__privateAdd", "__privateSet", "errors", "template", "json", "data", "available", "__privateGet", "err", "error", "__privateMethod", "lang", "key", "_a", "type", "message", "extra", "__spreadValues", "I18nService", "_breakpoints", "_mqls", "_handlers", "_matches", "_pendingUpdate", "_MediaQueryService_instances", "rangeToMediaQuery_fn", "dispatchChange_fn", "MediaQueryService", "breakpoints", "__privateAdd", "__privateSet", "key", "range", "__privateGet", "mqString", "__privateMethod", "mql", "listener", "evt", "parts", "detail"]
}