[ADD] web_editor: apply colors to whole selected table cells
With this, when whole table cells are selected and the user picks a color in the toolbar's colorpicker, that color is applied to the whole cell rather than to the text only. task-2858455 Part-of: odoo/odoo#92200
This commit is contained in:
@@ -778,13 +778,20 @@ export const editorCommands = {
|
||||
* @param {Element} [element]
|
||||
*/
|
||||
applyColor: (editor, color, mode, element) => {
|
||||
if (element) {
|
||||
const selectedTds = editor.editable.querySelectorAll('td.o_selected_td');
|
||||
let coloredTds = [];
|
||||
if (selectedTds.length) {
|
||||
for (const td of selectedTds) {
|
||||
colorElement(td, color, mode);
|
||||
}
|
||||
coloredTds = [...selectedTds];
|
||||
} else if (element) {
|
||||
colorElement(element, color, mode);
|
||||
return [element];
|
||||
}
|
||||
const selection = editor.document.getSelection();
|
||||
let wasCollapsed = false;
|
||||
if (selection.getRangeAt(0).collapsed) {
|
||||
if (selection.getRangeAt(0).collapsed && !selectedTds.length) {
|
||||
insertAndSelectZws(selection);
|
||||
wasCollapsed = true;
|
||||
}
|
||||
@@ -792,7 +799,7 @@ export const editorCommands = {
|
||||
if (!range) return;
|
||||
const restoreCursor = preserveCursor(editor.document);
|
||||
// Get the <font> nodes to color
|
||||
const selectedNodes = getSelectedNodes(editor.editable);
|
||||
const selectedNodes = getSelectedNodes(editor.editable).filter(node => !closestElement(node, 'table.o_selected_table'));
|
||||
const fonts = selectedNodes.flatMap(node => {
|
||||
let font = closestElement(node, 'font');
|
||||
const children = font && descendants(font);
|
||||
@@ -850,7 +857,7 @@ export const editorCommands = {
|
||||
newSelection.removeAllRanges();
|
||||
newSelection.addRange(range);
|
||||
}
|
||||
return fonts;
|
||||
return [...fonts, ...coloredTds];
|
||||
},
|
||||
// Table
|
||||
insertTable: (editor, { rowNumber = 2, colNumber = 2 } = {}) => {
|
||||
|
||||
@@ -4101,6 +4101,119 @@ X[]
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('color', () => {
|
||||
it('should apply a color to some characters and a table', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a[bc</p><table><tbody><tr>' +
|
||||
'<td>a]b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<p>a<font style="color: aquamarine;">[bc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a]b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to a table and some characters', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody><tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>e[f</td>' +
|
||||
'</tr></tbody></table><p>a]bc</p>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">e[f</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p><font style="color: aquamarine;">a]</font>bc</p>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to some characters, a table and some more characters', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a[bc</p>' +
|
||||
'<table><tbody><tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p>a]bc</p>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<p>a<font style="color: aquamarine;">[bc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p><font style="color: aquamarine;">a]</font>bc</p>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to some characters, a table, some more characters and another table', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a[bc</p>' +
|
||||
'<table><tbody><tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p>abc</p>' +
|
||||
'<table><tbody><tr>' +
|
||||
'<td>a]b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<p>a<font style="color: aquamarine;">[bc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p><font style="color: aquamarine;">abc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a]b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to some characters, a table, some more characters, another table and some more characters', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a[bc</p>' +
|
||||
'<table><tbody><tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p>abc</p>' +
|
||||
'<table><tbody><tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p>a]bc</p>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<p>a<font style="color: aquamarine;">[bc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p><font style="color: aquamarine;">abc</font></p>' +
|
||||
'<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr></tbody></table>' +
|
||||
'<p><font style="color: aquamarine;">a]</font>bc</p>',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('select columns on cross over', () => {
|
||||
describe('select', () => {
|
||||
@@ -4352,6 +4465,155 @@ X[]
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('color', () => {
|
||||
it('should apply a color to two columns', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody><tr>' +
|
||||
'<td>a[b</td>' +
|
||||
'<td>c]d</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a[b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">c]d</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr></tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to a whole row', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody><tr>' +
|
||||
'<td>a[b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>e]f</td>' +
|
||||
'</tr><tr><td>ab</td><td>cd</td><td>ef</td></tr></tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody><tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a[b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">e]f</td>' +
|
||||
'</tr><tr><td>ab</td><td>cd</td><td>ef</td></tr></tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to a whole column', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody>' +
|
||||
'<tr>' +
|
||||
'<td>a[b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>a]b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a[b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a]b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color from (0,0) to (1,1) in a 3x3 table', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody>' +
|
||||
'<tr>' +
|
||||
'<td>a[b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>c]d</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a[b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">c]d</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
});
|
||||
});
|
||||
it('should apply a color to a whole table', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<table><tbody>' +
|
||||
'<tr>' +
|
||||
'<td>a[b</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td>ab</td>' +
|
||||
'<td>cd</td>' +
|
||||
'<td>e]f</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
stepFunction: async editor => editor.execCommand('applyColor', 'aquamarine', 'color'),
|
||||
contentAfterEdit: '<table class="o_selected_table"><tbody>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">a[b</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ef</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">ab</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">cd</td>' +
|
||||
'<td class="o_selected_td" style="color: aquamarine;">e]f</td>' +
|
||||
'</tr>' +
|
||||
'</tbody></table>',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,7 @@ const preserveCursor = OdooEditorLib.preserveCursor;
|
||||
const closestElement = OdooEditorLib.closestElement;
|
||||
const setSelection = OdooEditorLib.setSelection;
|
||||
const endPos = OdooEditorLib.endPos;
|
||||
const hasValidSelection = OdooEditorLib.hasValidSelection;
|
||||
|
||||
var id = 0;
|
||||
const basicMediaSelector = 'img, .fa, .o_image, .media_iframe_video';
|
||||
@@ -1576,6 +1577,12 @@ const Wysiwyg = Widget.extend({
|
||||
// Unstash the mutations now that the color is picked.
|
||||
this.odooEditor.historyUnstash();
|
||||
this._processAndApplyColor(eventName, ev.data.color);
|
||||
// Deselect tables so the applied color can be seen
|
||||
// without using `!important` (otherwise the selection
|
||||
// hides it).
|
||||
if (this.odooEditor.deselectTable() && hasValidSelection(this.odooEditor.editable)) {
|
||||
this.odooEditor.document.getSelection().collapseToStart();
|
||||
}
|
||||
this._updateEditorUI();
|
||||
});
|
||||
colorpicker.on('color_hover', null, ev => {
|
||||
@@ -1584,7 +1591,7 @@ const Wysiwyg = Widget.extend({
|
||||
}
|
||||
this.odooEditor.historyPauseSteps();
|
||||
try {
|
||||
this._processAndApplyColor(eventName, ev.data.color);
|
||||
this._processAndApplyColor(eventName, ev.data.color, true);
|
||||
} finally {
|
||||
this.odooEditor.historyUnpauseSteps();
|
||||
}
|
||||
@@ -1615,25 +1622,34 @@ const Wysiwyg = Widget.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
_processAndApplyColor: function (eventName, color) {
|
||||
_processAndApplyColor: function (eventName, color, previewMode) {
|
||||
if (!color) {
|
||||
color = 'inherit';
|
||||
} else if (!ColorpickerWidget.isCSSColor(color) && !weUtils.isColorGradient(color)) {
|
||||
color = (eventName === "foreColor" ? 'text-' : 'bg-') + color;
|
||||
}
|
||||
const fonts = this.odooEditor.execCommand('applyColor', color, eventName === 'foreColor' ? 'color' : 'backgroundColor', this.lastMediaClicked);
|
||||
const coloredElements = this.odooEditor.execCommand('applyColor', color, eventName === 'foreColor' ? 'color' : 'backgroundColor', this.lastMediaClicked);
|
||||
|
||||
// Ensure the selection in the fonts tags, otherwise an undetermined
|
||||
// race condition could generate a wrong selection later.
|
||||
const first = fonts[0];
|
||||
const last = fonts[fonts.length - 1];
|
||||
const coloredTds = coloredElements.filter(coloredElement => coloredElement.classList.contains('o_selected_td'));
|
||||
if (coloredTds.length) {
|
||||
const propName = eventName === 'foreColor' ? 'color' : 'background-color';
|
||||
for (const td of coloredTds) {
|
||||
// Make it important so it has priority over selection color.
|
||||
td.style.setProperty(propName, td.style[propName], previewMode ? 'important' : '');
|
||||
}
|
||||
} else {
|
||||
// Ensure the selection in the fonts tags, otherwise an undetermined
|
||||
// race condition could generate a wrong selection later.
|
||||
const first = coloredElements[0];
|
||||
const last = coloredElements[coloredElements.length - 1];
|
||||
|
||||
const sel = this.odooEditor.document.getSelection();
|
||||
sel.removeAllRanges();
|
||||
const range = new Range();
|
||||
range.setStart(first, 0);
|
||||
range.setEnd(...endPos(last));
|
||||
sel.addRange(range);
|
||||
const sel = this.odooEditor.document.getSelection();
|
||||
sel.removeAllRanges();
|
||||
const range = new Range();
|
||||
range.setStart(first, 0);
|
||||
range.setEnd(...endPos(last));
|
||||
sel.addRange(range);
|
||||
}
|
||||
|
||||
const hexColor = this._colorToHex(color);
|
||||
this.odooEditor.updateColorpickerLabels({
|
||||
|
||||
Reference in New Issue
Block a user