Wave-2 user feedback (points 4–6): - Language picker: replace emoji flags (which don't render reliably on Windows) with inline-SVG flag components for GB/DE/FR/ES/IT. Used in both the Settings localisation tab and the onboarding wizard. - Timezone: replace the read-only Intl.DateTimeFormat display with a real <select> over a curated list of major IANA zones. Each option shows "(UTC±hh:mm) CODE — Region" and the list is sorted by current offset, then code, then IANA name. - Multi-timezone: new ExtraTimezonesEditor in the localisation tab lets users pin additional zones (stored in user.viewSettings.extraTimezones to avoid a migration). The time-grid axis now renders the equivalent hour for each pinned zone next to the local label, helpful for scheduling across regions. v1.100.0 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
"use client";
|
|
import React from "react";
|
|
|
|
// Tiny inline SVG country flags. Used by the language pickers so flags
|
|
// render identically on every OS (Windows ships without the regional
|
|
// emoji glyphs that "🇬🇧" relies on).
|
|
|
|
type FlagCode = "gb" | "de" | "fr" | "es" | "it";
|
|
|
|
const flags: Record<FlagCode, React.ReactNode> = {
|
|
// United Kingdom
|
|
gb: (
|
|
<svg viewBox="0 0 60 30" width="100%" height="100%">
|
|
<clipPath id="fl_gb_t"><path d="M30,15h30v15z v15h-30z h-30v-15z v-15h30z"/></clipPath>
|
|
<path d="M0,0v30h60v-30z" fill="#012169"/>
|
|
<path d="M0,0 60,30 M60,0 0,30" stroke="#fff" strokeWidth="6"/>
|
|
<path d="M0,0 60,30 M60,0 0,30" clipPath="url(#fl_gb_t)" stroke="#C8102E" strokeWidth="4"/>
|
|
<path d="M30,0v30 M0,15h60" stroke="#fff" strokeWidth="10"/>
|
|
<path d="M30,0v30 M0,15h60" stroke="#C8102E" strokeWidth="6"/>
|
|
</svg>
|
|
),
|
|
// Germany
|
|
de: (
|
|
<svg viewBox="0 0 5 3" width="100%" height="100%" preserveAspectRatio="none">
|
|
<rect width="5" height="3" fill="#000"/>
|
|
<rect width="5" height="2" y="1" fill="#D00"/>
|
|
<rect width="5" height="1" y="2" fill="#FFCE00"/>
|
|
</svg>
|
|
),
|
|
// France
|
|
fr: (
|
|
<svg viewBox="0 0 3 2" width="100%" height="100%" preserveAspectRatio="none">
|
|
<rect width="1" height="2" x="0" fill="#0055A4"/>
|
|
<rect width="1" height="2" x="1" fill="#fff"/>
|
|
<rect width="1" height="2" x="2" fill="#EF4135"/>
|
|
</svg>
|
|
),
|
|
// Spain
|
|
es: (
|
|
<svg viewBox="0 0 5 3" width="100%" height="100%" preserveAspectRatio="none">
|
|
<rect width="5" height="3" fill="#AA151B"/>
|
|
<rect width="5" height="1.5" y="0.75" fill="#F1BF00"/>
|
|
</svg>
|
|
),
|
|
// Italy
|
|
it: (
|
|
<svg viewBox="0 0 3 2" width="100%" height="100%" preserveAspectRatio="none">
|
|
<rect width="1" height="2" x="0" fill="#009246"/>
|
|
<rect width="1" height="2" x="1" fill="#fff"/>
|
|
<rect width="1" height="2" x="2" fill="#CE2B37"/>
|
|
</svg>
|
|
),
|
|
};
|
|
|
|
// Map UI language codes to flag codes (English uses the British flag per user request).
|
|
const LANG_FLAG: Record<string, FlagCode> = {
|
|
en: "gb",
|
|
de: "de",
|
|
fr: "fr",
|
|
es: "es",
|
|
it: "it",
|
|
};
|
|
|
|
interface FlagIconProps {
|
|
code: string;
|
|
width?: number;
|
|
height?: number;
|
|
className?: string;
|
|
}
|
|
|
|
export default function FlagIcon({ code, width = 22, height = 16, className }: FlagIconProps) {
|
|
const c = (LANG_FLAG[code] || code) as FlagCode;
|
|
const flag = flags[c];
|
|
if (!flag) return null;
|
|
return (
|
|
<span
|
|
className={className}
|
|
style={{
|
|
display: "inline-block",
|
|
width,
|
|
height,
|
|
lineHeight: 0,
|
|
borderRadius: 2,
|
|
overflow: "hidden",
|
|
boxShadow: "0 0 0 1px rgba(0,0,0,0.06)",
|
|
flexShrink: 0,
|
|
verticalAlign: "middle",
|
|
}}
|
|
aria-hidden="true"
|
|
>
|
|
{flag}
|
|
</span>
|
|
);
|
|
}
|