feat(donut): noms de catégories en arc tout à l'extérieur du donut

Les libellés des 6 catégories thématiques (Data, Démocratie, Habitat,
Territoire, Écologie, Éducation) étaient posés HORIZONTAUX au milieu
de leur wedge dans la zone sûre. Ils chevauchaient les points-projets
quand on en avait beaucoup et compliquaient la lecture.

Déplacement à R=275, ARC autour du donut, dans la couleur de la catégorie :

  - Nouvelle constante R_CAT_LABEL (la plus extérieure : > planétaires à 252)
  - viewBox 600x600 (vs 560x560) pour caser la nouvelle frontière
  - Fond cream étendu à R=295 pour soutenir les labels jusqu'au bord
  - Font-size 11 + weight 700 + lettre-espacement 0.5px pour lisibilité
  - Réutilisation du helper arcLabelPaths (halfSpan 28° = quasi-pleine
    largeur du secteur de 60°)
  - Couleur récupérée par index depuis la liste categories réactive

Suppression du rendu in-wedge des labels catégorie : redondance avec les
arcs extérieurs + meilleure lisibilité des points-projets dans la zone sûre.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 22:38:41 +02:00
parent 8fc8c21b7e
commit 14c8d87ad8
+31 -28
View File
@@ -58,13 +58,14 @@ onMounted(load)
// lisibles : circonférence ≈ 2π × R, donc plus R est grand, plus chaque label // lisibles : circonférence ≈ 2π × R, donc plus R est grand, plus chaque label
// d'arc dispose de place. Le centre et le ring extérieur ne bougent pas, c'est // d'arc dispose de place. Le centre et le ring extérieur ne bougent pas, c'est
// uniquement la zone intérieure qui grossit, en mangeant un peu sur l'espace sûr. // uniquement la zone intérieure qui grossit, en mangeant un peu sur l'espace sûr.
const R_PLANET_LABEL = 252 // étiquettes des limites planétaires (extérieur) const R_CAT_LABEL = 275 // noms de catégories EN ARC, tout à l'extérieur
const R_PLANET_LABEL = 252 // étiquettes des limites planétaires (entre catégories et ceiling)
const R_CEILING_OUTER = 225 const R_CEILING_OUTER = 225
const R_CEILING_INNER = 200 const R_CEILING_INNER = 200
const R_FLOOR_OUTER = 125 // ↑ (était 100) const R_FLOOR_OUTER = 125
const R_FLOOR_INNER = 102 // ↑ (était 78) const R_FLOOR_INNER = 102
const R_SOCIAL_LABEL = 88 // ↑ (était 67) — arc de label = 2π·88/12 ≈ 46u const R_SOCIAL_LABEL = 88 // arc de label = 2π·88/12 ≈ 46u
const R_CENTER = 55 // ↑ légèrement pour ne pas écraser "LE DONUT" const R_CENTER = 55
/** /**
* Format : chaque entrée est un tableau de string. Un tableau à 1 élément → * Format : chaque entrée est un tableau de string. Un tableau à 1 élément →
@@ -233,16 +234,12 @@ const wedgeDividers = computed(() => {
}) })
}) })
/** Position du label catégorie (juste sous l'anneau écologique, sur la zone sûre). */ /** Arcs des noms de catégories, tout à l'extérieur du donut, dans la couleur de la catégorie.
const categoryLabels = computed(() => { * 6 catégories × 60° d'arc chacune ; halfSpan=28° laisse une petite marge avant la suivante. */
const n = categories.value.length || 6 const categoryArcs = computed(() =>
const step = 360 / n arcLabelPaths(categories.value.map(c => [c.name] as Label), R_CAT_LABEL, 'cat', 28),
return categories.value.map((cat, i) => { )
const center = i * step + step / 2 const categoryArcLines = computed(() => categoryArcs.value.flatMap(c => c.lines))
const [x, y] = pt(center, (R_CEILING_INNER + R_FLOOR_OUTER) / 2)
return { cat, x, y }
})
})
/** Paths d'arc pour les 9 limites planétaires (anneau extérieur). */ /** Paths d'arc pour les 9 limites planétaires (anneau extérieur). */
const planetaryArcs = computed(() => arcLabelPaths(PLANETARY_BOUNDARIES, R_PLANET_LABEL, 'pb', 18)) const planetaryArcs = computed(() => arcLabelPaths(PLANETARY_BOUNDARIES, R_PLANET_LABEL, 'pb', 18))
@@ -361,15 +358,19 @@ function gotoProject(p: ProjectListRow) {
<div v-else class="mt-10 grid grid-cols-1 lg:grid-cols-5 gap-10 items-start"> <div v-else class="mt-10 grid grid-cols-1 lg:grid-cols-5 gap-10 items-start">
<!-- Donut SVG (3/5 sur grands écrans) --> <!-- Donut SVG (3/5 sur grands écrans) -->
<div class="lg:col-span-3 flex flex-col items-center"> <div class="lg:col-span-3 flex flex-col items-center">
<svg viewBox="-280 -280 560 560" class="w-full max-w-[640px] aspect-square"> <svg viewBox="-300 -300 600 600" class="w-full max-w-[680px] aspect-square">
<defs> <defs>
<!-- Anneaux gris légèrement striés --> <!-- Anneaux gris légèrement striés -->
<pattern id="ring-stripes" patternUnits="userSpaceOnUse" width="6" height="6" patternTransform="rotate(45)"> <pattern id="ring-stripes" patternUnits="userSpaceOnUse" width="6" height="6" patternTransform="rotate(45)">
<rect width="6" height="6" fill="#ECECE7" /> <rect width="6" height="6" fill="#ECECE7" />
<line x1="0" y1="0" x2="0" y2="6" stroke="#D1D5DB" stroke-width="0.9" /> <line x1="0" y1="0" x2="0" y2="6" stroke="#D1D5DB" stroke-width="0.9" />
</pattern> </pattern>
<!-- Arcs invisibles pour textPath : un par LIGNE de label (1 ligne par limite <!-- Arcs invisibles pour textPath : 1 par catégorie (extérieur), 1 par limite
planétaire, 1 à 2 lignes par dimension sociale selon la longueur). --> planétaire, 1 à 2 par dimension sociale selon la longueur. -->
<path
v-for="ln in categoryArcLines" :key="ln.pathId"
:id="ln.pathId" :d="ln.d" fill="none"
/>
<path <path
v-for="ln in planetaryArcLines" :key="ln.pathId" v-for="ln in planetaryArcLines" :key="ln.pathId"
:id="ln.pathId" :d="ln.d" fill="none" :id="ln.pathId" :d="ln.d" fill="none"
@@ -381,8 +382,8 @@ function gotoProject(p: ProjectListRow) {
</defs> </defs>
<!-- 1) ZONE D'OVERSHOOT (au-delà du plafond) --> <!-- 1) ZONE D'OVERSHOOT (au-delà du plafond) -->
<!-- fond cream clair pour soutenir les labels limites planétaires --> <!-- fond cream clair pour soutenir les labels limites planétaires + catégories -->
<circle r="270" fill="#F5F2EA" /> <circle r="295" fill="#F5F2EA" />
<!-- 2) Étiquettes des 9 LIMITES PLANÉTAIRES, le long de leur arc --> <!-- 2) Étiquettes des 9 LIMITES PLANÉTAIRES, le long de leur arc -->
<g> <g>
@@ -423,17 +424,19 @@ function gotoProject(p: ProjectListRow) {
/> />
</g> </g>
<!-- 7) Étiquettes catégories au milieu de leur wedge --> <!-- 7) Noms des CATÉGORIES en arc, tout à l'extérieur du donut, en couleur catégorie -->
<g> <g>
<text <text
v-for="l in categoryLabels" :key="`label-${l.cat.id}`" v-for="arc in categoryArcs" :key="`cat-text-${arc.key}`"
:x="l.x" :y="l.y" font-size="11"
text-anchor="middle"
font-size="9"
:fill="l.cat.color_hex"
font-weight="700" font-weight="700"
opacity="0.85" :fill="categories[arc.key]?.color_hex ?? '#111827'"
>{{ l.cat.name }}</text> style="letter-spacing: 0.5px"
>
<textPath :href="`#${arc.lines[0].pathId}`" startOffset="50%" text-anchor="middle">
{{ arc.lines[0].text }}
</textPath>
</text>
</g> </g>
<!-- 8) Étiquettes des 12 DIMENSIONS DU PLANCHER SOCIAL, le long de leur arc --> <!-- 8) Étiquettes des 12 DIMENSIONS DU PLANCHER SOCIAL, le long de leur arc -->