(members: DomEditSelection[])
| 44 | // `left_new = left_old - W.left` operates in — GSAP x/y and offset vars are |
| 45 | // transform deltas and stay correct without adjustment. |
| 46 | function computeGroupGeometry(members: DomEditSelection[]): GroupGeometry { |
| 47 | const boxes = members.map((m) => ({ |
| 48 | target: buildDomEditPatchTarget(m), |
| 49 | left: m.element.offsetLeft, |
| 50 | top: m.element.offsetTop, |
| 51 | right: m.element.offsetLeft + m.element.offsetWidth, |
| 52 | bottom: m.element.offsetTop + m.element.offsetHeight, |
| 53 | })); |
| 54 | const left = Math.min(...boxes.map((b) => b.left)); |
| 55 | const top = Math.min(...boxes.map((b) => b.top)); |
| 56 | const width = Math.max(...boxes.map((b) => b.right)) - left; |
| 57 | const height = Math.max(...boxes.map((b) => b.bottom)) - top; |
| 58 | return { |
| 59 | bbox: { left, top, width, height }, |
| 60 | targets: boxes.map((b) => b.target), |
| 61 | rebases: boxes.map((b) => ({ target: b.target, left: b.left - left, top: b.top - top })), |
| 62 | }; |
| 63 | } |
| 64 | |
| 65 | // Shared read → mutate-route → save-with-history → reload pipeline for both |
| 66 | // wrap (group) and unwrap (ungroup). Mirrors the structural-mutation pattern in |
no test coverage detected