- Page « équipe » : roster statique du collectif (composables/useTeam), portraits servis depuis public/portraits, grille ≤ 3 colonnes équilibrée (portrait/nom/rôle/présentation), sélection + surcharges par membre (repliables, réordonnables, réinitialisables) dans le composeur - Page « conclusion » : « Merci ! » centré + rappel des contacts - Page libre : 1 à 3 colonnes texte (markdown) ou images avec sous-titres, réordonnables ; migration des champs body/media legacy vers columns - Couverture : texte libre sous l'intro, adresse en bas à gauche (3 lignes), case « afficher les années » au niveau du document - Sommaire : items centrés (self-center), filet en points dégradés stable - Retrait de la dépendance morte pagedjs - Docs à jour (CLAUDE.md, README) ; spec marquée comme archive réalisée
84 lines
3.4 KiB
Vue
84 lines
3.4 KiB
Vue
<script setup lang="ts">
|
||
// Page « équipe » — langage du gabarit : filet couleur 5 mm en haut, marges
|
||
// 8 mm, titre Syne 45 pt. Les membres s'affichent en grille de 3 colonnes
|
||
// (portrait, nom, rôle, présentation), qui s'adapte à leur nombre. Pas de
|
||
// multi-colonnes CSS (Chromium les imprime en blanc) : grille explicite.
|
||
import type { Background } from '~~/types'
|
||
|
||
interface ResolvedMember {
|
||
id: string
|
||
name: string
|
||
portraitUrl: string
|
||
role?: string
|
||
bio?: string
|
||
}
|
||
|
||
const props = withDefaults(
|
||
defineProps<{
|
||
title?: string
|
||
members?: ResolvedMember[]
|
||
background: Background
|
||
bandColor?: string
|
||
page: { index: number; total: number }
|
||
renderMode?: 'preview' | 'print'
|
||
}>(),
|
||
{ title: 'L’équipe', renderMode: 'preview', members: () => [] },
|
||
)
|
||
|
||
const strip = computed(() => props.bandColor || FL_INK)
|
||
const pageStyle = computed(() => backgroundStyle(props.background, strip.value))
|
||
|
||
// 3 colonnes au plus. On prend le minimum de rangées possible, puis on répartit
|
||
// les membres pour équilibrer la grille (4 membres → 2×2, 5 → 3+2, etc.).
|
||
const count = computed(() => props.members.length || 1)
|
||
const rows = computed(() => Math.ceil(count.value / 3))
|
||
const cols = computed(() => Math.ceil(count.value / rows.value))
|
||
const layout = computed(() => {
|
||
if (rows.value <= 1) return { portrait: 280, name: 30, role: 20, bio: 15, top: 48, gapY: 40 }
|
||
if (rows.value === 2) return { portrait: 168, name: 23, role: 16, bio: 12, top: 36, gapY: 28 }
|
||
return { portrait: 124, name: 19, role: 14, bio: 10.5, top: 28, gapY: 22 }
|
||
})
|
||
|
||
// Peu de colonnes sur plusieurs rangées (ex. 4 membres en 2×2) = colonnes très
|
||
// larges : on rentre la grille des deux côtés pour resserrer les colonnes.
|
||
const gridInset = computed(() => (rows.value >= 2 && cols.value <= 2 ? 190 : 0))
|
||
|
||
const gridStyle = computed(() => ({
|
||
gridTemplateColumns: `repeat(${cols.value}, minmax(0, 1fr))`,
|
||
columnGap: '48px',
|
||
rowGap: `${layout.value.gapY}px`,
|
||
gridAutoRows: 'min-content',
|
||
marginTop: `${layout.value.top}px`,
|
||
paddingInline: `${gridInset.value}px`,
|
||
}))
|
||
</script>
|
||
|
||
<template>
|
||
<div class="fl-page" :style="pageStyle">
|
||
<div v-if="background.mode !== 'full'" class="absolute inset-x-0 top-0" :style="{ height: 'var(--fl-strip)', backgroundColor: strip }" />
|
||
|
||
<div class="absolute flex flex-col" :style="{ inset: 'var(--fl-margin)', top: '60px' }">
|
||
<h2 :style="{ fontSize: '60px', lineHeight: '1.05' }">{{ title }}</h2>
|
||
|
||
<div class="grid min-h-0 flex-1 content-start overflow-hidden" :style="gridStyle">
|
||
<div v-for="m in members" :key="m.id" class="flex min-w-0 flex-col">
|
||
<!-- Portrait carré, cadré (les portraits non carrés sont recentrés) -->
|
||
<div
|
||
class="w-full overflow-hidden"
|
||
:style="{ aspectRatio: '1 / 1', maxWidth: `${layout.portrait}px` }"
|
||
>
|
||
<img :src="m.portraitUrl" :alt="m.name" class="size-full object-cover">
|
||
</div>
|
||
<p class="mt-4" :style="{ fontSize: `${layout.name}px`, lineHeight: '1.15' }">{{ m.name }}</p>
|
||
<p v-if="m.role" class="opacity-70" :style="{ fontSize: `${layout.role}px`, lineHeight: '1.2' }">{{ m.role }}</p>
|
||
<p
|
||
v-if="m.bio"
|
||
class="mt-2"
|
||
:style="{ fontSize: `${layout.bio}px`, lineHeight: '1.35', hyphens: 'auto', textAlign: 'justify' }"
|
||
>{{ m.bio }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|