// Tweaks UI for parcours.html // Controls the vertical position of the criteria box in each of the // six profile sections, within the available height of its section. const { useEffect, useLayoutEffect } = React; const SECTIONS = [ { id: 'stagiaire', key: 'pos_stagiaire', label: 'I — Stagiaire IA' }, { id: 'interimaire', key: 'pos_interimaire', label: 'II — Intérimaire IA' }, { id: 'diagnostic', key: 'pos_diagnostic', label: 'III — Diagnostic (pivot)' }, { id: 'recrue', key: 'pos_recrue', label: 'IV — Recrue IA' }, { id: 'confirme', key: 'pos_confirme', label: 'V — Collaborateur Confirmé' }, { id: 'referent', key: 'pos_referent', label: 'VI — Orchestrateur IA' }, ]; function findColumns(sectionId) { const section = document.getElementById(sectionId); if (!section) return null; const grid = section.querySelector('.step__grid'); if (!grid) return null; const criteriaWrap = grid.querySelector('.step__criteria')?.closest('.step__grid > *'); if (!criteriaWrap) return null; const textCol = [...grid.children].find((c) => c !== criteriaWrap); if (!textCol) return null; return { grid, criteriaWrap, textCol }; } function usePositionSync(positions) { useLayoutEffect(() => { const cleanups = []; SECTIONS.forEach(({ id, key }) => { const cols = findColumns(id); if (!cols) return; const { grid, criteriaWrap, textCol } = cols; grid.style.alignItems = 'start'; criteriaWrap.style.transition = 'transform 120ms ease-out'; const apply = () => { const available = textCol.offsetHeight - criteriaWrap.offsetHeight; const pct = Math.max(0, Math.min(100, Number(positions[key]) || 0)); const offset = Math.max(0, available) * (pct / 100); criteriaWrap.style.transform = `translateY(${offset}px)`; }; apply(); const ro = new ResizeObserver(apply); ro.observe(textCol); ro.observe(criteriaWrap); window.addEventListener('resize', apply); cleanups.push(() => { ro.disconnect(); window.removeEventListener('resize', apply); }); }); return () => cleanups.forEach((fn) => fn()); }, [ positions.pos_stagiaire, positions.pos_interimaire, positions.pos_diagnostic, positions.pos_recrue, positions.pos_confirme, positions.pos_referent, ]); } function ParcoursTweaks() { const defaults = window.PARCOURS_TWEAK_DEFAULTS || {}; const [t, setTweak] = useTweaks(defaults); usePositionSync(t); return ( {SECTIONS.map((s) => ( setTweak(s.key, v)} /> ))} 0 % = haut · 100 % = bas. Glisse pour caler la boîte critères dans la hauteur de chaque section. ); } const __parcoursTweaksRoot = document.getElementById('tweaks-root'); if (__parcoursTweaksRoot) { ReactDOM.createRoot(__parcoursTweaksRoot).render(); }