@djangocfg/ui-core 2.1.307 → 2.1.308

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
@@ -113,7 +113,7 @@ import { Button, Dialog, Table } from '@djangocfg/ui-core';
113
113
  ### Specialized (9)
114
114
  `Kbd` `TokenIcon` `Item` `Portal` `ImageWithFallback` `CopyButton` `CopyField` `Flag` `LanguageFlag`
115
115
 
116
- **`Flag`** / **`LanguageFlag`** — SVG country flags (3:2 aspect) backed by `country-flag-icons`. `Flag` takes an ISO 3166-1 alpha-2 `countryCode`; `LanguageFlag` resolves a locale (`'en'`, `'pt-BR'`, `'zh_CN'`) to a country and renders the flag. Both render `null` for unknown codes.
116
+ **`Flag`** / **`LanguageFlag`** — SVG country flags (3:2 aspect) backed by `country-flag-icons`. `Flag` takes an ISO 3166-1 alpha-2 `countryCode` and covers every ISO country (~270 entries); `LanguageFlag` resolves a locale (`'en'`, `'pt-BR'`, `'zh_CN'`) to a country and renders the flag. Both render `null` for unknown codes. Used by `LocaleSwitcher`, `PhoneInput`, and `CountrySelect` — no emoji fallbacks anywhere.
117
117
 
118
118
  ```tsx
119
119
  <Flag countryCode="JP" rounded className="h-3 w-4" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.307",
3
+ "version": "2.1.308",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -91,7 +91,7 @@
91
91
  "playground": "playground dev"
92
92
  },
93
93
  "peerDependencies": {
94
- "@djangocfg/i18n": "^2.1.307",
94
+ "@djangocfg/i18n": "^2.1.308",
95
95
  "consola": "^3.4.2",
96
96
  "lucide-react": "^0.545.0",
97
97
  "moment": "^2.30.1",
@@ -160,9 +160,9 @@
160
160
  "vaul": "1.1.2"
161
161
  },
162
162
  "devDependencies": {
163
- "@djangocfg/i18n": "^2.1.307",
163
+ "@djangocfg/i18n": "^2.1.308",
164
164
  "@djangocfg/playground": "workspace:*",
165
- "@djangocfg/typescript-config": "^2.1.307",
165
+ "@djangocfg/typescript-config": "^2.1.308",
166
166
  "@types/node": "^24.7.2",
167
167
  "@types/react": "^19.1.0",
168
168
  "@types/react-dom": "^19.1.0",
@@ -13,13 +13,7 @@ import { cn } from '../../../lib';
13
13
  import {
14
14
  DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger
15
15
  } from '../../navigation/dropdown-menu';
16
-
17
- // Generate country flag emoji from country code
18
- const getCountryFlag = (countryCode: CountryCode): string => {
19
- return countryCode
20
- .toUpperCase()
21
- .replace(/./g, char => String.fromCodePoint(char.charCodeAt(0) + 127397))
22
- }
16
+ import { Flag } from '../../specialized/flag';
23
17
 
24
18
  // Get country name from country code using browser's built-in Intl.DisplayNames
25
19
  const getCountryName = (countryCode: CountryCode): string => {
@@ -37,7 +31,6 @@ const getAllCountries = () => {
37
31
  return getCountries().map(countryCode => ({
38
32
  code: countryCode,
39
33
  name: getCountryName(countryCode),
40
- flag: getCountryFlag(countryCode),
41
34
  dialCode: `+${getCountryCallingCode(countryCode)}`
42
35
  })).sort((a, b) => a.name.localeCompare(b.name))
43
36
  }
@@ -215,7 +208,7 @@ const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
215
208
  className="h-10 px-3 rounded-r-none border-r-0 flex items-center gap-2"
216
209
  disabled={disabled}
217
210
  >
218
- <span className="text-base">{currentCountry.flag}</span>
211
+ <Flag countryCode={currentCountry.code} rounded className="h-3 w-4" />
219
212
  <span className="text-sm font-mono">{currentCountry.dialCode}</span>
220
213
  <ChevronDown className="h-3 w-3 opacity-50" />
221
214
  </Button>
@@ -251,7 +244,7 @@ const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
251
244
  index === highlightedIndex && "bg-accent"
252
245
  )}
253
246
  >
254
- <span className="text-base">{country.flag}</span>
247
+ <Flag countryCode={country.code} rounded className="h-3 w-4" />
255
248
  <span className="flex-1 text-sm">{country.name}</span>
256
249
  <span className="text-sm font-mono text-muted-foreground">
257
250
  {country.dialCode}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Check, ChevronsUpDown, Search, X } from 'lucide-react';
4
4
  import * as React from 'react';
5
- import { countries, getEmojiFlag, type TCountryCode } from 'countries-list';
5
+ import { countries, type TCountryCode } from 'countries-list';
6
6
 
7
7
  import { cn } from '../../lib/utils';
8
8
  import { Badge } from '../data/badge';
@@ -14,12 +14,12 @@ import {
14
14
  import { Input } from '../forms/input';
15
15
  import { Popover, PopoverContent, PopoverTrigger } from '../overlay/popover';
16
16
  import { ScrollArea } from '../layout/scroll-area';
17
+ import { Flag } from '../specialized/flag';
17
18
 
18
19
  export interface CountryOption {
19
20
  code: TCountryCode;
20
21
  name: string;
21
22
  native: string;
22
- emoji: string;
23
23
  }
24
24
 
25
25
  export type CountrySelectVariant = 'dropdown' | 'inline';
@@ -64,7 +64,7 @@ export interface CountrySelectProps {
64
64
  }
65
65
 
66
66
  /**
67
- * Country Select component with emoji flags
67
+ * Country Select component with SVG flags
68
68
  *
69
69
  * Supports:
70
70
  * - Single and multiple selection
@@ -156,7 +156,6 @@ export function CountrySelect({
156
156
  code,
157
157
  name: getCountryName?.(code) ?? countries[code].name,
158
158
  native: countries[code].native,
159
- emoji: getEmojiFlag(code),
160
159
  }))
161
160
  .sort((a, b) => a.name.localeCompare(b.name));
162
161
  }, [getCountryName, allowedCountries, excludedCountries]);
@@ -256,7 +255,7 @@ export function CountrySelect({
256
255
  {isSelected && <div className="h-2 w-2 rounded-full bg-primary-foreground" />}
257
256
  </div>
258
257
  )}
259
- <span className="text-lg">{country.emoji}</span>
258
+ <Flag countryCode={country.code} rounded className="h-4 w-6 shrink-0" />
260
259
  <div className="flex flex-col flex-1 min-w-0">
261
260
  <span className={cn('text-sm', isSelected && 'font-medium')}>
262
261
  {country.name}
@@ -287,7 +286,7 @@ export function CountrySelect({
287
286
  const country = selectedCountries[0]!;
288
287
  return (
289
288
  <div className="flex items-center gap-2">
290
- <span>{country.emoji}</span>
289
+ <Flag countryCode={country.code} rounded className="h-3 w-4 shrink-0" />
291
290
  <span>{country.name}</span>
292
291
  </div>
293
292
  );
@@ -304,7 +303,7 @@ export function CountrySelect({
304
303
  variant="secondary"
305
304
  className="mr-1 text-xs"
306
305
  >
307
- <span className="mr-1">{country.emoji}</span>
306
+ <Flag countryCode={country.code} rounded className="mr-1 h-3 w-4 shrink-0" />
308
307
  {country.name}
309
308
  <button
310
309
  className="ml-1 rounded-full hover:bg-muted-foreground/20"
@@ -379,7 +378,7 @@ export function CountrySelect({
379
378
  isSelected ? "opacity-100" : "opacity-0"
380
379
  )}
381
380
  />
382
- <span className="mr-2 text-lg">{country.emoji}</span>
381
+ <Flag countryCode={country.code} rounded className="mr-2 h-4 w-6 shrink-0" />
383
382
  <div className="flex flex-col flex-1 min-w-0">
384
383
  <span className="truncate">{country.name}</span>
385
384
  {showNativeName && country.native !== country.name && (
@@ -402,4 +401,3 @@ export function CountrySelect({
402
401
 
403
402
  // Re-export types for convenience
404
403
  export type { TCountryCode } from 'countries-list';
405
- export { getEmojiFlag } from 'countries-list';
@@ -1,38 +1,15 @@
1
1
  /**
2
2
  * Static map: ISO 3166-1 alpha-2 country code → SVG React component.
3
3
  *
4
- * Only the country codes referenced by `LANGUAGE_TO_COUNTRY` are imported
5
- * keeps bundle size predictable while covering every locale supported
6
- * by the language picker. Add an entry here when a new locale joins the
7
- * mapping and its country code is not already in the list.
4
+ * Re-exports every flag from `country-flag-icons` (~270 entries) so the
5
+ * `Flag` component covers every consumer locale switchers, phone-input
6
+ * country picker, country select, etc. Tree-shaking keeps unused flags out
7
+ * of the final bundle when consumers reach for `<Flag countryCode>`
8
+ * directly with a constant code.
8
9
  */
9
10
 
10
11
  import type * as React from 'react';
11
-
12
- import {
13
- AF, AL, AM, AZ,
14
- BA, BD, BE, BG, BI, BO, BR, BT, BW, BY,
15
- CA, CD, CF, CH, CN, CZ,
16
- DE, DK,
17
- EE, ER, ES, ET,
18
- FI, FJ, FO, FR,
19
- GB, GE, GH, GL, GR, GU,
20
- HR, HT, HU,
21
- ID, IE, IL, IM, IN, IQ, IR, IS, IT,
22
- JP,
23
- KE, KG, KH, KR, KZ,
24
- LA, LK, LT, LU, LV,
25
- MD, MG, MH, MK, ML, MM, MN, MT, MV, MW, MY,
26
- NA, NE, NG, NL, NO, NP, NR, NZ,
27
- PE, PF, PG, PH, PK, PL, PY,
28
- RO, RS, RU, RW,
29
- SA, SE, SI, SK, SN, SO, SZ,
30
- TH, TJ, TM, TO, TR,
31
- UA, UG, US, UZ,
32
- VA, VN, VU,
33
- WS,
34
- ZA, ZW,
35
- } from 'country-flag-icons/react/3x2';
12
+ import * as Flags from 'country-flag-icons/react/3x2';
36
13
 
37
14
  /**
38
15
  * `country-flag-icons` types its components against `HTMLSVGElement`
@@ -42,27 +19,4 @@ import {
42
19
  */
43
20
  export type FlagSvgComponent = (props: React.SVGProps<SVGSVGElement> & React.HTMLAttributes<SVGSVGElement>) => React.JSX.Element;
44
21
 
45
- export const FLAG_COMPONENTS = {
46
- AF, AL, AM, AZ,
47
- BA, BD, BE, BG, BI, BO, BR, BT, BW, BY,
48
- CA, CD, CF, CH, CN, CZ,
49
- DE, DK,
50
- EE, ER, ES, ET,
51
- FI, FJ, FO, FR,
52
- GB, GE, GH, GL, GR, GU,
53
- HR, HT, HU,
54
- ID, IE, IL, IM, IN, IQ, IR, IS, IT,
55
- JP,
56
- KE, KG, KH, KR, KZ,
57
- LA, LK, LT, LU, LV,
58
- MD, MG, MH, MK, ML, MM, MN, MT, MV, MW, MY,
59
- NA, NE, NG, NL, NO, NP, NR, NZ,
60
- PE, PF, PG, PH, PK, PL, PY,
61
- RO, RS, RU, RW,
62
- SA, SE, SI, SK, SN, SO, SZ,
63
- TH, TJ, TM, TO, TR,
64
- UA, UG, US, UZ,
65
- VA, VN, VU,
66
- WS,
67
- ZA, ZW,
68
- } as unknown as Record<string, FlagSvgComponent>;
22
+ export const FLAG_COMPONENTS = Flags as unknown as Record<string, FlagSvgComponent>;