diff --git a/src/localize.js b/src/localize.js index 17129784..8124a766 100644 --- a/src/localize.js +++ b/src/localize.js @@ -166,12 +166,16 @@ export function msg(parts, ...args) { return getString(parts, args).replace(EXP_REGEX, (_, index) => args[index]); } +const PLACEHOLDER_SVG = getPlaceholder("svg"); +const PLACEHOLDER_MSG = getPlaceholder("msg"); + msg.html = function html(parts, ...args) { const input = getString(parts, args); return compile( input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)), args, + input + PLACEHOLDER_MSG, false, true, ); @@ -183,6 +187,7 @@ msg.svg = function svg(parts, ...args) { return compile( input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)), args, + input + PLACEHOLDER_SVG + PLACEHOLDER_MSG, true, true, ); diff --git a/src/render.js b/src/render.js index a9f53739..b4cf65ff 100644 --- a/src/render.js +++ b/src/render.js @@ -22,35 +22,20 @@ export default function render(key, desc) { }, }; + let shadowOptions = undefined; if (key === "render") { const options = desc.options || {}; - - const shadowOptions = { + shadowOptions = { mode: options.mode || "open", delegatesFocus: options.delegatesFocus || false, }; - - return { - value: (host) => { - const updateDOM = fn(host); - return () => { - const target = host.shadowRoot || host.attachShadow(shadowOptions); - updateDOM(host, target); - return target; - }; - }, - ...rest, - }; - } else { - return { - value: (host) => { - const updateDOM = fn(host); - return () => { - updateDOM(host, host); - return host; - }; - }, - ...rest, - }; } + + return { + value: (host) => { + const updateDOM = fn(host); + return () => updateDOM(host, null, shadowOptions); + }, + ...rest, + }; } diff --git a/src/template/core.js b/src/template/core.js index 42cbde01..b7d11d22 100644 --- a/src/template/core.js +++ b/src/template/core.js @@ -17,7 +17,7 @@ const PLACEHOLDER_REGEXP_EQUAL = new RegExp(`^${PLACEHOLDER_REGEXP_TEXT}$`); const PLACEHOLDER_REGEXP_ALL = new RegExp(PLACEHOLDER_REGEXP_TEXT, "g"); const PLACEHOLDER_REGEXP_ONLY = /^[^A-Za-z]+$/; -function createSignature(parts) { +function createContent(parts) { let signature = parts[0]; let tableMode = false; for (let index = 1; index < parts.length; index += 1) { @@ -190,12 +190,10 @@ function updateStyleElement(target, styles) { } export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { - let template = globalThis.document.createElement("template"); - const parts = {}; - - const signature = isMsg ? rawParts : createSignature(rawParts); + const content = isMsg ? rawParts : createContent(rawParts); - template.innerHTML = isSVG ? `${signature}` : signature; + let template = globalThis.document.createElement("template"); + template.innerHTML = isSVG ? `${content}` : content; if (isSVG) { const svgRoot = template.content.firstChild; @@ -235,7 +233,9 @@ export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { } const compileWalker = createWalker(template.content); + const parts = {}; const notDefinedElements = []; + let compileIndex = 0; let noTranslate = null; @@ -443,12 +443,20 @@ export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { .map((e) => `<${e}>`) .join(", ")} element${ notDefinedElements.length > 1 ? "s" : "" - } found in the template:\n${beautifyTemplateLog(signature, -1)}`, + } found in the template:\n${beautifyTemplateLog(content, -1)}`, ); } const partsKeys = Object.keys(parts); - return function updateTemplateInstance(host, target, args, { styleSheets }) { + return function updateTemplateInstance(host, target, options) { + const { args, shadowOptions, styleSheets } = options; + + if (shadowOptions) { + target = host.shadowRoot || host.attachShadow(shadowOptions); + } else { + target = target || host; + } + let meta = getMeta(target); if (template !== meta.template) { @@ -522,9 +530,12 @@ export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { for (const marker of meta.markers) { const value = args[marker.index]; - const prevValue = meta.prevArgs && meta.prevArgs[marker.index]; + let prevValue = undefined; - if (meta.prevArgs && value === prevValue) continue; + if (meta.prevArgs) { + prevValue = meta.prevArgs[marker.index]; + if (prevValue === value) continue; + } try { marker.fn(host, marker.node, value, prevValue, useLayout); @@ -532,7 +543,7 @@ export function compileTemplate(rawParts, isSVG, isMsg, useLayout) { console.error( `Error while updating template expression in ${stringifyElement( host, - )}:\n${beautifyTemplateLog(signature, marker.index)}`, + )}:\n${beautifyTemplateLog(content, marker.index)}`, ); throw error; diff --git a/src/template/index.js b/src/template/index.js index a096a8fa..fac0d2b2 100644 --- a/src/template/index.js +++ b/src/template/index.js @@ -6,42 +6,44 @@ import * as methods from "./methods.js"; const PLACEHOLDER = getPlaceholder(); const PLACEHOLDER_SVG = getPlaceholder("svg"); -const PLACEHOLDER_MSG = getPlaceholder("msg"); -const PLACEHOLDER_LAYOUT = getPlaceholder("layout"); const templates = new Map(); -export function compile(parts, args, isSVG, isMsg) { - function template(host, target = host) { - let id = isMsg ? parts + PLACEHOLDER_MSG : parts.join(PLACEHOLDER); - if (isSVG) id += PLACEHOLDER_SVG; - const useLayout = template.useLayout; - if (useLayout) id += PLACEHOLDER_LAYOUT; +export function compile(parts, args, id, isSVG, isMsg) { + function fn(host, target, shadowOptions) { + id = id + fn.useLayout; let render = templates.get(id); if (!render) { - render = compileTemplate(parts, isSVG, isMsg, useLayout); + render = compileTemplate(parts, isSVG, isMsg, fn.useLayout); templates.set(id, render); } - if (template.plugins) { - template.plugins.reduce( + fn.shadowOptions = shadowOptions; + + if (fn.plugins) { + fn.plugins.reduce( (acc, plugin) => plugin(acc), - () => render(host, target, args, template), + () => render(host, target, fn), )(host, target); } else { - render(host, target, args, template); + render(host, target, fn); } + + return target; } - return Object.assign(template, methods); + fn.args = args; + return Object.assign(fn, methods); } export function html(parts, ...args) { - return compile(parts, args, false, false); + const id = parts.join(PLACEHOLDER); + return compile(parts, args, id, false, false); } export function svg(parts, ...args) { - return compile(parts, args, true, false); + const id = parts.join(PLACEHOLDER) + PLACEHOLDER_SVG; + return compile(parts, args, id, true, false); } Object.freeze(Object.assign(html, helpers)); diff --git a/src/template/layout.js b/src/template/layout.js index d177604b..564de5df 100644 --- a/src/template/layout.js +++ b/src/template/layout.js @@ -214,7 +214,7 @@ export function inject(target) { const sheet = getCSSStyleSheet(); /* istanbul ignore else */ - if (hasAdoptedStylesheets) { + if (hasAdoptedStylesheets && root.adoptedStyleSheets) { root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet]; } else { if (root === globalThis.document) return; diff --git a/test/spec/layout.js b/test/spec/layout.js index 5975e0b4..7a7cb0cf 100644 --- a/test/spec/layout.js +++ b/test/spec/layout.js @@ -98,12 +98,16 @@ describe("layout:", () => { expect(nestedHost.hasAttribute("layout")).toBe(false); }); - it("uses layout engine for nested templates", () => { + it("uses layout engine for nested templates when parent is in layout mode", () => { html` `(host, host.attachShadow({ mode: "open" })); expect(host.shadowRoot.children[0].className).not.toBeFalsy(); + + const el = document.createElement("div"); + html`
`(el); + expect(el.children[0].getAttribute("layout")).toBe("column"); }); it("uses layout engine for nested array templates", () => {