diff --git a/addons/web/static/src/views/kanban/kanban_compiler.js b/addons/web/static/src/views/kanban/kanban_compiler.js index 23419b88fbc..7524d4ac4f2 100644 --- a/addons/web/static/src/views/kanban/kanban_compiler.js +++ b/addons/web/static/src/views/kanban/kanban_compiler.js @@ -8,7 +8,7 @@ import { getTag, } from "@web/core/utils/xml"; import { toStringExpression } from "@web/views/utils"; -import { ViewCompiler } from "@web/views/view_compiler"; +import { toInterpolatedStringExpression, ViewCompiler } from "@web/views/view_compiler"; /** * @typedef {Object} DropdownDef @@ -18,8 +18,6 @@ import { ViewCompiler } from "@web/views/view_compiler"; * @property {("dropdown" | "toggler" | "menu")[]} parts */ -const INTERP_REGEXP = /\{\{.*?\}\}|#\{.*?\}/g; - const ACTION_TYPES = ["action", "object"]; const SPECIAL_TYPES = [...ACTION_TYPES, "edit", "open", "delete", "url", "set_cover"]; @@ -262,11 +260,7 @@ export class KanbanCompiler extends ViewCompiler { const attrsParts = Object.entries(attrs).map(([key, value]) => { if (key.startsWith("t-attf-")) { key = key.slice(7); - value = value.replace( - INTERP_REGEXP, - (s) => "${" + s.slice(2, s[0] === "{" ? -2 : -1) + "}" - ); - value = toStringExpression(value); + value = toInterpolatedStringExpression(value); } else if (key.startsWith("t-att-")) { key = key.slice(6); value = `"" + (${value})`; diff --git a/addons/web/static/src/views/view_compiler.js b/addons/web/static/src/views/view_compiler.js index 88600efe1c7..75e94f2e4a7 100644 --- a/addons/web/static/src/views/view_compiler.js +++ b/addons/web/static/src/views/view_compiler.js @@ -35,6 +35,28 @@ const BUTTON_CLICK_PARAMS = [ "debounce", ]; const BUTTON_STRING_PROPS = ["string", "size", "title", "icon"]; +const INTERP_REGEXP = /(\{\{|#\{)(.*?)(\}{1,2})/g; + +/** + * @param {string} str + * @returns {string} the interpolated string to be injected into a component's node props. + */ +export function toInterpolatedStringExpression(str) { + const matches = str.matchAll(INTERP_REGEXP); + const parts = []; + let searchString = str; + for (const [match, head, expr] of matches) { + const index = searchString.indexOf(head); + const left = searchString.slice(0, index); + if (left) { + parts.push(toStringExpression(left)); + } + parts.push(`(${expr})`); + searchString = searchString.slice(index + match.length); + } + parts.push(toStringExpression(searchString)); + return parts.join("+"); +} /** * @param {Element} el @@ -72,15 +94,8 @@ export function assignOwlDirectives(target, ...sources) { for (const { name, value } of source.attributes) { if (name.startsWith("t-attf-")) { const propName = name.slice(7); - const tAttf = value - .split("}}") - .map((leftAndExpr) => { - const [left, expr] = leftAndExpr.split("{{"); - const part = toStringExpression(left); - return expr ? part + `+${expr}+` : part; - }) - .join(""); - target.setAttribute(propName, tAttf); + const interpolatedExpression = toInterpolatedStringExpression(value); + target.setAttribute(propName, interpolatedExpression); } else if (name.startsWith("t-att-")) { const propName = name.slice(6); target.setAttribute(propName, value);