Chaque échelle porte ses onglets (libellé, cible, actif(route)) ; echelleDeRoute rend le groupe courant ou null. Source unique de la nav B10. Test node : scripts/test-echelles-nav.mjs (23/23). B10 M1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBu7CSLy7PJgL4HJaV5oHP
118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
/**
|
|
* Les trois échelles du deck du 23/09/2026 (conférence AEP), slides 15 à 18.
|
|
* Texte repris mot pour mot : c'est la tête de chaque onglet (directive Jules du 25/09, point 2).
|
|
* Source unique : la nav S / M / XL (B10 : NavEchelles, NavTiroir, FilEchelle) lit aussi ce fichier.
|
|
* Aucun libellé d'échelle ni d'onglet ne se recopie ailleurs.
|
|
*/
|
|
export type EchelleId = 'S' | 'M' | 'XL'
|
|
|
|
/** Ce que la nav lit d'une route : compatible avec `useRoute()` sans en dépendre (testable en node). */
|
|
export interface RouteNav {
|
|
path: string
|
|
query?: Record<string, unknown>
|
|
}
|
|
|
|
export interface OngletNav {
|
|
libelle: string
|
|
to: string
|
|
actif: (route: RouteNav) => boolean
|
|
}
|
|
|
|
export interface Echelle {
|
|
id: EchelleId
|
|
titre: string
|
|
sousTitre?: string
|
|
mots: string[]
|
|
phrase: string
|
|
/** Catégorie de /proposer présélectionnée par le lien de contribution. */
|
|
categorieProposer: 'ecosysteme' | 'reseau' | 'jobs' | 'outils' | 'references'
|
|
/** Onglets du groupe, dans l'ordre de la barre, du menu et du fil d'échelle (B10, directive Jules du 29/09 + arbitrage 1A du 26/09). */
|
|
onglets: OngletNav[]
|
|
}
|
|
|
|
const sousChemin = (base: string) => (r: RouteNav) => r.path === base || r.path.startsWith(`${base}/`)
|
|
const surPublications = (r: RouteNav) => r.query?.tab === 'projets'
|
|
|
|
export const ECHELLES: Record<EchelleId, Echelle> = {
|
|
S: {
|
|
id: 'S',
|
|
titre: 'Partir de soi',
|
|
sousTitre: "pour s'émanciper",
|
|
mots: [
|
|
'quête de sens',
|
|
'où habiter, avec qui',
|
|
'gagner sa vie',
|
|
'santé mentale',
|
|
'mener ses projets',
|
|
'naviguer avec soi',
|
|
'les compétences psychosociales',
|
|
],
|
|
phrase: 'Des ressources, des méthodes, des pairs : matériel et mutualisé.',
|
|
categorieProposer: 'outils',
|
|
onglets: [
|
|
{ libelle: 'Jobs', to: '/trouver-du-taf', actif: sousChemin('/trouver-du-taf') },
|
|
{ libelle: 'Outils', to: '/outils', actif: sousChemin('/outils') },
|
|
],
|
|
},
|
|
M: {
|
|
id: 'M',
|
|
titre: 'La communauté',
|
|
mots: [
|
|
'entraide',
|
|
'mutualisation',
|
|
'appartenance',
|
|
'être vu, soutenu',
|
|
'traverser les creux',
|
|
'accomplissement',
|
|
'le codev',
|
|
],
|
|
phrase: "C'est le groupe qui remet un individu debout. L'affinité fait tenir, la confrontation fait avancer.",
|
|
categorieProposer: 'ecosysteme',
|
|
onglets: [
|
|
{ libelle: "Écosystème d'entraide", to: '/', actif: (r) => r.path === '/' },
|
|
{ libelle: 'Réseaux AEP', to: '/agences', actif: sousChemin('/agences') },
|
|
{ libelle: 'Codev', to: '/codev', actif: sousChemin('/codev') },
|
|
],
|
|
},
|
|
XL: {
|
|
id: 'XL',
|
|
titre: 'Contribuer',
|
|
mots: [
|
|
'contribution',
|
|
'affiliation, filiation',
|
|
'identité',
|
|
'sens',
|
|
'de nouveaux récits',
|
|
'un nouveau contrat social',
|
|
'la médecine du corps social',
|
|
],
|
|
phrase: 'La bibliothèque, les récits, la filiation entre projets.',
|
|
categorieProposer: 'references',
|
|
onglets: [
|
|
{ libelle: 'Recherche média', to: '/media', actif: (r) => sousChemin('/media')(r) && !surPublications(r) },
|
|
{ libelle: 'Publications', to: '/media?tab=projets', actif: (r) => sousChemin('/media')(r) && surPublications(r) },
|
|
],
|
|
},
|
|
}
|
|
|
|
/** Ordre des groupes dans la barre et le menu. */
|
|
export const ORDRE_ECHELLES: EchelleId[] = ['S', 'M', 'XL']
|
|
|
|
/** Slide 15, commune aux trois échelles. */
|
|
export const ECHELLES_CADRE = "Trois échelles, imbriquées. Pas un escalier : ça circule dans les deux sens. Entrez par celle qui presse."
|
|
|
|
/** Version courte de la slide 15 (tête du menu) : première et dernière phrase, tirées du texte ci-dessus. */
|
|
export const ECHELLES_CADRE_COURT = (() => {
|
|
const phrases = ECHELLES_CADRE.match(/[^.]+\./g) ?? [ECHELLES_CADRE]
|
|
return phrases.length > 1 ? `${phrases[0].trim()} ${phrases[phrases.length - 1].trim()}` : ECHELLES_CADRE
|
|
})()
|
|
|
|
/** Groupe de la page courante et son onglet actif, ou null hors groupe (Manifeste, À propos, Proposer, fiche…). */
|
|
export function echelleDeRoute(route: RouteNav): { echelle: EchelleId; onglet: OngletNav } | null {
|
|
for (const id of ORDRE_ECHELLES) {
|
|
const onglet = ECHELLES[id].onglets.find((o) => o.actif(route))
|
|
if (onglet) return { echelle: id, onglet }
|
|
}
|
|
return null
|
|
}
|