"""
Isolated test: empty Page + one floating, draggable Paper built as a JSComponent.
panel serve drag_test.py --show
Drag the ⠿ handle to move the bar. The toggle / stars / Submit stay clickable
because the drag only starts from the handle.
"""
import panel as pn
import panel_material_ui as pmui
from panel.custom import JSComponent, Children
pn.extension()
class FloatingPanel(JSComponent):
"""A fixed, draggable container. Whatever you pass as `objects` is slotted
in below a drag handle. Positioning is purely client-side."""
objects = Children()
_esm = """
export function render({ model, el }) {
// The component's own root element is the floating, draggable surface.
Object.assign(el.style, {
position: 'fixed', left: '50%', bottom: '24px',
transform: 'translateX(-50%)', zIndex: '1300',
display: 'flex', flexDirection: 'column', alignItems: 'center',
width: 'fit-content', height: 'fit-content',
padding: '6px 12px', borderRadius: '10px',
background: 'var(--mui-palette-background-paper, #fff)',
boxShadow: '0 6px 24px rgba(0,0,0,.25)', userSelect: 'none', cursor: 'grab',
});
// slotted Panel children (toggle / rating / button …)
for (const child of model.get_child('objects')) el.appendChild(child);
// drag from the panel background — clicks on the slotted widgets pass through
// (their target isn't `el` itself, so they stay interactive)
let on = false, sx, sy, baseL, baseT;
el.addEventListener('pointerdown', e => {
if (e.target !== el) return; // only the surface/padding drags
const r = el.getBoundingClientRect();
el.style.transform = 'none'; el.style.bottom = 'auto';
el.style.left = r.left + 'px'; el.style.top = r.top + 'px';
baseL = r.left; baseT = r.top; sx = e.clientX; sy = e.clientY;
on = true; el.setPointerCapture(e.pointerId); el.style.cursor = 'grabbing';
e.preventDefault();
});
el.addEventListener('pointermove', e => {
if (!on) return;
el.style.left = (baseL + e.clientX - sx) + 'px';
el.style.top = (baseT + e.clientY - sy) + 'px';
});
el.addEventListener('pointerup', () => { on = false; el.style.cursor = 'grab'; });
}
"""
toggle = pmui.ToggleIcon(icon="thumb_down", active_icon="thumb_up", margin=0)
rating = pmui.Rating(value=5, size="small", margin=0)
button = pmui.Button(label="Submit", size="small", margin=(0, 5, 5, 5))
controls = FloatingPanel(objects=[pmui.Row(toggle, rating), button])
pmui.Page(title="Drag test", main=[controls]).show()