Files
js/panel_resize.html
2025-11-26 22:34:43 +01:00

186 lines
6.4 KiB
HTML

<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Page une-colonne redimensionnable</title>
<style>
:root{
--min-panel: 40px;
--handle-size: 8px;
--bg-top: #2b6cb0;
--bg-middle: #edf2f7;
--bg-bottom: #2f855a;
}
html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial}
body{display:flex;align-items:center;justify-content:center;background:#f7fafc;padding:18px}
/* zone redimensionnable globale (vous pouvez la glisser depuis le coin) */
#app{
width:900px;
height:600px;
max-width:100%;
max-height:100%;
background:#fff;
box-shadow:0 8px 30px rgba(2,6,23,.12);
border-radius:10px;
overflow:hidden; /* les panneaux gèrent leur propre overflow */
resize:both; /* autorise redimension globale par coin */
display:flex;
flex-direction:column;
}
/* panneaux */
.panel{
min-height:var(--min-panel);
overflow:auto;
display:flex;
align-items:center;
justify-content:center;
padding:14px;
box-sizing:border-box;
}
header{background:var(--bg-top);color:#fff}
main{background:var(--bg-middle);color:#1a202c;flex:1}
footer{background:var(--bg-bottom);color:#fff}
/* handle horizontal (séparateur entre panneaux) */
.resizer{
height:var(--handle-size);
background:linear-gradient(90deg, rgba(0,0,0,0.08), rgba(0,0,0,0.02));
cursor:row-resize;
position:relative;
z-index:10;
}
.resizer::after{
content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
width:40px;height:4px;border-radius:4px;background:rgba(0,0,0,0.12)
}
/* petits controles d'aide */
.hint{font-size:13px;opacity:.9}
.controls{position:absolute;right:10px;top:8px;font-size:12px}
.panel .title{font-weight:700}
/* styles adaptatifs pour petits écrans */
@media (max-width:520px){
#app{width:100%;height:80vh;border-radius:6px}
}
</style>
</head>
<body>
<div id="app" role="application">
<header class="panel" id="topPanel" style="height:120px;">
<div>
<div class="title">Bandeau du haut</div>
<div class="hint">Glissez la barre grise sous ce bandeau pour redimensionner verticalement.</div>
</div>
</header>
<div class="resizer" data-target="top"></div>
<main class="panel" id="centerPanel" style="height:calc(100% - 240px);">
<div>
<div class="title">Conteneur central</div>
<div class="hint">Ce conteneur prend l'espace restant. Vous pouvez aussi redimensionner la fenêtre par le coin (resize).</div>
</div>
</main>
<div class="resizer" data-target="bottom"></div>
<footer class="panel" id="bottomPanel" style="height:120px;">
<div>
<div class="title">Bandeau du bas</div>
<div class="hint">Glissez la barre grise au-dessus pour modifier sa hauteur.</div>
</div>
</footer>
</div>
<script>
// Gestion du drag pour les séparateurs horizontaux
(function(){
const app = document.getElementById('app');
const topPanel = document.getElementById('topPanel');
const centerPanel = document.getElementById('centerPanel');
const bottomPanel = document.getElementById('bottomPanel');
const min = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--min-panel')) || 40;
let dragging = null;
let startY = 0;
let startTopH = 0;
let startBottomH = 0;
function onDown(e){
e.preventDefault();
dragging = this.getAttribute('data-target');
startY = e.touches ? e.touches[0].clientY : e.clientY;
startTopH = topPanel.getBoundingClientRect().height;
startBottomH = bottomPanel.getBoundingClientRect().height;
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
document.addEventListener('touchmove', onMove, {passive:false});
document.addEventListener('touchend', onUp);
app.style.userSelect = 'none';
}
function onMove(e){
if(!dragging) return;
e.preventDefault();
const y = e.touches ? e.touches[0].clientY : e.clientY;
const dy = y - startY;
if(dragging === 'top'){
// modifier hauteur top et center
let newTop = startTopH + dy;
let newCenter = centerPanel.getBoundingClientRect().height - dy;
if(newTop < min) newTop = min;
if(newCenter < min) newTop = startTopH + (centerPanel.getBoundingClientRect().height - min);
topPanel.style.height = newTop + 'px';
} else if(dragging === 'bottom'){
// modifier hauteur bottom et center inversé
let newBottom = startBottomH - dy; // parce que on déplace la ligne entre center et bottom
let newCenter = centerPanel.getBoundingClientRect().height + dy;
if(newBottom < min) newBottom = min;
if(newCenter < min) newBottom = startBottomH + (centerPanel.getBoundingClientRect().height - min);
bottomPanel.style.height = newBottom + 'px';
}
}
function onUp(){
dragging = null;
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
document.removeEventListener('touchmove', onMove);
document.removeEventListener('touchend', onUp);
app.style.userSelect = '';
}
document.querySelectorAll('.resizer').forEach(r => {
r.addEventListener('mousedown', onDown);
r.addEventListener('touchstart', onDown, {passive:false});
});
// Ajuster le panneau central automatiquement quand on redimensionne la boîte globale
const ro = new ResizeObserver(() => {
// calculer la hauteur restante pour center
const total = app.clientHeight;
const topH = topPanel.getBoundingClientRect().height;
const bottomH = bottomPanel.getBoundingClientRect().height;
const centerH = Math.max(min, total - topH - bottomH - 2 * parseInt(getComputedStyle(document.documentElement).getPropertyValue('--handle-size')));
centerPanel.style.height = centerH + 'px';
});
ro.observe(app);
// initialisation (force le calcul)
window.addEventListener('load', () => { ro.takeRecords(); });
})();
</script>
</body>
</html>