ZDDC/mdedit/js/resizer.js
ZDDC c95f07966d feat(tools,build): in-flight HTML-tool reworks and build-infra updates
Bundles a stretch of in-progress work across the SPA tools so the
tree returns to a coherent shippable state ahead of cutting a new
zddc-server stable image:

- landing: substantial rework of the project picker (sortable/filterable
  table, presets refactor, ?projects= filter, ?v= channel propagation,
  loading/error states)
- archive: presets cleanup, source.js refactor, filtering/url-state
  alignment with the landing page
- mdedit: file-system module split, resizer, file-tree improvements,
  base/toc styling tweaks
- transmittal/classifier: small template touch-ups for shared chrome
- shared: build-lib.sh helpers, new favicon.svg
- bootstrap, build.sh: pick up the channel-aware install/track zip
  generation
- tests: new landing.spec.js, expanded archive/mdedit/build-label specs
- docs: CLAUDE.md picks up the zddc-server section and freshens the
  alpha-build exception note
- regenerated artifacts: install.zip, track-{alpha,beta,stable}.zip,
  *_alpha.html — these are produced by `sh build.sh` and per project
  convention are committed alongside the source changes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:52:27 -05:00

137 lines
4.5 KiB
JavaScript

/**
* Pane resizing functionality
*/
/**
* Make an element resizable by dragging its resizer
* @param {HTMLElement} resizer - The resizer element
* @param {HTMLElement} pane - The pane to resize
*/
function makeResizable(resizer, pane) {
const initialWidth = pane.offsetWidth;
let x = 0;
let paneWidth = initialWidth;
const mouseDownHandler = function (e) {
x = e.clientX;
paneWidth = pane.offsetWidth;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
resizer.classList.add('active');
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
};
const mouseMoveHandler = function (e) {
const dx = e.clientX - x;
const newWidth = Math.max(150, paneWidth + dx);
pane.style.width = `${newWidth}px`;
};
const mouseUpHandler = function () {
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
resizer.classList.remove('active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
resizer.addEventListener('mousedown', mouseDownHandler);
}
/**
* Make a horizontal split height-adjustable: the resizer drags the height
* of `topPane` while it remains a sibling of the bottom section inside `container`.
*
* @param {HTMLElement} resizer - The horizontal resizer between the panes
* @param {HTMLElement} topPane - The pane whose height is set
* @param {HTMLElement} container - The flex column containing both panes
*/
function makeHeightResizable(resizer, topPane, container) {
let y = 0;
let topHeight = 0;
let containerHeight = 0;
const mouseDownHandler = (e) => {
y = e.clientY;
topHeight = topPane.offsetHeight;
containerHeight = container.offsetHeight;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
resizer.classList.add('active');
document.body.style.cursor = 'row-resize';
document.body.style.userSelect = 'none';
};
const mouseMoveHandler = (e) => {
const dy = e.clientY - y;
// Reserve at least 80px for the bottom pane (TOC); cap top at containerHeight - 80.
const minTop = 60;
const maxTop = Math.max(minTop, containerHeight - 100);
const newHeight = Math.max(minTop, Math.min(maxTop, topHeight + dy));
topPane.style.height = `${newHeight}px`;
};
const mouseUpHandler = () => {
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
resizer.classList.remove('active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
resizer.addEventListener('mousedown', mouseDownHandler);
}
/**
* Initialize the file navigation pane resizer
*/
function initializeFileNavResizer() {
const fileNavResizer = document.querySelector('.pane-resizer[data-resizer-for="file-nav"]');
if (fileNavResizer && !fileNavResizer.hasAttribute('data-resizer-initialized')) {
fileNavResizer.setAttribute('data-resizer-initialized', 'true');
let x = 0;
let navWidth = 0;
const mouseDownHandler = function (e) {
x = e.clientX;
const navPane = document.getElementById('file-nav');
navWidth = navPane.getBoundingClientRect().width;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
fileNavResizer.classList.add('bg-blue-500');
};
const mouseMoveHandler = function (e) {
const dx = e.clientX - x;
const navPane = document.getElementById('file-nav');
const newWidth = navWidth + dx;
if (newWidth >= 200) {
navPane.style.width = `${newWidth}px`;
}
};
const mouseUpHandler = function () {
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
fileNavResizer.classList.remove('bg-blue-500');
};
fileNavResizer.addEventListener('mousedown', mouseDownHandler);
}
}