solid-circle-flags
Version:
A Solid component with a collection of 300+ minimal circular SVG country flags.
308 lines (303 loc) • 6.04 kB
JSX
// src/countries.ts
var countryToCountryMap = {
ac: "sh-ac",
bq: "bq-bo",
bv: "no",
"cn-hk": "hk",
cp: "fr-cp",
dg: "io",
ea: "es-ce",
eu: "european_union",
fx: "fr",
hm: "au",
ic: "es-cn",
sh: "sh-hl",
sj: "no",
su: "soviet_union",
ta: "sh-ta",
uk: "gb",
um: "us",
"us-as": "as",
"us-gu": "gu",
"us-mp": "mp",
"us-pr": "pr",
"us-um": "us",
"us-vi": "vi"
};
// src/languages.ts
var languageToCountryMap = {
aa: "et-af",
ab: "ge-ab",
af: "za",
ak: "gh",
am: "et",
an: "es-ar",
as: "in-as",
av: "ru-da",
ay: "bo",
az: "az",
ba: "ru-ba",
be: "by",
bg: "bg",
bi: "vu",
bm: "ml",
bn: "bd",
bo: "tibet",
br: "fr-bre",
bs: "ba",
ca: "es-ct",
ce: "ru-ce",
ceb: "ph",
ch: "gu",
chm: "ru",
ckb: "iq-kr",
co: "fr-20r",
cs: "cz",
cv: "ru-cu",
cy: "gb-wls",
da: "dk",
de: "de",
dv: "mv",
dz: "bt",
ee: "gh",
el: "gr",
en: "us",
es: "es",
et: "ee",
eu: "es-pv",
fa: "ir",
fi: "fi",
fil: "ph",
fj: "fj",
fo: "fo",
fr: "fr",
fy: "nl-fr",
ga: "ie",
gd: "gb-sct",
gl: "es-ga",
gn: "py",
gu: "in-gj",
gv: "im",
ha: "ng",
haw: "us-hi",
he: "il",
hi: "in",
hmn: "hmong",
ho: "pg",
hr: "hr",
ht: "ht",
hu: "hu",
hy: "am",
id: "id",
ig: "ng",
ilo: "ph",
is: "is",
it: "it",
ja: "jp",
jv: "id-jt",
ka: "ge",
kg: "cd",
ki: "ke",
kk: "kz",
kl: "gl",
km: "kh",
kn: "in-ka",
ko: "kr",
kr: "ng",
kri: "sl",
ks: "in",
ku: "iq-kr",
kv: "ru-ko",
kw: "gb-con",
ky: "kg",
lb: "lu",
lg: "ug",
ln: "cd",
lo: "la",
lt: "lt",
lu: "cd",
lus: "in-mz",
lv: "lv",
mg: "mg",
mh: "mh",
mi: "maori",
mk: "mk",
ml: "malayali",
mn: "mn",
mni: "in-mn",
mrj: "ru",
ms: "my",
mt: "mt",
my: "mm",
na: "nr",
nb: "no",
nd: "zw",
ne: "np",
nl: "nl",
nn: "no",
no: "no",
nr: "za",
ny: "mw",
oc: "occitania",
om: "et",
or: "in-or",
os: "ru",
oto: "mx",
pa: "in",
pap: "cw",
pl: "pl",
pms: "it-21",
ps: "af",
"pt-br": "br",
pt: "pt",
qu: "pe",
rm: "ch-gr",
rn: "bi",
ro: "ro",
ru: "ru",
rw: "rw",
sc: "it-82",
sd: "pk-sd",
se: "no",
sg: "cf",
si: "lk",
sk: "sk",
sl: "si",
sm: "ws",
sn: "zw",
so: "so",
sq: "al",
sr: "rs",
ss: "sz",
st: "ls",
su: "id-jb",
sv: "se",
sw: "tz",
ta: "in-tn",
te: "in-tg",
tg: "tj",
th: "th",
ti: "er",
tk: "tm",
tl: "ph",
tn: "bw",
to: "to",
tr: "tr",
tt: "ru-ta",
ty: "pf",
udm: "ru-ud",
ug: "cn-xj",
uk: "ua",
ur: "pk",
uz: "uz",
vi: "vn",
xh: "za",
yo: "yorubaland",
yua: "mx",
zh: "cn",
zu: "za"
};
// src/components/FallbackSvg.tsx
function FallbackSvg(props) {
const maskId = "circular-graphic-fallback-mask";
return <svg
xmlns="http://www.w3.org/2000/svg"
width={props.width}
height={props.height}
viewBox="0 0 512 512"
aria-label="Loading"
role="img"
><mask id={maskId}><circle cx="256" cy="256" r="256" fill="#fff" /></mask><g mask={`url(#${maskId})`}><path fill="#eee" d="M0 0h512v512H0z" /><circle cx="253" cy="380" r="32" fill="#acabb1" /><path
fill="#acabb1"
d="M322.4 135.5c-15.6-13.6-37.4-20.3-65.5-20.3-27.9 0-49.9 7.2-66 21.4a74.9 74.9 0 0 0-24.3 55.4h-.2v12.8H224l.1-9a35.2 35.2 0 0 1 9.3-24.8c5.8-6.1 13.7-9 23.5-9 20.7 0 31 11 31 33.4 0 7.4-2 14.5-6 21.1a124.2 124.2 0 0 1-23.9 26 90.4 90.4 0 0 0-24.8 32.3c-4.5 11-6.8 26.7-6.8 45.2h51l.8-13.1a54 54 0 0 1 17.3-33.9l16.2-15.2a131.4 131.4 0 0 0 26.4-33.2 69.5 69.5 0 0 0 7.6-31.8c-.1-24.7-7.8-43.7-23.3-57.3z"
/></g></svg>;
}
// src/components/CircularGraphic.tsx
import { createSignal } from "solid-js";
var DEFAULT_CDN_URLS = {
country: "https://circle-flags.zeljko.me/flags/",
language: "https://circle-flags.zeljko.me/flags/language/"
};
var ENV_CDN_URL = import.meta.env.VITE_PUBLIC_CIRCLE_FLAGS_CDN_URL?.trim() || "";
function CircularGraphic(props) {
const { code, type, cdn, label, width, height, alt, src, onError, ...restProps } = props;
const [imgError, setImgError] = createSignal(false);
const getCdnUrl = () => {
const rawCdn = props.cdn?.trim() || ENV_CDN_URL || DEFAULT_CDN_URLS[props.type];
return rawCdn.replace(/\/+$/, "") + "/";
};
const getCleanCode = () => {
return props.code.replace(/^\/+/, "");
};
const getSourceUrl = () => {
return `${getCdnUrl()}${getCleanCode()}.svg`;
};
const getAriaLabel = () => {
return props.label || `${props.type} ${getCleanCode()} flag`;
};
const handleError = () => {
setImgError(true);
};
return <>{!imgError() ? <img
src={getSourceUrl()}
width={props.width}
height={props.height}
alt={getAriaLabel()}
onError={handleError}
{...restProps}
/> : <FallbackSvg width={props.width} height={props.height} />}</>;
}
// src/index.tsx
function CircleFlag(props) {
const { countryCode, width, height, cdn, label, ...imgProps } = props;
const mappedCountryCode = countryToCountryMap[countryCode];
if (mappedCountryCode !== void 0) {
return <CircularGraphic
code={mappedCountryCode}
type="country"
width={width}
height={height}
cdn={cdn}
label={label}
{...imgProps}
/>;
} else {
return <CircularGraphic
code={countryCode}
type="country"
width={width}
height={height}
cdn={cdn}
label={label}
{...imgProps}
/>;
}
}
function CircleFlagLanguage(props) {
const { languageCode, width, height, cdn, label, ...imgProps } = props;
const mappedCountryCode = languageToCountryMap[languageCode];
if (mappedCountryCode !== void 0) {
return <CircleFlag
countryCode={mappedCountryCode}
width={width}
height={height}
cdn={cdn}
label={label}
{...imgProps}
/>;
} else {
return <CircularGraphic
code={languageCode}
type="language"
width={width}
height={height}
cdn={cdn}
label={label}
{...imgProps}
/>;
}
}
export {
CircleFlag,
CircleFlagLanguage
};