[REF] web_editor,mass_mailing,website: make closestElement support predicate

To improve the api, make closestElement take a predicate as an
argument.

task-2984709

Part-of: odoo/odoo#105179
This commit is contained in:
Nicolas Bayet
2023-09-07 14:45:18 +00:00
parent e334606588
commit d0ab79a296
4 changed files with 28 additions and 14 deletions
@@ -2988,8 +2988,8 @@ export class OdooEditor extends EventTarget {
}
}
if (sel.rangeCount) {
const closestStartContainer = closestElement(sel.getRangeAt(0).startContainer, '*');
const selectionStartStyle = getComputedStyle(closestStartContainer);
const closestStartContainer = closestElement(sel.getRangeAt(0).startContainer);
const selectionStartStyle = closestStartContainer && getComputedStyle(closestStartContainer);
// queryCommandState does not take stylesheets into account
for (const format of ['bold', 'italic', 'underline', 'strikeThrough', 'switchDirection']) {
@@ -285,22 +285,27 @@ export function getFurthestUneditableParent(node, parentLimit) {
return nonEditableElement;
}
/**
* Returns the closest HTMLElement of the provided Node
* if a 'selector' is provided, Returns the closest HTMLElement that match the selector
* Returns the closest HTMLElement of the provided Node. If the predicate is a
* string, returns the closest HTMLElement that match the predicate selector. If
* the predicate is a function, returns the closest element that matches the
* predicate. Any returned element will be contained within the editable.
*
* @param {Node} node
* @param {string} [selector=undefined]
* @returns {HTMLElement}
* @param {string | Function} [predicate='*']
* @returns {HTMLElement|null}
*/
export function closestElement(node, selector) {
let element = node;
while (element && element.nodeType !== Node.ELEMENT_NODE) {
element = element.parentNode;
export function closestElement(node, predicate = "*") {
if (!node) return null;
let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (typeof predicate === 'function') {
while (element && !predicate(element)) {
element = element.parentElement;
}
} else {
element = element?.closest(predicate);
}
if (element && selector) {
element = element.closest(selector);
}
return element && element.querySelector('.odoo-editor-editable') ? null : element;
return element?.closest('.odoo-editor-editable') && element;
}
/**
@@ -64,6 +64,7 @@ describe('Powerbox', () => {
describe('class', () => {
it('should properly order default commands and categories', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
const powerbox = new Powerbox({
categories: [
@@ -97,6 +98,7 @@ describe('Powerbox', () => {
});
it('should navigate through commands with arrow keys', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
const powerbox = new Powerbox({
categories: [],
@@ -122,6 +124,7 @@ describe('Powerbox', () => {
});
it('should execute command on press Enter', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
const powerbox = new Powerbox({
categories: [],
@@ -144,6 +147,7 @@ describe('Powerbox', () => {
});
it('should filter commands with `commandFilters`', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
const powerbox = new Powerbox({
categories: [],
@@ -169,6 +173,7 @@ describe('Powerbox', () => {
});
it('should filter commands with `isDisabled`', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
let disableCommands = false;
const powerbox = new Powerbox({
@@ -198,6 +203,7 @@ describe('Powerbox', () => {
});
it('should filter commands with filter text', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
editable.append(document.createTextNode('original text'));
setSelection(editable.firstChild, 13);
@@ -236,6 +242,7 @@ describe('Powerbox', () => {
});
it('should close the Powerbox on remove last filter text with Backspace', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
editable.append(document.createTextNode('1'));
setSelection(editable.firstChild, 13);
@@ -277,6 +284,7 @@ describe('Powerbox', () => {
});
it('should close the Powerbox on press Escape', async () => {
const editable = document.createElement('div');
editable.classList.add('odoo-editor-editable');
document.body.append(editable);
const powerbox = new Powerbox({
categories: [],
@@ -55,6 +55,7 @@ const cleanTestHtml = () => {
const insertTestHtml = innerHtml => {
cleanTestHtml();
const container = document.createElement('DIV');
container.classList.add('odoo-editor-editable');
container.setAttribute('contenteditable', true);
container.innerHTML = innerHtml;
document.body.appendChild(container);