[Editor] Fix the resizing of an editor when it's rotated (bug 1847268)
There are 2 rotation we've to deal with: the viewer one and the editor one. The previous implementation was a bit complex and having to deal with these rotation would have potentially increase it. So this patch aims to simplify the implementation and deal with all the possible cases. The main idea is to transform the mouse deltas according to the rotations and then apply the resizing in the page coordinates system.
This commit is contained in:
parent
19c712c2d0
commit
aa71619c2d
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,26 +448,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(
|
||||||
@ -470,19 +475,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;
|
||||||
@ -490,41 +529,89 @@ 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];
|
||||||
|
break;
|
||||||
|
case "topMiddle":
|
||||||
|
getPoint = (w, h) => [w / 2, 0];
|
||||||
|
getOpposite = (w, h) => [w / 2, h];
|
||||||
|
break;
|
||||||
|
case "topRight":
|
||||||
|
isDiagonal = true;
|
||||||
|
getPoint = (w, h) => [w, 0];
|
||||||
|
getOpposite = (w, h) => [0, h];
|
||||||
|
break;
|
||||||
|
case "middleRight":
|
||||||
|
isHorizontal = true;
|
||||||
|
getPoint = (w, h) => [w, h / 2];
|
||||||
|
getOpposite = (w, h) => [0, h / 2];
|
||||||
|
break;
|
||||||
|
case "bottomRight":
|
||||||
|
isDiagonal = true;
|
||||||
|
getPoint = (w, h) => [w, h];
|
||||||
|
getOpposite = (w, h) => [0, 0];
|
||||||
|
break;
|
||||||
|
case "bottomMiddle":
|
||||||
|
getPoint = (w, h) => [w / 2, h];
|
||||||
|
getOpposite = (w, h) => [w / 2, 0];
|
||||||
|
break;
|
||||||
|
case "bottomLeft":
|
||||||
|
isDiagonal = true;
|
||||||
|
getPoint = (w, h) => [0, h];
|
||||||
|
getOpposite = (w, h) => [w, 0];
|
||||||
|
break;
|
||||||
|
case "middleLeft":
|
||||||
|
isHorizontal = true;
|
||||||
|
getPoint = (w, h) => [0, h / 2];
|
||||||
|
getOpposite = (w, h) => [w, h / 2];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
const dist = Math.hypot(deltaX, deltaY);
|
|
||||||
const oldDiag = Math.hypot(
|
const point = getPoint(savedWidth, savedHeight);
|
||||||
savedWidth * parentWidth,
|
const oppositePoint = getOpposite(savedWidth, savedHeight);
|
||||||
savedHeight * parentHeight
|
let transfOppositePoint = transf(...oppositePoint);
|
||||||
|
const oppositeX = round(savedX + transfOppositePoint[0]);
|
||||||
|
const oppositeY = round(savedY + transfOppositePoint[1]);
|
||||||
|
let ratioX = 1;
|
||||||
|
let ratioY = 1;
|
||||||
|
|
||||||
|
let [deltaX, deltaY] = this.screenToPageTranslation(
|
||||||
|
event.movementX,
|
||||||
|
event.movementY
|
||||||
);
|
);
|
||||||
const brX = round(savedX + savedWidth);
|
[deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight);
|
||||||
const brY = round(savedY + savedHeight);
|
|
||||||
const ratio = Math.max(
|
if (isDiagonal) {
|
||||||
|
const oldDiag = Math.hypot(savedWidth, savedHeight);
|
||||||
|
ratioX = ratioY = Math.max(
|
||||||
Math.min(
|
Math.min(
|
||||||
1 - Math.sign(deltaX) * (dist / oldDiag),
|
Math.hypot(
|
||||||
|
oppositePoint[0] - point[0] - deltaX,
|
||||||
|
oppositePoint[1] - point[1] - deltaY
|
||||||
|
) / oldDiag,
|
||||||
// Avoid the editor to be larger than the page.
|
// Avoid the editor to be larger than the page.
|
||||||
1 / savedWidth,
|
1 / savedWidth,
|
||||||
1 / savedHeight
|
1 / savedHeight
|
||||||
@ -533,161 +620,33 @@ class AnnotationEditor {
|
|||||||
minWidth / savedWidth,
|
minWidth / savedWidth,
|
||||||
minHeight / savedHeight
|
minHeight / savedHeight
|
||||||
);
|
);
|
||||||
const newWidth = round(savedWidth * ratio);
|
} else if (isHorizontal) {
|
||||||
const newHeight = round(savedHeight * ratio);
|
ratioX =
|
||||||
const newX = brX - newWidth;
|
Math.max(
|
||||||
const newY = brY - newHeight;
|
minWidth,
|
||||||
cmd = () => {
|
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.width = newWidth;
|
||||||
this.height = newHeight;
|
this.height = newHeight;
|
||||||
this.x = newX;
|
this.x = newX;
|
||||||
this.y = newY;
|
this.y = newY;
|
||||||
updatePosition(newWidth, newHeight);
|
|
||||||
};
|
this.setDims(parentWidth * newWidth, parentHeight * newHeight);
|
||||||
break;
|
this.fixAndSetPosition();
|
||||||
}
|
|
||||||
case "topMiddle": {
|
|
||||||
const bmY = round(this.y + savedHeight);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case "topRight": {
|
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case "middleRight": {
|
|
||||||
const newWidth = round(
|
|
||||||
Math.max(minWidth, Math.min(1, savedWidth + deltaX / parentWidth))
|
|
||||||
);
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
updatePosition(newWidth, savedHeight);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottomRight": {
|
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case "bottomMiddle": {
|
|
||||||
const newHeight = round(
|
|
||||||
Math.max(minHeight, Math.min(1, savedHeight + deltaY / parentHeight))
|
|
||||||
);
|
|
||||||
cmd = () => {
|
|
||||||
this.height = newHeight;
|
|
||||||
updatePosition(savedWidth, newHeight);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottomLeft": {
|
|
||||||
if (Math.sign(deltaX) * Math.sign(deltaY) > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case "middleLeft": {
|
|
||||||
const mrX = round(savedX + savedWidth);
|
|
||||||
const newWidth = round(
|
|
||||||
Math.max(minWidth, Math.min(1, savedWidth - deltaX / parentWidth))
|
|
||||||
);
|
|
||||||
const newX = mrX - newWidth;
|
|
||||||
cmd = () => {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.x = newX;
|
|
||||||
updatePosition(newWidth, savedHeight);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.addCommands({
|
|
||||||
cmd,
|
|
||||||
undo,
|
|
||||||
mustExec: true,
|
|
||||||
type: AnnotationEditorParamsType.RESIZE,
|
|
||||||
overwriteIfSameType: true,
|
|
||||||
keepUndo: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {
|
||||||
|
:is(.freeTextEditor, .inkEditor, .stampEditor) {
|
||||||
|
& > .resizers {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
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"]
|
||||||
|
: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;
|
cursor: nwse-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingTopMiddle, .resizingBottomMiddle) {
|
&.topMiddle,
|
||||||
|
&.bottomMiddle {
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingTopRight, .resizingBottomLeft) {
|
&.topRight,
|
||||||
|
&.bottomLeft {
|
||||||
cursor: nesw-resize;
|
cursor: nesw-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer:is(.resizingMiddleRight, .resizingMiddleLeft) {
|
&.middleRight,
|
||||||
|
&.middleLeft {
|
||||||
cursor: ew-resize;
|
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…
Reference in New Issue
Block a user