@vaadin/vaadin-template-renderer
Advanced tools
Comparing version 21.0.0-alpha1 to 21.0.0-alpha10
{ | ||
"name": "@vaadin/vaadin-template-renderer", | ||
"version": "21.0.0-alpha1", | ||
"version": "21.0.0-alpha10", | ||
"description": "vaadin-template-renderer", | ||
@@ -28,3 +28,6 @@ "main": "vaadin-template-renderer.js", | ||
"@esm-bundle/chai": "^4.1.5", | ||
"@vaadin/testing-helpers": "^0.2.1" | ||
"@vaadin/testing-helpers": "^0.2.1", | ||
"@vaadin/vaadin-checkbox": "^21.0.0-alpha10", | ||
"@vaadin/vaadin-grid": "^21.0.0-alpha10", | ||
"@vaadin/vaadin-grid-pro": "^21.0.0-alpha10" | ||
}, | ||
@@ -34,3 +37,3 @@ "publishConfig": { | ||
}, | ||
"gitHead": "8542c7dadc4c86e454a48613f0f2d027dcb5aa86" | ||
"gitHead": "9e75b3416edc041e35720c29a842423a1da66e60" | ||
} |
@@ -5,5 +5,6 @@ import { PolymerElement } from '@polymer/polymer'; | ||
export class Templatizer extends PolymerElement { | ||
static create(template) { | ||
const templatizer = new Templatizer(); | ||
static create(component, template) { | ||
const templatizer = new this(); | ||
templatizer.__template = template; | ||
templatizer.__component = component; | ||
return templatizer; | ||
@@ -20,2 +21,3 @@ } | ||
this.__template = null; | ||
this.__component = null; | ||
this.__TemplateClass = null; | ||
@@ -25,18 +27,41 @@ this.__templateInstances = new Set(); | ||
/** | ||
* If the template instance was created by this templatizer's instance and is still attached to DOM, | ||
* it only re-renders the instance with the new properties. | ||
* Otherwise, it disposes of the old template instance (if it exists), | ||
* creates a new template instance with the given properties and renders the instance's root to the element. | ||
*/ | ||
render(element, properties = {}) { | ||
// If the template instance exists and has been instantiated by this templatizer, | ||
// it only re-renders the instance with the new properties. | ||
if (this.__templateInstances.has(element.__templateInstance)) { | ||
element.__templateInstance.setProperties(properties); | ||
let instance = element.__templateInstance; | ||
if (this.__hasTemplateInstance(instance) && this.__isTemplateInstanceAttachedToDOM(instance)) { | ||
this.__updateProperties(instance, properties); | ||
return; | ||
} | ||
// Otherwise, it instantiates a new template instance | ||
// with the given properties and then renders the result to the element | ||
const templateInstance = this.__createTemplateInstance(properties); | ||
if (this.__hasTemplateInstance(instance)) { | ||
this.__disposeOfTemplateInstance(instance); | ||
} | ||
instance = this.__createTemplateInstance(properties); | ||
element.__templateInstance = instance; | ||
element.innerHTML = ''; | ||
element.__templateInstance = templateInstance; | ||
element.appendChild(templateInstance.root); | ||
element.appendChild(instance.root); | ||
} | ||
/** @private */ | ||
__updateProperties(instance, properties) { | ||
// The Polymer uses `===` to check whether a property is changed and should be re-rendered. | ||
// This means, object properties won't be re-rendered when mutated inside. | ||
// This workaround forces the `item` property to re-render even | ||
// the new item is stricly equal to the old item. | ||
if (instance.item === properties.item) { | ||
instance._setPendingProperty('item'); | ||
} | ||
instance.__properties = properties; | ||
instance.setProperties(properties); | ||
} | ||
/** @private */ | ||
__createTemplateInstance(properties) { | ||
@@ -46,6 +71,30 @@ this.__createTemplateClass(properties); | ||
const instance = new this.__TemplateClass(properties); | ||
instance.__properties = properties; | ||
this.__templateInstances.add(instance); | ||
return instance; | ||
} | ||
/** @private */ | ||
__disposeOfTemplateInstance(instance) { | ||
this.__templateInstances.delete(instance); | ||
} | ||
/** @private */ | ||
__hasTemplateInstance(instance) { | ||
return this.__templateInstances.has(instance); | ||
} | ||
/** @private */ | ||
__isTemplateInstanceAttachedToDOM(instance) { | ||
// The edge-case case when the template is empty | ||
if (instance.children.length === 0) { | ||
return false; | ||
} | ||
return !!instance.children[0].parentElement; | ||
} | ||
/** @private */ | ||
__createTemplateClass(properties) { | ||
@@ -69,2 +118,17 @@ if (this.__TemplateClass) return; | ||
}); | ||
}, | ||
notifyInstanceProp(instance, path, value) { | ||
let rootProperty; | ||
// Extracts the root property name from the path | ||
rootProperty = path.split('.')[0]; | ||
// Capitalizes the property name | ||
rootProperty = rootProperty[0].toUpperCase() + rootProperty.slice(1); | ||
const callback = `_on${rootProperty}PropertyChanged`; | ||
if (this[callback]) { | ||
this[callback](instance, path, value); | ||
} | ||
} | ||
@@ -71,0 +135,0 @@ }); |
@@ -0,19 +1,100 @@ | ||
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js'; | ||
import './vaadin-template-renderer-templatizer.js'; | ||
import './vaadin-template-renderer-grid-templatizer.js'; | ||
import { Templatizer } from './vaadin-template-renderer-templatizer.js'; | ||
import { GridTemplatizer } from './vaadin-template-renderer-grid-templatizer.js'; | ||
function createRenderer(template) { | ||
const templatizer = Templatizer.create(template); | ||
function createRenderer(component, template, TemplatizerClass = Templatizer) { | ||
const templatizer = TemplatizerClass.create(component, template); | ||
return (root, _owner, model) => { | ||
template.__templatizer = templatizer; | ||
template.__templatizer.render(root, model); | ||
const renderer = (root, _owner, model) => { | ||
templatizer.render(root, model); | ||
}; | ||
template.__templatizer = templatizer; | ||
renderer.__templatized = true; | ||
return renderer; | ||
} | ||
function assignRenderer(component, rendererName, renderer) { | ||
const oldRenderer = component[rendererName]; | ||
if (oldRenderer && !oldRenderer.__templatized) { | ||
const tag = component.localName; | ||
throw new Error(`Cannot use both a template and a renderer for <${tag} />.`); | ||
} | ||
component[rendererName] = renderer; | ||
} | ||
function showTemplateWarning(component) { | ||
if (component.__suppressTemplateWarning) { | ||
return; | ||
} | ||
if (component.hasAttribute('suppress-template-warning')) { | ||
return; | ||
} | ||
console.warn( | ||
`WARNING: <template> inside <${component.localName}> is deprecated. Use a renderer function instead (see https://vaad.in/template-renderer)` | ||
); | ||
component.__suppressTemplateWarning = true; | ||
} | ||
function processGridTemplate(grid, template) { | ||
if (template.matches('.row-details')) { | ||
const renderer = createRenderer(grid, template, GridTemplatizer); | ||
assignRenderer(grid, 'rowDetailsRenderer', renderer); | ||
return; | ||
} | ||
} | ||
function processGridColumnTemplate(column, template) { | ||
if (template.matches('.header')) { | ||
const renderer = createRenderer(column, template); | ||
assignRenderer(column, 'headerRenderer', renderer); | ||
return; | ||
} | ||
if (template.matches('.footer')) { | ||
const renderer = createRenderer(column, template); | ||
assignRenderer(column, 'footerRenderer', renderer); | ||
return; | ||
} | ||
if (template.matches('.editor')) { | ||
const renderer = createRenderer(column, template, GridTemplatizer); | ||
assignRenderer(column, 'editModeRenderer', renderer); | ||
return; | ||
} | ||
const renderer = createRenderer(column, template, GridTemplatizer); | ||
assignRenderer(column, 'renderer', renderer); | ||
} | ||
function processTemplate(component, template) { | ||
component.renderer = createRenderer(template); | ||
showTemplateWarning(component); | ||
if (component.__gridElement) { | ||
processGridTemplate(component, template); | ||
return; | ||
} | ||
if (component.__gridColumnElement) { | ||
processGridColumnTemplate(component, template); | ||
return; | ||
} | ||
const renderer = createRenderer(component, template); | ||
assignRenderer(component, 'renderer', renderer); | ||
} | ||
function processTemplates(component) { | ||
[...component.children] | ||
FlattenedNodesObserver.getFlattenedNodes(component) | ||
.filter((child) => { | ||
@@ -32,14 +113,17 @@ return child instanceof HTMLTemplateElement; | ||
const observer = new MutationObserver((mutations) => { | ||
mutations.forEach(({ target }) => { | ||
processTemplates(target); | ||
function observeTemplates(component) { | ||
if (component.__templateObserver) return; | ||
component.__templateObserver = new FlattenedNodesObserver(component, () => { | ||
processTemplates(component); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Public API | ||
*/ | ||
window.Vaadin = window.Vaadin || {}; | ||
window.Vaadin.templateRendererCallback = (component) => { | ||
processTemplates(component); | ||
// The observer stops observing automatically as the component node is removed | ||
observer.observe(component, { childList: true }); | ||
observeTemplates(component); | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
11990
6
304
5