@asafarim/shared-i18n 0.8.0 → 0.9.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.
Files changed (94) hide show
  1. package/README.md +266 -280
  2. package/demo/README.md +119 -0
  3. package/demo/index.html +12 -1
  4. package/demo/node_modules/.bin/kill-port +17 -0
  5. package/demo/node_modules/.bin/tsc +5 -9
  6. package/demo/node_modules/.bin/tsserver +5 -9
  7. package/demo/node_modules/.bin/vite +5 -9
  8. package/demo/package.json +7 -4
  9. package/demo/public/404.html +24 -0
  10. package/demo/public/favicon.svg +4 -4
  11. package/demo/public/logo.svg +24 -24
  12. package/demo/src/App.tsx +178 -129
  13. package/demo/src/components/CountryLanguageSelectorsPage.tsx +240 -0
  14. package/demo/src/components/GetStartedSection.tsx +56 -56
  15. package/demo/src/components/KeyTable.tsx +29 -29
  16. package/demo/src/components/LanguageBar.tsx +145 -103
  17. package/demo/src/components/LanguageSwitcherDemo.module.css +114 -114
  18. package/demo/src/components/LanguageSwitchersPage.tsx +245 -0
  19. package/demo/src/components/Logo.tsx +6 -6
  20. package/demo/src/components/OverviewSection.tsx +58 -43
  21. package/demo/src/components/Panel.tsx +15 -15
  22. package/demo/src/components/RoutingLabPage.tsx +147 -0
  23. package/demo/src/components/StatusCard.tsx +109 -109
  24. package/demo/src/data/countries.ts +48 -0
  25. package/demo/src/i18n/localeAdapter.ts +91 -0
  26. package/demo/src/i18n/localeRouting.ts +77 -0
  27. package/demo/src/index.css +1075 -644
  28. package/demo/src/locales/de/demo.json +202 -84
  29. package/demo/src/locales/en/demo.json +201 -85
  30. package/demo/src/locales/fr/demo.json +203 -85
  31. package/demo/src/locales/it/demo.json +202 -84
  32. package/demo/src/locales/lb/demo.json +201 -0
  33. package/demo/src/locales/nl/demo.json +203 -85
  34. package/demo/src/main.tsx +32 -29
  35. package/demo/tsconfig.json +18 -18
  36. package/demo/tsconfig.node.json +10 -10
  37. package/demo/tsconfig.tsbuildinfo +1 -1
  38. package/demo/vite-env.d.ts +7 -7
  39. package/demo/vite.config.d.ts +2 -2
  40. package/demo/vite.config.js +10 -10
  41. package/dist/components/LanguageSwitcher.module.css +303 -303
  42. package/dist/country-language-selector.css +431 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +2 -0
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +87 -85
  48. package/demo/dist/Icon Dropdown_Limited Languages.png +0 -0
  49. package/demo/dist/Select Dropdown_Text Only.png +0 -0
  50. package/demo/dist/assets/favicon-BZYZvBLo.svg +0 -4
  51. package/demo/dist/assets/index-BdjqKw_N.css +0 -1
  52. package/demo/dist/assets/index-C1Tq1uEr.js +0 -191
  53. package/demo/dist/favicon.svg +0 -4
  54. package/demo/dist/index.html +0 -27
  55. package/demo/dist/logo.svg +0 -24
  56. package/demo/node_modules/.bin/browserslist +0 -21
  57. package/demo/node_modules/.bin/browserslist.CMD +0 -12
  58. package/demo/node_modules/.bin/browserslist.ps1 +0 -41
  59. package/demo/node_modules/.bin/tsc.CMD +0 -12
  60. package/demo/node_modules/.bin/tsc.ps1 +0 -41
  61. package/demo/node_modules/.bin/tsserver.CMD +0 -12
  62. package/demo/node_modules/.bin/tsserver.ps1 +0 -41
  63. package/demo/node_modules/.bin/vite.CMD +0 -12
  64. package/demo/node_modules/.bin/vite.ps1 +0 -41
  65. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js +0 -848
  66. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js.map +0 -7
  67. package/demo/node_modules/.vite/deps/_metadata.json +0 -76
  68. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
  69. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
  70. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js +0 -1004
  71. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js.map +0 -7
  72. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js +0 -292
  73. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js.map +0 -7
  74. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js +0 -280
  75. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js.map +0 -7
  76. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js +0 -400
  77. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js.map +0 -7
  78. package/demo/node_modules/.vite/deps/i18next.js +0 -2392
  79. package/demo/node_modules/.vite/deps/i18next.js.map +0 -7
  80. package/demo/node_modules/.vite/deps/package.json +0 -3
  81. package/demo/node_modules/.vite/deps/react-dom.js +0 -6
  82. package/demo/node_modules/.vite/deps/react-dom.js.map +0 -7
  83. package/demo/node_modules/.vite/deps/react-dom_client.js +0 -20217
  84. package/demo/node_modules/.vite/deps/react-dom_client.js.map +0 -7
  85. package/demo/node_modules/.vite/deps/react-i18next.js +0 -869
  86. package/demo/node_modules/.vite/deps/react-i18next.js.map +0 -7
  87. package/demo/node_modules/.vite/deps/react.js +0 -5
  88. package/demo/node_modules/.vite/deps/react.js.map +0 -7
  89. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -278
  90. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
  91. package/demo/node_modules/.vite/deps/react_jsx-runtime.js +0 -6
  92. package/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
  93. package/demo/src/components/CountryLanguageDemo.tsx +0 -140
  94. package/demo/src/components/LanguageSwitcherDemo.tsx +0 -256
@@ -1,848 +0,0 @@
1
- import {
2
- require_jsx_runtime
3
- } from "./chunk-E6BG6WAU.js";
4
- import {
5
- require_react
6
- } from "./chunk-B3AHR5EX.js";
7
- import {
8
- __toESM
9
- } from "./chunk-5WRI5ZAA.js";
10
-
11
- // ../node_modules/.pnpm/@asafarim+country-language-_049cb8b81291b08ccd85a4446ad76c74/node_modules/@asafarim/country-language-selector/dist/index.js
12
- var import_react = __toESM(require_react());
13
- var import_jsx_runtime = __toESM(require_jsx_runtime());
14
- var defaultStrings = {
15
- ariaLabel: "Select country and language",
16
- searchPlaceholder: "Search country…",
17
- emptyResults: "No matching countries",
18
- chooseCountry: "Country",
19
- chooseLanguage: "Language",
20
- backToCountries: "Back",
21
- close: "Close"
22
- };
23
- var L = {
24
- en: { code: "en", label: "English", nativeLabel: "English" },
25
- nl: { code: "nl", label: "Dutch", nativeLabel: "Nederlands" },
26
- fr: { code: "fr", label: "French", nativeLabel: "Français" },
27
- de: { code: "de", label: "German", nativeLabel: "Deutsch" },
28
- es: { code: "es", label: "Spanish", nativeLabel: "Español" },
29
- it: { code: "it", label: "Italian", nativeLabel: "Italiano" },
30
- pt: { code: "pt", label: "Portuguese", nativeLabel: "Português" },
31
- sv: { code: "sv", label: "Swedish", nativeLabel: "Svenska" },
32
- da: { code: "da", label: "Danish", nativeLabel: "Dansk" },
33
- no: { code: "no", label: "Norwegian", nativeLabel: "Norsk" },
34
- fi: { code: "fi", label: "Finnish", nativeLabel: "Suomi" },
35
- pl: { code: "pl", label: "Polish", nativeLabel: "Polski" },
36
- ja: { code: "ja", label: "Japanese", nativeLabel: "日本語" },
37
- zh: { code: "zh", label: "Chinese", nativeLabel: "中文" },
38
- ar: { code: "ar", label: "Arabic", nativeLabel: "العربية" },
39
- tr: { code: "tr", label: "Turkish", nativeLabel: "Türkçe" },
40
- ru: { code: "ru", label: "Russian", nativeLabel: "Русский" },
41
- ko: { code: "ko", label: "Korean", nativeLabel: "한국어" },
42
- hi: { code: "hi", label: "Hindi", nativeLabel: "हिन्दी" },
43
- fa: { code: "fa", label: "Persian", nativeLabel: "فارسی" }
44
- };
45
- var defaultCountries = [
46
- {
47
- code: "BE",
48
- name: "Belgium",
49
- nativeName: "België",
50
- flag: "🇧🇪",
51
- languages: [L.nl, L.fr, L.de, L.en]
52
- },
53
- {
54
- code: "NL",
55
- name: "Netherlands",
56
- nativeName: "Nederland",
57
- flag: "🇳🇱",
58
- languages: [L.nl, L.en]
59
- },
60
- {
61
- code: "FR",
62
- name: "France",
63
- nativeName: "France",
64
- flag: "🇫🇷",
65
- languages: [L.fr, L.en]
66
- },
67
- {
68
- code: "DE",
69
- name: "Germany",
70
- nativeName: "Deutschland",
71
- flag: "🇩🇪",
72
- languages: [L.de, L.en]
73
- },
74
- {
75
- code: "CH",
76
- name: "Switzerland",
77
- nativeName: "Schweiz",
78
- flag: "🇨🇭",
79
- languages: [L.de, L.fr, L.it, L.en]
80
- },
81
- {
82
- code: "LU",
83
- name: "Luxembourg",
84
- nativeName: "Lëtzebuerg",
85
- flag: "🇱🇺",
86
- languages: [L.fr, L.de, L.en]
87
- },
88
- {
89
- code: "CA",
90
- name: "Canada",
91
- nativeName: "Canada",
92
- flag: "🇨🇦",
93
- languages: [L.en, L.fr]
94
- },
95
- {
96
- code: "US",
97
- name: "United States",
98
- flag: "🇺🇸",
99
- languages: [L.en, L.es]
100
- },
101
- {
102
- code: "GB",
103
- name: "United Kingdom",
104
- flag: "🇬🇧",
105
- languages: [L.en]
106
- },
107
- {
108
- code: "ES",
109
- name: "Spain",
110
- nativeName: "España",
111
- flag: "🇪🇸",
112
- languages: [L.es, L.en]
113
- },
114
- {
115
- code: "IT",
116
- name: "Italy",
117
- nativeName: "Italia",
118
- flag: "🇮🇹",
119
- languages: [L.it, L.en]
120
- },
121
- {
122
- code: "PT",
123
- name: "Portugal",
124
- flag: "🇵🇹",
125
- languages: [L.pt, L.en]
126
- },
127
- {
128
- code: "SE",
129
- name: "Sweden",
130
- nativeName: "Sverige",
131
- flag: "🇸🇪",
132
- languages: [L.sv, L.en]
133
- },
134
- {
135
- code: "DK",
136
- name: "Denmark",
137
- nativeName: "Danmark",
138
- flag: "🇩🇰",
139
- languages: [L.da, L.en]
140
- },
141
- {
142
- code: "NO",
143
- name: "Norway",
144
- nativeName: "Norge",
145
- flag: "🇳🇴",
146
- languages: [L.no, L.en]
147
- },
148
- {
149
- code: "FI",
150
- name: "Finland",
151
- nativeName: "Suomi",
152
- flag: "🇫🇮",
153
- languages: [L.fi, L.sv, L.en]
154
- },
155
- {
156
- code: "PL",
157
- name: "Poland",
158
- nativeName: "Polska",
159
- flag: "🇵🇱",
160
- languages: [L.pl, L.en]
161
- },
162
- {
163
- code: "JP",
164
- name: "Japan",
165
- nativeName: "日本",
166
- flag: "🇯🇵",
167
- languages: [L.ja, L.en]
168
- },
169
- {
170
- code: "CN",
171
- name: "China",
172
- nativeName: "中国",
173
- flag: "🇨🇳",
174
- languages: [L.zh, L.en]
175
- },
176
- {
177
- code: "KR",
178
- name: "South Korea",
179
- nativeName: "대한민국",
180
- flag: "🇰🇷",
181
- languages: [L.ko, L.en]
182
- },
183
- {
184
- code: "IN",
185
- name: "India",
186
- flag: "🇮🇳",
187
- languages: [L.hi, L.en]
188
- },
189
- {
190
- code: "TR",
191
- name: "Türkiye",
192
- nativeName: "Türkiye",
193
- flag: "🇹🇷",
194
- languages: [L.tr, L.en]
195
- },
196
- {
197
- code: "RU",
198
- name: "Russia",
199
- nativeName: "Россия",
200
- flag: "🇷🇺",
201
- languages: [L.ru, L.en]
202
- },
203
- {
204
- code: "IR",
205
- name: "Iran",
206
- nativeName: "ایران",
207
- flag: "🇮🇷",
208
- languages: [L.fa, L.en]
209
- },
210
- {
211
- code: "SA",
212
- name: "Saudi Arabia",
213
- nativeName: "السعودية",
214
- flag: "🇸🇦",
215
- languages: [L.ar, L.en]
216
- },
217
- {
218
- code: "AE",
219
- name: "United Arab Emirates",
220
- flag: "🇦🇪",
221
- languages: [L.ar, L.en]
222
- },
223
- {
224
- code: "BR",
225
- name: "Brazil",
226
- nativeName: "Brasil",
227
- flag: "🇧🇷",
228
- languages: [L.pt, L.en]
229
- },
230
- {
231
- code: "MX",
232
- name: "Mexico",
233
- nativeName: "México",
234
- flag: "🇲🇽",
235
- languages: [L.es, L.en]
236
- }
237
- ];
238
- var languageCatalogue = L;
239
- var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
240
- function readStorage(key) {
241
- if (!isBrowser()) return null;
242
- try {
243
- return window.localStorage.getItem(key);
244
- } catch {
245
- return null;
246
- }
247
- }
248
- function writeStorage(key, value) {
249
- if (!isBrowser()) return;
250
- try {
251
- window.localStorage.setItem(key, value);
252
- } catch {
253
- }
254
- }
255
- function isCoarsePointer() {
256
- var _a;
257
- if (!isBrowser()) return false;
258
- return ((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(pointer: coarse)").matches) ?? false;
259
- }
260
- function useCountryLanguage(args) {
261
- const { countries, value, defaultValue, onChange, persistKey } = args;
262
- const isControlled = value !== void 0;
263
- const initialRef = (0, import_react.useRef)(null);
264
- if (initialRef.current === null) {
265
- initialRef.current = (defaultValue && isValidLocale(defaultValue, countries) ? defaultValue : null) ?? fallbackLocale(countries);
266
- }
267
- const [internal, setInternal] = (0, import_react.useState)(initialRef.current);
268
- const current = isControlled ? value : internal;
269
- const didInitRef = (0, import_react.useRef)(false);
270
- (0, import_react.useEffect)(() => {
271
- if (didInitRef.current) return;
272
- didInitRef.current = true;
273
- if (!isControlled && persistKey) {
274
- const hydrated = readPersistedLocale(persistKey, countries);
275
- if (hydrated) {
276
- setInternal(hydrated);
277
- onChange == null ? void 0 : onChange(hydrated, { reason: "init", previous: initialRef.current });
278
- return;
279
- }
280
- }
281
- onChange == null ? void 0 : onChange(current, { reason: "init", previous: null });
282
- }, []);
283
- const byCode = (0, import_react.useMemo)(() => {
284
- const map = /* @__PURE__ */ new Map();
285
- for (const c of countries) map.set(c.code, c);
286
- return map;
287
- }, [countries]);
288
- const country = byCode.get(current.country) ?? countries[0];
289
- const language = country.languages.find((l) => l.code === current.language) ?? country.languages[0];
290
- const commit = (0, import_react.useCallback)(
291
- (next, reason) => {
292
- const previous = current;
293
- if (!isControlled) setInternal(next);
294
- if (persistKey && !isControlled) {
295
- writeStorage(persistKey, JSON.stringify(next));
296
- }
297
- onChange == null ? void 0 : onChange(next, { reason, previous });
298
- },
299
- [current, isControlled, onChange, persistKey]
300
- );
301
- const setCountry = (0, import_react.useCallback)(
302
- (code) => {
303
- const nextCountry = byCode.get(code);
304
- if (!nextCountry) return;
305
- const keep = nextCountry.languages.find(
306
- (l) => l.code === current.language
307
- );
308
- const nextLanguage = (keep ?? nextCountry.languages[0]).code;
309
- commit({ country: nextCountry.code, language: nextLanguage }, "country");
310
- },
311
- [byCode, commit, current.language]
312
- );
313
- const setLanguage = (0, import_react.useCallback)(
314
- (code) => {
315
- if (!country.languages.some((l) => l.code === code)) return;
316
- commit({ country: country.code, language: code }, "language");
317
- },
318
- [commit, country]
319
- );
320
- const setLocale = (0, import_react.useCallback)(
321
- (next, reason = "country") => {
322
- commit(next, reason);
323
- },
324
- [commit]
325
- );
326
- return {
327
- locale: { country: country.code, language: language.code },
328
- country,
329
- language,
330
- setCountry,
331
- setLanguage,
332
- setLocale,
333
- needsLanguageChoice: country.languages.length > 1
334
- };
335
- }
336
- function readPersistedLocale(key, countries) {
337
- const raw = readStorage(key);
338
- if (!raw) return null;
339
- try {
340
- const parsed = JSON.parse(raw);
341
- return isValidLocale(parsed, countries) ? parsed : null;
342
- } catch {
343
- return null;
344
- }
345
- }
346
- function isValidLocale(locale, countries) {
347
- const c = countries.find((x) => x.code === locale.country);
348
- if (!c) return false;
349
- return c.languages.some((l) => l.code === locale.language);
350
- }
351
- function fallbackLocale(countries) {
352
- const first = countries[0];
353
- if (!first) {
354
- throw new Error(
355
- "[country-language-selector] `countries` must contain at least one entry."
356
- );
357
- }
358
- return { country: first.code, language: first.languages[0].code };
359
- }
360
- function useClickOutside(refs, enabled, handler) {
361
- (0, import_react.useEffect)(() => {
362
- if (!enabled) return;
363
- const isInside = (target) => {
364
- if (!target) return false;
365
- return refs.some((ref) => {
366
- const el = ref.current;
367
- return !!el && el.contains(target);
368
- });
369
- };
370
- const onPointer = (event) => {
371
- if (!isInside(event.target)) handler(event);
372
- };
373
- const onKey = (event) => {
374
- if (event.key === "Escape") handler(event);
375
- };
376
- document.addEventListener("pointerdown", onPointer, true);
377
- document.addEventListener("keydown", onKey, true);
378
- return () => {
379
- document.removeEventListener("pointerdown", onPointer, true);
380
- document.removeEventListener("keydown", onKey, true);
381
- };
382
- }, [enabled, handler, refs]);
383
- }
384
- var FOCUSABLE_SELECTOR = [
385
- "a[href]",
386
- "button:not([disabled])",
387
- "input:not([disabled])",
388
- "textarea:not([disabled])",
389
- "select:not([disabled])",
390
- '[tabindex]:not([tabindex="-1"])'
391
- ].join(",");
392
- function useFocusTrap(container, enabled) {
393
- (0, import_react.useEffect)(() => {
394
- if (!enabled) return;
395
- const node = container.current;
396
- if (!node) return;
397
- const onKey = (event) => {
398
- if (event.key !== "Tab") return;
399
- const focusable = Array.from(
400
- node.querySelectorAll(FOCUSABLE_SELECTOR)
401
- ).filter((el) => !el.hasAttribute("data-focus-skip"));
402
- if (focusable.length === 0) {
403
- event.preventDefault();
404
- node.focus();
405
- return;
406
- }
407
- const first = focusable[0];
408
- const last = focusable[focusable.length - 1];
409
- const active = document.activeElement;
410
- if (event.shiftKey && (active === first || !node.contains(active))) {
411
- event.preventDefault();
412
- last.focus();
413
- } else if (!event.shiftKey && active === last) {
414
- event.preventDefault();
415
- first.focus();
416
- }
417
- };
418
- node.addEventListener("keydown", onKey);
419
- return () => node.removeEventListener("keydown", onKey);
420
- }, [container, enabled]);
421
- }
422
- function normalize(input) {
423
- return input.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim();
424
- }
425
- function scoreCountry(country, rawQuery) {
426
- const q = normalize(rawQuery);
427
- if (!q) return 0;
428
- const code = country.code.toLowerCase();
429
- const name = normalize(country.name);
430
- const native = country.nativeName ? normalize(country.nativeName) : "";
431
- if (code === q) return 1e3;
432
- if (code.startsWith(q)) return 900;
433
- if (name.startsWith(q) || native.startsWith(q)) return 700;
434
- for (const lang of country.languages) {
435
- if (lang.code.startsWith(q)) return 500;
436
- }
437
- if (name.includes(q) || native.includes(q)) return 300;
438
- for (const lang of country.languages) {
439
- const label = normalize(lang.label);
440
- const nativeLabel = lang.nativeLabel ? normalize(lang.nativeLabel) : "";
441
- if (label.includes(q) || nativeLabel.includes(q)) return 100;
442
- }
443
- return null;
444
- }
445
- function filterCountries(countries, query) {
446
- if (!query.trim()) return countries;
447
- return countries.map((c) => ({ c, s: scoreCountry(c, query) })).filter((x) => x.s !== null).sort((a, b) => b.s - a.s || a.c.name.localeCompare(b.c.name)).map((x) => x.c);
448
- }
449
- function ChevronDown(props) {
450
- return (0, import_jsx_runtime.jsx)(
451
- "svg",
452
- {
453
- viewBox: "0 0 16 16",
454
- fill: "none",
455
- stroke: "currentColor",
456
- strokeWidth: "2",
457
- strokeLinecap: "round",
458
- strokeLinejoin: "round",
459
- "aria-hidden": "true",
460
- ...props,
461
- children: (0, import_jsx_runtime.jsx)("path", { d: "m4 6 4 4 4-4" })
462
- }
463
- );
464
- }
465
- function SearchIcon(props) {
466
- return (0, import_jsx_runtime.jsxs)(
467
- "svg",
468
- {
469
- viewBox: "0 0 16 16",
470
- fill: "none",
471
- stroke: "currentColor",
472
- strokeWidth: "2",
473
- strokeLinecap: "round",
474
- strokeLinejoin: "round",
475
- "aria-hidden": "true",
476
- ...props,
477
- children: [
478
- (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "4.5" }),
479
- (0, import_jsx_runtime.jsx)("path", { d: "m14 14-3.4-3.4" })
480
- ]
481
- }
482
- );
483
- }
484
- function CheckIcon(props) {
485
- return (0, import_jsx_runtime.jsx)(
486
- "svg",
487
- {
488
- viewBox: "0 0 16 16",
489
- fill: "none",
490
- stroke: "currentColor",
491
- strokeWidth: "2.25",
492
- strokeLinecap: "round",
493
- strokeLinejoin: "round",
494
- "aria-hidden": "true",
495
- ...props,
496
- children: (0, import_jsx_runtime.jsx)("path", { d: "m3.5 8.5 3 3 6-6" })
497
- }
498
- );
499
- }
500
- function ArrowLeft(props) {
501
- return (0, import_jsx_runtime.jsxs)(
502
- "svg",
503
- {
504
- viewBox: "0 0 16 16",
505
- fill: "none",
506
- stroke: "currentColor",
507
- strokeWidth: "2",
508
- strokeLinecap: "round",
509
- strokeLinejoin: "round",
510
- "aria-hidden": "true",
511
- ...props,
512
- children: [
513
- (0, import_jsx_runtime.jsx)("path", { d: "M10 4 6 8l4 4" }),
514
- (0, import_jsx_runtime.jsx)("path", { d: "M6 8h7" })
515
- ]
516
- }
517
- );
518
- }
519
- var CountryLanguageSelector = (0, import_react.forwardRef)(function CountryLanguageSelector2(props, ref) {
520
- const {
521
- countries = defaultCountries,
522
- value,
523
- defaultValue,
524
- onChange,
525
- persistKey,
526
- ariaLabel,
527
- triggerVariant = "compact",
528
- align = "end",
529
- disabled,
530
- className,
531
- popoverClassName,
532
- renderTrigger,
533
- strings: stringsProp
534
- } = props;
535
- const strings = (0, import_react.useMemo)(
536
- () => ({ ...defaultStrings, ...stringsProp ?? {} }),
537
- [stringsProp]
538
- );
539
- const { country, language, setCountry, setLanguage } = useCountryLanguage({
540
- countries,
541
- value,
542
- defaultValue,
543
- onChange,
544
- persistKey
545
- });
546
- const [open, setOpen] = (0, import_react.useState)(false);
547
- const [step, setStep] = (0, import_react.useState)("country");
548
- const [query, setQuery] = (0, import_react.useState)("");
549
- const [activeIndex, setActiveIndex] = (0, import_react.useState)(0);
550
- const [isMobile, setIsMobile] = (0, import_react.useState)(false);
551
- const rootRef = (0, import_react.useRef)(null);
552
- const popoverRef = (0, import_react.useRef)(null);
553
- const triggerRef = (0, import_react.useRef)(null);
554
- const searchRef = (0, import_react.useRef)(null);
555
- const listboxId = (0, import_react.useId)();
556
- const activeOptionId = `${listboxId}-opt-${activeIndex}`;
557
- useClickOutside([rootRef, popoverRef], open, () => setOpen(false));
558
- useFocusTrap(popoverRef, open);
559
- (0, import_react.useEffect)(() => setIsMobile(isCoarsePointer()), []);
560
- (0, import_react.useEffect)(() => {
561
- if (!open) return;
562
- setStep("country");
563
- setQuery("");
564
- setActiveIndex(0);
565
- const t = window.setTimeout(() => {
566
- var _a;
567
- return (_a = searchRef.current) == null ? void 0 : _a.focus();
568
- }, 10);
569
- return () => window.clearTimeout(t);
570
- }, [open]);
571
- const filtered = (0, import_react.useMemo)(() => filterCountries(countries, query), [countries, query]);
572
- (0, import_react.useEffect)(() => {
573
- if (activeIndex > Math.max(0, filtered.length - 1)) setActiveIndex(0);
574
- }, [filtered.length, activeIndex]);
575
- const closeAndRefocus = (0, import_react.useCallback)(() => {
576
- setOpen(false);
577
- requestAnimationFrame(() => {
578
- var _a;
579
- return (_a = triggerRef.current) == null ? void 0 : _a.focus();
580
- });
581
- }, []);
582
- const commitCountry = (0, import_react.useCallback)(
583
- (c) => {
584
- setCountry(c.code);
585
- if (c.languages.length > 1) {
586
- setStep("language");
587
- setActiveIndex(0);
588
- } else {
589
- closeAndRefocus();
590
- }
591
- },
592
- [setCountry, closeAndRefocus]
593
- );
594
- const commitLanguage = (0, import_react.useCallback)(
595
- (l) => {
596
- setLanguage(l.code);
597
- closeAndRefocus();
598
- },
599
- [setLanguage, closeAndRefocus]
600
- );
601
- const onTriggerKeyDown = (0, import_react.useCallback)(
602
- (e) => {
603
- if (disabled) return;
604
- if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " " || e.key === "ArrowUp") {
605
- e.preventDefault();
606
- setOpen(true);
607
- }
608
- },
609
- [disabled]
610
- );
611
- const onListKeyDown = (0, import_react.useCallback)(
612
- (e) => {
613
- const list = step === "country" ? filtered : country.languages;
614
- if (list.length === 0) return;
615
- if (e.key === "ArrowDown") {
616
- e.preventDefault();
617
- setActiveIndex((i) => (i + 1) % list.length);
618
- } else if (e.key === "ArrowUp") {
619
- e.preventDefault();
620
- setActiveIndex((i) => (i - 1 + list.length) % list.length);
621
- } else if (e.key === "Home") {
622
- e.preventDefault();
623
- setActiveIndex(0);
624
- } else if (e.key === "End") {
625
- e.preventDefault();
626
- setActiveIndex(list.length - 1);
627
- } else if (e.key === "Enter") {
628
- e.preventDefault();
629
- const item = list[activeIndex];
630
- if (!item) return;
631
- if (step === "country") commitCountry(item);
632
- else commitLanguage(item);
633
- } else if (e.key === "Escape") {
634
- e.preventDefault();
635
- closeAndRefocus();
636
- } else if (e.key === "Backspace" && step === "language" && !query) {
637
- e.preventDefault();
638
- setStep("country");
639
- setActiveIndex(0);
640
- }
641
- },
642
- [step, filtered, country, activeIndex, commitCountry, commitLanguage, closeAndRefocus, query]
643
- );
644
- (0, import_react.useEffect)(() => {
645
- const el = document.getElementById(activeOptionId);
646
- if (el) el.scrollIntoView({ block: "nearest" });
647
- }, [activeOptionId]);
648
- return (0, import_jsx_runtime.jsxs)(
649
- "div",
650
- {
651
- ref: (el) => {
652
- rootRef.current = el;
653
- if (typeof ref === "function") ref(el);
654
- else if (ref) ref.current = el;
655
- },
656
- className: ["cls-root", className].filter(Boolean).join(" "),
657
- children: [
658
- (0, import_jsx_runtime.jsxs)(
659
- "button",
660
- {
661
- ref: triggerRef,
662
- type: "button",
663
- className: "cls-trigger",
664
- "aria-haspopup": "dialog",
665
- "aria-expanded": open,
666
- "aria-label": ariaLabel ?? strings.ariaLabel,
667
- disabled,
668
- onClick: () => !disabled && setOpen((v) => !v),
669
- onKeyDown: onTriggerKeyDown,
670
- children: [
671
- renderTrigger ? renderTrigger({ country, language, open }) : (0, import_jsx_runtime.jsx)(TriggerContent, { variant: triggerVariant, country, language }),
672
- (0, import_jsx_runtime.jsx)(ChevronDown, { className: "cls-trigger__chev" })
673
- ]
674
- }
675
- ),
676
- open && isMobile && (0, import_jsx_runtime.jsx)("div", { className: "cls-backdrop", "aria-hidden": "true" }),
677
- open && (0, import_jsx_runtime.jsx)(
678
- "div",
679
- {
680
- ref: popoverRef,
681
- role: "dialog",
682
- "aria-label": strings.ariaLabel,
683
- "data-align": align,
684
- tabIndex: -1,
685
- className: ["cls-popover", popoverClassName].filter(Boolean).join(" "),
686
- onKeyDown: onListKeyDown,
687
- children: step === "country" ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
688
- (0, import_jsx_runtime.jsxs)("div", { className: "cls-search", children: [
689
- (0, import_jsx_runtime.jsx)(SearchIcon, { className: "cls-search__icon" }),
690
- (0, import_jsx_runtime.jsx)(
691
- "input",
692
- {
693
- ref: searchRef,
694
- type: "text",
695
- role: "combobox",
696
- "aria-expanded": "true",
697
- "aria-controls": listboxId,
698
- "aria-activedescendant": activeOptionId,
699
- "aria-autocomplete": "list",
700
- className: "cls-search__input",
701
- placeholder: strings.searchPlaceholder,
702
- value: query,
703
- onChange: (e) => {
704
- setQuery(e.target.value);
705
- setActiveIndex(0);
706
- }
707
- }
708
- )
709
- ] }),
710
- (0, import_jsx_runtime.jsx)("div", { className: "cls-section-title", children: (0, import_jsx_runtime.jsx)("span", { children: strings.chooseCountry }) }),
711
- filtered.length === 0 ? (0, import_jsx_runtime.jsx)("div", { className: "cls-empty", children: strings.emptyResults }) : (0, import_jsx_runtime.jsx)(
712
- "ul",
713
- {
714
- id: listboxId,
715
- role: "listbox",
716
- "aria-label": strings.chooseCountry,
717
- className: "cls-list",
718
- children: filtered.map((c, i) => {
719
- const selected = c.code === country.code;
720
- return (0, import_jsx_runtime.jsxs)(
721
- "li",
722
- {
723
- id: `${listboxId}-opt-${i}`,
724
- role: "option",
725
- "aria-selected": selected,
726
- "data-active": i === activeIndex,
727
- className: "cls-option",
728
- onMouseEnter: () => setActiveIndex(i),
729
- onClick: () => commitCountry(c),
730
- children: [
731
- (0, import_jsx_runtime.jsx)("span", { className: "cls-option__flag", "aria-hidden": "true", children: c.flag }),
732
- (0, import_jsx_runtime.jsxs)("span", { className: "cls-option__body", children: [
733
- (0, import_jsx_runtime.jsx)("span", { className: "cls-option__title", children: c.name }),
734
- c.nativeName && c.nativeName !== c.name && (0, import_jsx_runtime.jsx)("span", { className: "cls-option__subtitle", children: c.nativeName })
735
- ] }),
736
- (0, import_jsx_runtime.jsxs)("span", { className: "cls-option__meta", children: [
737
- c.languages.length > 1 ? (0, import_jsx_runtime.jsxs)("span", { className: "cls-badge", children: [
738
- c.languages.length,
739
- " langs"
740
- ] }) : (0, import_jsx_runtime.jsx)("span", { className: "cls-badge", children: c.languages[0].code.toUpperCase() }),
741
- selected && (0, import_jsx_runtime.jsx)(CheckIcon, { className: "cls-check" })
742
- ] })
743
- ]
744
- },
745
- c.code
746
- );
747
- })
748
- }
749
- )
750
- ] }) : (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
751
- (0, import_jsx_runtime.jsxs)("div", { className: "cls-section-title", children: [
752
- (0, import_jsx_runtime.jsxs)(
753
- "button",
754
- {
755
- type: "button",
756
- className: "cls-back",
757
- onClick: () => {
758
- setStep("country");
759
- setActiveIndex(0);
760
- },
761
- children: [
762
- (0, import_jsx_runtime.jsx)(ArrowLeft, { width: 12, height: 12 }),
763
- strings.backToCountries
764
- ]
765
- }
766
- ),
767
- (0, import_jsx_runtime.jsxs)("span", { children: [
768
- country.flag,
769
- " ",
770
- country.name
771
- ] })
772
- ] }),
773
- (0, import_jsx_runtime.jsx)(
774
- "ul",
775
- {
776
- id: listboxId,
777
- role: "listbox",
778
- "aria-label": strings.chooseLanguage,
779
- className: "cls-list",
780
- children: country.languages.map((l, i) => {
781
- const selected = l.code === language.code;
782
- return (0, import_jsx_runtime.jsxs)(
783
- "li",
784
- {
785
- id: `${listboxId}-opt-${i}`,
786
- role: "option",
787
- "aria-selected": selected,
788
- "data-active": i === activeIndex,
789
- className: "cls-option",
790
- onMouseEnter: () => setActiveIndex(i),
791
- onClick: () => commitLanguage(l),
792
- children: [
793
- (0, import_jsx_runtime.jsxs)("span", { className: "cls-option__body", children: [
794
- (0, import_jsx_runtime.jsx)("span", { className: "cls-option__title", children: l.label }),
795
- l.nativeLabel && l.nativeLabel !== l.label && (0, import_jsx_runtime.jsx)("span", { className: "cls-option__subtitle", children: l.nativeLabel })
796
- ] }),
797
- (0, import_jsx_runtime.jsxs)("span", { className: "cls-option__meta", children: [
798
- (0, import_jsx_runtime.jsx)("span", { className: "cls-badge", children: l.code.toUpperCase() }),
799
- selected && (0, import_jsx_runtime.jsx)(CheckIcon, { className: "cls-check" })
800
- ] })
801
- ]
802
- },
803
- l.code
804
- );
805
- })
806
- }
807
- )
808
- ] })
809
- }
810
- )
811
- ]
812
- }
813
- );
814
- });
815
- function TriggerContent({
816
- variant,
817
- country,
818
- language
819
- }) {
820
- if (variant === "flag") {
821
- return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
822
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__flag", "aria-hidden": "true", children: country.flag }),
823
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__lang", children: language.code })
824
- ] });
825
- }
826
- if (variant === "full") {
827
- return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
828
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__flag", "aria-hidden": "true", children: country.flag }),
829
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__code", children: country.name }),
830
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__sep", "aria-hidden": "true", children: "·" }),
831
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__lang", children: language.label })
832
- ] });
833
- }
834
- return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
835
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__flag", "aria-hidden": "true", children: country.flag }),
836
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__code", children: country.code }),
837
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__sep", "aria-hidden": "true", children: "·" }),
838
- (0, import_jsx_runtime.jsx)("span", { className: "cls-trigger__lang", children: language.code })
839
- ] });
840
- }
841
- export {
842
- CountryLanguageSelector,
843
- defaultCountries,
844
- filterCountries,
845
- languageCatalogue,
846
- useCountryLanguage
847
- };
848
- //# sourceMappingURL=@asafarim_country-language-selector.js.map