- 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
70 lines
3.0 KiB
Vue
70 lines
3.0 KiB
Vue
<script setup lang="ts">
|
|
// Page libre (équipe, méthodologie…) — langage du gabarit : filet couleur
|
|
// 5 mm, marges 8 mm, titre Syne 45 pt, texte Syne 12/16 pt. Contenu réparti en
|
|
// 1 à 3 colonnes de texte ou d'images, chacune avec un sous-titre optionnel.
|
|
// Pas de multi-colonnes CSS (Chromium les imprime en blanc) : flexbox explicite.
|
|
import type { Background, ResolvedMedia } from '~~/types'
|
|
|
|
interface ResolvedColumn {
|
|
id: string
|
|
kind: 'text' | 'media'
|
|
heading?: string
|
|
html?: string // colonnes texte
|
|
media?: ResolvedMedia[] // colonnes images
|
|
}
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
title?: string
|
|
columns?: ResolvedColumn[]
|
|
background: Background
|
|
bandColor?: string
|
|
textScale?: number // taille du texte de labeur en % (défaut 100 = 12 pt)
|
|
page: { index: number; total: number }
|
|
renderMode?: 'preview' | 'print'
|
|
}>(),
|
|
{ renderMode: 'preview', textScale: 100, columns: () => [] },
|
|
)
|
|
|
|
const strip = computed(() => props.bandColor || FL_INK)
|
|
const pageStyle = computed(() => backgroundStyle(props.background, strip.value))
|
|
// Texte de labeur : base 16 px (12 pt), agrandissable ; interligne proportionnel.
|
|
const bodyStyle = computed(() => ({ fontSize: `${(16 * props.textScale) / 100}px`, lineHeight: '1.35' }))
|
|
</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 v-if="title" class="max-w-[1000px]" :style="{ fontSize: '60px', lineHeight: '1.05' }">{{ title }}</h2>
|
|
<div class="mt-12 flex min-h-0 flex-1" :style="{ gap: 'var(--fl-gutter)' }">
|
|
<section v-for="col in columns" :key="col.id" class="flex min-w-0 flex-1 flex-col">
|
|
<!-- Sous-titre optionnel en tête de colonne -->
|
|
<h3 v-if="col.heading" class="mb-4 shrink-0" :style="{ fontSize: '28px', lineHeight: '1.15' }">{{ col.heading }}</h3>
|
|
|
|
<!-- Colonne texte -->
|
|
<div
|
|
v-if="col.kind === 'text'"
|
|
class="fl-prose min-h-0 flex-1 overflow-hidden"
|
|
:style="bodyStyle"
|
|
v-html="col.html"
|
|
/>
|
|
|
|
<!-- Colonne images : entières, calées, titre du projet en légende -->
|
|
<div v-else class="flex min-h-0 flex-1 flex-col" :style="{ gap: 'var(--fl-gutter)' }">
|
|
<figure v-for="m in col.media" :key="m.filename" class="flex min-h-0 flex-1 flex-col">
|
|
<img :src="m.url" :alt="m.caption || m.filename" class="min-h-0 flex-1 object-contain object-left-top">
|
|
<figcaption
|
|
v-if="m.caption"
|
|
class="mt-1 shrink-0 text-left opacity-70"
|
|
:style="{ fontFamily: 'Syne, sans-serif', fontSize: '14px', lineHeight: '1.3' }"
|
|
>{{ m.caption }}</figcaption>
|
|
</figure>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|