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: `

[Test]

`, + stepFunction: setColor('rgb(255, 0, 0)', 'color'), + contentAfter: `
[

Test

]
`, + }); + + await testEditor(BasicEditor, { + contentBefore: `

[Test]

`, + stepFunction: setColor('rgb(255, 0, 0)', 'color'), + contentAfter: `
[

Test

]
`, + }); + }); 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: `

[Test]

`, + stepFunction: bold, + contentAfter: `

[Test]

`, + }); + await testEditor(BasicEditor, { + contentBefore: `

[Test]

`, + stepFunction: bold, + contentAfter: `

[Test]

`, + }); + }); + it('should make qweb tag bold even with partial selection', async () => { + await testEditor(BasicEditor, { + contentBefore: `

T[e]st

`, + stepFunction: bold, + contentAfter: `

T[e]st

`, + }); + }); 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: `

[Test]

`, + stepFunction: italic, + contentAfter: `

[Test]

`, + }); + }); 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: `

[Test]

`, + stepFunction: underline, + contentAfter: `

[Test]

`, + }); + }); 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: `

[Test]

`, + stepFunction: strikeThrough, + contentAfter: `

[Test]

`, + }); + }); 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: `

[Test]

`, + stepFunction: setFontSize('36px'), + contentAfter: `

[Test]

`, + }); + }); it('should change the font size of a whole heading after a triple click', async () => { await testEditor(BasicEditor, { contentBefore: '

[ab

]cd

',