Compare commits
6 Commits
feat/v12-p
...
feat/v13-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa410ce7aa | ||
|
|
e43ec60ecf | ||
|
|
1033099663 | ||
|
|
9bb55bc311 | ||
|
|
7ec0efdeb5 | ||
|
|
a1d6271b65 |
@@ -4,6 +4,9 @@
|
|||||||
# statut: gestation (draft/en cours) | edite (publie)
|
# statut: gestation (draft/en cours) | edite (publie)
|
||||||
# nature: essai (texte politique) | projet (projet archi)
|
# nature: essai (texte politique) | projet (projet archi)
|
||||||
# niveau: 0 (centre) | 1 (concepts force) | 2 (thematiques/projets)
|
# niveau: 0 (centre) | 1 (concepts force) | 2 (thematiques/projets)
|
||||||
|
# domain (optionnel) : domaine plateforme source pour logo Brandfetch CDN
|
||||||
|
# - affiche logo en bas-droite du node si zoom > 1.5x
|
||||||
|
# - V1.2 par defaut : substack.com pour tous les essais AEP
|
||||||
|
|
||||||
version: "1.1"
|
version: "1.1"
|
||||||
|
|
||||||
@@ -23,76 +26,91 @@ thematiques:
|
|||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "pratiques-collectives"
|
- id: "pratiques-collectives"
|
||||||
label: "Pratiques collectives"
|
label: "Pratiques collectives"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "art-narration"
|
- id: "art-narration"
|
||||||
label: "Art & narration"
|
label: "Art & narration"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "pouvoir-domination"
|
- id: "pouvoir-domination"
|
||||||
label: "Rapport au pouvoir"
|
label: "Rapport au pouvoir"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "medias-critique"
|
- id: "medias-critique"
|
||||||
label: "Medias & pensee critique"
|
label: "Medias & pensee critique"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "justice-securite"
|
- id: "justice-securite"
|
||||||
label: "Justice & securite"
|
label: "Justice & securite"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "sante-globale"
|
- id: "sante-globale"
|
||||||
label: "Sante globale"
|
label: "Sante globale"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "agriculture"
|
- id: "agriculture"
|
||||||
label: "Agriculture"
|
label: "Agriculture"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "post-croissance"
|
- id: "post-croissance"
|
||||||
label: "Post-croissance"
|
label: "Post-croissance"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "anthropocene"
|
- id: "anthropocene"
|
||||||
label: "Anthropocene & effondrement"
|
label: "Anthropocene & effondrement"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "education"
|
- id: "education"
|
||||||
label: "Education a la transformation"
|
label: "Education a la transformation"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "urbanisme"
|
- id: "urbanisme"
|
||||||
label: "Urbanisme"
|
label: "Urbanisme"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "geopolitique"
|
- id: "geopolitique"
|
||||||
label: "Geopolitique & decolonisation"
|
label: "Geopolitique & decolonisation"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "ia-technologie"
|
- id: "ia-technologie"
|
||||||
label: "IA & technologie"
|
label: "IA & technologie"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
- id: "spiritualite"
|
- id: "spiritualite"
|
||||||
label: "Spiritualite"
|
label: "Spiritualite"
|
||||||
niveau: 2
|
niveau: 2
|
||||||
nature: essai
|
nature: essai
|
||||||
statut: gestation
|
statut: gestation
|
||||||
|
domain: "substack.com"
|
||||||
|
|
||||||
projets:
|
projets:
|
||||||
- id: "tmip"
|
- id: "tmip"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "1.1",
|
"version": "1.1",
|
||||||
"generatedAt": "2026-05-11T16:41:21.600Z",
|
"generatedAt": "2026-05-11T16:47:45.459Z",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"id": "contrat-social-medecine-corps-social",
|
"id": "contrat-social-medecine-corps-social",
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pratiques-collectives",
|
"id": "pratiques-collectives",
|
||||||
@@ -30,7 +31,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "art-narration",
|
"id": "art-narration",
|
||||||
@@ -40,7 +42,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pouvoir-domination",
|
"id": "pouvoir-domination",
|
||||||
@@ -50,7 +53,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "medias-critique",
|
"id": "medias-critique",
|
||||||
@@ -60,7 +64,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "justice-securite",
|
"id": "justice-securite",
|
||||||
@@ -70,7 +75,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sante-globale",
|
"id": "sante-globale",
|
||||||
@@ -80,7 +86,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "agriculture",
|
"id": "agriculture",
|
||||||
@@ -90,7 +97,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "post-croissance",
|
"id": "post-croissance",
|
||||||
@@ -100,7 +108,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "anthropocene",
|
"id": "anthropocene",
|
||||||
@@ -110,7 +119,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "education",
|
"id": "education",
|
||||||
@@ -120,7 +130,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "urbanisme",
|
"id": "urbanisme",
|
||||||
@@ -130,7 +141,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "geopolitique",
|
"id": "geopolitique",
|
||||||
@@ -140,7 +152,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ia-technologie",
|
"id": "ia-technologie",
|
||||||
@@ -150,7 +163,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "spiritualite",
|
"id": "spiritualite",
|
||||||
@@ -160,7 +174,8 @@
|
|||||||
"statut": "gestation",
|
"statut": "gestation",
|
||||||
"resume": null,
|
"resume": null,
|
||||||
"radius": 12,
|
"radius": 12,
|
||||||
"family": "concept"
|
"family": "concept",
|
||||||
|
"domain": "substack.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "tmip",
|
"id": "tmip",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addNode(obj) {
|
function addNode(obj) {
|
||||||
nodes.push({
|
const node = {
|
||||||
id: obj.id,
|
id: obj.id,
|
||||||
label: obj.label,
|
label: obj.label,
|
||||||
niveau: obj.niveau,
|
niveau: obj.niveau,
|
||||||
@@ -52,7 +52,10 @@ async function main() {
|
|||||||
resume: obj.resume || null,
|
resume: obj.resume || null,
|
||||||
radius: getRadius(obj.niveau, obj.nature),
|
radius: getRadius(obj.niveau, obj.nature),
|
||||||
family: getFamily(obj.nature),
|
family: getFamily(obj.nature),
|
||||||
})
|
}
|
||||||
|
// V1.2-O : propage le champ optionnel domain (logo plateforme via Brandfetch CDN)
|
||||||
|
if (obj.domain) node.domain = obj.domain
|
||||||
|
nodes.push(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
const centreId = data.centre.id
|
const centreId = data.centre.id
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ const categories = [
|
|||||||
],
|
],
|
||||||
hasSelector: false,
|
hasSelector: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'pro',
|
||||||
|
label: 'Pro',
|
||||||
|
color: '#0F172A',
|
||||||
|
hashtags: ['#building-public', '#pro'],
|
||||||
|
plateformes: [
|
||||||
|
{ id: 'linkedin', label: 'LinkedIn', url: 'https://www.linkedin.com/in/jules-neny/' },
|
||||||
|
],
|
||||||
|
hasSelector: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
<div class="h-full flex flex-col p-4 pt-20 md:pt-6 gap-5">
|
<div class="h-full flex flex-col p-4 pt-20 md:pt-6 gap-5">
|
||||||
@@ -42,7 +52,7 @@ const categories = [
|
|||||||
<details id="hashtags-accordion" class="border-t border-neutral-200 pt-4">
|
<details id="hashtags-accordion" class="border-t border-neutral-200 pt-4">
|
||||||
<summary class="font-semibold cursor-pointer select-none flex items-center justify-between">
|
<summary class="font-semibold cursor-pointer select-none flex items-center justify-between">
|
||||||
<span>Hashtags</span>
|
<span>Hashtags</span>
|
||||||
<span class="text-xs text-neutral-400 font-normal">3 categories</span>
|
<span class="text-xs text-neutral-400 font-normal">4 categories</span>
|
||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
<div class="mt-3 flex flex-wrap gap-2" id="category-badges">
|
<div class="mt-3 flex flex-wrap gap-2" id="category-badges">
|
||||||
@@ -126,7 +136,7 @@ const categories = [
|
|||||||
const PLATFORM_KEY = 'tf-platform-filter';
|
const PLATFORM_KEY = 'tf-platform-filter';
|
||||||
|
|
||||||
// Active state : map categoryId -> boolean
|
// Active state : map categoryId -> boolean
|
||||||
const activeCategories: Record<string, boolean> = { politique: true, art: true, outils: true };
|
const activeCategories: Record<string, boolean> = { politique: true, art: true, outils: true, pro: true };
|
||||||
|
|
||||||
// Platform filter : map categoryId -> platformId | null
|
// Platform filter : map categoryId -> platformId | null
|
||||||
const platformFilters: Record<string, string | null> = { politique: null };
|
const platformFilters: Record<string, string | null> = { politique: null };
|
||||||
@@ -138,15 +148,18 @@ const categories = [
|
|||||||
const politiqueHashtags = ['#politique', '#aep-politique'];
|
const politiqueHashtags = ['#politique', '#aep-politique'];
|
||||||
const artHashtags = ['#peinture', '#art'];
|
const artHashtags = ['#peinture', '#art'];
|
||||||
const outilsHashtags = ['#stack', '#building-public'];
|
const outilsHashtags = ['#stack', '#building-public'];
|
||||||
|
const proHashtags = ['#building-public', '#pro'];
|
||||||
|
|
||||||
const allPolitique = politiqueHashtags.every(h => storedHashtags[h] !== false);
|
const allPolitique = politiqueHashtags.every(h => storedHashtags[h] !== false);
|
||||||
const allArt = artHashtags.every(h => storedHashtags[h] !== false);
|
const allArt = artHashtags.every(h => storedHashtags[h] !== false);
|
||||||
const allOutils = outilsHashtags.every(h => storedHashtags[h] !== false);
|
const allOutils = outilsHashtags.every(h => storedHashtags[h] !== false);
|
||||||
|
const allPro = proHashtags.every(h => storedHashtags[h] !== false);
|
||||||
|
|
||||||
if (Object.keys(storedHashtags).length > 0) {
|
if (Object.keys(storedHashtags).length > 0) {
|
||||||
activeCategories['politique'] = allPolitique;
|
activeCategories['politique'] = allPolitique;
|
||||||
activeCategories['art'] = allArt;
|
activeCategories['art'] = allArt;
|
||||||
activeCategories['outils'] = allOutils;
|
activeCategories['outils'] = allOutils;
|
||||||
|
activeCategories['pro'] = allPro;
|
||||||
}
|
}
|
||||||
} catch { /* mode prive */ }
|
} catch { /* mode prive */ }
|
||||||
|
|
||||||
@@ -163,6 +176,7 @@ const categories = [
|
|||||||
politique: ['#politique', '#aep-politique'],
|
politique: ['#politique', '#aep-politique'],
|
||||||
art: ['#peinture', '#art'],
|
art: ['#peinture', '#art'],
|
||||||
outils: ['#stack', '#building-public'],
|
outils: ['#stack', '#building-public'],
|
||||||
|
pro: ['#building-public', '#pro'],
|
||||||
};
|
};
|
||||||
for (const [catId, tags] of Object.entries(catHashtags)) {
|
for (const [catId, tags] of Object.entries(catHashtags)) {
|
||||||
const active = activeCategories[catId] ?? true;
|
const active = activeCategories[catId] ?? true;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<button
|
<button
|
||||||
id="hamburger-trigger"
|
id="hamburger-trigger"
|
||||||
type="button"
|
type="button"
|
||||||
class="fixed top-4 right-4 z-50 p-3 bg-white/95 border border-neutral-200 rounded-lg shadow-md hover:bg-white transition-colors md:top-6 md:right-6"
|
class="fixed top-4 right-4 z-50 p-3 bg-white/95 border border-neutral-200 rounded-lg shadow-md hover:bg-white transition-colors md:hidden"
|
||||||
aria-label="Ouvrir le menu"
|
aria-label="Ouvrir le menu"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-controls="hamburger-drawer"
|
aria-controls="hamburger-drawer"
|
||||||
|
|||||||
@@ -19,8 +19,15 @@ interface CarteNode {
|
|||||||
slug?: string
|
slug?: string
|
||||||
theme?: string
|
theme?: string
|
||||||
path?: string
|
path?: string
|
||||||
|
domain?: string // V1.2-O : domaine plateforme source (logo Brandfetch CDN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// V1.2-O : logos plateforme via Brandfetch CDN (visible zoom > 1.5x seulement)
|
||||||
|
const BRANDFETCH_CLIENT_ID = '4ae58bd85c8140eab0cee72f40656120'
|
||||||
|
const LOGO_ZOOM_THRESHOLD = 1.5
|
||||||
|
const logoUrl = (domain: string) =>
|
||||||
|
`https://cdn.brandfetch.io/${domain}/w/64/h/64?c=${BRANDFETCH_CLIENT_ID}`
|
||||||
|
|
||||||
interface CarteEdge {
|
interface CarteEdge {
|
||||||
source: string | CarteNode
|
source: string | CarteNode
|
||||||
target: string | CarteNode
|
target: string | CarteNode
|
||||||
@@ -187,6 +194,11 @@ function initSvg() {
|
|||||||
.scaleExtent([0.3, 4])
|
.scaleExtent([0.3, 4])
|
||||||
.on('zoom', (event) => {
|
.on('zoom', (event) => {
|
||||||
gZoom!.attr('transform', event.transform.toString())
|
gZoom!.attr('transform', event.transform.toString())
|
||||||
|
// V1.2-O : toggle visibilite logos plateforme selon le niveau de zoom
|
||||||
|
// (evite la surcharge visuelle au niveau d'ensemble, montre detail au drill-down)
|
||||||
|
const scale = event.transform.k
|
||||||
|
gZoom!.selectAll('.logo-overlay')
|
||||||
|
.style('opacity', scale > LOGO_ZOOM_THRESHOLD ? 1 : 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
svgRoot.call(zoomBehavior as any)
|
svgRoot.call(zoomBehavior as any)
|
||||||
@@ -290,6 +302,32 @@ function render() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// V1.2-O : Logo plateforme (visible zoom > 1.5x seulement)
|
||||||
|
// Cercle blanc 14px en bas-droite du node (offset +60% x/+60% y du radius)
|
||||||
|
// Image clip-path circle pour avatar style. Hidden par defaut (opacity 0).
|
||||||
|
const nodeGroupsWithLogo = nodeGroups.filter(d => !!d.domain)
|
||||||
|
|
||||||
|
const logoOverlay = nodeGroupsWithLogo.append('g')
|
||||||
|
.attr('class', 'logo-overlay')
|
||||||
|
.style('opacity', 0)
|
||||||
|
.attr('pointer-events', 'none')
|
||||||
|
|
||||||
|
logoOverlay.append('circle')
|
||||||
|
.attr('cx', d => getRadius(d) * 0.6)
|
||||||
|
.attr('cy', d => getRadius(d) * 0.6)
|
||||||
|
.attr('r', 9)
|
||||||
|
.attr('fill', '#FFFFFF')
|
||||||
|
.attr('stroke', '#0F172A')
|
||||||
|
.attr('stroke-width', 0.5)
|
||||||
|
|
||||||
|
logoOverlay.append('image')
|
||||||
|
.attr('href', d => logoUrl(d.domain!))
|
||||||
|
.attr('x', d => getRadius(d) * 0.6 - 7)
|
||||||
|
.attr('y', d => getRadius(d) * 0.6 - 7)
|
||||||
|
.attr('width', 14)
|
||||||
|
.attr('height', 14)
|
||||||
|
.attr('clip-path', d => `circle(7px at ${getRadius(d) * 0.6}px ${getRadius(d) * 0.6}px)`)
|
||||||
|
|
||||||
// Tooltip <title>
|
// Tooltip <title>
|
||||||
nodeGroups.append('title')
|
nodeGroups.append('title')
|
||||||
.text(d => `${d.label}\n[${d.family}]\n${truncate(d.resume || d.intention || '', 200)}`)
|
.text(d => `${d.label}\n[${d.family}]\n${truncate(d.resume || d.intention || '', 200)}`)
|
||||||
|
|||||||
@@ -23,24 +23,43 @@ const iframeStyle = computed(() => ({
|
|||||||
transformOrigin: '0 0',
|
transformOrigin: '0 0',
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
let fallbackTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
const revealIframe = () => {
|
||||||
|
if (iframeRef.value) {
|
||||||
|
iframeRef.value.classList.remove('opacity-0')
|
||||||
|
iframeRef.value.classList.add('opacity-100')
|
||||||
|
}
|
||||||
|
skeletonHidden.value = true
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (wrapperRef.value && typeof ResizeObserver !== 'undefined') {
|
if (wrapperRef.value && typeof ResizeObserver !== 'undefined') {
|
||||||
updateScale()
|
updateScale()
|
||||||
resizeObs = new ResizeObserver(updateScale)
|
resizeObs = new ResizeObserver(updateScale)
|
||||||
resizeObs.observe(wrapperRef.value)
|
resizeObs.observe(wrapperRef.value)
|
||||||
}
|
}
|
||||||
|
// Fallback : si @load ne fire pas dans 2.5s (transform/scale peut bloquer l'event),
|
||||||
|
// on revele quand meme l'iframe pour ne pas laisser le skeleton infini.
|
||||||
|
fallbackTimer = setTimeout(() => {
|
||||||
|
revealIframe()
|
||||||
|
}, 2500)
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
resizeObs?.disconnect()
|
resizeObs?.disconnect()
|
||||||
resizeObs = null
|
resizeObs = null
|
||||||
|
if (fallbackTimer) {
|
||||||
|
clearTimeout(fallbackTimer)
|
||||||
|
fallbackTimer = null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onIframeLoad = () => {
|
const onIframeLoad = () => {
|
||||||
if (iframeRef.value) {
|
if (fallbackTimer) {
|
||||||
iframeRef.value.classList.remove('opacity-0')
|
clearTimeout(fallbackTimer)
|
||||||
iframeRef.value.classList.add('opacity-100')
|
fallbackTimer = null
|
||||||
}
|
}
|
||||||
skeletonHidden.value = true
|
revealIframe()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -51,7 +70,7 @@ const onIframeLoad = () => {
|
|||||||
<div
|
<div
|
||||||
v-if="!skeletonHidden"
|
v-if="!skeletonHidden"
|
||||||
id="embed-skeleton"
|
id="embed-skeleton"
|
||||||
class="absolute inset-0 flex items-center justify-center bg-neutral-50 animate-pulse z-10"
|
class="absolute inset-0 flex items-center justify-center bg-neutral-50 animate-pulse"
|
||||||
>
|
>
|
||||||
<span class="text-neutral-400 text-sm">Chargement de la carte AEP...</span>
|
<span class="text-neutral-400 text-sm">Chargement de la carte AEP...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const {
|
|||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="fr">
|
<html lang="fr" class="h-screen">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
@@ -29,11 +29,15 @@ const {
|
|||||||
<meta name="twitter:title" content={title} />
|
<meta name="twitter:title" content={title} />
|
||||||
<meta name="twitter:description" content={description} />
|
<meta name="twitter:description" content={description} />
|
||||||
</head>
|
</head>
|
||||||
<body class="m-0 bg-white text-neutral-900 antialiased min-h-screen flex flex-col">
|
<body class="m-0 bg-white text-neutral-900 antialiased h-screen flex flex-col overflow-hidden">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
<div class="flex-1 flex flex-col min-h-0">
|
</div>
|
||||||
|
<div class="flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
<Footer />
|
<Footer />
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import HamburgerMenu from '../components/astro/HamburgerMenu.astro';
|
|||||||
>
|
>
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
|
|
||||||
<main class="min-h-screen bg-white">
|
<main class="h-full overflow-y-auto bg-white">
|
||||||
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
||||||
|
|
||||||
<header class="mb-10">
|
<header class="mb-10">
|
||||||
|
|||||||
@@ -15,18 +15,16 @@ import PopupOnboarding from '../components/astro/PopupOnboarding.astro';
|
|||||||
<MobileTabBar />
|
<MobileTabBar />
|
||||||
<PopupOnboarding />
|
<PopupOnboarding />
|
||||||
|
|
||||||
<!-- Desktop : grid 3 colonnes (header 64px deja consommes par SiteHeader, on prend le reste) -->
|
<!-- Desktop : grid 3 colonnes V1.3-BG : prend toute la place du wrapper flex-1 du BaseLayout.
|
||||||
<div
|
Header et footer sont fixes (flex-shrink-0), pas besoin de calc(100vh - X). -->
|
||||||
class="hidden md:grid md:grid-cols-[320px_1fr_320px] overflow-hidden"
|
<div class="hidden md:grid md:grid-cols-[320px_1fr_320px] h-full overflow-hidden">
|
||||||
style="height: calc(100vh - 64px);"
|
<aside class="border-r border-neutral-200 overflow-y-auto h-full"><ColJournal /></aside>
|
||||||
>
|
<main class="overflow-hidden h-full"><ColCentre /></main>
|
||||||
<aside class="border-r border-neutral-200 overflow-y-auto"><ColJournal /></aside>
|
<aside class="border-l border-neutral-200 overflow-y-auto h-full"><ColInsta /></aside>
|
||||||
<main class="overflow-hidden"><ColCentre /></main>
|
|
||||||
<aside class="border-l border-neutral-200 overflow-y-auto"><ColInsta /></aside>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile : SwipeContainer Vue island - header 48px + tabbar 44px = 92px reserves -->
|
<!-- Mobile : SwipeContainer Vue island - tabbar 44px reserve dans la zone flex-1 -->
|
||||||
<div class="md:hidden overflow-hidden" style="height: calc(100dvh - 48px - 44px); margin-top: 44px;">
|
<div class="md:hidden h-full overflow-hidden" style="padding-top: 44px;">
|
||||||
<SwipeContainer client:load>
|
<SwipeContainer client:load>
|
||||||
<ColJournal slot="left" />
|
<ColJournal slot="left" />
|
||||||
<ColCentre slot="center" />
|
<ColCentre slot="center" />
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import HamburgerMenu from '../components/astro/HamburgerMenu.astro';
|
|||||||
>
|
>
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
|
|
||||||
<main class="min-h-screen bg-white">
|
<main class="h-full overflow-y-auto bg-white">
|
||||||
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
||||||
|
|
||||||
<!-- En-tete -->
|
<!-- En-tete -->
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import HamburgerMenu from '../../components/astro/HamburgerMenu.astro';
|
|||||||
>
|
>
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
|
|
||||||
<main class="min-h-screen bg-white">
|
<main class="h-full overflow-y-auto bg-white">
|
||||||
<article class="max-w-xl mx-auto px-6 py-16 md:py-24">
|
<article class="max-w-xl mx-auto px-6 py-16 md:py-24">
|
||||||
|
|
||||||
<header class="mb-10">
|
<header class="mb-10">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import HamburgerMenu from '../components/astro/HamburgerMenu.astro';
|
|||||||
>
|
>
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
|
|
||||||
<main class="min-h-screen bg-white">
|
<main class="h-full overflow-y-auto bg-white">
|
||||||
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
<article class="max-w-2xl mx-auto px-6 py-16 md:py-24">
|
||||||
|
|
||||||
<header class="mb-10">
|
<header class="mb-10">
|
||||||
|
|||||||
Reference in New Issue
Block a user