Skip to content

Commit

Permalink
Merge pull request #4 from Print-one/feat/editor-rotation
Browse files Browse the repository at this point in the history
feat(PO-2381): rotated editor
  • Loading branch information
UnderKoen authored Nov 25, 2024
2 parents 8a8a1f2 + 5687e06 commit f6a5ebc
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 66 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@print-one/grapesjs",
"description": "Free and Open Source Web Builder Framework",
"version": "0.21.16",
"version": "0.21.17",
"author": "Print.one",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",
Expand Down
30 changes: 28 additions & 2 deletions src/canvas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import Frame from './model/Frame';
import { CanvasEvents, ToWorldOption } from './types';
import CanvasView, { FitViewportOptions } from './view/CanvasView';
import FrameView from './view/FrameView';
import { rotateCoordinate } from '../utils/Rotator';

export type CanvasEvent = `${CanvasEvents}`;

Expand Down Expand Up @@ -488,9 +489,26 @@ export default class CanvasModule extends Module<CanvasConfig> {
const zoom = this.getZoomDecimal();
const zoomOffset = 1 / zoom;

let y = (e.clientY + addTop - yOffset) * zoomOffset;
let x = (e.clientX + addLeft - xOffset) * zoomOffset;

const rotated = rotateCoordinate(
{
l: x,
t: y,
},
{
l: 0,
t: 0,
w: frame?.model?.width ?? 0,
h: frame?.model?.height ?? 0,
r: -this.getRotationAngle(),
}
);

return {
y: (e.clientY + addTop - yOffset) * zoomOffset,
x: (e.clientX + addLeft - xOffset) * zoomOffset,
y: rotated.t,
x: rotated.l,
};
}

Expand Down Expand Up @@ -596,6 +614,14 @@ export default class CanvasModule extends Module<CanvasConfig> {
return parseFloat(this.canvas.get('zoom'));
}

getRotationAngle() {
return this.canvas.get('rotationAngle');
}

setRotationAngle(value: number) {
this.canvas.set('rotationAngle', value);
}

/**
* Set canvas position coordinates
* @param {Number} x Horizontal position
Expand Down
1 change: 1 addition & 0 deletions src/canvas/model/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default class Canvas extends ModuleModel<CanvasModule> {
frames: [],
rulers: false,
zoom: 100,
rotationAngle: 0,
x: 0,
y: 0,
// Scripts to apply on all frames
Expand Down
50 changes: 45 additions & 5 deletions src/canvas/view/CanvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Frame from '../model/Frame';
import { GetBoxRectOptions, ToWorldOption } from '../types';
import FrameView from './FrameView';
import FramesView from './FramesView';
import { rotateCoordinate } from '../../utils/Rotator';

export interface MarginPaddingOffsets {
marginTop?: number;
Expand All @@ -36,6 +37,8 @@ export type ElementPosOpts = {
avoidFrameZoom?: boolean;
noScroll?: boolean;
nativeBoundingRect?: boolean;
avoidRotate?: boolean;
overrideRect?: ElementRect;
};

export interface FitViewportOptions {
Expand Down Expand Up @@ -252,8 +255,9 @@ export default class CanvasView extends ModuleView<Canvas> {
if (framesArea) {
const { x, y } = model.attributes;
const zoomDc = module.getZoomDecimal();
const rotation = module.getRotationAngle();

framesArea.style.transform = `scale(${zoomDc}) translate(${x * mpl}px, ${y * mpl}px)`;
framesArea.style.transform = `scale(${zoomDc}) translate(${x * mpl}px, ${y * mpl}px) rotate(${rotation}deg)`;
}

if (cvStyle) {
Expand Down Expand Up @@ -455,7 +459,26 @@ export default class CanvasView extends ModuleView<Canvas> {
const frame = this.frame?.el;
const winEl = el?.ownerDocument.defaultView;
const frEl = winEl ? (winEl.frameElement as HTMLElement) : frame;
this.frmOff = this.offset(frEl || frame, { nativeBoundingRect: true });
const zoom = this.module.getZoomDecimal();
//Native offset has been transformed by the zoom and rotation
const nativeOffset = this.offset(frEl || frame, { nativeBoundingRect: true });
//Original offset of the element has not been transformed
const originalOffset = this.offset(frEl || frame, { nativeBoundingRect: false });

//We want to get the offset without the rotation
const middle = {
x: nativeOffset.left + nativeOffset.width / 2,
y: nativeOffset.top + nativeOffset.height / 2,
};

let width = originalOffset.width * zoom;
let height = originalOffset.height * zoom;
this.frmOff = {
width: width,
height: height,
top: middle.y - height / 2,
left: middle.x - width / 2,
};
}
return this.frmOff;
}
Expand All @@ -482,12 +505,29 @@ export default class CanvasView extends ModuleView<Canvas> {
const frameOffset = this.getFrameOffset(el);
const canvasEl = this.el;
const canvasOffset = this.getCanvasOffset();
const elRect = this.offset(el, opts);
const elRect = opts.overrideRect ?? this.offset(el, opts);
const frameTop = opts.avoidFrameOffset ? 0 : frameOffset.top;
const frameLeft = opts.avoidFrameOffset ? 0 : frameOffset.left;

const elTop = opts.avoidFrameZoom ? elRect.top : elRect.top * zoom;
const elLeft = opts.avoidFrameZoom ? elRect.left : elRect.left * zoom;
const rotated = rotateCoordinate(
{
l: elRect.left + elRect.width / 2,
t: elRect.top + elRect.height / 2,
},
{
l: 0,
t: 0,
w: this.frame?.model?.width ?? 0,
h: this.frame?.model?.height ?? 0,
r: opts.avoidRotate ? 0 : this.module.getRotationAngle(),
}
);

rotated.l -= elRect.width / 2;
rotated.t -= elRect.height / 2;

const elTop = opts.avoidFrameZoom ? rotated.t : rotated.t * zoom;
const elLeft = opts.avoidFrameZoom ? rotated.l : rotated.l * zoom;

const top = opts.avoidFrameOffset ? elTop : elTop + frameTop - canvasOffset.top + canvasEl.scrollTop;
const left = opts.avoidFrameOffset ? elLeft : elLeft + frameLeft - canvasOffset.left + canvasEl.scrollLeft;
Expand Down
31 changes: 26 additions & 5 deletions src/commands/view/ComponentDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,12 @@ export default {
},

getElementPos(el: HTMLElement) {
return this.editor.Canvas.getElementPos(el, { noScroll: 1 });
return this.editor.Canvas.getElementPos(el, {
noScroll: 1,
avoidRotate: 1,
avoidFrameOffset: 1,
avoidFrameZoom: 1,
});
},

getElementGuides(el: HTMLElement) {
Expand Down Expand Up @@ -428,14 +433,30 @@ export default {
const statEdge2Raw = isY ? rect.left + rect.width : rect.top + rect.height;
const posFirst = isY ? item.y : item.x;
const posSecond = isEdge1 ? statEdge2 : origEdge2;
const pos2 = `${posFirst}px`;
const size = isEdge1 ? origEdge1 - statEdge2 : statEdge1 - origEdge2;
const sizeRaw = isEdge1 ? origEdge1Raw - statEdge2Raw : statEdge1Raw - origEdge2Raw;

const position = this.editor.Canvas.getElementPos(origin, {
noScroll: 1,
overrideRect: {
top: isY ? posFirst : posSecond,
left: isY ? posSecond : posFirst,
width: isY ? size : 0,
height: isY ? 0 : size,
},
});

const rotationAngle = this.canvas.getRotationAngle();
const zoom = this.canvas.getZoomDecimal();

guideInfoStyle.display = '';
guideInfoStyle[isY ? 'top' : 'left'] = pos2;
guideInfoStyle[isY ? 'left' : 'top'] = `${posSecond}px`;
guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`;
guideInfoStyle.top = `${position.top}px`;
guideInfoStyle.left = `${position.left}px`;
guideInfoStyle.rotate = `${rotationAngle}deg`;
guideInfoStyle[isY ? 'width' : 'height'] = `${size * zoom}px`;
elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`;
elGuideInfoCnt.style.rotate = `${-rotationAngle}deg`;
elGuideInfoCnt.style.transformOrigin = isY ? '50% 100%' : '100% 50%';
this.em.trigger(`${evName}:active`, {
...this.getEventOpts(),
guide: item,
Expand Down
1 change: 1 addition & 0 deletions src/commands/view/Resize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
prefix: editor.getConfig().stylePrefix,
posFetcher: canvasView.getElementPos.bind(canvasView),
mousePosFetcher: canvas.getMouseRelativePos.bind(canvas),
rotationAngle: canvas.getRotationAngle(),
...(opt.options || {}),
};
let { canvasResizer } = this;
Expand Down
1 change: 1 addition & 0 deletions src/commands/view/Rotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
prefix: editor.getConfig().stylePrefix,
posFetcher: canvasView.getElementPos.bind(canvasView),
mousePosFetcher: (ev: MouseEvent) => canvas.getMouseRelativeCanvas(ev, {}),
rotationAngle: canvas.getRotationAngle(),
...(opt.options || {}),
};
let { canvasRotator } = this;
Expand Down
2 changes: 2 additions & 0 deletions src/commands/view/SelectComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ export default {
style.width = pos.width + unit;
style.height = pos.height + unit;
style.rotate = window.getComputedStyle(el).getPropertyValue('rotate');
style.transform = `rotate(${this.canvas.getRotationAngle()}deg)`;

this._trgToolUp('local', {
component,
Expand Down Expand Up @@ -794,6 +795,7 @@ export default {
style.width = pos.width + unit;
style.height = pos.height + unit;
style.rotate = window.getComputedStyle(el).getPropertyValue('rotate');
style.transform = `rotate(${this.canvas.getRotationAngle()}deg)`;

this.updateToolbarPos({ top: targetToElem.top, left: targetToElem.left });
this._trgToolUp('global', {
Expand Down
Loading

0 comments on commit f6a5ebc

Please sign in to comment.