Show real flag image in registration phone field

Flag emojis don't render on Windows, so derive an ISO code from the
emoji's regional indicators and load a flagcdn image in front of the
country-code selector (Account + Verify steps).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 20:00:37 +05:30
parent 0b76c97445
commit b79d925552
3 changed files with 28 additions and 8 deletions
-3
View File
@@ -891,9 +891,6 @@
.dash-root .tm .ds-btn.v-primary { background: rgba(253, 169, 19, 0.92); color: #fff; border-radius: 9.132px; box-shadow: 0 8px 20px -10px rgba(253, 169, 19, 0.75); } .dash-root .tm .ds-btn.v-primary { background: rgba(253, 169, 19, 0.92); color: #fff; border-radius: 9.132px; box-shadow: 0 8px 20px -10px rgba(253, 169, 19, 0.75); }
.dash-root .tm .ds-btn.v-primary svg { color: #fff; } .dash-root .tm .ds-btn.v-primary svg { color: #fff; }
.dash-root .tm .ds-btn.v-primary:not(:disabled):hover { background: rgba(253, 169, 19, 1); } .dash-root .tm .ds-btn.v-primary:not(:disabled):hover { background: rgba(253, 169, 19, 1); }
.dash-root .view-body { display: flex; flex-direction: column; gap: 14px; }
.dash-root .tm-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .dash-root .tm-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dash-root .tm-search { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 220px; height: 44px; padding: 0 13px; border-radius: 13px; border: 1px solid var(--border-2); background: var(--panel-2); color: var(--muted); transition: 0.14s; } .dash-root .tm-search { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 220px; height: 44px; padding: 0 13px; border-radius: 13px; border: 1px solid var(--border-2); background: var(--panel-2); color: var(--muted); transition: 0.14s; }
.dash-root .tm-search:focus-within { border-color: var(--orange); box-shadow: 0 0 0 3px var(--ring); color: var(--orange); } .dash-root .tm-search:focus-within { border-color: var(--orange); box-shadow: 0 0 0 3px var(--ring); color: var(--orange); }
+5 -1
View File
@@ -508,8 +508,12 @@ select.input option { background: var(--ink); color: var(--text); }
/* ---- country code select ---- */ /* ---- country code select ---- */
.phone-row { display: flex; gap: 8px; } .phone-row { display: flex; gap: 8px; }
.cc-field { position: relative; flex: 0 0 118px; width: 118px; }
.cc-field .cc-select { width: 100%; flex: none; padding-left: 40px; }
.cc-flag-img { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 22px; height: 16px; border-radius: 3px; object-fit: cover; pointer-events: none; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18); }
.cc-flag-img.static { position: static; transform: none; }
.cc-select { width: 110px; flex: 0 0 110px; } .cc-select { width: 110px; flex: 0 0 110px; }
@media (max-width: 420px) { .cc-select { width: 96px; flex-basis: 96px; } } @media (max-width: 420px) { .cc-field { flex-basis: 104px; width: 104px; } .cc-select { width: 96px; flex-basis: 96px; } }
/* ---- misc login/register bits ---- */ /* ---- misc login/register bits ---- */
.tips { list-style: none; padding: 0; margin: 16px 0 0; display: flex; flex-direction: column; gap: 8px; } .tips { list-style: none; padding: 0; margin: 16px 0 0; display: flex; flex-direction: column; gap: 8px; }
+21 -2
View File
@@ -191,9 +191,13 @@ function StepAccount(p: {
<div className="field" style={{ marginTop: 14 }}> <div className="field" style={{ marginTop: 14 }}>
<label className="label">Mobile number</label> <label className="label">Mobile number</label>
<div className="phone-row"> <div className="phone-row">
<div className="cc-field">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className="cc-flag-img" src={flagUrl(p.country.flag)} alt={p.country.name} width={22} height={16} />
<select className="input cc-select" value={p.cc} onChange={(e) => p.setCc(e.target.value)}> <select className="input cc-select" value={p.cc} onChange={(e) => p.setCc(e.target.value)}>
{countryCodes.map((c) => <option key={c.code} value={c.code}>{c.flag} {c.code}</option>)} {countryCodes.map((c) => <option key={c.code} value={c.code}>{c.code}</option>)}
</select> </select>
</div>
<input className="input grow" inputMode="numeric" value={p.phone} onChange={(e) => p.setPhone(e.target.value.replace(/\D/g, ""))} placeholder={p.country.example} /> <input className="input grow" inputMode="numeric" value={p.phone} onChange={(e) => p.setPhone(e.target.value.replace(/\D/g, ""))} placeholder={p.country.example} />
</div> </div>
{p.phone && !p.phoneOk && <span style={{ fontSize: 12, color: "#fca5a5" }}>Enter a valid {p.country.digits}-digit number.</span>} {p.phone && !p.phoneOk && <span style={{ fontSize: 12, color: "#fca5a5" }}>Enter a valid {p.country.digits}-digit number.</span>}
@@ -398,7 +402,11 @@ function VerifyChannel({ kind, initial, country, cc, initialPhone, verified, onV
<input className="input" value={value} onChange={(e) => { setValue(e.target.value); setState("idle"); }} placeholder="you@example.com" /> <input className="input" value={value} onChange={(e) => { setValue(e.target.value); setState("idle"); }} placeholder="you@example.com" />
) : ( ) : (
<div className="phone-row"> <div className="phone-row">
<span className="input cc-select" style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>{country.flag} {cc}</span> <span className="input cc-select" style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 7 }}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className="cc-flag-img static" src={flagUrl(country.flag)} alt={country.name} width={22} height={16} />
{cc}
</span>
<input className="input grow" inputMode="numeric" value={value} onChange={(e) => { setValue(e.target.value.replace(/\D/g, "")); setState("idle"); }} placeholder={country.example} /> <input className="input grow" inputMode="numeric" value={value} onChange={(e) => { setValue(e.target.value.replace(/\D/g, "")); setState("idle"); }} placeholder={country.example} />
</div> </div>
)} )}
@@ -618,3 +626,14 @@ function Stepper({ current }: { current: number }) {
} }
function cap(s: string) { return s.charAt(0).toUpperCase() + s.slice(1); } function cap(s: string) { return s.charAt(0).toUpperCase() + s.slice(1); }
// Flag emojis don't render on Windows, so map the emoji's regional-indicator
// code points to an ISO-2 code and use a flag image instead.
function flagUrl(flag: string): string {
const iso = Array.from(flag)
.map((ch) => ch.codePointAt(0))
.filter((cp): cp is number => cp != null && cp >= 0x1f1e6 && cp <= 0x1f1ff)
.map((cp) => String.fromCharCode(cp - 0x1f1e6 + 97))
.join("");
return `https://flagcdn.com/w40/${iso}.png`;
}