183 lines
6.3 KiB
HTML
183 lines
6.3 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;overflow:hidden}
|
|
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:100%;
|
|
height:100%;
|
|
background:#fff;
|
|
box-shadow:0 8px 30px rgba(2,6,23,.12);
|
|
border-radius:0;
|
|
overflow:hidden;
|
|
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>
|