[Editor] Add a color picker in the toolbar of Ink and Freetext annotations
This commit is contained in:
parent
e0783cd075
commit
dbd6f8cdd4
@ -306,9 +306,13 @@ pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fo
|
|||||||
|
|
||||||
pdfjs-editor-free-text-button =
|
pdfjs-editor-free-text-button =
|
||||||
.title = Text
|
.title = Text
|
||||||
|
pdfjs-editor-color-picker-free-text-input =
|
||||||
|
.title = Change text color
|
||||||
pdfjs-editor-free-text-button-label = Text
|
pdfjs-editor-free-text-button-label = Text
|
||||||
pdfjs-editor-ink-button =
|
pdfjs-editor-ink-button =
|
||||||
.title = Draw
|
.title = Draw
|
||||||
|
pdfjs-editor-color-picker-ink-input =
|
||||||
|
.title = Change drawing color
|
||||||
pdfjs-editor-ink-button-label = Draw
|
pdfjs-editor-ink-button-label = Draw
|
||||||
pdfjs-editor-stamp-button =
|
pdfjs-editor-stamp-button =
|
||||||
.title = Add or edit images
|
.title = Add or edit images
|
||||||
|
|||||||
@ -17,6 +17,11 @@ import { AnnotationEditorParamsType, shadow } from "../../shared/util.js";
|
|||||||
import { KeyboardManager } from "./tools.js";
|
import { KeyboardManager } from "./tools.js";
|
||||||
import { noContextMenu } from "../display_utils.js";
|
import { noContextMenu } from "../display_utils.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColorPicker class provides a color picker for the annotation editor.
|
||||||
|
* It displays a dropdown with some predefined colors and allows the user
|
||||||
|
* to select a color for the annotation.
|
||||||
|
*/
|
||||||
class ColorPicker {
|
class ColorPicker {
|
||||||
#button = null;
|
#button = null;
|
||||||
|
|
||||||
@ -304,4 +309,64 @@ class ColorPicker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ColorPicker };
|
/**
|
||||||
|
* BasicColorPicker class provides a simple color picker.
|
||||||
|
* It displays an input element (with type="color") that allows the user
|
||||||
|
* to select a color for the annotation.
|
||||||
|
*/
|
||||||
|
class BasicColorPicker {
|
||||||
|
#input = null;
|
||||||
|
|
||||||
|
#editor = null;
|
||||||
|
|
||||||
|
#uiManager = null;
|
||||||
|
|
||||||
|
static #l10nColor = null;
|
||||||
|
|
||||||
|
constructor(editor) {
|
||||||
|
this.#editor = editor;
|
||||||
|
this.#uiManager = editor._uiManager;
|
||||||
|
|
||||||
|
BasicColorPicker.#l10nColor ||= Object.freeze({
|
||||||
|
freetext: "pdfjs-editor-color-picker-free-text-input",
|
||||||
|
ink: "pdfjs-editor-color-picker-ink-input",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton() {
|
||||||
|
if (this.#input) {
|
||||||
|
return this.#input;
|
||||||
|
}
|
||||||
|
const { editorType, colorType, colorValue } = this.#editor;
|
||||||
|
const input = (this.#input = document.createElement("input"));
|
||||||
|
input.type = "color";
|
||||||
|
input.value = colorValue || "#000000";
|
||||||
|
input.className = "basicColorPicker";
|
||||||
|
input.tabIndex = 0;
|
||||||
|
input.setAttribute("data-l10n-id", BasicColorPicker.#l10nColor[editorType]);
|
||||||
|
input.addEventListener(
|
||||||
|
"input",
|
||||||
|
() => {
|
||||||
|
this.#uiManager.updateParams(colorType, input.value);
|
||||||
|
},
|
||||||
|
{ signal: this.#uiManager._signal }
|
||||||
|
);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(value) {
|
||||||
|
if (!this.#input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#input.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.#input?.remove();
|
||||||
|
this.#input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideDropdown() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BasicColorPicker, ColorPicker };
|
||||||
|
|||||||
@ -67,6 +67,8 @@ class DrawingEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#mustBeCommitted;
|
#mustBeCommitted;
|
||||||
|
|
||||||
|
_colorPicker = null;
|
||||||
|
|
||||||
_drawId = null;
|
_drawId = null;
|
||||||
|
|
||||||
static _currentDrawId = -1;
|
static _currentDrawId = -1;
|
||||||
@ -240,6 +242,9 @@ class DrawingEditor extends AnnotationEditor {
|
|||||||
this._drawId,
|
this._drawId,
|
||||||
options.toSVGProperties()
|
options.toSVGProperties()
|
||||||
);
|
);
|
||||||
|
if (type === this.colorType) {
|
||||||
|
this._colorPicker?.update(val);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.addCommands({
|
this.addCommands({
|
||||||
cmd: setter.bind(this, value),
|
cmd: setter.bind(this, value),
|
||||||
|
|||||||
@ -1059,7 +1059,7 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the toolbar buttons for this editor.
|
* Get the toolbar buttons for this editor.
|
||||||
* @returns {Array<Array<string|object>>|null}
|
* @returns {Array<Array<string|object|null>>|null}
|
||||||
*/
|
*/
|
||||||
get toolbarButtons() {
|
get toolbarButtons() {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import {
|
|||||||
} from "../../shared/util.js";
|
} from "../../shared/util.js";
|
||||||
import { AnnotationEditorUIManager, KeyboardManager } from "./tools.js";
|
import { AnnotationEditorUIManager, KeyboardManager } from "./tools.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
|
import { BasicColorPicker } from "./color_picker.js";
|
||||||
import { FreeTextAnnotationElement } from "../annotation_layer.js";
|
import { FreeTextAnnotationElement } from "../annotation_layer.js";
|
||||||
|
|
||||||
const EOL_PATTERN = /\r\n?|\n/g;
|
const EOL_PATTERN = /\r\n?|\n/g;
|
||||||
@ -44,6 +45,8 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#fontSize;
|
#fontSize;
|
||||||
|
|
||||||
|
_colorPicker = null;
|
||||||
|
|
||||||
static _freeTextDefaultContent = "";
|
static _freeTextDefaultContent = "";
|
||||||
|
|
||||||
static _internalPadding = 0;
|
static _internalPadding = 0;
|
||||||
@ -202,6 +205,20 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
get toolbarButtons() {
|
||||||
|
this._colorPicker ||= new BasicColorPicker(this);
|
||||||
|
return [["colorPicker", this._colorPicker]];
|
||||||
|
}
|
||||||
|
|
||||||
|
get colorType() {
|
||||||
|
return AnnotationEditorParamsType.FREETEXT_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
get colorValue() {
|
||||||
|
return this.#color;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the font size and make this action as undoable.
|
* Update the font size and make this action as undoable.
|
||||||
* @param {number} fontSize
|
* @param {number} fontSize
|
||||||
@ -232,6 +249,7 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
#updateColor(color) {
|
#updateColor(color) {
|
||||||
const setColor = col => {
|
const setColor = col => {
|
||||||
this.#color = this.editorDiv.style.color = col;
|
this.#color = this.editorDiv.style.color = col;
|
||||||
|
this._colorPicker?.update(col);
|
||||||
};
|
};
|
||||||
const savedColor = this.#color;
|
const savedColor = this.#color;
|
||||||
this.addCommands({
|
this.addCommands({
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
import { DrawingEditor, DrawingOptions } from "./draw.js";
|
import { DrawingEditor, DrawingOptions } from "./draw.js";
|
||||||
import { InkDrawOutline, InkDrawOutliner } from "./drawers/inkdraw.js";
|
import { InkDrawOutline, InkDrawOutliner } from "./drawers/inkdraw.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
|
import { BasicColorPicker } from "./color_picker.js";
|
||||||
import { InkAnnotationElement } from "../annotation_layer.js";
|
import { InkAnnotationElement } from "../annotation_layer.js";
|
||||||
|
|
||||||
class InkDrawingOptions extends DrawingOptions {
|
class InkDrawingOptions extends DrawingOptions {
|
||||||
@ -177,6 +178,20 @@ class InkEditor extends DrawingEditor {
|
|||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
get toolbarButtons() {
|
||||||
|
this._colorPicker ||= new BasicColorPicker(this);
|
||||||
|
return [["colorPicker", this._colorPicker]];
|
||||||
|
}
|
||||||
|
|
||||||
|
get colorType() {
|
||||||
|
return AnnotationEditorParamsType.INK_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
get colorValue() {
|
||||||
|
return this._drawingOptions.stroke;
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
onScaleChanging() {
|
onScaleChanging() {
|
||||||
if (!this.parent) {
|
if (!this.parent) {
|
||||||
|
|||||||
@ -1805,14 +1805,16 @@ class AnnotationEditorUIManager {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.hasSelection) {
|
||||||
for (const editor of this.#selectedEditors) {
|
for (const editor of this.#selectedEditors) {
|
||||||
editor.updateParams(type, value);
|
editor.updateParams(type, value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
for (const editorType of this.#editorTypes) {
|
for (const editorType of this.#editorTypes) {
|
||||||
editorType.updateDefaultParams(type, value);
|
editorType.updateDefaultParams(type, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showAllEditors(type, visible, updateButton = false) {
|
showAllEditors(type, visible, updateButton = false) {
|
||||||
for (const editor of this.#allEditors.values()) {
|
for (const editor of this.#allEditors.values()) {
|
||||||
|
|||||||
@ -3502,4 +3502,48 @@ describe("FreeText Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("FreeText must update its color", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the text color is the one chosen from the color picker", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([_, page]) => {
|
||||||
|
await switchToFreeText(page);
|
||||||
|
|
||||||
|
const rect = await getRect(page, ".annotationEditorLayer");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
const data = "Hello PDF.js World !!";
|
||||||
|
await page.mouse.click(
|
||||||
|
rect.x + rect.width / 2,
|
||||||
|
rect.y + rect.height / 2
|
||||||
|
);
|
||||||
|
await page.waitForSelector(editorSelector, { visible: true });
|
||||||
|
await page.type(`${editorSelector} .internal`, data);
|
||||||
|
await commit(page);
|
||||||
|
|
||||||
|
const colorPickerSelector = `${editorSelector} input.basicColorPicker`;
|
||||||
|
await page.waitForSelector(colorPickerSelector, { visible: true });
|
||||||
|
await page.locator(colorPickerSelector).fill("#ff0000");
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
sel => {
|
||||||
|
const el = document.querySelector(sel);
|
||||||
|
return getComputedStyle(el).color === "rgb(255, 0, 0)";
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
`${editorSelector} .internal`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1294,3 +1294,45 @@ describe("Should switch from an editor and mode to others by double clicking", (
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Ink must update its color", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the stroke color is the one chosen from the color picker", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([_, page]) => {
|
||||||
|
await switchToInk(page);
|
||||||
|
|
||||||
|
const rect = await getRect(page, ".annotationEditorLayer");
|
||||||
|
|
||||||
|
const x = rect.x + 20;
|
||||||
|
const y = rect.y + 20;
|
||||||
|
const clickHandle = await waitForPointerUp(page);
|
||||||
|
await page.mouse.move(x, y);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(x + 50, y + 50);
|
||||||
|
await page.mouse.up();
|
||||||
|
await awaitPromise(clickHandle);
|
||||||
|
await commit(page);
|
||||||
|
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
const colorPickerSelector = `${editorSelector} input.basicColorPicker`;
|
||||||
|
await page.waitForSelector(colorPickerSelector, { visible: true });
|
||||||
|
await page.locator(colorPickerSelector).fill("#ff0000");
|
||||||
|
|
||||||
|
await page.waitForSelector(
|
||||||
|
".canvasWrapper svg.draw[stroke='#ff0000']",
|
||||||
|
{ visible: true }
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1057,6 +1057,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.basicColorPicker {
|
||||||
|
width: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer {
|
.annotationEditorLayer {
|
||||||
&[data-main-rotation="0"] {
|
&[data-main-rotation="0"] {
|
||||||
.highlightEditor:not(.free) > .editToolbar {
|
.highlightEditor:not(.free) > .editToolbar {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user