From ffef01e327b0263a2af4892e9e438a2c0ef22184 Mon Sep 17 00:00:00 2001
From: Bastien PIERRE
Date: Wed, 4 Oct 2023 09:47:00 +0200
Subject: [PATCH] [IMP] web_editor: Allow to stylize field on report editor
Before this commit, the tags t-field,t-esc,t-out were difficult to select
and couldn't be stylized.
After this commit, when clicking or selecting a tag t-field,t-esc,t-out
the whole tag is automatically selected and a style can be applied onto it.
task-id-3457404
closes odoo/odoo#135265
Related: odoo/enterprise#47089
Signed-off-by: Lucas Perais (lpe)
---
.../static/src/js/backend/QWebPlugin.js | 14 ++++++
.../odoo-editor/src/commands/commands.js | 9 ++++
.../js/editor/odoo-editor/src/utils/utils.js | 12 +++++
.../odoo-editor/test/spec/color.test.js | 13 +++++
.../odoo-editor/test/spec/format.test.js | 47 +++++++++++++++++++
5 files changed, 95 insertions(+)
diff --git a/addons/web_editor/static/src/js/backend/QWebPlugin.js b/addons/web_editor/static/src/js/backend/QWebPlugin.js
index ac1440542e2..28ac503030b 100644
--- a/addons/web_editor/static/src/js/backend/QWebPlugin.js
+++ b/addons/web_editor/static/src/js/backend/QWebPlugin.js
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { ancestors } from '@web_editor/js/common/wysiwyg_utils';
+import { closestElement } from "@web_editor/js/editor/odoo-editor/src/utils/utils";
export class QWebPlugin {
constructor(options = {}) {
@@ -8,6 +9,7 @@ export class QWebPlugin {
if (this._options.editor) {
this._editable = this._options.editor.editable;
this._document = this._options.editor.document;
+ this._selectQwebNode(this._options.editor);
} else {
this._editable = this._options.editable;
this._document = this._options.document || window.document;
@@ -131,6 +133,18 @@ export class QWebPlugin {
}
});
}
+ _selectQwebNode(editor) {
+ editor.addDomListener(editor.document, 'selectionchange', e => {
+ const selection = e.target.getSelection();
+ const qwebNode = selection.anchorNode && closestElement(selection.anchorNode, '[t-field],[t-esc],[t-out]');
+ if (qwebNode){
+ const range = new Range();
+ range.selectNode(qwebNode);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ });
+ }
_makeBranchingSelection() {
const document = this._options.document || window.document;
this._selectElWrapper = document.createElement('div');
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
index 5e1dcc53118..1611b538656 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
@@ -594,6 +594,10 @@ export const editorCommands = {
const selectedNodes = mode === "backgroundColor"
? selectionNodes.filter(node => !closestElement(node, 'table.o_selected_table'))
: selectionNodes;
+ const selectedFieldNodes = new Set(getSelectedNodes(editor.editable)
+ .map(n => closestElement(n, "*[t-field],*[t-out],*[t-esc]"))
+ .filter(Boolean));
+
const fonts = selectedNodes.flatMap(node => {
let font = closestElement(node, 'font') || closestElement(node, 'span');
const children = font && descendants(font);
@@ -642,6 +646,11 @@ export const editorCommands = {
}
return font;
});
+
+ for (const fieldNode of selectedFieldNodes) {
+ colorElement(fieldNode, color, mode);
+ }
+
// Color the selected s and remove uncolored fonts.
const fontsSet = new Set(fonts);
for (const font of fontsSet) {
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js
index e64b6b85cf4..1bb6c062d6c 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js
@@ -1068,6 +1068,10 @@ export const formatSelection = (editor, formatName, {applyStyle, formatProps} =
.filter(n => n.nodeType === Node.TEXT_NODE && closestElement(n).isContentEditable && (isVisibleTextNode(n) || isZWS(n)));
const selectedTextNodes = selectedNodes.length ? selectedNodes : selectedNodesInTds;
+ const selectedFieldNodes = new Set(getSelectedNodes(editor.editable)
+ .map(n =>closestElement(n, "*[t-field],*[t-out],*[t-esc]"))
+ .filter(Boolean));
+
const formatSpec = formatsSpecs[formatName];
for (const selectedTextNode of selectedTextNodes) {
const inlineAncestors = [];
@@ -1117,6 +1121,14 @@ export const formatSelection = (editor, formatName, {applyStyle, formatProps} =
}
}
+ for (const selectedFieldNode of selectedFieldNodes) {
+ if (applyStyle) {
+ formatSpec.addStyle(selectedFieldNode, formatProps);
+ } else {
+ formatSpec.removeStyle(selectedFieldNode);
+ }
+ }
+
if (zws) {
const siblings = [...zws.parentElement.childNodes];
if (
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/color.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/color.test.js
index 0210f31be74..e9bb07f1771 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/color.test.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/color.test.js
@@ -19,6 +19,19 @@ describe('applyColor', () => {
'ghi
',
});
});
+ it('should apply a color to the qweb tag', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: setColor('rgb(255, 0, 0)', 'color'),
+ contentAfter: ``,
+ });
+
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: setColor('rgb(255, 0, 0)', 'color'),
+ contentAfter: ``,
+ });
+ });
it('should apply a background color to a slice of text in a span in a font', async () => {
await testEditor(BasicEditor, {
contentBefore: 'abc[def]ghi
',
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/format.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/format.test.js
index 36aa230dabd..f75f54f628b 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/format.test.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/format.test.js
@@ -77,6 +77,25 @@ describe('Format', () => {
contentAfter: `[abc
def]
`,
});
});
+ it('should make qweb tag bold', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: bold,
+ contentAfter: ``,
+ });
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: bold,
+ contentAfter: ``,
+ });
+ });
+ it('should make qweb tag bold even with partial selection', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: bold,
+ contentAfter: ``,
+ });
+ });
it('should make a whole heading bold after a triple click', async () => {
await testEditor(BasicEditor, {
contentBefore: `${notStrong(`[ab`)}
]cd
`,
@@ -251,6 +270,13 @@ describe('Format', () => {
contentAfter: `[abc
def]
`,
});
});
+ it('should make qweb tag italic', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: italic,
+ contentAfter: ``,
+ });
+ });
it('should make a whole heading italic after a triple click', async () => {
await testEditor(BasicEditor, {
contentBefore: `[ab
]cd
`,
@@ -339,6 +365,13 @@ describe('Format', () => {
contentAfter: '[abc
def]
',
});
});
+ it('should make qweb tag underline', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: underline,
+ contentAfter: ``,
+ });
+ });
it('should make a whole heading underline after a triple click', async () => {
await testEditor(BasicEditor, {
contentBefore: `[ab
]cd
`,
@@ -483,6 +516,13 @@ describe('Format', () => {
contentAfter: '[abc
def]
',
});
});
+ it('should make qweb tag strikeThrough', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: strikeThrough,
+ contentAfter: ``,
+ });
+ });
it('should make a whole heading strikeThrough after a triple click', async () => {
await testEditor(BasicEditor, {
contentBefore: `[ab
]cd
`,
@@ -728,6 +768,13 @@ describe('Format', () => {
contentAfter: 'ab[cde]fg
',
});
});
+ it('should change the font size the qweb tag', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: ``,
+ stepFunction: setFontSize('36px'),
+ contentAfter: ``,
+ });
+ });
it('should change the font size of a whole heading after a triple click', async () => {
await testEditor(BasicEditor, {
contentBefore: '[ab
]cd
',