client / static /js /resizer.js
P01yH3dr0n's picture
launch
774fe36
Raw
History Blame Contribute Delete
6.28 kB
/* ===== Panel Resizer (Universal) ===== */
window.NAIResizer = {
instances: [],
create(containerId, leftSel, rightSel, opts = {}) {
const container = document.querySelector(containerId);
if (!container) return null;
const left = container.querySelector(leftSel);
const right = container.querySelector(rightSel);
if (!left || !right) return null;
left.style.minHeight = '0';
right.style.minHeight = '0';
const minLeft = opts.minLeft ?? 200;
const minRight = opts.minRight ?? 200;
const storeKey = opts.storeKey || containerId;
// Create resizer element
const resizer = document.createElement('div');
resizer.className = 'panel-resizer';
resizer.style.flexShrink = '0';
// Insert between left and right
right.parentNode.insertBefore(resizer, right);
const state = {
container, left, right, resizer,
minLeft, minRight, storeKey,
savedSize: null,
dragging: false,
};
// Detect orientation
const isVertical = () => {
const style = getComputedStyle(container);
return style.flexDirection === 'column' || style.flexDirection === 'column-reverse';
};
// Apply orientation class
const updateOrientation = () => {
const vert = isVertical();
resizer.classList.toggle('vertical', vert);
};
// Observe container for flex-direction changes (responsive breakpoints)
let prevVertical = isVertical();
let resizeTimer;
const resizeObserver = new ResizeObserver(() => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
const nowVertical = isVertical();
if (nowVertical !== prevVertical) {
prevVertical = nowVertical;
this._resetSizes(state);
updateOrientation();
}
}, 100);
const nowVertical = isVertical();
if (nowVertical !== prevVertical) {
prevVertical = nowVertical;
this._resetSizes(state);
updateOrientation();
}
});
resizeObserver.observe(container);
updateOrientation();
// ── Drag logic ──
let startPos, startSize;
const onDown = (e) => {
e.preventDefault();
state.dragging = true;
const vert = isVertical();
startPos = vert
? (e.touches ? e.touches[0].clientY : e.clientY)
: (e.touches ? e.touches[0].clientX : e.clientX);
startSize = vert
? left.getBoundingClientRect().height
: left.getBoundingClientRect().width;
resizer.classList.add('active');
document.body.style.cursor = vert ? 'row-resize' : 'col-resize';
document.body.style.userSelect = 'none';
};
const onMove = (e) => {
if (!state.dragging) return;
const vert = isVertical();
const clientPos = vert
? (e.touches ? e.touches[0].clientY : e.clientY)
: (e.touches ? e.touches[0].clientX : e.clientX);
const delta = clientPos - startPos;
const resizerSize = vert ? resizer.getBoundingClientRect().height : resizer.getBoundingClientRect().width;
const containerSize = vert
? container.getBoundingClientRect().height - resizerSize
: container.getBoundingClientRect().width - resizerSize;
const newSize = Math.max(minLeft, Math.min(containerSize - minRight, startSize + delta));
const prop = vert ? 'height' : 'width';
const minProp = vert ? 'minHeight' : 'minWidth';
const maxProp = vert ? 'maxHeight' : 'maxWidth';
left.style[prop] = newSize + 'px';
left.style[minProp] = newSize + 'px';
left.style[maxProp] = newSize + 'px';
left.style.flex = 'none';
right.style.flex = '1';
};
const onUp = () => {
if (!state.dragging) return;
state.dragging = false;
resizer.classList.remove('active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
resizer.addEventListener('mousedown', onDown);
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
resizer.addEventListener('touchstart', onDown, { passive: false });
document.addEventListener('touchmove', onMove, { passive: false });
document.addEventListener('touchend', onUp);
// Double click to reset
resizer.addEventListener('dblclick', () => {
this._resetSizes(state);
});
updateOrientation();
this.instances.push(state);
return state;
},
_resetSizes(state) {
const props = ['width', 'minWidth', 'maxWidth', 'height', 'minHeight', 'maxHeight',
'flex', 'overflow', 'padding', 'border'];
for (const p of props) {
state.left.style[p] = '';
state.right.style[p] = '';
}
},
init() {
this.create('.gen-layout', '.gen-params', '.gen-result', {
minLeft: 280, minRight: 200, storeKey: 'gen',
});
this.create('.dt-layout', '.dt-left', '.dt-right', {
minLeft: 250, minRight: 200, storeKey: 'dt',
});
this.create('.pi-layout', '.pi-left', '.pi-right', {
minLeft: 200, minRight: 200, storeKey: 'pi',
});
this.create('.tagger-layout', '.tagger-left', '.tagger-right', {
minLeft: 250, minRight: 200, storeKey: 'tagger',
});
this.create('.history-layout', '.history-left', '.history-right', {
minLeft: 200, minRight: 250, storeKey: 'history',
});
// Cloud: between main and detail panel
this.create('.cloud-layout', '.cloud-main', '.cloud-detail-panel', {
minLeft: 200, minRight: 250, storeKey: 'cloud',
});
},
};