/** Registre des partis côté web — miroir du registre du moteur (engine/qc26/parties.py). * Toute couleur ou logo de parti passe par ici : aucun code hex dans les composants. */ import type { PartyId, PartyKey } from "./api"; export interface PartyDef { id: PartyId; abbreviation: string; officialName: string; shortName: string; color: string; secondaryColor: string; website: string; logo: string; logoWhite?: string; /** Le logo officiel est blanc : afficher sur une pastille de cette couleur. */ logoBackdrop?: string; logoRatio: number; // largeur / hauteur order: number; } export const PARTIES: PartyDef[] = [ { id: "caq", abbreviation: "CAQ", officialName: "Coalition avenir Québec", shortName: "CAQ", color: "#20A8E0", secondaryColor: "#0B6FA6", website: "https://coalitionavenirquebec.org", logo: "/parties/caq/logo.png", logoWhite: "/parties/caq/logo-white.png", logoRatio: 2560 / 1134, order: 1 }, { id: "plq", abbreviation: "PLQ", officialName: "Parti libéral du Québec", shortName: "PLQ", color: "#ED1B2D", secondaryColor: "#007AC2", website: "https://plq.org", logo: "/parties/plq/logo.svg", logoWhite: "/parties/plq/logo-white.svg", logoRatio: 516.1 / 278.3, order: 2 }, { id: "pq", abbreviation: "PQ", officialName: "Parti québécois", shortName: "PQ", color: "#1E295C", secondaryColor: "#4C5B9E", website: "https://pq.org", logo: "/parties/pq/logo.svg", logoWhite: "/parties/pq/wordmark-white.png", logoRatio: 180 / 45, order: 3 }, { id: "qs", abbreviation: "QS", officialName: "Québec solidaire", shortName: "QS", color: "#FF5505", secondaryColor: "#FFF0A8", website: "https://quebecsolidaire.net", logo: "/parties/qs/logo.svg", logoRatio: 720 / 300, order: 4 }, { id: "pcq", abbreviation: "PCQ", officialName: "Parti conservateur du Québec", shortName: "PCQ", color: "#6E4C9F", secondaryColor: "#102850", website: "https://conservateur.quebec", logo: "/parties/pcq/logo.svg", logoBackdrop: "#102850", logoRatio: 324 / 79, order: 5 }, ]; export const OTHERS = { id: "aut" as const, abbreviation: "Autres", officialName: "Autres candidatures", color: "#9AA0A6" }; export const PARTY_IDS: PartyId[] = PARTIES.map((p) => p.id); export const PARTY_BY_ID: Record = Object.fromEntries(PARTIES.map((p) => [p.id, p])); export function party(id: string | null | undefined): PartyDef | null { return id ? PARTY_BY_ID[id] ?? null : null; } export function partyColor(id: string | null | undefined): string { if (!id) return "#C4C8CE"; return PARTY_BY_ID[id]?.color ?? OTHERS.color; } export function partyLabel(id: string | null | undefined): string { if (!id) return "—"; return PARTY_BY_ID[id]?.abbreviation ?? OTHERS.abbreviation; } export function partyName(id: string | null | undefined): string { if (!id) return "—"; return PARTY_BY_ID[id]?.officialName ?? OTHERS.officialName; } /** Ordonne des identifiants de partis par une valeur décroissante. */ export function sortParties(rec: Record, key: (v: T) => number, includeOthers = false): [PartyKey, T][] { return (Object.entries(rec) as [PartyKey, T][]) .filter(([id]) => includeOthers || id !== "aut") .sort((a, b) => key(b[1]) - key(a[1])); } /** Couleur de parti via variable CSS (adaptée au thème clair/sombre dans globals.css). À utiliser dans l'UI ; * `partyColor` (hex) reste pour les images OG (satori) et les exports. */ export function partyVar(id: string | null | undefined): string { if (!id) return "var(--p-none)"; return PARTY_BY_ID[id] ? `var(--p-${id})` : "var(--p-aut)"; } /** Couleur de parti mélangée à la surface : alpha 0…1 → color-mix (thème-safe). */ export function partyAlpha(id: string | null | undefined, alpha: number): string { const a = Math.max(0, Math.min(1, alpha)); return `color-mix(in srgb, ${partyVar(id)} ${Math.round(a * 100)}%, transparent)`; } /** Couleur atténuée par une probabilité (incertitude → moins d'opacité). */ export function withAlpha(hex: string, alpha: number): string { const h = hex.replace("#", ""); const n = parseInt(h.length === 3 ? h.split("").map((c) => c + c).join("") : h, 16); const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255; return `rgba(${r}, ${g}, ${b}, ${Math.max(0, Math.min(1, alpha)).toFixed(3)})`; }