Merge pull request #16793 from calixteman/editor_resize_rotated
[Editor] Fix the resizing of an editor when it's rotated (bug 1847268)
This commit is contained in:
commit
e914870c14
20
gulpfile.mjs
20
gulpfile.mjs
@ -27,6 +27,7 @@ import { mkdirp } from "mkdirp";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import postcss from "gulp-postcss";
|
import postcss from "gulp-postcss";
|
||||||
import postcssDirPseudoClass from "postcss-dir-pseudo-class";
|
import postcssDirPseudoClass from "postcss-dir-pseudo-class";
|
||||||
|
import postcssNesting from "postcss-nesting";
|
||||||
import { preprocessPDFJSCode } from "./external/builder/preprocessor2.mjs";
|
import { preprocessPDFJSCode } from "./external/builder/preprocessor2.mjs";
|
||||||
import rename from "gulp-rename";
|
import rename from "gulp-rename";
|
||||||
import replace from "gulp-replace";
|
import replace from "gulp-replace";
|
||||||
@ -979,7 +980,11 @@ function buildGeneric(defines, dir) {
|
|||||||
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
||||||
preprocessCSS("web/viewer.css", defines)
|
preprocessCSS("web/viewer.css", defines)
|
||||||
.pipe(
|
.pipe(
|
||||||
postcss([postcssDirPseudoClass(), autoprefixer(AUTOPREFIXER_CONFIG)])
|
postcss([
|
||||||
|
postcssDirPseudoClass(),
|
||||||
|
postcssNesting(),
|
||||||
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(dir + "web")),
|
.pipe(gulp.dest(dir + "web")),
|
||||||
|
|
||||||
@ -1062,7 +1067,11 @@ function buildComponents(defines, dir) {
|
|||||||
gulp.src(COMPONENTS_IMAGES).pipe(gulp.dest(dir + "images")),
|
gulp.src(COMPONENTS_IMAGES).pipe(gulp.dest(dir + "images")),
|
||||||
preprocessCSS("web/pdf_viewer.css", defines)
|
preprocessCSS("web/pdf_viewer.css", defines)
|
||||||
.pipe(
|
.pipe(
|
||||||
postcss([postcssDirPseudoClass(), autoprefixer(AUTOPREFIXER_CONFIG)])
|
postcss([
|
||||||
|
postcssDirPseudoClass(),
|
||||||
|
postcssNesting(),
|
||||||
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(dir)),
|
.pipe(gulp.dest(dir)),
|
||||||
]);
|
]);
|
||||||
@ -1154,7 +1163,11 @@ function buildMinified(defines, dir) {
|
|||||||
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
||||||
preprocessCSS("web/viewer.css", defines)
|
preprocessCSS("web/viewer.css", defines)
|
||||||
.pipe(
|
.pipe(
|
||||||
postcss([postcssDirPseudoClass(), autoprefixer(AUTOPREFIXER_CONFIG)])
|
postcss([
|
||||||
|
postcssDirPseudoClass(),
|
||||||
|
postcssNesting(),
|
||||||
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(dir + "web")),
|
.pipe(gulp.dest(dir + "web")),
|
||||||
|
|
||||||
@ -1495,6 +1508,7 @@ gulp.task(
|
|||||||
.pipe(
|
.pipe(
|
||||||
postcss([
|
postcss([
|
||||||
postcssDirPseudoClass(),
|
postcssDirPseudoClass(),
|
||||||
|
postcssNesting(),
|
||||||
autoprefixer(AUTOPREFIXER_CONFIG),
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
27
package-lock.json
generated
27
package-lock.json
generated
@ -48,6 +48,7 @@
|
|||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.27",
|
||||||
"postcss-dir-pseudo-class": "^8.0.0",
|
"postcss-dir-pseudo-class": "^8.0.0",
|
||||||
|
"postcss-nesting": "^12.0.1",
|
||||||
"prettier": "^3.0.1",
|
"prettier": "^3.0.1",
|
||||||
"puppeteer": "^21.0.1",
|
"puppeteer": "^21.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
@ -16730,6 +16731,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss-nesting": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-6LCqCWP9pqwXw/njMvNK0hGY44Fxc4B2EsGbn6xDcxbNRzP8GYoxT7yabVVMLrX3quqOJ9hg2jYMsnkedOf8pA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/csstools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/csstools"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@csstools/selector-specificity": "^3.0.0",
|
||||||
|
"postcss-selector-parser": "^6.0.13"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss-resolve-nested-selector": {
|
"node_modules/postcss-resolve-nested-selector": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz",
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.27",
|
||||||
"postcss-dir-pseudo-class": "^8.0.0",
|
"postcss-dir-pseudo-class": "^8.0.0",
|
||||||
|
"postcss-nesting": "^12.0.1",
|
||||||
"prettier": "^3.0.1",
|
"prettier": "^3.0.1",
|
||||||
"puppeteer": "^21.0.1",
|
"puppeteer": "^21.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
@ -18,13 +18,8 @@
|
|||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("./tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
/** @typedef {import("./tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
||||||
|
|
||||||
import {
|
|
||||||
AnnotationEditorParamsType,
|
|
||||||
FeatureTest,
|
|
||||||
shadow,
|
|
||||||
unreachable,
|
|
||||||
} from "../../shared/util.js";
|
|
||||||
import { bindEvents, ColorManager } from "./tools.js";
|
import { bindEvents, ColorManager } from "./tools.js";
|
||||||
|
import { FeatureTest, shadow, unreachable } from "../../shared/util.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AnnotationEditorParameters
|
* @typedef {Object} AnnotationEditorParameters
|
||||||
@ -43,8 +38,6 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
#resizersDiv = null;
|
#resizersDiv = null;
|
||||||
|
|
||||||
#resizePosition = null;
|
|
||||||
|
|
||||||
#boundFocusin = this.focusin.bind(this);
|
#boundFocusin = this.focusin.bind(this);
|
||||||
|
|
||||||
#boundFocusout = this.focusout.bind(this);
|
#boundFocusout = this.focusout.bind(this);
|
||||||
@ -328,13 +321,8 @@ class AnnotationEditor {
|
|||||||
this.div.style.top = `${(100 * this.y).toFixed(2)}%`;
|
this.div.style.top = `${(100 * this.y).toFixed(2)}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static #rotatePoint(x, y, angle) {
|
||||||
* Convert a screen translation into a page one.
|
switch (angle) {
|
||||||
* @param {number} x
|
|
||||||
* @param {number} y
|
|
||||||
*/
|
|
||||||
screenToPageTranslation(x, y) {
|
|
||||||
switch (this.parentRotation) {
|
|
||||||
case 90:
|
case 90:
|
||||||
return [y, -x];
|
return [y, -x];
|
||||||
case 180:
|
case 180:
|
||||||
@ -346,21 +334,38 @@ class AnnotationEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a screen translation into a page one.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
*/
|
||||||
|
screenToPageTranslation(x, y) {
|
||||||
|
return AnnotationEditor.#rotatePoint(x, y, this.parentRotation);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a page translation into a screen one.
|
* Convert a page translation into a screen one.
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @param {number} y
|
* @param {number} y
|
||||||
*/
|
*/
|
||||||
pageTranslationToScreen(x, y) {
|
pageTranslationToScreen(x, y) {
|
||||||
switch (this.parentRotation) {
|
return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation);
|
||||||
case 90:
|
}
|
||||||
return [-y, x];
|
|
||||||
|
#getRotationMatrix(rotation) {
|
||||||
|
switch (rotation) {
|
||||||
|
case 90: {
|
||||||
|
const [pageWidth, pageHeight] = this.pageDimensions;
|
||||||
|
return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0];
|
||||||
|
}
|
||||||
case 180:
|
case 180:
|
||||||
return [-x, -y];
|
return [-1, 0, 0, -1];
|
||||||
case 270:
|
case 270: {
|
||||||
return [y, -x];
|
const [pageWidth, pageHeight] = this.pageDimensions;
|
||||||
|
return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0];
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return [x, y];
|
return [1, 0, 0, 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,26 +454,26 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
#resizerPointerdown(name, event) {
|
#resizerPointerdown(name, event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.#resizePosition = [event.clientX, event.clientY];
|
|
||||||
const boundResizerPointermove = this.#resizerPointermove.bind(this, name);
|
const boundResizerPointermove = this.#resizerPointermove.bind(this, name);
|
||||||
const savedDraggable = this._isDraggable;
|
const savedDraggable = this._isDraggable;
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
const resizingClassName = `resizing${name
|
|
||||||
.charAt(0)
|
|
||||||
.toUpperCase()}${name.slice(1)}`;
|
|
||||||
this.parent.div.classList.add(resizingClassName);
|
|
||||||
const pointerMoveOptions = { passive: true, capture: true };
|
const pointerMoveOptions = { passive: true, capture: true };
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"pointermove",
|
"pointermove",
|
||||||
boundResizerPointermove,
|
boundResizerPointermove,
|
||||||
pointerMoveOptions
|
pointerMoveOptions
|
||||||
);
|
);
|
||||||
|
const savedX = this.x;
|
||||||
|
const savedY = this.y;
|
||||||
|
const savedWidth = this.width;
|
||||||
|
const savedHeight = this.height;
|
||||||
|
const savedParentCursor = this.parent.div.style.cursor;
|
||||||
|
const savedCursor = this.div.style.cursor;
|
||||||
|
this.div.style.cursor = this.parent.div.style.cursor =
|
||||||
|
window.getComputedStyle(event.target).cursor;
|
||||||
|
|
||||||
const pointerUpCallback = () => {
|
const pointerUpCallback = () => {
|
||||||
// Stop the undo accumulation in order to have an undo action for each
|
|
||||||
// resize session.
|
|
||||||
this._uiManager.stopUndoAccumulation();
|
|
||||||
this._isDraggable = savedDraggable;
|
this._isDraggable = savedDraggable;
|
||||||
this.parent.div.classList.remove(resizingClassName);
|
|
||||||
window.removeEventListener("pointerup", pointerUpCallback);
|
window.removeEventListener("pointerup", pointerUpCallback);
|
||||||
window.removeEventListener("blur", pointerUpCallback);
|
window.removeEventListener("blur", pointerUpCallback);
|
||||||
window.removeEventListener(
|
window.removeEventListener(
|
||||||
@ -476,19 +481,53 @@ class AnnotationEditor {
|
|||||||
boundResizerPointermove,
|
boundResizerPointermove,
|
||||||
pointerMoveOptions
|
pointerMoveOptions
|
||||||
);
|
);
|
||||||
|
this.parent.div.style.cursor = savedParentCursor;
|
||||||
|
this.div.style.cursor = savedCursor;
|
||||||
|
|
||||||
|
const newX = this.x;
|
||||||
|
const newY = this.y;
|
||||||
|
const newWidth = this.width;
|
||||||
|
const newHeight = this.height;
|
||||||
|
if (
|
||||||
|
newX === savedX &&
|
||||||
|
newY === savedY &&
|
||||||
|
newWidth === savedWidth &&
|
||||||
|
newHeight === savedHeight
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addCommands({
|
||||||
|
cmd: () => {
|
||||||
|
this.width = newWidth;
|
||||||
|
this.height = newHeight;
|
||||||
|
this.x = newX;
|
||||||
|
this.y = newY;
|
||||||
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
|
this.setDims(parentWidth * newWidth, parentHeight * newHeight);
|
||||||
|
this.fixAndSetPosition();
|
||||||
|
this.parent.moveEditorInDOM(this);
|
||||||
|
},
|
||||||
|
undo: () => {
|
||||||
|
this.width = savedWidth;
|
||||||
|
this.height = savedHeight;
|
||||||
|
this.x = savedX;
|
||||||
|
this.y = savedY;
|
||||||
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
|
this.setDims(parentWidth * savedWidth, parentHeight * savedHeight);
|
||||||
|
this.fixAndSetPosition();
|
||||||
|
this.parent.moveEditorInDOM(this);
|
||||||
|
},
|
||||||
|
mustExec: true,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
window.addEventListener("pointerup", pointerUpCallback);
|
window.addEventListener("pointerup", pointerUpCallback);
|
||||||
// If the user switch to another window (with alt+tab), then we end the
|
// If the user switches to another window (with alt+tab), then we end the
|
||||||
// resize session.
|
// resize session.
|
||||||
window.addEventListener("blur", pointerUpCallback);
|
window.addEventListener("blur", pointerUpCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#resizerPointermove(name, event) {
|
#resizerPointermove(name, event) {
|
||||||
const { clientX, clientY } = event;
|
|
||||||
const deltaX = clientX - this.#resizePosition[0];
|
|
||||||
const deltaY = clientY - this.#resizePosition[1];
|
|
||||||
this.#resizePosition[0] = clientX;
|
|
||||||
this.#resizePosition[1] = clientY;
|
|
||||||
const [parentWidth, parentHeight] = this.parentDimensions;
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
const savedX = this.x;
|
const savedX = this.x;
|
||||||
const savedY = this.y;
|
const savedY = this.y;
|
||||||
@ -496,204 +535,124 @@ class AnnotationEditor {
|
|||||||
const savedHeight = this.height;
|
const savedHeight = this.height;
|
||||||
const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;
|
const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;
|
||||||
const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;
|
const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;
|
||||||
let cmd;
|
|
||||||
|
|
||||||
// 10000 because we multiply by 100 and use toFixed(2) in fixAndSetPosition.
|
// 10000 because we multiply by 100 and use toFixed(2) in fixAndSetPosition.
|
||||||
// Without rounding, the positions of the corners other than the top left
|
// Without rounding, the positions of the corners other than the top left
|
||||||
// one can be slightly wrong.
|
// one can be slightly wrong.
|
||||||
const round = x => Math.round(x * 10000) / 10000;
|
const round = x => Math.round(x * 10000) / 10000;
|
||||||
const updatePosition = (width, height) => {
|
const rotationMatrix = this.#getRotationMatrix(this.rotation);
|
||||||
// We must take the parent dimensions as they are when undo/redo.
|
const transf = (x, y) => [
|
||||||
const [pWidth, pHeight] = this.parentDimensions;
|
rotationMatrix[0] * x + rotationMatrix[2] * y,
|
||||||
this.setDims(pWidth * width, pHeight * height);
|
rotationMatrix[1] * x + rotationMatrix[3] * y,
|
||||||
this.fixAndSetPosition();
|
];
|
||||||
};
|
const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation);
|
||||||
const undo = () => {
|
const invTransf = (x, y) => [
|
||||||
this.width = savedWidth;
|
invRotationMatrix[0] * x + invRotationMatrix[2] * y,
|
||||||
this.height = savedHeight;
|
invRotationMatrix[1] * x + invRotationMatrix[3] * y,
|
||||||
this.x = savedX;
|
];
|
||||||
this.y = savedY;
|
let getPoint;
|
||||||
updatePosition(savedWidth, savedHeight);
|
let getOpposite;
|
||||||
};
|
let isDiagonal = false;
|
||||||
|
let isHorizontal = false;
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "topLeft": {
|
case "topLeft":
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) < 0) {
|
isDiagonal = true;
|
||||||
return;
|
getPoint = (w, h) => [0, 0];
|
||||||
}
|
getOpposite = (w, h) => [w, h];
|
||||||
const dist = Math.hypot(deltaX, deltaY);
|
|
||||||
const oldDiag = Math.hypot(
|
|
||||||
savedWidth * parentWidth,
|
|
||||||
savedHeight * parentHeight
|
|
||||||
);
|
|
||||||
const brX = round(savedX + savedWidth);
|
|
||||||
const brY = round(savedY + savedHeight);
|
|
||||||
const ratio = Math.max(
|
|
||||||
Math.min(
|
|
||||||
1 - Math.sign(deltaX) * (dist / oldDiag),
|
|
||||||
// Avoid the editor to be larger than the page.
|
|
||||||
1 / savedWidth,
|
|
||||||
1 / savedHeight
|
|
||||||
),
|
|
||||||
// Avoid the editor to be smaller than the minimum size.
|
|
||||||
minWidth / savedWidth,
|
|
||||||
minHeight / savedHeight
|
|
||||||
);
|
|
||||||
const newWidth = round(savedWidth * ratio);
|
|
||||||
const newHeight = round(savedHeight * ratio);
|
|
||||||
const newX = brX - newWidth;
|
|
||||||
const newY = brY - newHeight;
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.height = newHeight;
|
|
||||||
this.x = newX;
|
|
||||||
this.y = newY;
|
|
||||||
updatePosition(newWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "topMiddle":
|
||||||
case "topMiddle": {
|
getPoint = (w, h) => [w / 2, 0];
|
||||||
const bmY = round(this.y + savedHeight);
|
getOpposite = (w, h) => [w / 2, h];
|
||||||
const newHeight = round(
|
|
||||||
Math.max(minHeight, Math.min(1, savedHeight - deltaY / parentHeight))
|
|
||||||
);
|
|
||||||
const newY = bmY - newHeight;
|
|
||||||
cmd = () => {
|
|
||||||
this.height = newHeight;
|
|
||||||
this.y = newY;
|
|
||||||
updatePosition(savedWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "topRight":
|
||||||
case "topRight": {
|
isDiagonal = true;
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) > 0) {
|
getPoint = (w, h) => [w, 0];
|
||||||
return;
|
getOpposite = (w, h) => [0, h];
|
||||||
}
|
|
||||||
const dist = Math.hypot(deltaX, deltaY);
|
|
||||||
const oldDiag = Math.hypot(
|
|
||||||
this.width * parentWidth,
|
|
||||||
this.height * parentHeight
|
|
||||||
);
|
|
||||||
const blY = round(savedY + this.height);
|
|
||||||
const ratio = Math.max(
|
|
||||||
Math.min(
|
|
||||||
1 + Math.sign(deltaX) * (dist / oldDiag),
|
|
||||||
1 / savedWidth,
|
|
||||||
1 / savedHeight
|
|
||||||
),
|
|
||||||
minWidth / savedWidth,
|
|
||||||
minHeight / savedHeight
|
|
||||||
);
|
|
||||||
const newWidth = round(savedWidth * ratio);
|
|
||||||
const newHeight = round(savedHeight * ratio);
|
|
||||||
const newY = blY - newHeight;
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.height = newHeight;
|
|
||||||
this.y = newY;
|
|
||||||
updatePosition(newWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "middleRight":
|
||||||
case "middleRight": {
|
isHorizontal = true;
|
||||||
const newWidth = round(
|
getPoint = (w, h) => [w, h / 2];
|
||||||
Math.max(minWidth, Math.min(1, savedWidth + deltaX / parentWidth))
|
getOpposite = (w, h) => [0, h / 2];
|
||||||
);
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
updatePosition(newWidth, savedHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "bottomRight":
|
||||||
case "bottomRight": {
|
isDiagonal = true;
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) < 0) {
|
getPoint = (w, h) => [w, h];
|
||||||
return;
|
getOpposite = (w, h) => [0, 0];
|
||||||
}
|
|
||||||
const dist = Math.hypot(deltaX, deltaY);
|
|
||||||
const oldDiag = Math.hypot(
|
|
||||||
this.width * parentWidth,
|
|
||||||
this.height * parentHeight
|
|
||||||
);
|
|
||||||
const ratio = Math.max(
|
|
||||||
Math.min(
|
|
||||||
1 + Math.sign(deltaX) * (dist / oldDiag),
|
|
||||||
1 / savedWidth,
|
|
||||||
1 / savedHeight
|
|
||||||
),
|
|
||||||
minWidth / savedWidth,
|
|
||||||
minHeight / savedHeight
|
|
||||||
);
|
|
||||||
const newWidth = round(savedWidth * ratio);
|
|
||||||
const newHeight = round(savedHeight * ratio);
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.height = newHeight;
|
|
||||||
updatePosition(newWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "bottomMiddle":
|
||||||
case "bottomMiddle": {
|
getPoint = (w, h) => [w / 2, h];
|
||||||
const newHeight = round(
|
getOpposite = (w, h) => [w / 2, 0];
|
||||||
Math.max(minHeight, Math.min(1, savedHeight + deltaY / parentHeight))
|
|
||||||
);
|
|
||||||
cmd = () => {
|
|
||||||
this.height = newHeight;
|
|
||||||
updatePosition(savedWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "bottomLeft":
|
||||||
case "bottomLeft": {
|
isDiagonal = true;
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) > 0) {
|
getPoint = (w, h) => [0, h];
|
||||||
return;
|
getOpposite = (w, h) => [w, 0];
|
||||||
}
|
|
||||||
const dist = Math.hypot(deltaX, deltaY);
|
|
||||||
const oldDiag = Math.hypot(
|
|
||||||
this.width * parentWidth,
|
|
||||||
this.height * parentHeight
|
|
||||||
);
|
|
||||||
const trX = round(savedX + this.width);
|
|
||||||
const ratio = Math.max(
|
|
||||||
Math.min(
|
|
||||||
1 - Math.sign(deltaX) * (dist / oldDiag),
|
|
||||||
1 / savedWidth,
|
|
||||||
1 / savedHeight
|
|
||||||
),
|
|
||||||
minWidth / savedWidth,
|
|
||||||
minHeight / savedHeight
|
|
||||||
);
|
|
||||||
const newWidth = round(savedWidth * ratio);
|
|
||||||
const newHeight = round(savedHeight * ratio);
|
|
||||||
const newX = trX - newWidth;
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.height = newHeight;
|
|
||||||
this.x = newX;
|
|
||||||
updatePosition(newWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
case "middleLeft":
|
||||||
case "middleLeft": {
|
isHorizontal = true;
|
||||||
const mrX = round(savedX + savedWidth);
|
getPoint = (w, h) => [0, h / 2];
|
||||||
const newWidth = round(
|
getOpposite = (w, h) => [w, h / 2];
|
||||||
Math.max(minWidth, Math.min(1, savedWidth - deltaX / parentWidth))
|
|
||||||
);
|
|
||||||
const newX = mrX - newWidth;
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.x = newX;
|
|
||||||
updatePosition(newWidth, savedHeight);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.addCommands({
|
|
||||||
cmd,
|
const point = getPoint(savedWidth, savedHeight);
|
||||||
undo,
|
const oppositePoint = getOpposite(savedWidth, savedHeight);
|
||||||
mustExec: true,
|
let transfOppositePoint = transf(...oppositePoint);
|
||||||
type: AnnotationEditorParamsType.RESIZE,
|
const oppositeX = round(savedX + transfOppositePoint[0]);
|
||||||
overwriteIfSameType: true,
|
const oppositeY = round(savedY + transfOppositePoint[1]);
|
||||||
keepUndo: true,
|
let ratioX = 1;
|
||||||
});
|
let ratioY = 1;
|
||||||
|
|
||||||
|
let [deltaX, deltaY] = this.screenToPageTranslation(
|
||||||
|
event.movementX,
|
||||||
|
event.movementY
|
||||||
|
);
|
||||||
|
[deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight);
|
||||||
|
|
||||||
|
if (isDiagonal) {
|
||||||
|
const oldDiag = Math.hypot(savedWidth, savedHeight);
|
||||||
|
ratioX = ratioY = Math.max(
|
||||||
|
Math.min(
|
||||||
|
Math.hypot(
|
||||||
|
oppositePoint[0] - point[0] - deltaX,
|
||||||
|
oppositePoint[1] - point[1] - deltaY
|
||||||
|
) / oldDiag,
|
||||||
|
// Avoid the editor to be larger than the page.
|
||||||
|
1 / savedWidth,
|
||||||
|
1 / savedHeight
|
||||||
|
),
|
||||||
|
// Avoid the editor to be smaller than the minimum size.
|
||||||
|
minWidth / savedWidth,
|
||||||
|
minHeight / savedHeight
|
||||||
|
);
|
||||||
|
} else if (isHorizontal) {
|
||||||
|
ratioX =
|
||||||
|
Math.max(
|
||||||
|
minWidth,
|
||||||
|
Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))
|
||||||
|
) / savedWidth;
|
||||||
|
} else {
|
||||||
|
ratioY =
|
||||||
|
Math.max(
|
||||||
|
minHeight,
|
||||||
|
Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))
|
||||||
|
) / savedHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newWidth = round(savedWidth * ratioX);
|
||||||
|
const newHeight = round(savedHeight * ratioY);
|
||||||
|
transfOppositePoint = transf(...getOpposite(newWidth, newHeight));
|
||||||
|
const newX = oppositeX - transfOppositePoint[0];
|
||||||
|
const newY = oppositeY - transfOppositePoint[1];
|
||||||
|
|
||||||
|
this.width = newWidth;
|
||||||
|
this.height = newHeight;
|
||||||
|
this.x = newX;
|
||||||
|
this.y = newY;
|
||||||
|
|
||||||
|
this.setDims(parentWidth * newWidth, parentHeight * newHeight);
|
||||||
|
this.fixAndSetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -305,12 +305,6 @@ class CommandManager {
|
|||||||
this.#commands.push(save);
|
this.#commands.push(save);
|
||||||
}
|
}
|
||||||
|
|
||||||
stopUndoAccumulation() {
|
|
||||||
if (this.#position !== -1) {
|
|
||||||
this.#commands[this.#position].type = NaN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo the last command.
|
* Undo the last command.
|
||||||
*/
|
*/
|
||||||
@ -1294,10 +1288,6 @@ class AnnotationEditorUIManager {
|
|||||||
return this.#selectedEditors.size !== 0;
|
return this.#selectedEditors.size !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopUndoAccumulation() {
|
|
||||||
this.#commandManager.stopUndoAccumulation();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo the last command.
|
* Undo the last command.
|
||||||
*/
|
*/
|
||||||
|
@ -18,6 +18,7 @@ const {
|
|||||||
getEditorDimensions,
|
getEditorDimensions,
|
||||||
loadAndWait,
|
loadAndWait,
|
||||||
serializeBitmapDimensions,
|
serializeBitmapDimensions,
|
||||||
|
waitForAnnotationEditorLayer,
|
||||||
} = require("./test_utils.js");
|
} = require("./test_utils.js");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
@ -37,9 +38,8 @@ describe("Stamp Editor", () => {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
pages.map(async ([browserName, page]) => {
|
pages.map(async ([browserName, page]) => {
|
||||||
if (browserName === "firefox") {
|
if (browserName === "firefox") {
|
||||||
pending(
|
// Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847.
|
||||||
"Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847."
|
return;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.click("#editorStamp");
|
await page.click("#editorStamp");
|
||||||
@ -59,12 +59,11 @@ describe("Stamp Editor", () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
const { width, height } = await getEditorDimensions(page, 0);
|
const { width } = await getEditorDimensions(page, 0);
|
||||||
|
|
||||||
// The image is bigger than the page, so it has been scaled down to
|
// The image is bigger than the page, so it has been scaled down to
|
||||||
// 75% of the page width.
|
// 75% of the page width.
|
||||||
expect(width).toEqual("75%");
|
expect(width).toEqual("75%");
|
||||||
expect(height).toEqual("auto");
|
|
||||||
|
|
||||||
const [bitmap] = await serializeBitmapDimensions(page);
|
const [bitmap] = await serializeBitmapDimensions(page);
|
||||||
expect(bitmap.width).toEqual(512);
|
expect(bitmap.width).toEqual(512);
|
||||||
@ -84,9 +83,8 @@ describe("Stamp Editor", () => {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
pages.map(async ([browserName, page]) => {
|
pages.map(async ([browserName, page]) => {
|
||||||
if (browserName === "firefox") {
|
if (browserName === "firefox") {
|
||||||
pending(
|
// Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847.
|
||||||
"Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847."
|
return;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rect = await page.$eval(".annotationEditorLayer", el => {
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
@ -104,10 +102,9 @@ describe("Stamp Editor", () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
const { width, height } = await getEditorDimensions(page, 1);
|
const { width } = await getEditorDimensions(page, 1);
|
||||||
|
|
||||||
expect(Math.round(parseFloat(width))).toEqual(40);
|
expect(Math.round(parseFloat(width))).toEqual(40);
|
||||||
expect(height).toEqual("auto");
|
|
||||||
|
|
||||||
const [bitmap] = await serializeBitmapDimensions(page);
|
const [bitmap] = await serializeBitmapDimensions(page);
|
||||||
// The original size is 80x242 but to increase the resolution when it
|
// The original size is 80x242 but to increase the resolution when it
|
||||||
@ -144,9 +141,8 @@ describe("Stamp Editor", () => {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
pages.map(async ([browserName, page]) => {
|
pages.map(async ([browserName, page]) => {
|
||||||
if (browserName === "firefox") {
|
if (browserName === "firefox") {
|
||||||
pending(
|
// Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847.
|
||||||
"Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847."
|
return;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.click("#editorStamp");
|
await page.click("#editorStamp");
|
||||||
@ -175,4 +171,98 @@ describe("Stamp Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Resize", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that an added image stay within the page", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
if (browserName === "firefox") {
|
||||||
|
// Disabled in Firefox, because of https://bugzilla.mozilla.org/1553847.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.click("#editorStamp");
|
||||||
|
const names = ["bottomLeft", "bottomRight", "topRight", "topLeft"];
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
if (i !== 0) {
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("a");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
await page.keyboard.press("Backspace");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
|
// With Chrome something is wrong when serializing a DomRect,
|
||||||
|
// hence we extract the values and just return them.
|
||||||
|
const { x, y } = el.getBoundingClientRect();
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.mouse.click(rect.x + 10, rect.y + 10);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
const input = await page.$("#stampEditorFileInput");
|
||||||
|
await input.uploadFile(
|
||||||
|
`${path.join(__dirname, "../images/firefox_logo.png")}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.waitForFunction(
|
||||||
|
`getComputedStyle(document.querySelector(".resizers")).display === "none"`
|
||||||
|
);
|
||||||
|
|
||||||
|
const promise = waitForAnnotationEditorLayer(page);
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.PDFViewerApplication.rotatePages(90);
|
||||||
|
});
|
||||||
|
await promise;
|
||||||
|
await page.focus(".stampEditor");
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
`getComputedStyle(document.querySelector(".resizers")).display === "block"`
|
||||||
|
);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
|
const [name, cursor] = await page.evaluate(() => {
|
||||||
|
const { x, y } = document
|
||||||
|
.querySelector(".stampEditor")
|
||||||
|
.getBoundingClientRect();
|
||||||
|
const el = document.elementFromPoint(x, y);
|
||||||
|
const cornerName = Array.from(el.classList).find(
|
||||||
|
c => c !== "resizer"
|
||||||
|
);
|
||||||
|
return [cornerName, window.getComputedStyle(el).cursor];
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(name).withContext(`In ${browserName}`).toEqual(names[j]);
|
||||||
|
expect(cursor)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual("nwse-resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = waitForAnnotationEditorLayer(page);
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.PDFViewerApplication.rotatePages(90);
|
||||||
|
});
|
||||||
|
await promise;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -202,3 +202,15 @@ async function dragAndDropAnnotation(page, startX, startY, tX, tY) {
|
|||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
}
|
}
|
||||||
exports.dragAndDropAnnotation = dragAndDropAnnotation;
|
exports.dragAndDropAnnotation = dragAndDropAnnotation;
|
||||||
|
|
||||||
|
async function waitForAnnotationEditorLayer(page) {
|
||||||
|
return page.evaluate(() => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.PDFViewerApplication.eventBus.on(
|
||||||
|
"annotationeditorlayerrendered",
|
||||||
|
resolve
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.waitForAnnotationEditorLayer = waitForAnnotationEditorLayer;
|
||||||
|
@ -186,86 +186,128 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .resizers {
|
.annotationEditorLayer {
|
||||||
width: 100%;
|
:is(.freeTextEditor, .inkEditor, .stampEditor) {
|
||||||
height: 100%;
|
& > .resizers {
|
||||||
position: absolute;
|
width: 100%;
|
||||||
inset: 0;
|
height: 100%;
|
||||||
}
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
|
||||||
.annotationEditorLayer .resizers.hidden {
|
&.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .resizer {
|
& > .resizer {
|
||||||
width: var(--resizer-size);
|
width: var(--resizer-size);
|
||||||
height: var(--resizer-size);
|
height: var(--resizer-size);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--resizer-color);
|
background: var(--resizer-color);
|
||||||
border: var(--focus-outline);
|
border: var(--focus-outline);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.topLeft {
|
&.topLeft {
|
||||||
cursor: nwse-resize;
|
top: var(--resizer-shift);
|
||||||
top: var(--resizer-shift);
|
left: var(--resizer-shift);
|
||||||
left: var(--resizer-shift);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.topMiddle {
|
&.topMiddle {
|
||||||
cursor: ns-resize;
|
top: var(--resizer-shift);
|
||||||
top: var(--resizer-shift);
|
left: calc(50% + var(--resizer-shift));
|
||||||
left: calc(50% + var(--resizer-shift));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.topRight {
|
&.topRight {
|
||||||
cursor: nesw-resize;
|
top: var(--resizer-shift);
|
||||||
top: var(--resizer-shift);
|
right: var(--resizer-shift);
|
||||||
right: var(--resizer-shift);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.middleRight {
|
&.middleRight {
|
||||||
cursor: ew-resize;
|
top: calc(50% + var(--resizer-shift));
|
||||||
top: calc(50% + var(--resizer-shift));
|
right: var(--resizer-shift);
|
||||||
right: var(--resizer-shift);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.bottomRight {
|
&.bottomRight {
|
||||||
cursor: nwse-resize;
|
bottom: var(--resizer-shift);
|
||||||
bottom: var(--resizer-shift);
|
right: var(--resizer-shift);
|
||||||
right: var(--resizer-shift);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.bottomMiddle {
|
&.bottomMiddle {
|
||||||
cursor: ns-resize;
|
bottom: var(--resizer-shift);
|
||||||
bottom: var(--resizer-shift);
|
left: calc(50% + var(--resizer-shift));
|
||||||
left: calc(50% + var(--resizer-shift));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.bottomLeft {
|
&.bottomLeft {
|
||||||
cursor: nesw-resize;
|
bottom: var(--resizer-shift);
|
||||||
bottom: var(--resizer-shift);
|
left: var(--resizer-shift);
|
||||||
left: var(--resizer-shift);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .resizer.middleLeft {
|
&.middleLeft {
|
||||||
cursor: ew-resize;
|
top: calc(50% + var(--resizer-shift));
|
||||||
top: calc(50% + var(--resizer-shift));
|
left: var(--resizer-shift);
|
||||||
left: var(--resizer-shift);
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingTopLeft, .resizingBottomRight) {
|
&[data-main-rotation="0"]
|
||||||
cursor: nwse-resize;
|
:is([data-editor-rotation="0"], [data-editor-rotation="180"]),
|
||||||
}
|
&[data-main-rotation="90"]
|
||||||
|
:is([data-editor-rotation="270"], [data-editor-rotation="90"]),
|
||||||
|
&[data-main-rotation="180"]
|
||||||
|
:is([data-editor-rotation="180"], [data-editor-rotation="0"]),
|
||||||
|
&[data-main-rotation="270"]
|
||||||
|
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) {
|
||||||
|
& > .resizers > .resizer {
|
||||||
|
&.topLeft,
|
||||||
|
&.bottomRight {
|
||||||
|
cursor: nwse-resize;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingTopMiddle, .resizingBottomMiddle) {
|
&.topMiddle,
|
||||||
cursor: ns-resize;
|
&.bottomMiddle {
|
||||||
}
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingTopRight, .resizingBottomLeft) {
|
&.topRight,
|
||||||
cursor: nesw-resize;
|
&.bottomLeft {
|
||||||
}
|
cursor: nesw-resize;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingMiddleRight, .resizingMiddleLeft) {
|
&.middleRight,
|
||||||
cursor: ew-resize;
|
&.middleLeft {
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-main-rotation="0"]
|
||||||
|
:is([data-editor-rotation="90"], [data-editor-rotation="270"]),
|
||||||
|
&[data-main-rotation="90"]
|
||||||
|
:is([data-editor-rotation="0"], [data-editor-rotation="180"]),
|
||||||
|
&[data-main-rotation="180"]
|
||||||
|
:is([data-editor-rotation="270"], [data-editor-rotation="90"]),
|
||||||
|
&[data-main-rotation="270"]
|
||||||
|
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) {
|
||||||
|
& > .resizers > .resizer {
|
||||||
|
&.topLeft,
|
||||||
|
&.bottomRight {
|
||||||
|
cursor: nesw-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.topMiddle,
|
||||||
|
&.bottomMiddle {
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.topRight,
|
||||||
|
&.bottomLeft {
|
||||||
|
cursor: nwse-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.middleRight,
|
||||||
|
&.middleLeft {
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user