Skip to content

Instantly share code, notes, and snippets.

@hbouhadji
Last active February 1, 2025 06:43
Show Gist options
  • Save hbouhadji/0562f0c578d4acf6e977d89071e47998 to your computer and use it in GitHub Desktop.
Save hbouhadji/0562f0c578d4acf6e977d89071e47998 to your computer and use it in GitHub Desktop.
(function() {
'use strict';
class ConfigurablePanel {
constructor() {
this.position = 'right';
this.isDragging = false;
this.isResizing = false;
this.offset = { x: 0, y: 0 };
this.createPanel();
this.attachEvents();
}
createPanel() {
// Création du container pour le shadow DOM
this.container = document.createElement('div');
document.body.appendChild(this.container);
// Création du shadow DOM
this.shadow = this.container.attachShadow({ mode: 'open' });
// Styles pour le panel
const style = document.createElement('style');
style.textContent = `
.panel {
position: fixed;
width: 300px;
height: 400px;
background: #1e1e1e;
border: 1px solid #333;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
z-index: 9999;
color: #e0e0e0;
}
.panel.detached {
position: fixed;
}
.panel-header {
padding: 10px;
background: #2d2d2d;
cursor: move;
display: flex;
justify-content: space-between;
align-items: center;
min-height: 40px;
box-sizing: border-box;
border-bottom: 1px solid #404040;
}
.panel-content {
flex-grow: 1;
overflow: auto;
padding: 10px;
}
.position-controls button {
margin: 0 5px;
padding: 2px 5px;
background: #404040;
color: #e0e0e0;
border: 1px solid #555;
cursor: pointer;
}
.position-controls button:hover {
background: #505050;
}
.close-button {
background: #404040;
color: #e0e0e0;
border: 1px solid #555;
cursor: pointer;
}
.close-button:hover {
background: #505050;
}
.resize-handle {
position: absolute;
width: 20px;
height: 20px;
cursor: se-resize;
background: linear-gradient(135deg, transparent 50%, #404040 50%);
transition: background 0.2s ease;
}
.resize-handle:hover {
background: linear-gradient(135deg, transparent 50%, #505050 50%);
}
.panel.left .resize-handle {
right: 0;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 60px;
cursor: ew-resize;
background: linear-gradient(90deg, transparent 0%, #404040 100%);
}
.panel.left .resize-handle:hover {
background: linear-gradient(90deg, transparent 0%, #505050 100%);
}
.panel.right .resize-handle {
left: 0;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 60px;
cursor: ew-resize;
background: linear-gradient(270deg, transparent 0%, #404040 100%);
}
.panel.right .resize-handle:hover {
background: linear-gradient(270deg, transparent 0%, #505050 100%);
}
.panel.top .resize-handle {
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 8px;
cursor: ns-resize;
background: linear-gradient(180deg, transparent 0%, #404040 100%);
}
.panel.top .resize-handle:hover {
background: linear-gradient(180deg, transparent 0%, #505050 100%);
}
.panel.bottom .resize-handle {
top: 0;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 8px;
cursor: ns-resize;
background: linear-gradient(0deg, transparent 0%, #404040 100%);
}
.panel.bottom .resize-handle:hover {
background: linear-gradient(0deg, transparent 0%, #505050 100%);
}
.panel.detached .resize-handle {
bottom: 0;
right: 0;
width: 20px;
height: 20px;
transform: none;
cursor: se-resize;
background: linear-gradient(135deg, transparent 50%, #404040 50%);
}
.panel.detached .resize-handle:hover {
background: linear-gradient(135deg, transparent 50%, #505050 50%);
}
.panel.left {
left: 0;
right: auto;
top: 0;
height: 100vh !important;
}
.panel.right {
right: 0;
left: auto;
top: 0;
height: 100vh !important;
}
.panel.top {
top: 0;
bottom: auto;
left: 0;
width: 100vw !important;
height: 200px;
}
.panel.bottom {
bottom: 0;
top: auto;
left: 0;
width: 100vw !important;
height: 200px;
}
`;
// Création du panel
const panel = document.createElement('div');
panel.className = 'panel';
panel.innerHTML = `
<div class="panel-header">
<div class="position-controls">
<button data-position="left">←</button>
<button data-position="right">→</button>
<button data-position="top">↑</button>
<button data-position="bottom">↓</button>
<button data-position="detach">⇱</button>
</div>
<button class="close-button">×</button>
</div>
<div class="panel-content">
Contenu du panel
</div>
<div class="resize-handle"></div>
`;
this.shadow.appendChild(style);
this.shadow.appendChild(panel);
this.panel = panel;
// Stocker la référence à la poignée de redimensionnement
this.resizeHandle = this.shadow.querySelector('.resize-handle');
this.setPosition('right');
}
attachEvents() {
const header = this.shadow.querySelector('.panel-header');
const closeButton = this.shadow.querySelector('.close-button');
const positionButtons = this.shadow.querySelectorAll('.position-controls button');
// Gestion du drag
header.addEventListener('mousedown', (e) => {
if (e.target === header) {
this.isDragging = true;
const rect = this.panel.getBoundingClientRect();
this.offset = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
});
// Gestion du resize
this.resizeHandle.addEventListener('mousedown', (e) => {
this.isResizing = true;
e.preventDefault();
});
// Gestion des événements de la souris
document.addEventListener('mousemove', (e) => {
if (this.isDragging && this.panel.classList.contains('detached')) {
this.panel.style.left = `${e.clientX - this.offset.x}px`;
this.panel.style.top = `${e.clientY - this.offset.y}px`;
}
if (this.isResizing) {
const rect = this.panel.getBoundingClientRect();
if (this.position === 'left' || this.position === 'right') {
const newWidth = this.position === 'left' ?
e.clientX - rect.left :
rect.right - e.clientX;
this.panel.style.width = `${newWidth}px`;
this.adjustBodyMargins(`${newWidth}px`);
} else if (this.position === 'top' || this.position === 'bottom') {
const newHeight = this.position === 'top' ?
e.clientY - rect.top :
rect.bottom - e.clientY;
this.panel.style.height = `${newHeight}px`;
this.adjustBodyMargins(`${newHeight}px`);
} else if (this.position === 'detach') {
this.panel.style.width = `${e.clientX - rect.left}px`;
this.panel.style.height = `${e.clientY - rect.top}px`;
}
}
});
document.addEventListener('mouseup', () => {
this.isDragging = false;
this.isResizing = false;
});
// Gestion des positions
positionButtons.forEach(button => {
button.addEventListener('click', () => {
const position = button.dataset.position;
this.setPosition(position);
});
});
// Fermeture du panel
closeButton.addEventListener('click', () => {
this.container.remove();
this.adjustBodyMargins('0px');
});
}
setPosition(position) {
// Réinitialiser toutes les positions et dimensions personnalisées
this.panel.style.left = '';
this.panel.style.right = '';
this.panel.style.top = '';
this.panel.style.bottom = '';
this.panel.style.width = '';
this.panel.style.height = '';
this.panel.className = 'panel';
this.position = position;
if (position === 'detach') {
this.panel.classList.add('detached');
// En mode détaché, on garde la dernière position mais on ajuste les marges
this.adjustBodyMargins('0px');
this.panel.style.width = '300px';
this.panel.style.height = '400px';
if (!this.panel.style.top && !this.panel.style.left) {
this.panel.style.top = '20px';
this.panel.style.right = '20px';
}
this.resizeHandle.style.display = 'block';
return;
}
this.panel.classList.add(position);
// Ajuster le body en fonction de la position
const panelSize = position === 'top' || position === 'bottom' ?
this.panel.offsetHeight :
this.panel.offsetWidth;
this.adjustBodyMargins(panelSize + 'px');
}
adjustBodyMargins(offset) {
const body = document.body;
// Réinitialiser toutes les marges et les styles computés
body.style.removeProperty('width');
body.style.removeProperty('margin-left');
body.style.removeProperty('margin-right');
body.style.removeProperty('margin-top');
body.style.removeProperty('margin-bottom');
body.style.removeProperty('height');
body.style.removeProperty('min-height');
// Appliquer la marge selon la position seulement si un offset est spécifié
if (offset !== '0px') {
const computedStyle = window.getComputedStyle(document.body);
offset = parseFloat(offset);
let newOffset;
switch (this.position) {
case 'left':
body.style.marginLeft = `${offset}px`;
body.style.width = `calc(100% - ${offset}px)`;
// newOffset = offset / 2 + parseFloat(computedStyle.marginLeft);
// body.style.marginLeft = `${newOffset}px`;
break;
case 'right':
// body.style.marginRight = (parseFloat(offset) + parseFloat(computedStyle.marginRight)) + 'px';
// margin-right: calc(608px + 150px);
// console.log(`${parseFloat(offset)/2 + parseFloat(computedStyle.marginRight)}px`);
body.style.width = `calc(100% - ${offset}px)`;
// newOffset = offset / 2 + parseFloat(computedStyle.marginRight);
// body.style.marginRight = `${newOffset}px`;
break;
case 'top':
body.style.marginTop = `${offset}px`;
body.style.height = `calc(100% - ${offset}px)`;
// const mt = parseFloat(computedStyle.marginTop);
// newOffset = Math.max(offset / 2 + mt, offset);
// body.style.marginTop = `${newOffset}px`;
break;
case 'bottom':
// body.style.marginBottom = offset;
body.style.height = `calc(100% - ${offset}px)`;
// const mb = parseFloat(computedStyle.marginBottom);
// newOffset = Math.max(offset / 2 + mb, offset);
// body.style.marginBottom = `${newOffset}px`;
body.style.minHeight = '0px';
break;
}
}
}
}
// Création d'un bouton pour ouvrir le panel
const openButton = document.createElement('button');
openButton.textContent = 'Ouvrir Panel';
openButton.style.position = 'fixed';
openButton.style.top = '10px';
openButton.style.right = '10px';
openButton.style.zIndex = '9998';
document.body.appendChild(openButton);
openButton.addEventListener('click', () => {
new ConfigurablePanel();
openButton.remove();
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment