(node, id)
| 4521 | |
| 4522 | |
| 4523 | function enableDrag(node, id) { |
| 4524 | let isDragging = false; |
| 4525 | let startX = 0; |
| 4526 | let startY = 0; |
| 4527 | let origX = 0; |
| 4528 | let origY = 0; |
| 4529 | let dragIds = []; |
| 4530 | let dragOrig = new Map(); // id -> {x,y} |
| 4531 | |
| 4532 | node.addEventListener("mousedown", (e) => { |
| 4533 | if (e.button !== 0) return; |
| 4534 | if (e.target.classList.contains("resize-handle") || e.target.classList.contains("rotate-handle")) { |
| 4535 | return; |
| 4536 | } |
| 4537 | const el0 = elements.find((el) => el.id === id); |
| 4538 | if (!el0 || el0.locked || el0.hidden) return; |
| 4539 | e.preventDefault(); |
| 4540 | // If the clicked element isn't part of the current multi-selection, select it. |
| 4541 | if (!(selectedIds && selectedIds.size > 1 && selectedIds.has(id))) { |
| 4542 | selectElement(id, false); |
| 4543 | } |
| 4544 | isDragging = true; |
| 4545 | startX = e.clientX; |
| 4546 | startY = e.clientY; |
| 4547 | |
| 4548 | const el = elements.find((el) => el.id === id); |
| 4549 | if (!el) return; |
| 4550 | origX = el.x; |
| 4551 | origY = el.y; |
| 4552 | |
| 4553 | // Multi-drag: move all selected, unlocked, visible elements. |
| 4554 | dragIds = (selectedIds && selectedIds.size ? Array.from(selectedIds) : [id]) |
| 4555 | .filter((sid) => { |
| 4556 | const s = elements.find((x) => x.id === sid); |
| 4557 | return s && !s.locked && !s.hidden; |
| 4558 | }); |
| 4559 | if (!dragIds.includes(id)) dragIds.push(id); |
| 4560 | dragOrig = new Map(); |
| 4561 | dragIds.forEach((sid) => { |
| 4562 | const s = elements.find((x) => x.id === sid); |
| 4563 | if (s) dragOrig.set(sid, { x: s.x, y: s.y }); |
| 4564 | }); |
| 4565 | |
| 4566 | // Add dragging class for visual feedback |
| 4567 | node.classList.add('dragging'); |
| 4568 | preview.classList.add('manipulating'); |
| 4569 | |
| 4570 | document.addEventListener("mousemove", onMouseMove); |
| 4571 | document.addEventListener("mouseup", onMouseUp); |
| 4572 | }); |
| 4573 | |
| 4574 | function onMouseMove(e) { |
| 4575 | if (!isDragging) return; |
| 4576 | const scale = parseFloat(preview.dataset.scale || "1"); |
| 4577 | const z = clamp(zoomFactor || 1, 0.5, 3); |
| 4578 | const effectiveScale = scale * z; |
| 4579 | let dx = (e.clientX - startX) / effectiveScale; |
| 4580 | let dy = (e.clientY - startY) / effectiveScale; |
no test coverage detected