[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) <lpe@odoo.com>
This commit is contained in:
committed by
Lucas Perais
parent
d5b839f713
commit
ffef01e327
@@ -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');
|
||||
|
||||
@@ -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 <font>s and remove uncolored fonts.
|
||||
const fontsSet = new Set(fonts);
|
||||
for (const font of fontsSet) {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -19,6 +19,19 @@ describe('applyColor', () => {
|
||||
'<font><span>g</span>h</font>i</p>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to the qweb tag', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: setColor('rgb(255, 0, 0)', 'color'),
|
||||
contentAfter: `<div>[<p t-esc="'Test'" contenteditable="false" style="color: rgb(255, 0, 0);">Test</p>]</div>`,
|
||||
});
|
||||
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-field="record.display_name" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: setColor('rgb(255, 0, 0)', 'color'),
|
||||
contentAfter: `<div>[<p t-field="record.display_name" contenteditable="false" style="color: rgb(255, 0, 0);">Test</p>]</div>`,
|
||||
});
|
||||
});
|
||||
it('should apply a background color to a slice of text in a span in a font', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a<font>b<span>c[def]g</span>h</font>i</p>',
|
||||
|
||||
@@ -77,6 +77,25 @@ describe('Format', () => {
|
||||
contentAfter: `<p>[abc</p><p>def]</p>`,
|
||||
});
|
||||
});
|
||||
it('should make qweb tag bold', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: bold,
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="font-weight: bolder;">[Test]</p></div>`,
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-field="record.name" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: bold,
|
||||
contentAfter: `<div><p t-field="record.name" contenteditable="false" style="font-weight: bolder;">[Test]</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should make qweb tag bold even with partial selection', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">T[e]st</p></div>`,
|
||||
stepFunction: bold,
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="font-weight: bolder;">T[e]st</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should make a whole heading bold after a triple click', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<h1>${notStrong(`[ab`)}</h1><p>]cd</p>`,
|
||||
@@ -251,6 +270,13 @@ describe('Format', () => {
|
||||
contentAfter: `<p>[abc</p><p>def]</p>`,
|
||||
});
|
||||
});
|
||||
it('should make qweb tag italic', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: italic,
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="font-style: italic;">[Test]</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should make a whole heading italic after a triple click', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<h1>[ab</h1><p>]cd</p>`,
|
||||
@@ -339,6 +365,13 @@ describe('Format', () => {
|
||||
contentAfter: '<p>[abc</p><p>def]</p>',
|
||||
});
|
||||
});
|
||||
it('should make qweb tag underline', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: underline,
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="text-decoration-line: underline;">[Test]</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should make a whole heading underline after a triple click', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<h1>[ab</h1><p>]cd</p>`,
|
||||
@@ -483,6 +516,13 @@ describe('Format', () => {
|
||||
contentAfter: '<p>[abc</p><p>def]</p>',
|
||||
});
|
||||
});
|
||||
it('should make qweb tag strikeThrough', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: strikeThrough,
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="text-decoration-line: line-through;">[Test]</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should make a whole heading strikeThrough after a triple click', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<h1>[ab</h1><p>]cd</p>`,
|
||||
@@ -728,6 +768,13 @@ describe('Format', () => {
|
||||
contentAfter: '<p>ab<span style="font-size: 10px;">[cde]</span>fg</p>',
|
||||
});
|
||||
});
|
||||
it('should change the font size the qweb tag', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: `<div><p t-esc="'Test'" contenteditable="false">[Test]</p></div>`,
|
||||
stepFunction: setFontSize('36px'),
|
||||
contentAfter: `<div><p t-esc="'Test'" contenteditable="false" style="font-size: 36px;">[Test]</p></div>`,
|
||||
});
|
||||
});
|
||||
it('should change the font size of a whole heading after a triple click', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<h1>[ab</h1><p>]cd</p>',
|
||||
|
||||
Reference in New Issue
Block a user