diff --git a/components/CartePensees.vue b/components/CartePensees.vue index 3123994..2dc755b 100644 --- a/components/CartePensees.vue +++ b/components/CartePensees.vue @@ -166,11 +166,18 @@ async function initGraph() { }) // ---- SIMULATION D3 (auteurs) ---- - const auteurNodes: any[] = props.data.auteurs.map(a => ({ - id: a.id, type: 'auteur', nom: a.nom, dates: a.dates, bio_courte: a.bio_courte, - ecole_principale: a.ecole_principale, - color: ecoleMap.get(a.ecole_principale)?.color ?? '#888', r: 11, - })) + // Pre-positionner chaque auteur pres de son ecole + jitter aleatoire pour eviter le rush initial vers la droite + const auteurNodes: any[] = props.data.auteurs.map(a => { + const ecole = ecoleMap.get(a.ecole_principale) + const jitter = () => (Math.random() - 0.5) * 80 + return { + id: a.id, type: 'auteur', nom: a.nom, dates: a.dates, bio_courte: a.bio_courte, + ecole_principale: a.ecole_principale, + color: ecole?.color ?? '#888', r: 11, + x: W * (ecole?.x_hint ?? 0.5) + jitter(), + y: H * (ecole?.y_hint ?? 0.5) + jitter(), + } + }) // Liens appartenance auteur -> ecole (vers centroid fixe) const links: any[] = [] diff --git a/pages/media.vue b/pages/media.vue index 789c450..6fc597c 100644 --- a/pages/media.vue +++ b/pages/media.vue @@ -24,6 +24,7 @@ layoutMode === 'carte-full' ? 'carte-full' : '', layoutMode === 'chatbot-full' ? 'carte-hidden' : '', ]" + :style="layoutMode === 'split' ? { flexBasis: carteFlexBasis } : {}" > Carte plein ecran + - + + + +
+
@@ -86,6 +97,7 @@ layoutMode === 'chatbot-full' ? 'chatbot-full-mode' : '', layoutMode === 'carte-full' ? 'chatbot-hidden' : '', ]" + :style="layoutMode === 'split' ? { flexBasis: chatbotFlexBasis } : {}" > @@ -116,6 +128,8 @@ interface PenseesData { meta: any; ecoles: EcoleData[]; auteurs: AuteurData[] } type LayoutMode = 'split' | 'carte-full' | 'chatbot-full' const STORAGE_KEY = 'media-layout-mode' +const SPLIT_RATIO_KEY = 'media-split-ratio' +const DEFAULT_SPLIT_RATIO = 0.66 const ficheOpen = ref(false) const ficheAuteurId = ref(null) @@ -124,8 +138,45 @@ const penseesData = ref(null) const layoutMode = ref('split') const cartePenseesRef = ref<{ triggerResize: () => void } | null>(null) +// Ratio de la carte vs chatbot en mode split (0.2 a 0.8) +const splitRatio = ref(DEFAULT_SPLIT_RATIO) +const carteFlexBasis = computed(() => `${splitRatio.value * 100}%`) +const chatbotFlexBasis = computed(() => `${(1 - splitRatio.value) * 100}%`) + const corpusCount = computed(() => penseesData.value?.auteurs.length ?? 0) +// Logique poignee draggable +let dragStartY = 0 +let dragStartRatio = DEFAULT_SPLIT_RATIO +let containerHeight = 0 + +function onHandleMousedown(e: MouseEvent) { + dragStartY = e.clientY + dragStartRatio = splitRatio.value + // Hauteur du layout-container (carte + handle + chatbot) + const container = (e.target as HTMLElement)?.closest('.layout-container') as HTMLElement | null + containerHeight = container ? container.clientHeight : window.innerHeight + + window.addEventListener('mousemove', onHandleMousemove) + window.addEventListener('mouseup', onHandleMouseup) +} + +function onHandleMousemove(e: MouseEvent) { + const delta = e.clientY - dragStartY + const newRatio = dragStartRatio + delta / containerHeight + splitRatio.value = Math.min(0.80, Math.max(0.20, newRatio)) +} + +function onHandleMouseup() { + window.removeEventListener('mousemove', onHandleMousemove) + window.removeEventListener('mouseup', onHandleMouseup) + if (typeof window !== 'undefined') { + localStorage.setItem(SPLIT_RATIO_KEY, String(splitRatio.value)) + } + // Notifier D3 du resize apres relachement + cartePenseesRef.value?.triggerResize() +} + onMounted(async () => { // Restaurer le mode de layout depuis localStorage if (typeof window !== 'undefined') { @@ -133,6 +184,10 @@ onMounted(async () => { if (saved && ['split', 'carte-full', 'chatbot-full'].includes(saved)) { layoutMode.value = saved } + const savedRatio = parseFloat(localStorage.getItem(SPLIT_RATIO_KEY) ?? '') + if (!isNaN(savedRatio) && savedRatio >= 0.20 && savedRatio <= 0.80) { + splitRatio.value = savedRatio + } } try { penseesData.value = await $fetch('/data/auteurs-pensees.json') @@ -209,11 +264,11 @@ useHead({ title: 'AEP - Media - Carte FRACAS Bonpote' }) .carte-slot { overflow: hidden; position: relative; - transition: flex-basis 0.3s ease, height 0.3s ease, opacity 0.2s ease; + transition: opacity 0.2s ease; } .carte-split { - flex: 2 1 0; + flex: 0 0 66%; min-height: 0; opacity: 1; } @@ -270,26 +325,55 @@ useHead({ title: 'AEP - Media - Carte FRACAS Bonpote' }) border-color: var(--nav-primary); } -.toggle-btn-reset { - margin-left: auto; - background: var(--nav-surface); - color: var(--nav-text); +/* --- Poignee draggable entre carte et chatbot --- */ +.split-handle { + flex-shrink: 0; + height: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: row-resize; + background: transparent; + position: relative; + z-index: 10; + user-select: none; } -.toggle-btn-reset:hover { - background: var(--nav-bg-alt); +.split-handle:hover { + background: rgba(180, 170, 160, 0.18); +} + +.split-handle-grip { + display: block; + width: 32px; + height: 4px; + border-radius: 2px; + background: repeating-linear-gradient( + to bottom, + rgba(160, 150, 140, 0.55) 0px, + rgba(160, 150, 140, 0.55) 1px, + transparent 1px, + transparent 3px + ); +} + +/* Masquer la poignee sur mobile (ratio fixe) */ +@media (max-width: 767px) { + .split-handle { + display: none; + } } /* --- Slot chatbot --- */ .chatbot-slot { overflow: hidden; position: relative; - transition: flex-basis 0.3s ease, height 0.3s ease, opacity 0.2s ease; + transition: opacity 0.2s ease; border-top: 1px solid rgba(180, 170, 160, 0.28); } .chatbot-split { - flex: 1 1 0; + flex: 0 0 34%; min-height: 0; opacity: 1; }