SPB Git forge

spb/qc26

Public
10commits 1branches 0releases
2.8 MBsize
maindefault branch
17 days agolast push
Python 51.6% TypeScript 46.7% CSS 1.7%
2.5 KB · 39 lines tsx
Raw Blame History
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