@ptx-showcase/utils 1.0.0 → 1.1.0

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/README.md CHANGED
@@ -131,6 +131,35 @@ await downloadBlob(blob, { hash: 12345 })
131
131
 
132
132
  ---
133
133
 
134
+ ### Flags
135
+
136
+ Resolves a flag icon URL for a language/country code, with a three-tier fallback: primary source ([flagcdn.com](https://flagcdn.com)) → secondary source ([flagicons.lipis.dev](https://flagicons.lipis.dev)) → an "unknown flag" placeholder. The placeholder is also returned directly when `languageCode` isn't a usable string at runtime (`null`, `undefined`, `NaN`, an object, an empty/whitespace string, etc.) — the functions never throw.
137
+
138
+ `FLAG_CODE_ALIASES` maps language codes that differ from their representative country's ISO code (e.g. `en` → `gb`, `ja` → `jp`); codes with no entry are used as-is.
139
+
140
+ ```ts
141
+ // from root
142
+ import { getFlagUrl, handleFlagImageError } from '@ptx-showcase/utils'
143
+
144
+ // from subpath
145
+ import { getFlagUrl, handleFlagImageError } from '@ptx-showcase/utils/flags'
146
+
147
+ getFlagUrl('fr') // → 'https://flagcdn.com/fr.svg'
148
+ getFlagUrl('en') // → 'https://flagcdn.com/gb.svg' (resolved via FLAG_CODE_ALIASES)
149
+ getFlagUrl(null as unknown as string) // → unknown-flag placeholder URL
150
+
151
+ // React — escalates primary → fallback → unknown placeholder on load failure
152
+ <img src={getFlagUrl(code)} onError={(e) => handleFlagImageError(e, code)} />
153
+
154
+ // Vue
155
+ <img :src="getFlagUrl(code)" @error="(e) => handleFlagImageError(e, code)" />
156
+
157
+ // Svelte
158
+ <img src={getFlagUrl(code)} on:error={(e) => handleFlagImageError(e, code)} />
159
+ ```
160
+
161
+ ---
162
+
134
163
  ### getCurrencySymbol
135
164
 
136
165
  Resolves the symbol for an ISO 4217 currency code via `Intl`. A handful of currencies (`AUD`, `CAD`, `NZD`, `CHF`, etc.) are overridden where the `Intl` narrow symbol would be ambiguous.
@@ -270,6 +299,7 @@ truncateDecimals(undefined) // → 'N/A'
270
299
  | `@ptx-showcase/utils/cookies` | `setCookie`, `getCookie`, `deleteCookie`, `defaultCookieOptions` |
271
300
  | `@ptx-showcase/utils/currency-symbol` | `getCurrencySymbol` |
272
301
  | `@ptx-showcase/utils/download-file` | `downloadBlob` |
302
+ | `@ptx-showcase/utils/flags` | `getFlagUrl`, `getFlagFallbackUrl`, `getFlagUnknownUrl`, `handleFlagImageError`, `FLAG_CODE_ALIASES` |
273
303
  | `@ptx-showcase/utils/handle-api-error` | `handleApiError`, `initHandleApiError` |
274
304
  | `@ptx-showcase/utils/normalize-input` | `normalizeDecimalInput`, `normalizePhoneNumberInput` |
275
305
  | `@ptx-showcase/utils/truncate-decimals` | `truncateDecimals` |
@@ -0,0 +1 @@
1
+ var e={uk:`gb`,en:`gb`,ja:`jp`,zh:`cn`,fr:`fr`,es:`es`,de:`de`,cn:`cn`,ko:`kr`,ar:`sa`,he:`il`,hi:`in`,vi:`vn`,el:`gr`,cs:`cz`,sv:`se`,da:`dk`,fa:`ir`,ur:`pk`,bn:`bd`,ms:`my`,nb:`no`,nn:`no`,ca:`es`,cy:`gb`,ga:`ie`,gd:`gb`,lb:`lu`,eu:`es`,gl:`es`,rm:`ch`,kk:`kz`,hy:`am`,ka:`ge`,sq:`al`,sl:`si`,sr:`rs`,et:`ee`,fil:`ph`,tl:`ph`,sw:`ke`},t=`https://flagcdn.com`,n=`https://flagicons.lipis.dev/flags/4x3`,r=e=>typeof e==`string`&&e.trim().length>0,i=t=>{let n=t.trim().toLowerCase();return Object.hasOwn(e,n)?e[n]:n},a=()=>`${n}/xx.svg`,o=e=>r(e)?`${t}/${i(e)}.svg`:a(),s=e=>r(e)?`${n}/${i(e)}.svg`:a(),c=(e,t)=>{let n=s(t),r=a(),i=e.currentTarget.src;i!==r&&(e.currentTarget.src=i===n?r:n)};Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
@@ -0,0 +1,52 @@
1
+ //#region src/core/flags/constants/flags.constant.ts
2
+ var e = {
3
+ uk: "gb",
4
+ en: "gb",
5
+ ja: "jp",
6
+ zh: "cn",
7
+ fr: "fr",
8
+ es: "es",
9
+ de: "de",
10
+ cn: "cn",
11
+ ko: "kr",
12
+ ar: "sa",
13
+ he: "il",
14
+ hi: "in",
15
+ vi: "vn",
16
+ el: "gr",
17
+ cs: "cz",
18
+ sv: "se",
19
+ da: "dk",
20
+ fa: "ir",
21
+ ur: "pk",
22
+ bn: "bd",
23
+ ms: "my",
24
+ nb: "no",
25
+ nn: "no",
26
+ ca: "es",
27
+ cy: "gb",
28
+ ga: "ie",
29
+ gd: "gb",
30
+ lb: "lu",
31
+ eu: "es",
32
+ gl: "es",
33
+ rm: "ch",
34
+ kk: "kz",
35
+ hy: "am",
36
+ ka: "ge",
37
+ sq: "al",
38
+ sl: "si",
39
+ sr: "rs",
40
+ et: "ee",
41
+ fil: "ph",
42
+ tl: "ph",
43
+ sw: "ke"
44
+ }, t = "https://flagcdn.com", n = "https://flagicons.lipis.dev/flags/4x3", r = (e) => typeof e == "string" && e.trim().length > 0, i = (t) => {
45
+ let n = t.trim().toLowerCase();
46
+ return Object.hasOwn(e, n) ? e[n] : n;
47
+ }, a = () => `${n}/xx.svg`, o = (e) => r(e) ? `${t}/${i(e)}.svg` : a(), s = (e) => r(e) ? `${n}/${i(e)}.svg` : a(), c = (e, t) => {
48
+ let n = s(t), r = a(), i = e.currentTarget.src;
49
+ i !== r && (e.currentTarget.src = i === n ? r : n);
50
+ };
51
+ //#endregion
52
+ export { c as i, a as n, o as r, s as t };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Maps an ISO 639-1 language code to the ISO 3166-1 country code of the flag
3
+ * that should represent it, for languages where the two codes differ.
4
+ *
5
+ * Some mappings are inherently ambiguous (e.g. Arabic, Swahili are official in
6
+ * many countries) — the chosen country is the most common default for that case.
7
+ */
8
+ export declare const FLAG_CODE_ALIASES: Record<string, string>;
9
+ export declare const FLAGS_BASE_URL: "https://flagcdn.com";
10
+ export declare const FLAGS_FALLBACK_URL: "https://flagicons.lipis.dev/flags/4x3";
11
+ /**
12
+ * Code for the "unknown flag" placeholder (a plain flag with no country emblem),
13
+ * only available on {@link FLAGS_FALLBACK_URL} — flagcdn.com has no equivalent.
14
+ */
15
+ export declare const FLAG_UNKNOWN_CODE: "xx";
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Returns the "unknown flag" placeholder URL, used when both {@link getFlagUrl}
3
+ * and {@link getFlagFallbackUrl} fail to load, and as the direct result of either
4
+ * when `languageCode` isn't a usable string (e.g. `null`, `undefined`, `NaN`, an object).
5
+ */
6
+ export declare const getFlagUnknownUrl: () => string;
7
+ /**
8
+ * Returns the primary flag icon URL ({@link FLAGS_BASE_URL}) for the given language/country code.
9
+ * Falls back to {@link getFlagUnknownUrl} if `languageCode` isn't a non-empty string at runtime.
10
+ *
11
+ * @param languageCode - Language or country code (e.g. `en`, `uk`, `fr`)
12
+ */
13
+ export declare const getFlagUrl: (languageCode: string) => string;
14
+ /**
15
+ * Returns the fallback flag icon URL ({@link FLAGS_FALLBACK_URL}), used when
16
+ * the image from {@link getFlagUrl} fails to load. Falls back to {@link getFlagUnknownUrl}
17
+ * if `languageCode` isn't a non-empty string at runtime.
18
+ *
19
+ * @param languageCode - Language or country code (e.g. `en`, `uk`, `fr`)
20
+ */
21
+ export declare const getFlagFallbackUrl: (languageCode: string) => string;
22
+ /**
23
+ * `onError` handler for an `<img>` displaying a flag from {@link getFlagUrl}. Escalates
24
+ * through three tiers, stopping once it reaches the last one to avoid an infinite loop:
25
+ * primary ({@link getFlagUrl}) → fallback ({@link getFlagFallbackUrl}) → unknown-flag
26
+ * placeholder ({@link getFlagUnknownUrl}).
27
+ *
28
+ * @param event - Image error event (DOM `Event` or React's `SyntheticEvent<HTMLImageElement>`)
29
+ * @param languageCode - Same code that was passed to {@link getFlagUrl}
30
+ *
31
+ * @example React
32
+ * ```tsx
33
+ * <img src={getFlagUrl(code)} onError={(e) => handleFlagImageError(e, code)} />
34
+ * ```
35
+ *
36
+ * @example Vue
37
+ * ```vue
38
+ * <img :src="getFlagUrl(code)" @error="(e) => handleFlagImageError(e, code)" />
39
+ * ```
40
+ *
41
+ * @example Svelte
42
+ * ```svelte
43
+ * <img src={getFlagUrl(code)} on:error={(e) => handleFlagImageError(e, code)} />
44
+ * ```
45
+ */
46
+ export declare const handleFlagImageError: (event: {
47
+ currentTarget: HTMLImageElement;
48
+ }, languageCode: string) => void;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../build-query-params/index.cjs"),t=require("../clean-object/index.cjs"),n=require("../cookies/index.cjs"),r=require("../build-sort-param/index.cjs"),i=require("../chunks/currency-symbol-gb63Q_9Q.cjs"),a=require("../download-file/index.cjs"),o=require("../chunks/handle-api-error-D714W9rp.cjs"),s=require("../normalize-input/index.cjs"),c=require("../chunks/truncate-decimals-CArL-IeO.cjs");exports.buildQueryParams=e.buildQueryParams,exports.buildSortParam=r.buildSortParam,exports.cleanObject=t.cleanObject,exports.defaultCookieOptions=n.defaultCookieOptions,exports.deleteCookie=n.deleteCookie,exports.downloadBlob=a.downloadBlob,exports.getCookie=n.getCookie,exports.getCurrencySymbol=i.t,exports.handleApiError=o.t,exports.initHandleApiError=o.n,exports.normalizeDecimalInput=s.normalizeDecimalInput,exports.normalizePhoneNumberInput=s.normalizePhoneNumberInput,exports.setCookie=n.setCookie,exports.truncateDecimals=c.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../build-query-params/index.cjs"),t=require("../clean-object/index.cjs"),n=require("../cookies/index.cjs"),r=require("../build-sort-param/index.cjs"),i=require("../chunks/currency-symbol-gb63Q_9Q.cjs"),a=require("../download-file/index.cjs"),o=require("../chunks/flags-BRQ9Jims.cjs"),s=require("../chunks/handle-api-error-D714W9rp.cjs"),c=require("../normalize-input/index.cjs"),l=require("../chunks/truncate-decimals-CArL-IeO.cjs");exports.buildQueryParams=e.buildQueryParams,exports.buildSortParam=r.buildSortParam,exports.cleanObject=t.cleanObject,exports.defaultCookieOptions=n.defaultCookieOptions,exports.deleteCookie=n.deleteCookie,exports.downloadBlob=a.downloadBlob,exports.getCookie=n.getCookie,exports.getCurrencySymbol=i.t,exports.getFlagFallbackUrl=o.t,exports.getFlagUnknownUrl=o.n,exports.getFlagUrl=o.r,exports.handleApiError=s.t,exports.handleFlagImageError=o.i,exports.initHandleApiError=s.n,exports.normalizeDecimalInput=c.normalizeDecimalInput,exports.normalizePhoneNumberInput=c.normalizePhoneNumberInput,exports.setCookie=n.setCookie,exports.truncateDecimals=l.t;
@@ -4,6 +4,7 @@ export * from './cookies';
4
4
  export * from './build-sort-param';
5
5
  export * from './currency-symbol';
6
6
  export * from './download-file';
7
+ export * from './flags';
7
8
  export * from './handle-api-error';
8
9
  export * from './normalize-input';
9
10
  export * from './truncate-decimals';
@@ -4,7 +4,8 @@ import { defaultCookieOptions as n, deleteCookie as r, getCookie as i, setCookie
4
4
  import { buildSortParam as o } from "../build-sort-param/index.js";
5
5
  import { t as s } from "../chunks/currency-symbol-CJp1EcR5.es";
6
6
  import { downloadBlob as c } from "../download-file/index.js";
7
- import { n as l, t as u } from "../chunks/handle-api-error-BYygTKYM.es";
8
- import { normalizeDecimalInput as d, normalizePhoneNumberInput as f } from "../normalize-input/index.js";
9
- import { t as p } from "../chunks/truncate-decimals-BPaTWeHm.es";
10
- export { e as buildQueryParams, o as buildSortParam, t as cleanObject, n as defaultCookieOptions, r as deleteCookie, c as downloadBlob, i as getCookie, s as getCurrencySymbol, u as handleApiError, l as initHandleApiError, d as normalizeDecimalInput, f as normalizePhoneNumberInput, a as setCookie, p as truncateDecimals };
7
+ import { i as l, n as u, r as d, t as f } from "../chunks/flags-C5E6W9lm.es";
8
+ import { n as p, t as m } from "../chunks/handle-api-error-BYygTKYM.es";
9
+ import { normalizeDecimalInput as h, normalizePhoneNumberInput as g } from "../normalize-input/index.js";
10
+ import { t as _ } from "../chunks/truncate-decimals-BPaTWeHm.es";
11
+ export { e as buildQueryParams, o as buildSortParam, t as cleanObject, n as defaultCookieOptions, r as deleteCookie, c as downloadBlob, i as getCookie, s as getCurrencySymbol, f as getFlagFallbackUrl, u as getFlagUnknownUrl, d as getFlagUrl, m as handleApiError, l as handleFlagImageError, p as initHandleApiError, h as normalizeDecimalInput, g as normalizePhoneNumberInput, a as setCookie, _ as truncateDecimals };
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/flags-BRQ9Jims.cjs");exports.getFlagFallbackUrl=e.t,exports.getFlagUnknownUrl=e.n,exports.getFlagUrl=e.r,exports.handleFlagImageError=e.i;
@@ -0,0 +1,2 @@
1
+ import { i as e, n as t, r as n, t as r } from "../chunks/flags-C5E6W9lm.es";
2
+ export { r as getFlagFallbackUrl, t as getFlagUnknownUrl, n as getFlagUrl, e as handleFlagImageError };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunks/base-DLHy3wps.cjs"),t=require("./chunks/admin-DR1tBkNV.cjs"),n=require("./build-query-params/index.cjs"),r=require("./clean-object/index.cjs"),i=require("./cookies/index.cjs"),a=require("./build-sort-param/index.cjs"),o=require("./chunks/currency-symbol-gb63Q_9Q.cjs"),s=require("./download-file/index.cjs"),c=require("./chunks/handle-api-error-D714W9rp.cjs"),l=require("./normalize-input/index.cjs"),u=require("./chunks/truncate-decimals-CArL-IeO.cjs");require("./core/index.cjs"),exports.ADMIN_QUERY_KEYS=t.n,exports.DISCOUNT_TYPES=e.t,exports.ORDER_STATUSES=e.n,exports.SUPPORT_STATUSES=e.r,exports.buildQueryParams=n.buildQueryParams,exports.buildSortParam=a.buildSortParam,exports.cleanObject=r.cleanObject,exports.defaultCookieOptions=i.defaultCookieOptions,exports.deleteCookie=i.deleteCookie,exports.downloadBlob=s.downloadBlob,exports.getCookie=i.getCookie,exports.getCurrencySymbol=o.t,exports.handleApiError=c.t,exports.initHandleApiError=c.n,exports.isAdminToken=t.t,exports.normalizeDecimalInput=l.normalizeDecimalInput,exports.normalizePhoneNumberInput=l.normalizePhoneNumberInput,exports.setCookie=i.setCookie,exports.truncateDecimals=u.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunks/base-DLHy3wps.cjs"),t=require("./chunks/admin-DR1tBkNV.cjs"),n=require("./build-query-params/index.cjs"),r=require("./clean-object/index.cjs"),i=require("./cookies/index.cjs"),a=require("./build-sort-param/index.cjs"),o=require("./chunks/currency-symbol-gb63Q_9Q.cjs"),s=require("./download-file/index.cjs"),c=require("./chunks/flags-BRQ9Jims.cjs"),l=require("./chunks/handle-api-error-D714W9rp.cjs"),u=require("./normalize-input/index.cjs"),d=require("./chunks/truncate-decimals-CArL-IeO.cjs");require("./core/index.cjs"),exports.ADMIN_QUERY_KEYS=t.n,exports.DISCOUNT_TYPES=e.t,exports.ORDER_STATUSES=e.n,exports.SUPPORT_STATUSES=e.r,exports.buildQueryParams=n.buildQueryParams,exports.buildSortParam=a.buildSortParam,exports.cleanObject=r.cleanObject,exports.defaultCookieOptions=i.defaultCookieOptions,exports.deleteCookie=i.deleteCookie,exports.downloadBlob=s.downloadBlob,exports.getCookie=i.getCookie,exports.getCurrencySymbol=o.t,exports.getFlagFallbackUrl=c.t,exports.getFlagUnknownUrl=c.n,exports.getFlagUrl=c.r,exports.handleApiError=l.t,exports.handleFlagImageError=c.i,exports.initHandleApiError=l.n,exports.isAdminToken=t.t,exports.normalizeDecimalInput=u.normalizeDecimalInput,exports.normalizePhoneNumberInput=u.normalizePhoneNumberInput,exports.setCookie=i.setCookie,exports.truncateDecimals=d.t;
package/dist/index.js CHANGED
@@ -6,8 +6,9 @@ import { defaultCookieOptions as s, deleteCookie as c, getCookie as l, setCookie
6
6
  import { buildSortParam as d } from "./build-sort-param/index.js";
7
7
  import { t as f } from "./chunks/currency-symbol-CJp1EcR5.es";
8
8
  import { downloadBlob as p } from "./download-file/index.js";
9
- import { n as m, t as h } from "./chunks/handle-api-error-BYygTKYM.es";
10
- import { normalizeDecimalInput as g, normalizePhoneNumberInput as _ } from "./normalize-input/index.js";
11
- import { t as v } from "./chunks/truncate-decimals-BPaTWeHm.es";
9
+ import { i as m, n as h, r as g, t as _ } from "./chunks/flags-C5E6W9lm.es";
10
+ import { n as v, t as y } from "./chunks/handle-api-error-BYygTKYM.es";
11
+ import { normalizeDecimalInput as b, normalizePhoneNumberInput as x } from "./normalize-input/index.js";
12
+ import { t as S } from "./chunks/truncate-decimals-BPaTWeHm.es";
12
13
  import "./core/index.js";
13
- export { r as ADMIN_QUERY_KEYS, n as DISCOUNT_TYPES, e as ORDER_STATUSES, t as SUPPORT_STATUSES, a as buildQueryParams, d as buildSortParam, o as cleanObject, s as defaultCookieOptions, c as deleteCookie, p as downloadBlob, l as getCookie, f as getCurrencySymbol, h as handleApiError, m as initHandleApiError, i as isAdminToken, g as normalizeDecimalInput, _ as normalizePhoneNumberInput, u as setCookie, v as truncateDecimals };
14
+ export { r as ADMIN_QUERY_KEYS, n as DISCOUNT_TYPES, e as ORDER_STATUSES, t as SUPPORT_STATUSES, a as buildQueryParams, d as buildSortParam, o as cleanObject, s as defaultCookieOptions, c as deleteCookie, p as downloadBlob, l as getCookie, f as getCurrencySymbol, _ as getFlagFallbackUrl, h as getFlagUnknownUrl, g as getFlagUrl, y as handleApiError, m as handleFlagImageError, v as initHandleApiError, i as isAdminToken, b as normalizeDecimalInput, x as normalizePhoneNumberInput, u as setCookie, S as truncateDecimals };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@ptx-showcase/utils",
3
3
  "author": "ptx-showcase",
4
4
  "description": "Shared utilities for PTX Showcase projects",
5
- "version": "1.0.0",
5
+ "version": "1.1.0",
6
6
  "private": false,
7
7
  "type": "module",
8
8
  "license": "MIT",
@@ -77,6 +77,11 @@
77
77
  "require": "./dist/download-file/index.cjs",
78
78
  "types": "./dist/core/download-file/index.d.ts"
79
79
  },
80
+ "./flags": {
81
+ "import": "./dist/flags/index.js",
82
+ "require": "./dist/flags/index.cjs",
83
+ "types": "./dist/core/flags/index.d.ts"
84
+ },
80
85
  "./handle-api-error": {
81
86
  "import": "./dist/handle-api-error/index.js",
82
87
  "require": "./dist/handle-api-error/index.cjs",