script report
All checks were successful
Deploy / Deploy to Production (push) Successful in 11s

This commit is contained in:
Julie Blanc 2026-01-29 12:18:16 +01:00
parent dbf9097701
commit 9ead1c51f4
7 changed files with 384 additions and 125 deletions

View file

@ -2,7 +2,7 @@ body{
min-height: 100dvh;
min-height: 100vh;
width: 100vw;
overflow-x: hidden;
// overflow-x: hidden;
display: flex;
flex-direction: column;

View file

@ -2056,7 +2056,6 @@ body {
min-height: 100dvh;
min-height: 100vh;
width: 100vw;
overflow-x: hidden;
display: flex;
flex-direction: column;
}
@ -2464,9 +2463,9 @@ main .page__header .description-medium {
@media screen and (min-width: 768px) {
[data-template=report] .report__header {
margin-inline: auto;
margin-top: calc(var(--spacing) * 1);
max-width: var(--max-w-cards);
margin-bottom: calc(var(--spacing) * 2.5);
margin-top: calc(var(--spacing) * 3);
margin-bottom: calc(var(--spacing) * 4);
}
[data-template=report] .report__content {
display: grid;
@ -2474,10 +2473,19 @@ main .page__header .description-medium {
grid-gap: var(--padding-body);
}
[data-template=report] .report__content .report__txt {
grid-row: 1;
grid-column: 1;
max-width: var(--max-w-content);
}
[data-template=report] .report__content .report__medias {
background-color: yellow;
[data-template=report] .report__content #report__medias {
position: sticky;
top: calc(var(--header-h) + var(--padding-body));
align-self: start;
background-color: red;
grid-row: 1;
grid-column: 2;
border: var(--border-light);
min-height: 100px;
}
[data-template=report] #toggle-panel {
position: fixed;
@ -2566,7 +2574,6 @@ main .page__header .description-medium {
grid-row: 2;
grid-column: 1;
align-self: end;
font-size: var(--fs-small);
border-bottom: var(--border-light);
align-items: flex-start;
}
@ -2595,6 +2602,7 @@ main .page__header .description-medium {
.report__content .section-content {
margin-bottom: calc(var(--spacing) * 3);
border-bottom: 1px solid red;
}
.report__content .section-content:target {
padding-top: calc(var(--header-h) + var(--spacing) * 2);
@ -2627,7 +2635,7 @@ main .page__header .description-medium {
}
#report__aside {
--border-aside: var(--border-light);
--border-aside: 1px solid var(--color-txt);
}
#report__aside .panel__header {
display: flex;
@ -2656,10 +2664,10 @@ main .page__header .description-medium {
}
#report__aside .panel__header .close svg {
width: 10px;
fill: var(--grey-600);
fill: var(--color-txt);
}
#report__aside .panel__header:hover .close svg {
fill: var(--color-txt);
fill: var(--grey-200);
}
#report__aside .panel__content {
padding: var(--padding-body);
@ -2714,10 +2722,6 @@ main .page__header .description-medium {
#report {
--fig-outside: calc(var(--spacing)*2);
}
#report .container-figure {
width: 100%;
display: none;
}
#report .fig-simple {
height: calc(100vh - var(--header-h) - var(--spacing) * 2);
}

File diff suppressed because one or more lines are too long

View file

@ -56,10 +56,9 @@
.report__header {
margin-inline: auto;
margin-top: calc(var(--spacing)*1);
max-width: var(--max-w-cards);
margin-bottom: calc(var(--spacing)*2.5);
margin-top: calc(var(--spacing) * 3);
margin-bottom: calc(var(--spacing) * 4);
}
.report__content {
@ -69,11 +68,21 @@
.report__txt {
grid-row: 1;
grid-column: 1;
max-width: var(--max-w-content);
}
.report__medias {
background-color: yellow;
#report__medias {
position: sticky;
top: calc(var(--header-h) + var(--padding-body));
align-self: start;
background-color: red;
grid-row: 1;
grid-column: 2;
border: var(--border-light);
min-height: 100px;
}
}
@ -91,7 +100,7 @@
position: fixed;
top: var(--header-h);
left: var(--padding-body);
width: var(--panel-w);
width: var(--panel-w);
height: calc(100vh - var(--header-h));
z-index: calc(var(--z-header) + 200);
background-color: var(--color-bg);
@ -187,7 +196,7 @@
grid-row: 2;
grid-column: 1;
align-self: end;
font-size: var(--fs-small);
// font-size: var(--fs-small);
border-bottom: var(--border-light);
align-items: flex-start;
@ -232,6 +241,7 @@
.section-content {
margin-bottom: calc(var(--spacing)*3);
border-bottom: 1px solid red;
&:target{
padding-top: calc(var(--header-h) + var(--spacing)*2);
@media #{$small}{ padding-top: calc(var(--header-h) + var(--spacing)*0.5); }
@ -271,7 +281,7 @@
}
#report__aside {
--border-aside: var(--border-light);
--border-aside: 1px solid var(--color-txt);
.panel__header {
display: flex;
@ -303,7 +313,7 @@
svg {
width: 10px;
fill: var(--grey-600);
fill: var(--color-txt);
}
@ -313,7 +323,7 @@
&:hover {
.close svg {
fill: var(--color-txt);
fill: var(--grey-200);
}
}
@ -396,10 +406,10 @@
#report {
--fig-outside: calc(var(--spacing)*2);
.container-figure {
width: 100%;
display: none;
}
// .container-figure {
// width: 100%;
// display: none;
// }
.fig-simple {
height: calc(100vh - var(--header-h) - var(--spacing)*2);

178
assets/js/report.js Normal file
View file

@ -0,0 +1,178 @@
export function report(responsiveSmall) {
if (document.body.dataset.template === 'report') {
console.log('report');
// Ne fonctionne que pour les écrans plus grands que responsiveSmall
if (window.matchMedia(responsiveSmall).matches) {
return;
}
initMediaDisplay();
}
}
function initMediaDisplay() {
const reportMedias = document.querySelector('#report__medias');
if (!reportMedias) return;
// Calculer la hauteur depuis les variables CSS: calc(var(--header-h) + var(--padding-body))
const rootStyles = getComputedStyle(document.documentElement);
const headerH = rootStyles.getPropertyValue('--header-h').trim();
const paddingBody = rootStyles.getPropertyValue('--padding-body').trim();
// Convertir en pixels si nécessaire
const headerHPx = parseFloat(headerH);
const paddingBodyPx = parseFloat(paddingBody);
const totalOffset = headerHPx + paddingBodyPx;
const sections = document.querySelectorAll('.section-content');
const mediaElements = [];
let mediaCounter = 0;
// 1. Pour chaque section, traiter les .media
sections.forEach((section) => {
const medias = section.querySelectorAll('.media');
medias.forEach((media) => {
// Générer un ID unique si nécessaire
if (!media.id) {
media.id = `media-${mediaCounter++}`;
}
// Créer une ancre
const anchor = document.createElement('div');
anchor.className = 'media-anchor';
anchor.dataset.mediaId = media.id;
// Style visuel pour debug (carré rouge de 10px)
anchor.style.width = '10px';
anchor.style.height = '10px';
anchor.style.backgroundColor = 'red';
anchor.style.position = 'relative';
// Vérifier si le media est précédé d'un titre
let previousElement = media.previousElementSibling;
let insertBeforeElement = media;
// Si l'élément précédent est un titre (h1-h6), insérer l'ancre avant le titre
if (previousElement && /^H[1-6]$/.test(previousElement.tagName)) {
insertBeforeElement = previousElement;
}
// Insérer l'ancre
insertBeforeElement.parentNode.insertBefore(anchor, insertBeforeElement);
// Stocker la référence pour l'observer
mediaElements.push({
anchor: anchor,
media: media.cloneNode(true), // Cloner le media
originalMedia: media,
section: section
});
// Masquer le media original
media.style.display = 'none';
});
});
// 2. Fonction pour trouver et afficher le media le plus proche de la ligne de déclenchement
let currentMediaId = null;
function updateActiveMedia() {
// Trouver l'ancre qui est la plus proche de la ligne de déclenchement (totalOffset du haut)
let closestAnchor = null;
let closestDistance = Infinity;
mediaElements.forEach(({ anchor }) => {
const rect = anchor.getBoundingClientRect();
// Si l'ancre est au-dessus ou à la ligne de déclenchement
if (rect.top <= totalOffset) {
const distance = totalOffset - rect.top;
// Prendre celle qui vient juste de passer (la plus proche en dessous de la ligne)
if (distance < closestDistance) {
closestDistance = distance;
closestAnchor = anchor;
}
}
});
// Si on a trouvé une ancre
if (closestAnchor) {
const mediaId = closestAnchor.dataset.mediaId;
// Si c'est déjà le media affiché, ne rien faire
if (currentMediaId === mediaId) return;
// Trouver le media correspondant
const mediaData = mediaElements.find(m => m.anchor === closestAnchor);
if (mediaData) {
// Vider le conteneur
reportMedias.innerHTML = '';
// Ajouter le nouveau media
reportMedias.appendChild(mediaData.media.cloneNode(true));
currentMediaId = mediaId;
}
} else {
// Aucune ancre n'a encore franchi la ligne, vider le conteneur
if (currentMediaId !== null) {
reportMedias.innerHTML = '';
currentMediaId = null;
}
}
}
// 3. Écouter le scroll
let scrollTimeout;
window.addEventListener('scroll', () => {
// Throttle pour optimiser les performances
if (scrollTimeout) return;
scrollTimeout = setTimeout(() => {
updateActiveMedia();
scrollTimeout = null;
}, 10);
});
// Appeler une première fois au chargement
updateActiveMedia();
// 4. Gérer les sections sans media immédiat
// Observer aussi les sections elles-mêmes
const sectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const section = entry.target;
// Vérifier si cette section a un media juste après son premier titre
const firstTitle = section.querySelector('h1, h2, h3, h4, h5, h6');
if (firstTitle) {
let nextElement = firstTitle.nextElementSibling;
// Chercher le prochain élément qui n'est pas une ancre
while (nextElement && nextElement.classList.contains('media-anchor')) {
nextElement = nextElement.nextElementSibling;
}
// Si le prochain élément n'est pas un .media, vider #report__medias
if (!nextElement || !nextElement.classList.contains('media')) {
reportMedias.innerHTML = '';
currentMediaId = null;
}
}
}
});
}, {
root: null,
rootMargin: `-${totalOffset}px 0px 0px 0px`,
threshold: 0
});
// Observer toutes les sections
sections.forEach(section => {
sectionObserver.observe(section);
});
}

View file

@ -8,6 +8,8 @@ import { initHeroSlider } from './hero-slider.js';
import { playVideo } from './hero-video.js';
import { initDropdowns } from './dropdown.js';
import { initHomeInvestigationsSlider } from './home-investigations-slider.js';
import { report } from './report.js';
const responsiveMedium = 1080;
const responsiveSmall = 768;
@ -18,6 +20,9 @@ window.onload = async function () {
panelToggle(responsiveSmall);
themeToggle();
report(responsiveSmall);
tocMobile(responsiveSmall);
copyLink();
btnGroupMobile(responsiveSmall)