Python 51.6%
TypeScript 46.7%
CSS 1.7%
1"use client";23import { geoMercator, geoPath } from "d3-geo";4import type { FeatureCollection, Geometry } from "geojson";5import { useEffect, useMemo, useState } from "react";67/** Mini-carte de localisation : la circonscription en accent sur la silhouette du Québec + zoom local. */8export function RidingMap({ code }: { code: number }) {9 const [geo, setGeo] = useState<FeatureCollection<Geometry, { code: number; name: string }> | null>(null);10 useEffect(() => { fetch("/api/geo").then((r) => r.json()).then(setGeo).catch(() => {}); }, []);11 const svg = useMemo(() => {12 if (!geo) return null;13 const W = 360, H = 300;14 const proj = geoMercator().fitExtent([[6, 6], [W - 6, H - 6]], geo);15 const path = geoPath(proj);16 const f = geo.features.find((x) => x.properties.code === code);17 if (!f) return null;18 const [[x0, y0], [x1, y1]] = path.bounds(f);19 const local = geoMercator().fitExtent([[8, 8], [W - 8, H - 8]], { type: "FeatureCollection", features: [f] } as FeatureCollection<Geometry>);20 const lp = geoPath(local);21 const neighbors = geo.features.filter((g) => { const b = lp.bounds(g); return b[0][0] < W && b[1][0] > 0 && b[0][1] < H && b[1][1] > 0; });22 return { W, H, all: geo.features.map((g) => path(g) ?? ""), me: path(f) ?? "", box: [x0, y0, x1 - x0, y1 - y0], localMe: lp(f) ?? "", neighbors: neighbors.map((g) => lp(g) ?? "") };23 }, [geo, code]);24 if (!svg) return <div className="h-[300px] bg-surface-2 animate-pulse" />;25 return (26 <div className="grid grid-cols-2">27 <svg viewBox={`0 0 ${svg.W} ${svg.H}`} className="w-full h-auto bg-surface-2" role="img" aria-label="Localisation au Québec">28 {svg.all.map((d, i) => <path key={i} d={d} fill="var(--p-none)" stroke="var(--surface)" strokeWidth={0.5} />)}29 <path d={svg.me} fill="var(--qc26-accent)" stroke="var(--qc26-accent)" strokeWidth={4} strokeLinejoin="round" />30 <rect x={svg.box[0] - 8} y={svg.box[1] - 8} width={svg.box[2] + 16} height={svg.box[3] + 16} fill="none" stroke="var(--text)" strokeWidth={1} strokeDasharray="3 2" />31 </svg>32 <svg viewBox={`0 0 ${svg.W} ${svg.H}`} className="w-full h-auto bg-surface border-l border-border" role="img" aria-label="Zoom sur la circonscription">33 {svg.neighbors.map((d, i) => <path key={i} d={d} fill="var(--surface-3)" stroke="var(--border-strong)" strokeWidth={0.8} />)}34 <path d={svg.localMe} fill="rgba(37,99,235,.18)" stroke="var(--qc26-accent)" strokeWidth={2} />35 </svg>36 </div>37 );38}39