Files
MajoorWaldi-ComfyUI-Majoor-…/ui/features/viewer/floatingViewerPanel.ts
T

278 lines
10 KiB
TypeScript

import { MFV_RESIZE_EDGE_HIT_PX } from "./floatingViewerConstants.js";
export function getFloatingViewerResizeCursor(dir: string): string {
const map = {
n: "ns-resize",
s: "ns-resize",
e: "ew-resize",
w: "ew-resize",
ne: "nesw-resize",
nw: "nwse-resize",
se: "nwse-resize",
sw: "nesw-resize",
};
return (map as Record<string, any>)[dir] || "";
}
export function getFloatingViewerResizeDirectionFromPoint(clientX: number, clientY: number, rect: DOMRect): string {
if (!rect) return "";
const nearLeft = clientX <= rect.left + MFV_RESIZE_EDGE_HIT_PX;
const nearRight = clientX >= rect.right - MFV_RESIZE_EDGE_HIT_PX;
const nearTop = clientY <= rect.top + MFV_RESIZE_EDGE_HIT_PX;
const nearBottom = clientY >= rect.bottom - MFV_RESIZE_EDGE_HIT_PX;
if (nearTop && nearLeft) return "nw";
if (nearTop && nearRight) return "ne";
if (nearBottom && nearLeft) return "sw";
if (nearBottom && nearRight) return "se";
if (nearTop) return "n";
if (nearBottom) return "s";
if (nearLeft) return "w";
if (nearRight) return "e";
return "";
}
export function stopFloatingViewerEdgeResize(viewer: any): void {
if (!viewer.element) return;
try {
viewer._resizeWindowCleanup?.();
} catch (e: any) {
console.debug?.(e);
}
viewer._resizeWindowCleanup = null;
if (viewer._resizeState?.pointerId != null) {
try {
viewer.element.releasePointerCapture(viewer._resizeState.pointerId);
} catch (e: any) {
console.debug?.(e);
}
}
viewer._resizeState = null;
viewer.element.classList.remove("mjr-mfv--resizing");
viewer.element.style.cursor = "";
}
export function bindFloatingViewerPanelInteractions(viewer: any): void {
if (!viewer.element) return;
viewer._stopEdgeResize();
try {
viewer._panelAC?.abort();
} catch (e: any) {
console.debug?.(e);
}
viewer._panelAC = new AbortController();
viewer._initEdgeResize(viewer.element);
viewer._initDrag(viewer.element.querySelector(".mjr-mfv-header"));
}
export function initFloatingViewerEdgeResize(viewer: any, el: HTMLElement): void {
if (!el) return;
const resolveDir = (e: any) => {
if (!viewer.element || viewer._isPopped) return "";
const rect = viewer.element.getBoundingClientRect();
return viewer._getResizeDirectionFromPoint(e.clientX, e.clientY, rect);
};
const signal = viewer._panelAC?.signal;
let resizeAC: any = null;
const cleanupResizeWindowListeners = () => {
try {
resizeAC?.abort();
} catch (err: any) {
console.debug?.(err);
}
resizeAC = null;
viewer._resizeWindowCleanup = null;
};
const onPointerDown = (e: any) => {
if (e.button !== 0 || !viewer.element || viewer._isPopped) return;
try {
if (
e.target?.closest?.(
"button, input, textarea, select, a, [contenteditable='true'], .mjr-mfv-idrop, .mjr-mfv-pin-group",
)
) {
return;
}
} catch (err: any) {
console.debug?.(err);
}
const dir = resolveDir(e);
if (!dir) return;
e.preventDefault();
e.stopPropagation();
const rect = viewer.element.getBoundingClientRect();
const style = window.getComputedStyle(viewer.element);
const minWidth = Math.max(120, Number.parseFloat(style.minWidth) || 0);
const minHeight = Math.max(100, Number.parseFloat(style.minHeight) || 0);
viewer._resizeState = {
pointerId: e.pointerId,
dir,
startX: e.clientX,
startY: e.clientY,
startLeft: rect.left,
startTop: rect.top,
startWidth: rect.width,
startHeight: rect.height,
minWidth,
minHeight,
};
viewer.element.style.left = `${Math.round(rect.left)}px`;
viewer.element.style.top = `${Math.round(rect.top)}px`;
viewer.element.style.right = "auto";
viewer.element.style.bottom = "auto";
viewer.element.classList.add("mjr-mfv--resizing");
viewer.element.style.cursor = viewer._resizeCursorForDirection(dir);
try {
viewer.element.setPointerCapture(e.pointerId);
} catch (err: any) {
console.debug?.(err);
}
try {
cleanupResizeWindowListeners();
resizeAC = new AbortController();
viewer._resizeWindowCleanup = cleanupResizeWindowListeners;
const win = viewer.element?.ownerDocument?.defaultView || window;
win.addEventListener("pointermove", onPointerMove, { signal: resizeAC.signal });
win.addEventListener("pointerup", onPointerEnd, { signal: resizeAC.signal });
win.addEventListener("pointercancel", onPointerEnd, { signal: resizeAC.signal });
} catch (err: any) {
console.debug?.(err);
}
};
const onPointerMove = (e: any) => {
if (!viewer.element || viewer._isPopped) return;
const state = viewer._resizeState;
if (!state) {
const dir = resolveDir(e);
viewer.element.style.cursor = dir ? viewer._resizeCursorForDirection(dir) : "";
return;
}
if (state.pointerId !== e.pointerId) return;
const dx = e.clientX - state.startX;
const dy = e.clientY - state.startY;
let width = state.startWidth;
let height = state.startHeight;
let left = state.startLeft;
let top = state.startTop;
if (state.dir.includes("e")) width = state.startWidth + dx;
if (state.dir.includes("s")) height = state.startHeight + dy;
if (state.dir.includes("w")) {
width = state.startWidth - dx;
left = state.startLeft + dx;
}
if (state.dir.includes("n")) {
height = state.startHeight - dy;
top = state.startTop + dy;
}
if (width < state.minWidth) {
if (state.dir.includes("w")) left -= state.minWidth - width;
width = state.minWidth;
}
if (height < state.minHeight) {
if (state.dir.includes("n")) top -= state.minHeight - height;
height = state.minHeight;
}
width = Math.min(width, Math.max(state.minWidth, window.innerWidth));
height = Math.min(height, Math.max(state.minHeight, window.innerHeight));
left = Math.min(Math.max(0, left), Math.max(0, window.innerWidth - width));
top = Math.min(Math.max(0, top), Math.max(0, window.innerHeight - height));
viewer.element.style.width = `${Math.round(width)}px`;
viewer.element.style.height = `${Math.round(height)}px`;
viewer.element.style.left = `${Math.round(left)}px`;
viewer.element.style.top = `${Math.round(top)}px`;
viewer.element.style.right = "auto";
viewer.element.style.bottom = "auto";
};
const onPointerEnd = (e: any) => {
if (!viewer.element || !viewer._resizeState) return;
if (viewer._resizeState.pointerId !== e.pointerId) return;
const dir = resolveDir(e);
cleanupResizeWindowListeners();
viewer._stopEdgeResize();
if (dir) viewer.element.style.cursor = viewer._resizeCursorForDirection(dir);
};
el.addEventListener("pointerdown", onPointerDown, { capture: true, signal });
el.addEventListener("pointermove", onPointerMove, { signal });
el.addEventListener("pointerup", onPointerEnd, { signal });
el.addEventListener("pointercancel", onPointerEnd, { signal });
el.addEventListener(
"pointerleave",
() => {
if (!viewer._resizeState && viewer.element) viewer.element.style.cursor = "";
},
{ signal },
);
}
export function initFloatingViewerDrag(viewer: any, handle: HTMLElement): void {
if (!handle) return;
const signal = viewer._panelAC?.signal;
let dragAC: any = null;
handle.addEventListener(
"pointerdown",
(e) => {
if (e.button !== 0) return;
if ((e.target as Element).closest("button")) return;
if ((e.target as Element).closest("select")) return;
if (viewer._isPopped || !viewer.element) return;
const edgeDir = viewer._getResizeDirectionFromPoint(
e.clientX,
e.clientY,
viewer.element.getBoundingClientRect(),
);
if (edgeDir) return;
e.preventDefault();
handle.setPointerCapture(e.pointerId);
try {
dragAC?.abort();
} catch {}
dragAC = new AbortController();
const dragSig = dragAC.signal;
const el = viewer.element;
const rect = el.getBoundingClientRect();
const offX = e.clientX - rect.left;
const offY = e.clientY - rect.top;
const onMove = (me: any) => {
const x = Math.min(
window.innerWidth - el.offsetWidth,
Math.max(0, me.clientX - offX),
);
const y = Math.min(
window.innerHeight - el.offsetHeight,
Math.max(0, me.clientY - offY),
);
el.style.left = `${x}px`;
el.style.top = `${y}px`;
el.style.right = "auto";
el.style.bottom = "auto";
};
const onUp = () => {
try {
handle.releasePointerCapture?.(e.pointerId);
} catch (err: any) {
console.debug?.(err);
}
try {
dragAC?.abort();
} catch {}
};
const win = handle.ownerDocument?.defaultView || window;
win.addEventListener("pointermove", onMove, { signal: dragSig });
win.addEventListener("pointerup", onUp, { signal: dragSig });
win.addEventListener("pointercancel", onUp, { signal: dragSig });
},
signal ? { signal } : undefined,
);
}