mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-22 06:42:20 +02:00
feat(photo-editor): Ajax Image Editor client (Photoshop-parity tools)
Photoshop-parity editor client: layers, selection/masking, brushes,
smart filters, LUT import, Quick Edit, mobile gestures, workspaces,
batch operations, and the service-worker versioning (v978->v1006).
Source: vendor/photocraft (ad1cff44) photo-editor client files, merged
onto upstream/dev (a8c147b2). Conflict resolution documented in
pr_work/A_conflicts.md; editor files taken from vendor/photocraft
(upstream's editor is an earlier snapshot of the same feature).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c74f475679
commit
5fac693c1c
@@ -0,0 +1,72 @@
|
||||
// Makes a modal dialog draggable by its header, Photoshop-style.
|
||||
//
|
||||
// On the first grab the dialog is switched to `position:fixed` anchored at
|
||||
// its current on-screen box, so it follows the pointer independently of the
|
||||
// host modal's centering/transform layout. Dragging is clamped to the
|
||||
// viewport and ignores presses that start on interactive controls (buttons,
|
||||
// inputs, selects) so clicking Cancel/Create never becomes a drag.
|
||||
export function makeDraggable(dialog, handle) {
|
||||
if (!dialog || !handle || dialog.__geDraggable) return;
|
||||
dialog.__geDraggable = true;
|
||||
handle.classList.add('ge-draggable-handle');
|
||||
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let originLeft = 0;
|
||||
let originTop = 0;
|
||||
let dragging = false;
|
||||
|
||||
const onDown = (event) => {
|
||||
if (event.button != null && event.button !== 0) return;
|
||||
if (event.target?.closest?.('button, input, select, textarea, a, [role="button"]')) return;
|
||||
const rect = dialog.getBoundingClientRect();
|
||||
dialog.style.position = 'fixed';
|
||||
dialog.style.margin = '0';
|
||||
dialog.style.left = `${rect.left}px`;
|
||||
dialog.style.top = `${rect.top}px`;
|
||||
dialog.style.right = 'auto';
|
||||
dialog.style.bottom = 'auto';
|
||||
dialog.style.transform = 'none';
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
originLeft = rect.left;
|
||||
originTop = rect.top;
|
||||
dragging = true;
|
||||
handle.setPointerCapture?.(event.pointerId);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const onMove = (event) => {
|
||||
if (!dragging) return;
|
||||
const width = dialog.offsetWidth;
|
||||
const height = dialog.offsetHeight;
|
||||
const left = Math.max(0, Math.min(window.innerWidth - width, originLeft + (event.clientX - startX)));
|
||||
const top = Math.max(0, Math.min(window.innerHeight - height, originTop + (event.clientY - startY)));
|
||||
dialog.style.left = `${left}px`;
|
||||
dialog.style.top = `${top}px`;
|
||||
};
|
||||
|
||||
const onUp = (event) => {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
handle.releasePointerCapture?.(event.pointerId);
|
||||
};
|
||||
|
||||
handle.addEventListener('pointerdown', onDown);
|
||||
handle.addEventListener('pointermove', onMove);
|
||||
handle.addEventListener('pointerup', onUp);
|
||||
handle.addEventListener('pointercancel', onUp);
|
||||
}
|
||||
|
||||
// Clears the inline positioning set by makeDraggable so the next open
|
||||
// re-centers the dialog inside its host modal.
|
||||
export function resetDraggable(dialog) {
|
||||
if (!dialog) return;
|
||||
dialog.style.position = '';
|
||||
dialog.style.margin = '';
|
||||
dialog.style.left = '';
|
||||
dialog.style.top = '';
|
||||
dialog.style.right = '';
|
||||
dialog.style.bottom = '';
|
||||
dialog.style.transform = '';
|
||||
}
|
||||
Reference in New Issue
Block a user