1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 22:58:07 +02:00

[Editor] Add a color picker with predefined colors for highlighting text (bug 1866434)

The doorhanger for highlighting has a basic color picker composed of 5 predefined colors
to set the default color to use.
These colors can be changed thanks to a preference for now but it's something which could
be changed in the Firefox settings in the future.
Each highlight has in its own toolbar a color picker to just change its color.
The different color pickers are so similar (modulo few differences in their styles) that
this patch introduces a new class ColorPicker which provides a color picker component
which could be reused in future editors.
All in all, a large part of this patch is dedicated to color picker itself and its style
and the rest is almost a matter of wiring the component.
This commit is contained in:
Calixte Denizet 2023-11-30 16:21:13 +01:00
parent c0436013a0
commit ff23d37fa2
22 changed files with 573 additions and 87 deletions

View file

@ -904,6 +904,38 @@
}
}
.colorPicker {
--hover-outline-color: #0250bb;
--selected-outline-color: #0060df;
--swatch-border-color: #cfcfd8;
@media (prefers-color-scheme: dark) {
--hover-outline-color: #80ebff;
--selected-outline-color: #aaf2ff;
--swatch-border-color: #52525e;
}
@media screen and (forced-colors: active) {
--hover-outline-color: Highlight;
--selected-outline-color: var(--hover-outline-color);
--swatch-border-color: ButtonText;
}
.swatch {
width: 16px;
height: 16px;
border: 1px solid var(--swatch-border-color);
border-radius: 100%;
outline-offset: 2px;
box-sizing: border-box;
forced-color-adjust: none;
}
button:is(:hover, .selected) > .swatch {
border: none;
}
}
.annotationEditorLayer {
&[data-main-rotation="0"] {
.highlightEditor > .editToolbar {
@ -962,7 +994,144 @@
}
.editToolbar {
--editor-toolbar-colorpicker-arrow-image: url(images/toolbarButton-menuArrow.svg);
transform-origin: center !important;
.buttons {
.colorPicker {
position: relative;
width: auto;
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
padding: 4px;
&::after {
content: "";
mask-image: var(--editor-toolbar-colorpicker-arrow-image);
mask-repeat: no-repeat;
mask-position: center;
display: inline-block;
background-color: var(--editor-toolbar-fg-color);
width: 12px;
height: 12px;
}
&:hover::after {
background-color: var(--editor-toolbar-hover-fg-color);
}
&:has(.dropdown:not(.hidden)) {
background-color: var(--editor-toolbar-hover-bg-color);
}
.dropdown {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 11px;
padding-block: 8px;
border-radius: 6px;
background-color: var(--editor-toolbar-bg-color);
border: 1px solid var(--editor-toolbar-border-color);
box-shadow: var(--editor-toolbar-shadow);
inset-block-start: calc(100% + 4px);
width: calc(100% + 2 * var(--editor-toolbar-padding));
button {
width: 100%;
height: auto;
border: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
background: none;
&:is(:active, :focus-visible) {
outline: none;
}
> .swatch {
outline-offset: 2px;
}
&[aria-selected="true"] > .swatch {
outline: 2px solid var(--selected-outline-color);
}
&:is(:hover, :active, :focus-visible) > .swatch {
outline: 2px solid var(--hover-outline-color);
}
}
}
}
}
}
}
}
.editorParamsToolbar:has(#highlightParamsToolbarContainer) {
padding: unset;
}
#highlightParamsToolbarContainer {
height: auto;
padding-inline: 10px;
padding-block: 10px 16px;
display: flex;
flex-direction: column;
box-sizing: border-box;
.colorPicker {
display: flex;
flex-direction: column;
gap: 8px;
#highlightColorPickerLabel {
width: fit-content;
inset-inline-start: 0;
}
.dropdown {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
height: auto;
button {
width: auto;
height: auto;
border: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
background: none;
flex: 0 0 auto;
.swatch {
width: 24px;
height: 24px;
}
&:is(:active, :focus-visible) {
outline: none;
}
&[aria-selected="true"] > .swatch {
outline: 2px solid var(--selected-outline-color);
}
&:is(:hover, :active, :focus-visible) > .swatch {
outline: 2px solid var(--hover-outline-color);
}
}
}
}
}

View file

@ -28,8 +28,6 @@ class AnnotationEditorParams {
#bindListeners({
editorFreeTextFontSize,
editorFreeTextColor,
editorHighlightColor,
editorHighlightOpacity,
editorInkColor,
editorInkThickness,
editorInkOpacity,
@ -48,12 +46,6 @@ class AnnotationEditorParams {
editorFreeTextColor.addEventListener("input", function () {
dispatchEvent("FREETEXT_COLOR", this.value);
});
editorHighlightColor.addEventListener("input", function () {
dispatchEvent("HIGHLIGHT_COLOR", this.value);
});
editorHighlightOpacity.addEventListener("input", function () {
dispatchEvent("HIGHLIGHT_OPACITY", this.valueAsNumber);
});
editorInkColor.addEventListener("input", function () {
dispatchEvent("INK_COLOR", this.value);
});
@ -76,12 +68,6 @@ class AnnotationEditorParams {
case AnnotationEditorParamsType.FREETEXT_COLOR:
editorFreeTextColor.value = value;
break;
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
editorHighlightColor.value = value;
break;
case AnnotationEditorParamsType.HIGHLIGHT_OPACITY:
editorHighlightOpacity.value = value;
break;
case AnnotationEditorParamsType.INK_COLOR:
editorInkColor.value = value;
break;

View file

@ -442,6 +442,7 @@ const PDFViewerApplication = {
textLayerMode: AppOptions.get("textLayerMode"),
annotationMode: AppOptions.get("annotationMode"),
annotationEditorMode,
annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
imageResourcesPath: AppOptions.get("imageResourcesPath"),
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
isOffscreenCanvasSupported,

View file

@ -158,6 +158,11 @@ const defaultOptions = {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
highlightEditorColors: {
/** @type {string} */
value: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
historyUpdateUrl: {
/** @type {boolean} */
value: false,

View file

@ -36,8 +36,14 @@
}
&.highlight {
--blend-mode: multiply;
@media screen and (forced-colors: active) {
--blend-mode: difference;
}
position: absolute;
mix-blend-mode: multiply;
mix-blend-mode: var(--blend-mode);
fill-rule: evenodd;
}

View file

@ -109,6 +109,8 @@ function isValidAnnotationEditorMode(mode) {
* @property {number} [annotationEditorMode] - Enables the creation and editing
* of new Annotations. The constants from {@link AnnotationEditorType} should
* be used. The default value is `AnnotationEditorType.NONE`.
* @property {string} [annotationEditorHighlightColors] - A comma separated list
* of colors to propose to highlight some text in the pdf.
* @property {string} [imageResourcesPath] - Path for image resources, mainly
* mainly for annotation icons. Include trailing slash.
* @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of
@ -202,6 +204,8 @@ class PDFViewer {
#altTextManager = null;
#annotationEditorHighlightColors = null;
#annotationEditorMode = AnnotationEditorType.NONE;
#annotationEditorUIManager = null;
@ -276,6 +280,8 @@ class PDFViewer {
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
this.#annotationEditorMode =
options.annotationEditorMode ?? AnnotationEditorType.NONE;
this.#annotationEditorHighlightColors =
options.annotationEditorHighlightColors || null;
this.imageResourcesPath = options.imageResourcesPath || "";
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
@ -862,8 +868,13 @@ class PDFViewer {
this.#altTextManager,
this.eventBus,
pdfDocument,
this.pageColors
this.pageColors,
this.#annotationEditorHighlightColors
);
this.eventBus.dispatch("annotationeditoruimanager", {
source: this,
uiManager: this.#annotationEditorUIManager,
});
if (mode !== AnnotationEditorType.NONE) {
this.#annotationEditorUIManager.updateMode(mode);
}

View file

@ -32,6 +32,7 @@ const {
AnnotationMode,
build,
CMapCompressionType,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,
DrawLayer,
@ -80,6 +81,7 @@ export {
AnnotationMode,
build,
CMapCompressionType,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,
DrawLayer,

View file

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { AnnotationEditorType, noContextMenu } from "pdfjs-lib";
import { AnnotationEditorType, ColorPicker, noContextMenu } from "pdfjs-lib";
import {
DEFAULT_SCALE,
DEFAULT_SCALE_VALUE,
@ -120,9 +120,24 @@ class Toolbar {
// Bind the event listeners for click and various other actions.
this.#bindListeners(options);
if (options.editorHighlightColorPicker) {
this.eventBus._on("annotationeditoruimanager", ({ uiManager }) => {
this.#setAnnotationEditorUIManager(
uiManager,
options.editorHighlightColorPicker
);
});
}
this.reset();
}
#setAnnotationEditorUIManager(uiManager, parentContainer) {
const colorPicker = new ColorPicker({ uiManager });
uiManager.setMainHighlightColorPicker(colorPicker);
parentContainer.append(colorPicker.renderMainDropdown());
}
setPageNumber(pageNumber, pageLabel) {
this.pageNumber = pageNumber;
this.pageLabel = pageLabel;

View file

@ -532,6 +532,11 @@ body {
.editorParamsToolbarContainer .editorParamsLabel {
padding-inline-end: 10px;
flex: none;
font: menu;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 150%;
color: var(--main-color);
}

View file

@ -172,14 +172,9 @@ See https://github.com/adobe-type-tools/cmap-resources
</div> <!-- findbar -->
<div class="editorParamsToolbar hidden doorHangerRight" id="editorHighlightParamsToolbar">
<div class="editorParamsToolbarContainer">
<div class="editorParamsSetter">
<label for="editorHighlightColor" class="editorParamsLabel" data-l10n-id="editor_highlight_color">Color</label>
<input type="color" value="#FFFF00" id="editorHighlightColor" class="editorParamsColor" tabindex="100">
</div>
<div class="editorParamsSetter">
<label for="editorHighlightOpacity" class="editorParamsLabel" data-l10n-id="editor_highlight_opacity">Opacity</label>
<input type="range" id="editorHighlightOpacity" class="editorParamsSlider" value="100" min="1" max="100" step="1" tabindex="101">
<div id="highlightParamsToolbarContainer" class="editorParamsToolbarContainer">
<div id="editorHighlightColorPicker" class="colorPicker">
<span id="highlightColorPickerLabel" class="editorParamsLabel" data-l10n-id="pdfjs-editor-highlight-colorpicker-label">Highlight color</span>
</div>
</div>
</div>
@ -217,7 +212,7 @@ See https://github.com/adobe-type-tools/cmap-resources
<div class="editorParamsToolbar hidden doorHangerRight" id="editorStampParamsToolbar">
<div class="editorParamsToolbarContainer">
<button id="editorStampAddImage" class="secondaryToolbarButton" title="Add image" tabindex="107" data-l10n-id="pdfjs-editor-stamp-add-image-button">
<span data-l10n-id="pdfjs-editor-stamp-add-image-button-label">Add image</span>
<span class="editorParamsLabel" data-l10n-id="pdfjs-editor-stamp-add-image-button-label">Add image</span>
</button>
</div>
</div>

View file

@ -61,6 +61,9 @@ function getViewerConfiguration() {
editorHighlightParamsToolbar: document.getElementById(
"editorHighlightParamsToolbar"
),
editorHighlightColorPicker: document.getElementById(
"editorHighlightColorPicker"
),
editorInkButton: document.getElementById("editorInk"),
editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"),
editorStampButton: document.getElementById("editorStamp"),
@ -168,8 +171,6 @@ function getViewerConfiguration() {
annotationEditorParams: {
editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
editorFreeTextColor: document.getElementById("editorFreeTextColor"),
editorHighlightColor: document.getElementById("editorHighlightColor"),
editorHighlightOpacity: document.getElementById("editorHighlightOpacity"),
editorInkColor: document.getElementById("editorInkColor"),
editorInkThickness: document.getElementById("editorInkThickness"),
editorInkOpacity: document.getElementById("editorInkOpacity"),