- 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
105 lines
4.8 KiB
Vue
105 lines
4.8 KiB
Vue
<script setup lang="ts">
|
|
// Couverture du portfolio — reprend l'accueil du site figureslibres.cc
|
|
// (docs/portfolios-examples/screenshot-site-figures-libres.png) : la phrase
|
|
// d'intro seule, bloc centré à 65 % de large, chaque keyword dans sa fonte
|
|
// (sans soulignement, contrairement au site), flèche vers le bas. Rien d'autre.
|
|
import type { Background } from '~~/types'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
title: string // titre du portfolio — en-tête + métadonnées PDF
|
|
body?: string // HTML : texte libre sous la phrase d'intro (plus petit, centré avec elle)
|
|
subtitle?: string
|
|
recipient?: string
|
|
background: Background
|
|
bandColor?: string // filet couleur en bas (définit aussi celui du sommaire)
|
|
date?: string // texte libre de l'en-tête (ex. « Juillet 2026 »)
|
|
siteUrl?: string
|
|
contactEmail?: string
|
|
address?: string // adresse postale — angle bas-gauche
|
|
renderMode?: 'preview' | 'print'
|
|
}>(),
|
|
{ renderMode: 'preview' },
|
|
)
|
|
|
|
const strip = computed(() => props.bandColor || FL_INK)
|
|
|
|
// Fond : « Plein » = couleur du filet ; « Blanc » rend le gris charte du site
|
|
// (l'intro y est présentée sur gris clair) ; « Teinté » = aplat beige.
|
|
const pageStyle = computed(() => {
|
|
const base
|
|
= props.background.mode === 'full'
|
|
? strip.value
|
|
: props.background.mode === 'tint'
|
|
? TINT_COLOR
|
|
: FL_BASE
|
|
return { backgroundColor: base, color: inkOn(base) }
|
|
})
|
|
|
|
// En-tête : coordonnées en liens actifs (le PDF et l'aperçu partagent le
|
|
// template ; Chromium convertit les <a href> en liens cliquables du PDF).
|
|
const siteHref = computed(() => props.siteUrl?.trim() || DEFAULT_SITE_URL)
|
|
const siteLabel = computed(() => siteHref.value.replace(/^https?:\/\//, '').replace(/\/$/, ''))
|
|
const email = computed(() => props.contactEmail?.trim() || DEFAULT_CONTACT_EMAIL)
|
|
// Laissée vide, la date affiche par défaut le mois courant (ex. « Juillet 2026 »).
|
|
const displayDate = computed(() => props.date?.trim() || formatMonthYear())
|
|
// Adresse coupée aux virgules : une ligne par segment (« Figures Libres » /
|
|
// « 55 avenue… » / « 94110 Arcueil »).
|
|
const addressLines = computed(() =>
|
|
(props.address?.trim() || DEFAULT_ADDRESS).split(',').map(s => s.trim()).filter(Boolean),
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="fl-page" :style="pageStyle">
|
|
<!-- En-tête : titre du portfolio + date, site et mail (liens actifs PDF/viewer) -->
|
|
<header
|
|
class="absolute inset-x-0 top-0 flex items-start justify-between gap-8"
|
|
:style="{ padding: 'var(--fl-margin)', fontFamily: 'Syne, sans-serif' }"
|
|
>
|
|
<div class="max-w-[60%]">
|
|
<h1 :style="{ fontSize: '30px', lineHeight: '1.2' }">{{ title }}</h1>
|
|
<p class="opacity-70" :style="{ fontSize: '19px', lineHeight: '1.5' }">{{ displayDate }}</p>
|
|
</div>
|
|
<div class="flex shrink-0 flex-col items-end" :style="{ fontSize: '19px', lineHeight: '1.5' }">
|
|
<a :href="siteHref" target="_blank" rel="noopener" class="text-current no-underline">{{ siteLabel }}</a>
|
|
<a :href="`mailto:${email}`" class="text-current no-underline">{{ email }}</a>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="absolute inset-0 flex flex-col items-center justify-center">
|
|
<!-- Phrase d'intro du site (pages/01.home) — Lato, keywords dans les
|
|
fontes d'« utilité » du thème Grav, non soulignés -->
|
|
<p class="w-[65%]" :style="{ fontFamily: 'Lato, sans-serif', fontSize: '54px', lineHeight: '1.35' }">
|
|
<span class="font-bold" style="font-family: 'Syne', sans-serif">Figures Libres</span>
|
|
est un collectif de design graphique et interactif porteur de messages d'utilité
|
|
<span class="fl-font-publique">publique</span>,
|
|
<span class="fl-font-sociale">sociale</span> et
|
|
<span class="fl-font-culturelle">culturelle</span>
|
|
qui œuvre sur logiciels libres.
|
|
</p>
|
|
<!-- Texte libre optionnel, plus petit, sous la phrase d'intro -->
|
|
<div
|
|
v-if="body"
|
|
class="fl-prose mt-12 w-[65%]"
|
|
:style="{ fontFamily: 'Lato, sans-serif', fontSize: '24px', lineHeight: '1.45' }"
|
|
v-html="body"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Adresse postale dans l'angle bas-gauche (au-dessus du filet) -->
|
|
<p
|
|
v-if="addressLines.length"
|
|
class="absolute left-0"
|
|
:style="{
|
|
bottom: background.mode === 'full' ? '0' : 'var(--fl-strip)',
|
|
padding: 'var(--fl-margin)',
|
|
fontFamily: 'Syne, sans-serif', fontSize: '19px', lineHeight: '1.5',
|
|
}"
|
|
><span v-for="(line, i) in addressLines" :key="i" class="block">{{ line }}</span></p>
|
|
|
|
<!-- Filet couleur pleine largeur en bas (masqué en fond plein) -->
|
|
<div v-if="background.mode !== 'full'" class="absolute inset-x-0 bottom-0" :style="{ height: 'var(--fl-strip)', backgroundColor: strip }" />
|
|
</div>
|
|
</template>
|